Skip to content

Commit 3a009e6

Browse files
committed
feat(e2e): add JSON file support for extra test cases
Fixes: #123 Signed-off-by: Md Raiyan <alikhurshid842001@gmail.com>
1 parent f6ddb03 commit 3a009e6

5 files changed

Lines changed: 277 additions & 4 deletions

File tree

tests/e2e/json_test_cases.go

Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
// Copyright (c) 2023-2026, Nubificus LTD
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
package urunce2etesting
16+
17+
import (
18+
"encoding/json"
19+
"fmt"
20+
"os"
21+
)
22+
23+
// jsonTestCase is the JSON-serializable form of containerTestArgs.
24+
// TestFunc is omitted — JSON cases always have a nil TestFunc, which
25+
// routes them to runForegroundTest (output matching via ExpectOut).
26+
type jsonTestCase struct {
27+
Image string `json:"image"`
28+
Name string `json:"name"`
29+
Devmapper bool `json:"devmapper"`
30+
Seccomp bool `json:"seccomp"`
31+
UID int `json:"uid"`
32+
GID int `json:"gid"`
33+
Groups []int64 `json:"groups"`
34+
Memory string `json:"memory"`
35+
Cli string `json:"cli"`
36+
Volumes []containerVolume `json:"volumes"`
37+
StaticNet bool `json:"staticNet"`
38+
SideContainers []string `json:"sideContainers"`
39+
Skippable bool `json:"skippable"`
40+
ExpectOut string `json:"expectOut"`
41+
}
42+
43+
func (j jsonTestCase) toContainerTestArgs() containerTestArgs {
44+
groups := j.Groups
45+
if groups == nil {
46+
groups = []int64{}
47+
}
48+
volumes := j.Volumes
49+
if volumes == nil {
50+
volumes = []containerVolume{}
51+
}
52+
sideContainers := j.SideContainers
53+
if sideContainers == nil {
54+
sideContainers = []string{}
55+
}
56+
return containerTestArgs{
57+
Image: j.Image,
58+
Name: j.Name,
59+
Devmapper: j.Devmapper,
60+
Seccomp: j.Seccomp,
61+
UID: j.UID,
62+
GID: j.GID,
63+
Groups: groups,
64+
Memory: j.Memory,
65+
Cli: j.Cli,
66+
Volumes: volumes,
67+
StaticNet: j.StaticNet,
68+
SideContainers: sideContainers,
69+
Skippable: j.Skippable,
70+
ExpectOut: j.ExpectOut,
71+
}
72+
}
73+
74+
// loadTestCasesFromJSON reads a JSON array of test cases from path.
75+
func loadTestCasesFromJSON(path string) ([]containerTestArgs, error) {
76+
data, err := os.ReadFile(path) //nolint:gosec
77+
if err != nil {
78+
return nil, fmt.Errorf("read %s: %w", path, err)
79+
}
80+
81+
var cases []jsonTestCase
82+
if err := json.Unmarshal(data, &cases); err != nil {
83+
return nil, fmt.Errorf("unmarshal %s: %w", path, err)
84+
}
85+
86+
result := make([]containerTestArgs, len(cases))
87+
for i, c := range cases {
88+
result[i] = c.toContainerTestArgs()
89+
}
90+
return result, nil
91+
}
92+
93+
// optionalJSONTestCases returns test cases from path, or an empty slice if
94+
// the file does not exist. Parse errors are reported to stderr.
95+
func optionalJSONTestCases(path string) []containerTestArgs {
96+
if _, err := os.Stat(path); os.IsNotExist(err) {
97+
return []containerTestArgs{}
98+
}
99+
cases, err := loadTestCasesFromJSON(path)
100+
if err != nil {
101+
fmt.Fprintf(os.Stderr, "warning: failed to load JSON test cases from %s: %v\n", path, err) //nolint:errcheck
102+
return []containerTestArgs{}
103+
}
104+
return cases
105+
}

tests/e2e/json_test_cases_test.go

Lines changed: 162 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,162 @@
1+
// Copyright (c) 2023-2026, Nubificus LTD
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
package urunce2etesting
16+
17+
import (
18+
"encoding/json"
19+
"os"
20+
"path/filepath"
21+
"testing"
22+
23+
"github.com/stretchr/testify/assert"
24+
"github.com/stretchr/testify/require"
25+
)
26+
27+
func TestLoadTestCasesFromJSON(t *testing.T) {
28+
tests := []struct {
29+
name string
30+
content string
31+
wantErr bool
32+
wantLen int
33+
checkCase func(t *testing.T, got []containerTestArgs)
34+
}{
35+
{
36+
name: "valid single case",
37+
content: `[{"image":"img:latest","name":"tc","expectOut":"hello","seccomp":true}]`,
38+
wantLen: 1,
39+
checkCase: func(t *testing.T, got []containerTestArgs) {
40+
assert.Equal(t, "img:latest", got[0].Image)
41+
assert.Equal(t, "tc", got[0].Name)
42+
assert.Equal(t, "hello", got[0].ExpectOut)
43+
assert.True(t, got[0].Seccomp)
44+
assert.Nil(t, got[0].TestFunc)
45+
},
46+
},
47+
{
48+
name: "empty array returns empty slice",
49+
content: `[]`,
50+
wantLen: 0,
51+
},
52+
{
53+
name: "nil slices become empty slices",
54+
content: `[{"image":"i","name":"n","expectOut":""}]`,
55+
wantLen: 1,
56+
checkCase: func(t *testing.T, got []containerTestArgs) {
57+
assert.Equal(t, []int64{}, got[0].Groups)
58+
assert.Equal(t, []containerVolume{}, got[0].Volumes)
59+
assert.Equal(t, []string{}, got[0].SideContainers)
60+
},
61+
},
62+
{
63+
name: "volumes are decoded correctly",
64+
content: func() string {
65+
cases := []jsonTestCase{{
66+
Image: "i",
67+
Name: "n",
68+
ExpectOut: "",
69+
Volumes: []containerVolume{{Source: "/src", Dest: "/dst"}},
70+
}}
71+
b, _ := json.Marshal(cases)
72+
return string(b)
73+
}(),
74+
wantLen: 1,
75+
checkCase: func(t *testing.T, got []containerTestArgs) {
76+
require.Len(t, got[0].Volumes, 1)
77+
assert.Equal(t, "/src", got[0].Volumes[0].Source)
78+
assert.Equal(t, "/dst", got[0].Volumes[0].Dest)
79+
},
80+
},
81+
{
82+
name: "file not found returns error",
83+
content: "", // signal to use a non-existent path
84+
wantErr: true,
85+
},
86+
{
87+
name: "invalid JSON returns error",
88+
content: `{not valid json`,
89+
wantErr: true,
90+
},
91+
}
92+
93+
for _, tt := range tests {
94+
t.Run(tt.name, func(t *testing.T) {
95+
t.Parallel()
96+
97+
var path string
98+
if tt.wantErr && tt.content == "" {
99+
path = filepath.Join(t.TempDir(), "nonexistent.json")
100+
} else {
101+
path = filepath.Join(t.TempDir(), "cases.json")
102+
require.NoError(t, os.WriteFile(path, []byte(tt.content), 0o600))
103+
}
104+
105+
got, err := loadTestCasesFromJSON(path)
106+
if tt.wantErr {
107+
assert.Error(t, err)
108+
return
109+
}
110+
require.NoError(t, err)
111+
assert.Len(t, got, tt.wantLen)
112+
if tt.checkCase != nil {
113+
tt.checkCase(t, got)
114+
}
115+
})
116+
}
117+
}
118+
119+
func TestOptionalJSONTestCases(t *testing.T) {
120+
tests := []struct {
121+
name string
122+
setup func(t *testing.T) string
123+
wantLen int
124+
}{
125+
{
126+
name: "absent file returns empty slice",
127+
setup: func(t *testing.T) string {
128+
return filepath.Join(t.TempDir(), "missing.json")
129+
},
130+
wantLen: 0,
131+
},
132+
{
133+
name: "valid file returns cases",
134+
setup: func(t *testing.T) string {
135+
cases := []jsonTestCase{{Image: "img:latest", Name: "tc", ExpectOut: "ok"}}
136+
data, _ := json.Marshal(cases)
137+
path := filepath.Join(t.TempDir(), "cases.json")
138+
require.NoError(t, os.WriteFile(path, data, 0o600))
139+
return path
140+
},
141+
wantLen: 1,
142+
},
143+
{
144+
name: "invalid JSON returns empty slice",
145+
setup: func(t *testing.T) string {
146+
path := filepath.Join(t.TempDir(), "cases.json")
147+
require.NoError(t, os.WriteFile(path, []byte("{bad json"), 0o600))
148+
return path
149+
},
150+
wantLen: 0,
151+
},
152+
}
153+
154+
for _, tt := range tests {
155+
t.Run(tt.name, func(t *testing.T) {
156+
t.Parallel()
157+
path := tt.setup(t)
158+
got := optionalJSONTestCases(path)
159+
assert.Len(t, got, tt.wantLen)
160+
})
161+
}
162+
}

tests/e2e/test_cases.go

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
package urunce2etesting
1616

1717
func nerdctlTestCases() []containerTestArgs {
18-
return []containerTestArgs{
18+
cases := []containerTestArgs{
1919
{
2020
Image: "harbor.nbfc.io/nubificus/urunc/hello-hvt-rumprun:latest",
2121
Name: "Hvt-rumprun-capture-hello",
@@ -457,10 +457,11 @@ func nerdctlTestCases() []containerTestArgs {
457457
TestFunc: pingTest,
458458
},
459459
}
460+
return append(cases, optionalJSONTestCases("testdata/nerdctl_extra_test_cases.json")...)
460461
}
461462

462463
func ctrTestCases() []containerTestArgs {
463-
return []containerTestArgs{
464+
cases := []containerTestArgs{
464465
{
465466
Image: "harbor.nbfc.io/nubificus/urunc/hello-hvt-rumprun-nonet:latest",
466467
Name: "Hvt-rumprun-hello",
@@ -751,10 +752,11 @@ func ctrTestCases() []containerTestArgs {
751752
TestFunc: matchTest,
752753
},
753754
}
755+
return append(cases, optionalJSONTestCases("testdata/ctr_extra_test_cases.json")...)
754756
}
755757

756758
func crictlTestCases() []containerTestArgs {
757-
return []containerTestArgs{
759+
cases := []containerTestArgs{
758760
{
759761
Image: "harbor.nbfc.io/nubificus/urunc/redis-hvt-rumprun-raw:latest",
760762
Name: "Hvt-rumprun-redis",
@@ -964,10 +966,11 @@ func crictlTestCases() []containerTestArgs {
964966
TestFunc: userGroupTest,
965967
},
966968
}
969+
return cases
967970
}
968971

969972
func dockerTestCases() []containerTestArgs {
970-
return []containerTestArgs{
973+
cases := []containerTestArgs{
971974
{
972975
Image: "harbor.nbfc.io/nubificus/urunc/net-spt-mirage:latest",
973976
Name: "Spt-mirage-net",
@@ -1209,4 +1212,5 @@ func dockerTestCases() []containerTestArgs {
12091212
TestFunc: namespaceTest,
12101213
},
12111214
}
1215+
return cases
12121216
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
[]
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
[]

0 commit comments

Comments
 (0)