Skip to content

Commit c28949c

Browse files
authored
Feature: support deleted files in kusion deps cmd
* BugFix: deps subcommand supports deleted files in change list * Update: kusion deps reduce time cost * Update: kusion deps add unit and bench tests * Update go.mod * add more tests * fix lint problem * update kclvm-go version to v0.4.3-alpha.1 * Update deps/options.go: comments/redundant funcs * revert removing toSlice function
1 parent 126b10f commit c28949c

File tree

26 files changed

+525
-95
lines changed

26 files changed

+525
-95
lines changed

go.mod

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,7 @@ require (
7272
k8s.io/component-base v0.21.2
7373
k8s.io/kubectl v0.21.2
7474
kusionstack.io/kcl-plugin v0.4.1-alpha2
75-
kusionstack.io/kclvm-go v0.4.2-alpha.9
75+
kusionstack.io/kclvm-go v0.4.3-alpha.1
7676
sigs.k8s.io/kustomize/api v0.8.11
7777
sigs.k8s.io/kustomize/kustomize/v4 v4.1.2
7878
sigs.k8s.io/kustomize/kyaml v0.11.0

go.sum

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -205,8 +205,6 @@ github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSs
205205
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
206206
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
207207
github.com/daviddengcn/go-colortext v0.0.0-20160507010035-511bcaf42ccd/go.mod h1:dv4zxwHi5C/8AeI+4gX4dCWOIvNi7I6JCSX0HvlKPgE=
208-
github.com/deckarep/golang-set v1.8.0 h1:sk9/l/KqpunDwP7pSjUg0keiOOLEnOBHzykLrsPppp4=
209-
github.com/deckarep/golang-set v1.8.0/go.mod h1:5nI87KwE7wgsBU1F4GKAw2Qod7p5kyS383rP6+o6qqo=
210208
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
211209
github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no=
212210
github.com/didi/gendry v1.7.0 h1:dFR6+TVCnbjvLfNiGN53xInG/C5HqG7u0gfnkF5J/Vo=
@@ -1350,8 +1348,8 @@ k8s.io/utils v0.0.0-20201110183641-67b214c5f920 h1:CbnUZsM497iRC5QMVkHwyl8s2tB3g
13501348
k8s.io/utils v0.0.0-20201110183641-67b214c5f920/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA=
13511349
kusionstack.io/kcl-plugin v0.4.1-alpha2 h1:m43JJhpJSjl/Q3FqghJf5dRIwdXsrJeKxrHhgqLJqbA=
13521350
kusionstack.io/kcl-plugin v0.4.1-alpha2/go.mod h1:VgB7qXVbDGWFOh/qb/yXf75+UrliP5EPXOQUDqBCdAQ=
1353-
kusionstack.io/kclvm-go v0.4.2-alpha.9 h1:c4mDJFlKuP3CbGTtEkFEQidxcsJpGHNCDBtv7eexlH8=
1354-
kusionstack.io/kclvm-go v0.4.2-alpha.9/go.mod h1:LKGVud6Ch0dLLACwPqZQyU4E+NkLAXzveJFQWVb5Pbk=
1351+
kusionstack.io/kclvm-go v0.4.3-alpha.1 h1:wHrTajwtT28e0gpfSk6gkmJLiUHx5vCimS5J0B+89W8=
1352+
kusionstack.io/kclvm-go v0.4.3-alpha.1/go.mod h1:cxsYIWWMiDk7mwWJMm6H9kzNNtoIpOQHgzJBZvHThzo=
13551353
rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=
13561354
rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0=
13571355
rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA=
Lines changed: 292 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,292 @@
1+
package deps
2+
3+
import (
4+
"fmt"
5+
"os"
6+
"path/filepath"
7+
"testing"
8+
9+
"github.com/stretchr/testify/assert"
10+
"kusionstack.io/kusion/pkg/projectstack"
11+
)
12+
13+
var workDir string
14+
15+
func init() {
16+
cwd, err := os.Getwd()
17+
if err != nil {
18+
os.Exit(1)
19+
}
20+
workDir = filepath.Join(cwd, "testdata")
21+
}
22+
23+
func TestNewCmdDeps(t *testing.T) {
24+
cmd := NewCmdDeps()
25+
if err := cmd.Execute(); err == nil {
26+
t.Fatal(err)
27+
}
28+
}
29+
30+
func TestDepsOptions_Validate(t *testing.T) {
31+
tCases := []struct {
32+
name string
33+
only string
34+
direct string
35+
workDir string
36+
focus []string
37+
ignore []string
38+
errMsg string
39+
}{
40+
41+
{
42+
name: "invalid output filter",
43+
errMsg: "invalid output downstream type. supported types: project, stack",
44+
},
45+
{
46+
name: "invalid direct",
47+
only: "project",
48+
errMsg: "invalid output direction of the dependency inspection. supported directions: up, down",
49+
},
50+
{
51+
name: "invalid workdir",
52+
only: "project",
53+
direct: "up",
54+
errMsg: "invalid work dir: stat : no such file or directory",
55+
},
56+
{
57+
name: "invalid focus",
58+
only: "project",
59+
direct: "up",
60+
workDir: workDir,
61+
errMsg: "invalid focus paths. cannot be empty",
62+
},
63+
{
64+
name: "invalid ignore",
65+
only: "project",
66+
direct: "up",
67+
workDir: workDir,
68+
focus: []string{"file.k"},
69+
ignore: []string{"invalid_path.k"},
70+
errMsg: fmt.Sprintf("invalid ignore path. need to be valid relative path from the workdir: stat %s: no such file or directory", filepath.Join(workDir, "invalid_path.k")),
71+
},
72+
{
73+
name: "valid",
74+
only: "project",
75+
direct: "up",
76+
workDir: workDir,
77+
focus: []string{"file.k"},
78+
errMsg: "",
79+
},
80+
}
81+
for _, tc := range tCases {
82+
t.Run(tc.name, func(t *testing.T) {
83+
opt := DepsOptions{
84+
workDir: tc.workDir,
85+
Direct: tc.direct,
86+
Only: tc.only,
87+
Focus: tc.focus,
88+
Ignore: tc.ignore,
89+
}
90+
err := opt.Validate()
91+
if err != nil && err.Error() != tc.errMsg {
92+
t.Fatalf("wrong validate errMsg, actual: %s, expect: %s", err.Error(), tc.errMsg)
93+
}
94+
if err == nil && tc.errMsg != "" {
95+
t.Fatalf("wrong validate result: actual: validate success, expect err: %s", tc.errMsg)
96+
}
97+
})
98+
}
99+
}
100+
101+
func TestDepsOptions_Complete(t *testing.T) {
102+
tCases := []struct {
103+
name string
104+
args []string
105+
completed string
106+
}{
107+
{
108+
name: "omit workdir in args",
109+
completed: filepath.Dir(workDir),
110+
},
111+
{
112+
name: "specify workdir in args",
113+
args: []string{"workdir path"},
114+
completed: "workdir path",
115+
},
116+
}
117+
for _, tc := range tCases {
118+
t.Run(tc.name, func(t *testing.T) {
119+
opt := NewDepsOptions()
120+
opt.Complete(tc.args)
121+
if opt.workDir != tc.completed {
122+
t.Fatalf("wrong completed workdir, actual: %s, expect: %s", opt.workDir, tc.completed)
123+
}
124+
})
125+
}
126+
}
127+
128+
func TestDepsOptions_Run(t *testing.T) {
129+
tCases := []struct {
130+
workDir string
131+
direct string
132+
only string
133+
}{
134+
{
135+
workDir: workDir,
136+
direct: "up",
137+
},
138+
{
139+
workDir: workDir,
140+
direct: "down",
141+
only: "project",
142+
},
143+
}
144+
for _, tc := range tCases {
145+
t.Run(tc.direct, func(t *testing.T) {
146+
opt := DepsOptions{
147+
workDir: tc.workDir,
148+
Direct: tc.direct,
149+
Only: tc.only,
150+
}
151+
if err := opt.Run(); err != nil {
152+
t.Fatal(err)
153+
}
154+
})
155+
}
156+
}
157+
158+
func TestDepsOptions_Run2(t *testing.T) {
159+
opt := DepsOptions{
160+
workDir: workDir,
161+
Direct: "up",
162+
Focus: []string{
163+
"appops/projectC/dev/main.k",
164+
},
165+
}
166+
err := opt.Run()
167+
if err != nil {
168+
t.Fatal(err)
169+
}
170+
}
171+
172+
var downstreamTestCases = []struct {
173+
name string
174+
focus []string
175+
downStreamProjects []string
176+
ignore []string
177+
}{
178+
{
179+
name: "change base and entrance files",
180+
focus: []string{
181+
"base/frontend/container/container_port.k",
182+
"appops/projectA/base/base.k",
183+
"appops/projectA/dev/main.k",
184+
"appops/projectA/dev/datafile.sql",
185+
},
186+
downStreamProjects: []string{
187+
"appops/projectA",
188+
"appops/projectB",
189+
"appops/projectC",
190+
},
191+
},
192+
{
193+
name: "change common base: container port",
194+
focus: []string{"base/frontend/container/container_port.k"},
195+
downStreamProjects: []string{
196+
"appops/projectA",
197+
"appops/projectB",
198+
"appops/projectC",
199+
},
200+
},
201+
{
202+
name: "change server render base",
203+
focus: []string{"base/render/server/server_render.k"},
204+
downStreamProjects: []string{
205+
"appops/projectA",
206+
"appops/projectC",
207+
},
208+
},
209+
{
210+
name: "change job render base",
211+
focus: []string{"base/render/job/job_render.k"},
212+
downStreamProjects: []string{
213+
"appops/projectB",
214+
},
215+
},
216+
{
217+
name: "ignore job render base",
218+
focus: []string{
219+
"base/render/server/server_render.k",
220+
"base/render/job/job_render.k",
221+
},
222+
ignore: []string{
223+
"base/render/job/job_render.k",
224+
},
225+
downStreamProjects: []string{
226+
"appops/projectA",
227+
"appops/projectC",
228+
},
229+
},
230+
{
231+
name: "only change entrance files",
232+
focus: []string{
233+
"appops/projectA/base/base.k",
234+
"appops/projectA/dev/main.k",
235+
"appops/projectB/dev/main.k",
236+
},
237+
downStreamProjects: []string{
238+
"appops/projectA",
239+
"appops/projectB",
240+
},
241+
},
242+
{
243+
name: "only change data files",
244+
focus: []string{
245+
"appops/projectA/dev/datafile.sql",
246+
},
247+
downStreamProjects: []string{
248+
"appops/projectA",
249+
},
250+
},
251+
{
252+
name: "delete files",
253+
focus: []string{
254+
"appops/projectC/dev/non_exist.sql",
255+
"base/frontend/non_exist/non_exist.k",
256+
},
257+
downStreamProjects: []string{
258+
"appops/projectC",
259+
},
260+
},
261+
}
262+
263+
func TestFindDownStreams(t *testing.T) {
264+
projects, err := projectstack.FindAllProjectsFrom(workDir)
265+
if err != nil {
266+
t.Fatal(err)
267+
}
268+
for _, tc := range downstreamTestCases {
269+
t.Run(tc.name, func(t *testing.T) {
270+
result, err := findDownStreams(workDir, projects, toSet(tc.focus), toSet(tc.ignore), true)
271+
if err != nil {
272+
t.Fatal(err)
273+
}
274+
assert.ElementsMatch(t, result.toSlice(), tc.downStreamProjects, "test result mismatch")
275+
})
276+
}
277+
}
278+
279+
func BenchmarkDownStream(b *testing.B) {
280+
tc := downstreamTestCases[0]
281+
for i := 0; i < b.N; i++ {
282+
projects, err := projectstack.FindAllProjectsFrom(workDir)
283+
if err != nil {
284+
b.Fatal(err)
285+
}
286+
result, err := findDownStreams(workDir, projects, toSet(tc.focus), toSet(tc.ignore), true)
287+
if err != nil {
288+
b.Fatal(err)
289+
}
290+
assert.ElementsMatch(b, result.toSlice(), tc.downStreamProjects, "test result mismatch")
291+
}
292+
}

0 commit comments

Comments
 (0)