Monorepo for Tangled
tangled.org
1package pages
2
3import (
4 "bytes"
5 "io"
6 "log/slog"
7 "strings"
8 "testing"
9
10 "tangled.org/core/appview/config"
11 "tangled.org/core/appview/models"
12 "tangled.org/core/appview/pages/repoinfo"
13 "tangled.org/core/patchutil"
14 "tangled.org/core/types"
15)
16
17func TestPullComposeTemplatesParse(t *testing.T) {
18 cfg := &config.Config{}
19 p := NewPages(cfg, nil, nil, nil, slog.New(slog.NewTextHandler(io.Discard, nil)))
20
21 cases := []struct {
22 name string
23 stack []string
24 }{
25 {"new.html via repo base", []string{"layouts/base", "layouts/repobase", "repo/pulls/new"}},
26 {"pullComposeHost", []string{"repo/pulls/fragments/pullComposeHost"}},
27 {"pullStepSource", []string{"repo/pulls/fragments/pullStepSource"}},
28 {"pullStepReview", []string{"repo/pulls/fragments/pullStepReview"}},
29 {"pullStepDetails", []string{"repo/pulls/fragments/pullStepDetails"}},
30 {"pullCompareForks", []string{"repo/pulls/fragments/pullCompareForks"}},
31 {"pullCompareBranches", []string{"repo/pulls/fragments/pullCompareBranches"}},
32 {"pullCompareForksBranches", []string{"repo/pulls/fragments/pullCompareForksBranches"}},
33 }
34
35 for _, c := range cases {
36 t.Run(c.name, func(t *testing.T) {
37 if _, err := p.rawParse(c.stack...); err != nil {
38 t.Fatalf("parse %v: %v", c.stack, err)
39 }
40 })
41 }
42}
43
44func TestPullComposeHostRender(t *testing.T) {
45 cfg := &config.Config{}
46 p := NewPages(cfg, nil, nil, nil, slog.New(slog.NewTextHandler(io.Discard, nil)))
47
48 base := RepoNewPullParams{
49 RepoInfo: repoinfo.RepoInfo{
50 OwnerDid: "did:plc:test",
51 Name: "test-repo",
52 },
53 }
54
55 for _, source := range []Source{"", SourceBranch, SourceFork, SourcePatch} {
56 for _, stacked := range []bool{false, true} {
57 if source == SourcePatch && stacked {
58 continue
59 }
60 params := base
61 params.Source = source
62 params.IsStacked = stacked
63 name := string(source)
64 if name == "" {
65 name = "default"
66 }
67 if stacked {
68 name += "-stacked"
69 }
70 t.Run(name, func(t *testing.T) {
71 if err := p.PullComposeHostFragment(io.Discard, params); err != nil {
72 t.Fatalf("render source=%q stacked=%v: %v", source, stacked, err)
73 }
74 })
75 }
76 }
77}
78
79func TestPullComposeHostRenderWithData(t *testing.T) {
80 cfg := &config.Config{}
81 p := NewPages(cfg, nil, nil, nil, slog.New(slog.NewTextHandler(io.Discard, nil)))
82
83 sampleBranches := []types.Branch{
84 {Reference: types.Reference{Name: "feature"}},
85 {Reference: types.Reference{Name: "main"}, IsDefault: true},
86 }
87
88 formatPatch := `From 1111111111111111111111111111111111111111 Mon Sep 11 00:00:00 2001
89From: Test <test@best.fest>
90Date: Tue, 1 Jan 2020 00:00:00 +0000
91Subject: [PATCH] example commit
92
93---
94 a.txt | 1 +
95 1 file changed, 1 insertion(+)
96
97diff --git a/a.txt b/a.txt
98index 0000000..1111111 100644
99--- a/a.txt
100+++ b/a.txt
101@@ -0,0 +1 @@
102+hello
103`
104 patches, err := patchutil.ExtractPatches(formatPatch)
105 if err != nil {
106 t.Fatalf("extract patches: %v", err)
107 }
108 comparison := &types.RepoFormatPatchResponse{
109 FormatPatchRaw: formatPatch,
110 FormatPatch: patches,
111 }
112 diff := patchutil.AsNiceDiff(formatPatch, "main")
113
114 params := RepoNewPullParams{
115 RepoInfo: repoinfo.RepoInfo{
116 OwnerDid: "did:plc:test",
117 Name: "test-repo",
118 },
119 Branches: sampleBranches,
120 SourceBranches: []types.Branch{sampleBranches[0]},
121 ForkBranches: []types.Branch{sampleBranches[0]},
122 Source: SourceBranch,
123 SourceBranch: "feature",
124 TargetBranch: "main",
125 Comparison: comparison,
126 Diff: &diff,
127 }
128
129 if err := p.PullComposeHostFragment(io.Discard, params); err != nil {
130 t.Fatalf("render with data: %v", err)
131 }
132
133 params.IsStacked = true
134 if err := p.PullComposeHostFragment(io.Discard, params); err != nil {
135 t.Fatalf("render stacked: %v", err)
136 }
137
138 params.PrefillError = "branch not found"
139 params.Comparison = nil
140 params.Diff = nil
141 if err := p.PullComposeHostFragment(io.Discard, params); err != nil {
142 t.Fatalf("render with prefill error: %v", err)
143 }
144
145 bugDef := &models.LabelDefinition{
146 Did: "did:plc:test",
147 Rkey: "bug",
148 Name: "bug",
149 ValueType: models.ValueType{Type: models.ConcreteTypeNull},
150 Scope: []string{"sh.tangled.repo.pull"},
151 }
152 priorityDef := &models.LabelDefinition{
153 Did: "did:plc:test",
154 Rkey: "priority",
155 Name: "priority",
156 ValueType: models.ValueType{Type: models.ConcreteTypeString, Enum: []string{"low", "med", "high"}},
157 Scope: []string{"sh.tangled.repo.pull"},
158 }
159 assigneeDef := &models.LabelDefinition{
160 Did: "did:plc:test",
161 Rkey: "assignee",
162 Name: "assignee",
163 ValueType: models.ValueType{Type: models.ConcreteTypeString, Format: models.ValueTypeFormatDid},
164 Scope: []string{"sh.tangled.repo.pull"},
165 Multiple: true,
166 }
167 labelDefs := map[string]*models.LabelDefinition{
168 bugDef.AtUri().String(): bugDef,
169 priorityDef.AtUri().String(): priorityDef,
170 assigneeDef.AtUri().String(): assigneeDef,
171 }
172
173 pushRepoInfo := repoinfo.RepoInfo{
174 OwnerDid: "did:plc:test",
175 Name: "test-repo",
176 Roles: repoinfo.RolesInRepo{Roles: []string{"repo:push"}},
177 }
178 params = RepoNewPullParams{
179 RepoInfo: pushRepoInfo,
180 Branches: sampleBranches,
181 SourceBranches: []types.Branch{sampleBranches[0]},
182 Source: SourceBranch,
183 SourceBranch: "feature",
184 TargetBranch: "main",
185 Comparison: comparison,
186 Diff: &diff,
187 LabelDefs: labelDefs,
188 LabelState: models.NewLabelState(),
189 }
190 if err := p.PullComposeHostFragment(io.Discard, params); err != nil {
191 t.Fatalf("render with labels: %v", err)
192 }
193
194 params.IsStacked = true
195 if err := p.PullComposeHostFragment(io.Discard, params); err != nil {
196 t.Fatalf("render stacked with labels: %v", err)
197 }
198
199 params.StackedDiffs = []StackedDiff{{
200 Diff: &diff,
201 Opts: types.DiffOpts{Split: true, RefreshUrl: "/r", Target: "#stack-diff-x", Field: "stackSplit[x]"},
202 }}
203 if err := p.PullComposeHostFragment(io.Discard, params); err != nil {
204 t.Fatalf("render stacked with per-commit diffs: %v", err)
205 }
206}
207
208func TestPullComposeLabelStateRoundTrip(t *testing.T) {
209 cfg := &config.Config{}
210 p := NewPages(cfg, nil, nil, nil, slog.New(slog.NewTextHandler(io.Discard, nil)))
211
212 sampleBranches := []types.Branch{
213 {Reference: types.Reference{Name: "feature"}},
214 {Reference: types.Reference{Name: "main"}, IsDefault: true},
215 }
216
217 bugDef := &models.LabelDefinition{
218 Did: "did:plc:test", Rkey: "bug", Name: "bug",
219 ValueType: models.ValueType{Type: models.ConcreteTypeNull},
220 Scope: []string{"sh.tangled.repo.pull"},
221 }
222 priorityDef := &models.LabelDefinition{
223 Did: "did:plc:test", Rkey: "priority", Name: "priority",
224 ValueType: models.ValueType{Type: models.ConcreteTypeString, Enum: []string{"low", "med", "high"}},
225 Scope: []string{"sh.tangled.repo.pull"},
226 }
227 bugKey := bugDef.AtUri().String()
228 priorityKey := priorityDef.AtUri().String()
229 labelDefs := map[string]*models.LabelDefinition{
230 bugKey: bugDef,
231 priorityKey: priorityDef,
232 }
233
234 state := models.NewLabelState()
235 actx := &models.LabelApplicationCtx{Defs: labelDefs}
236 for _, op := range []models.LabelOp{
237 {OperandKey: bugKey, OperandValue: "null", Operation: models.LabelOperationAdd},
238 {OperandKey: priorityKey, OperandValue: "high", Operation: models.LabelOperationAdd},
239 } {
240 if err := actx.ApplyLabelOp(state, op); err != nil {
241 t.Fatalf("seed state: %v", err)
242 }
243 }
244
245 formatPatch := `From 1111111111111111111111111111111111111111 Mon Sep 11 00:00:00 2001
246From: Test <test@best.fest>
247Date: Tue, 1 Jan 2020 00:00:00 +0000
248Subject: [PATCH] example commit
249
250---
251 a.txt | 1 +
252 1 file changed, 1 insertion(+)
253
254diff --git a/a.txt b/a.txt
255index 0000000..1111111 100644
256--- a/a.txt
257+++ b/a.txt
258@@ -0,0 +1 @@
259+hello
260`
261 patches, err := patchutil.ExtractPatches(formatPatch)
262 if err != nil {
263 t.Fatalf("extract patches: %v", err)
264 }
265 comparison := &types.RepoFormatPatchResponse{
266 FormatPatchRaw: formatPatch,
267 FormatPatch: patches,
268 }
269
270 params := RepoNewPullParams{
271 RepoInfo: repoinfo.RepoInfo{
272 OwnerDid: "did:plc:test",
273 Name: "test-repo",
274 Roles: repoinfo.RolesInRepo{Roles: []string{"repo:push"}},
275 },
276 Branches: sampleBranches,
277 SourceBranches: []types.Branch{sampleBranches[0]},
278 Source: SourceBranch,
279 SourceBranch: "feature",
280 TargetBranch: "main",
281 Comparison: comparison,
282 LabelDefs: labelDefs,
283 LabelState: state,
284 }
285
286 var buf bytes.Buffer
287 if err := p.PullComposeHostFragment(&buf, params); err != nil {
288 t.Fatalf("render: %v", err)
289 }
290 out := buf.String()
291 for _, want := range []string{
292 `value="null" checked`,
293 `value="high" checked`,
294 } {
295 if !strings.Contains(out, want) {
296 t.Errorf("missing pre-selection %q", want)
297 }
298 }
299}
300
301func TestParseSource(t *testing.T) {
302 cases := []struct {
303 in string
304 want Source
305 wantOk bool
306 }{
307 {"branch", SourceBranch, true},
308 {"BRANCH", SourceBranch, true},
309 {"fork", SourceFork, true},
310 {"patch", SourcePatch, true},
311 {"", "", false},
312 {"method", "", false},
313 {"strategy", "", false},
314 {"unknown", "", false},
315 }
316 for _, c := range cases {
317 got, ok := ParseSource(c.in)
318 if got != c.want || ok != c.wantOk {
319 t.Errorf("ParseSource(%q) = %q, %v; want %q, %v", c.in, got, ok, c.want, c.wantOk)
320 }
321 }
322}