|
1 | 1 | package cmd |
2 | 2 |
|
3 | 3 | import ( |
4 | | - "fmt" |
5 | 4 | "strings" |
6 | 5 | "testing" |
7 | | - |
8 | | - "github.com/google/go-cmp/cmp" |
9 | 6 | ) |
10 | 7 |
|
11 | | -type runResult struct { |
12 | | - out string |
13 | | - err error |
14 | | -} |
15 | | -type waitForCommitMockRunner struct { |
16 | | - mockRunner |
17 | | - runResults map[string][]runResult |
18 | | -} |
19 | | - |
20 | | -func (mr *waitForCommitMockRunner) Run(name string, args []string, env map[string]string) (string, error) { |
21 | | - mr.calledMethods["Run"] = append(mr.calledMethods["Run"], ParameterList{mr.cwd, name, args, env}) |
22 | | - cmd := fmt.Sprintf("%s %s %v %s", mr.cwd, name, args, sortedEnvString(env)) |
23 | | - if result, ok := mr.runResults[cmd]; ok { |
24 | | - if result == nil { |
25 | | - return "", fmt.Errorf("no results") |
26 | | - } |
27 | | - headRet := result[0] |
28 | | - result = result[1:] |
29 | | - mr.runResults[cmd] = result |
30 | | - return headRet.out, headRet.err |
31 | | - } |
32 | | - return "", fmt.Errorf("unknown command %s\n", cmd) |
33 | | -} |
34 | | - |
35 | 8 | func TestExecWaitForCommit(t *testing.T) { |
36 | 9 | testCases := []struct { |
37 | 10 | name string |
38 | 11 | baseBranch string |
39 | | - calledMethods []string |
40 | | - runResults map[string][]runResult |
| 12 | + syncBranch string |
| 13 | + setupRemote func(sb *sandbox, shas []string, targetBranch string) |
| 14 | + waitFunc func(sb *sandbox, shas []string, targetBranch string, calls int) |
| 15 | + expectedCalls int |
| 16 | + expectErr bool |
41 | 17 | }{ |
42 | 18 | { |
43 | | - name: "base branch is main", |
| 19 | + name: "already in sync branch", |
44 | 20 | baseBranch: "main", |
45 | | - calledMethods: []string{ |
46 | | - "git merge-base --is-ancestor sha origin/sync-branch", |
47 | | - "git rev-parse --short origin/sync-branch", |
48 | | - "git rev-parse --short sha~", |
49 | | - "git fetch origin sync-branch", |
50 | | - "git rev-parse --short origin/sync-branch", |
51 | | - "git rev-parse --short sha~", |
| 21 | + syncBranch: "sync-branch", |
| 22 | + setupRemote: func(sb *sandbox, shas []string, targetBranch string) { |
| 23 | + sb.Runner.MustRun("git", []string{"push", "origin", shas[3] + ":refs/heads/" + targetBranch}, nil) |
52 | 24 | }, |
53 | | - runResults: map[string][]runResult{ |
54 | | - "cwd git [merge-base --is-ancestor sha origin/sync-branch] map[]": { |
55 | | - { |
56 | | - out: "", |
57 | | - err: fmt.Errorf("exit error 1"), |
58 | | - }, |
59 | | - }, |
60 | | - "cwd git [rev-parse --short origin/sync-branch] map[]": { |
61 | | - { |
62 | | - out: "sha-x", |
63 | | - }, |
64 | | - { |
65 | | - out: "sha-z", |
66 | | - }, |
67 | | - }, |
68 | | - "cwd git [rev-parse --short sha~] map[]": { |
69 | | - { |
70 | | - out: "sha-y", |
71 | | - }, |
72 | | - { |
73 | | - out: "sha-z", |
74 | | - }, |
75 | | - }, |
76 | | - "cwd git [fetch origin sync-branch] map[]": { |
77 | | - { |
78 | | - out: "", |
79 | | - }, |
80 | | - }, |
| 25 | + waitFunc: func(sb *sandbox, shas []string, targetBranch string, calls int) { |
81 | 26 | }, |
| 27 | + expectedCalls: 0, |
| 28 | + expectErr: false, |
| 29 | + }, |
| 30 | + { |
| 31 | + name: "needs update once", |
| 32 | + baseBranch: "main", |
| 33 | + syncBranch: "sync-branch", |
| 34 | + setupRemote: func(sb *sandbox, shas []string, targetBranch string) { |
| 35 | + sb.Runner.MustRun("git", []string{"push", "origin", shas[1] + ":refs/heads/" + targetBranch}, nil) |
| 36 | + }, |
| 37 | + waitFunc: func(sb *sandbox, shas []string, targetBranch string, calls int) { |
| 38 | + sb.Runner.MustRun("git", []string{"push", "origin", shas[2] + ":refs/heads/" + targetBranch}, nil) |
| 39 | + }, |
| 40 | + expectedCalls: 1, |
| 41 | + expectErr: false, |
82 | 42 | }, |
83 | 43 | { |
84 | 44 | name: "base branch is not main", |
85 | 45 | baseBranch: "feature-branch", |
86 | | - calledMethods: []string{ |
87 | | - "git merge-base --is-ancestor sha origin/sync-branch-feature-branch", |
88 | | - "git rev-parse --short origin/sync-branch-feature-branch", |
89 | | - "git rev-parse --short sha~", |
90 | | - "git fetch origin sync-branch-feature-branch", |
91 | | - "git rev-parse --short origin/sync-branch-feature-branch", |
92 | | - "git rev-parse --short sha~", |
| 46 | + syncBranch: "sync-branch", |
| 47 | + setupRemote: func(sb *sandbox, shas []string, targetBranch string) { |
| 48 | + sb.Runner.MustRun("git", []string{"push", "origin", shas[1] + ":refs/heads/" + targetBranch}, nil) |
93 | 49 | }, |
94 | | - runResults: map[string][]runResult{ |
95 | | - "cwd git [merge-base --is-ancestor sha origin/sync-branch-feature-branch] map[]": { |
96 | | - { |
97 | | - out: "", |
98 | | - err: fmt.Errorf("exit error 1"), |
99 | | - }, |
100 | | - }, |
101 | | - "cwd git [rev-parse --short origin/sync-branch-feature-branch] map[]": { |
102 | | - { |
103 | | - out: "sha-x", |
104 | | - }, |
105 | | - { |
106 | | - out: "sha-z", |
107 | | - }, |
108 | | - }, |
109 | | - "cwd git [rev-parse --short sha~] map[]": { |
110 | | - { |
111 | | - out: "sha-y", |
112 | | - }, |
113 | | - { |
114 | | - out: "sha-z", |
115 | | - }, |
116 | | - }, |
117 | | - "cwd git [fetch origin sync-branch-feature-branch] map[]": { |
118 | | - { |
119 | | - out: "", |
120 | | - }, |
121 | | - }, |
| 50 | + waitFunc: func(sb *sandbox, shas []string, targetBranch string, calls int) { |
| 51 | + sb.Runner.MustRun("git", []string{"push", "origin", shas[2] + ":refs/heads/" + targetBranch}, nil) |
122 | 52 | }, |
| 53 | + expectedCalls: 1, |
| 54 | + expectErr: false, |
| 55 | + }, |
| 56 | + { |
| 57 | + name: "needs update multiple times", |
| 58 | + baseBranch: "main", |
| 59 | + syncBranch: "sync-branch", |
| 60 | + setupRemote: func(sb *sandbox, shas []string, targetBranch string) { |
| 61 | + sb.Runner.MustRun("git", []string{"push", "origin", shas[0] + ":refs/heads/" + targetBranch}, nil) |
| 62 | + }, |
| 63 | + waitFunc: func(sb *sandbox, shas []string, targetBranch string, calls int) { |
| 64 | + if calls == 0 { |
| 65 | + sb.Runner.MustRun("git", []string{"push", "origin", shas[1] + ":refs/heads/" + targetBranch}, nil) |
| 66 | + } else if calls == 1 { |
| 67 | + sb.Runner.MustRun("git", []string{"push", "origin", shas[2] + ":refs/heads/" + targetBranch}, nil) |
| 68 | + } |
| 69 | + }, |
| 70 | + expectedCalls: 2, |
| 71 | + expectErr: false, |
| 72 | + }, |
| 73 | + { |
| 74 | + name: "error case bad sync branch", |
| 75 | + baseBranch: "main", |
| 76 | + syncBranch: "bad-branch", |
| 77 | + setupRemote: func(sb *sandbox, shas []string, targetBranch string) { |
| 78 | + }, |
| 79 | + waitFunc: func(sb *sandbox, shas []string, targetBranch string, calls int) {}, |
| 80 | + expectedCalls: 0, |
| 81 | + expectErr: true, |
123 | 82 | }, |
124 | 83 | } |
125 | 84 |
|
126 | | - for _, test := range testCases { |
127 | | - t.Run(test.name, func(t *testing.T) { |
128 | | - origWaitFunc := waitFunc |
129 | | - defer func() { |
130 | | - waitFunc = origWaitFunc |
131 | | - }() |
132 | | - waitFunc = func() {} |
| 85 | + for _, tc := range testCases { |
| 86 | + tc := tc |
| 87 | + t.Run(tc.name, func(t *testing.T) { |
| 88 | + t.Parallel() |
| 89 | + sb := newSandbox(t) |
| 90 | + originDir := t.TempDir() |
| 91 | + sb.Runner.MustRun("git", []string{"init", "--bare", "-b", "main", originDir}, nil) |
| 92 | + sb.Runner.MustRun("git", []string{"remote", "add", "origin", originDir}, nil) |
133 | 93 |
|
134 | | - runner := &waitForCommitMockRunner{ |
135 | | - mockRunner: mockRunner{ |
136 | | - cwd: "cwd", |
137 | | - calledMethods: make(map[string][]ParameterList), |
138 | | - }, |
139 | | - runResults: test.runResults, |
| 94 | + var shas []string |
| 95 | + for _, msg := range []string{"Commit A", "Commit B", "Commit C", "Commit D"} { |
| 96 | + sb.Runner.MustRun("git", []string{"commit", "--allow-empty", "-m", msg}, nil) |
| 97 | + shas = append(shas, strings.TrimSpace(sb.Runner.MustRun("git", []string{"rev-parse", "HEAD"}, nil))) |
140 | 98 | } |
141 | 99 |
|
142 | | - err := execWaitForCommit("sync-branch", test.baseBranch, "sha", runner) |
143 | | - if err != nil { |
144 | | - t.Fatalf("execWaitForCommit = %s, want = nil", err) |
145 | | - } |
| 100 | + targetBranch := getSyncBranch(tc.syncBranch, tc.baseBranch) |
| 101 | + tc.setupRemote(sb, shas, targetBranch) |
| 102 | + sb.Runner.MustRun("git", []string{"fetch", "origin"}, nil) |
146 | 103 |
|
147 | | - got, ok := runner.Calls("Run") |
148 | | - if !ok { |
149 | | - t.Fatalf("execWaitForCommit() got no calls") |
| 104 | + calls := 0 |
| 105 | + testWaitFunc := func() { |
| 106 | + tc.waitFunc(sb, shas, targetBranch, calls) |
| 107 | + calls++ |
150 | 108 | } |
151 | | - var want []ParameterList |
152 | | - for _, cmd := range test.calledMethods { |
153 | | - words := strings.Split(cmd, " ") |
154 | | - if len(words) > 0 { |
155 | | - want = append(want, []any{"cwd", words[0], words[1:], map[string]string(nil)}) |
| 109 | + |
| 110 | + err := execWaitForCommit(tc.syncBranch, tc.baseBranch, shas[3], sb.Runner, testWaitFunc) |
| 111 | + |
| 112 | + if tc.expectErr { |
| 113 | + if err == nil { |
| 114 | + t.Fatalf("expected error, got nil") |
| 115 | + } |
| 116 | + } else { |
| 117 | + if err != nil { |
| 118 | + t.Fatalf("execWaitForCommit() failed: %v", err) |
156 | 119 | } |
157 | 120 | } |
158 | | - if diff := cmp.Diff(want, got); diff != "" { |
159 | | - t.Fatalf("execWaitForCommit() executed commands diff = %s\n want = %+v, got = %+v", diff, want, got) |
| 121 | + |
| 122 | + if calls != tc.expectedCalls { |
| 123 | + t.Fatalf("expected waitFunc to be called %d times, got %d", tc.expectedCalls, calls) |
160 | 124 | } |
161 | 125 | }) |
162 | 126 | } |
|
0 commit comments