v0.29.0 55fcc8f

window_test.go RawHistoryFolder

1// SPDX-License-Identifier: AGPL-3.0-or-later
2
3package usecase_test
4
5import (
6	"fmt"
7	"strings"
8	"testing"
9
10	"github.com/stretchr/testify/assert"
11	"github.com/stretchr/testify/require"
12
13	"bnema.dev/ermite/internal/domain"
14	"bnema.dev/ermite/internal/ports"
15	"bnema.dev/ermite/internal/usecase"
16)
17
18func TestWindowLimits(t *testing.T) {
19	def := domain.DefaultDiffLimits()
20	win := def
21	win.MaxFiles, win.From, win.To, win.SkipGenerated = 0, 2, 5, true
22	open := win
23	open.From, open.To = 7, 0
24
25	tests := []struct {
26		name string
27		w    *ports.FileWindow
28		want domain.DiffLimits
29		bad  bool
30	}{
31		{name: "nil is the default", want: def},
32		{name: "a window", w: &ports.FileWindow{From: 2, To: 5}, want: win},
33		{name: "an open window", w: &ports.FileWindow{From: 7}, want: open},
34		{name: "full", w: &ports.FileWindow{From: 3, To: 4, Full: true}, want: domain.OneFileDiffLimits(3)},
35		{name: "negative from", w: &ports.FileWindow{From: -1}, bad: true},
36		{name: "negative to", w: &ports.FileWindow{To: -1}, bad: true},
37		{name: "to before from", w: &ports.FileWindow{From: 5, To: 3}, bad: true},
38		{name: "full without to", w: &ports.FileWindow{From: 3, Full: true}, bad: true},
39		{name: "full of two files", w: &ports.FileWindow{From: 3, To: 5, Full: true}, bad: true},
40		{name: "full of no file", w: &ports.FileWindow{From: 3, To: 3, Full: true}, bad: true},
41	}
42	for _, tt := range tests {
43		t.Run(tt.name, func(t *testing.T) {
44			got, err := usecase.WindowLimits(tt.w)
45			if tt.bad {
46				require.ErrorIs(t, err, domain.ErrInvalid)
47				return
48			}
49			require.NoError(t, err)
50			assert.Equal(t, tt.want, got)
51		})
52	}
53}
54
55func TestGetMergeRequestFullAndGenerated(t *testing.T) {
56	r := newMRRig(t)
57	r.seed()
58	r.sh("checkout", "-q", "-B", "feat", "main")
59	r.commit(author, "a.txt", "one\n", "a")
60	r.commit(author, "go.sum", "x y z\n", "sum")
61	r.push("feat")
62	n := r.open("feat").Number
63
64	t.Run("full with a wider window is invalid", func(t *testing.T) {
65		_, err := r.mrs.Get(r.ctx, reader, r.repo, n, ports.GetMergeRequestOptions{Files: &ports.FileWindow{From: 0, To: 2, Full: true}})
66		require.ErrorIs(t, err, domain.ErrInvalid)
67	})
68
69	t.Run("a windowed read collapses a generated file, a nil read does not", func(t *testing.T) {
70		d, err := r.mrs.Get(r.ctx, reader, r.repo, n, ports.GetMergeRequestOptions{Files: &ports.FileWindow{From: 0, To: 10}})
71		require.NoError(t, err)
72		byPath := map[string]domain.FileDiff{}
73		for _, f := range d.Diff.Files {
74			byPath[f.NewPath] = f
75		}
76		assert.Equal(t, domain.OmitGenerated, byPath["go.sum"].Omitted)
77		assert.Empty(t, byPath["go.sum"].Hunks)
78		assert.Equal(t, domain.OmitNone, byPath["a.txt"].Omitted)
79		assert.NotEmpty(t, byPath["a.txt"].Hunks)
80
81		d, err = r.mrs.Get(r.ctx, reader, r.repo, n, ports.GetMergeRequestOptions{})
82		require.NoError(t, err)
83		for _, f := range d.Diff.Files {
84			assert.Equal(t, domain.OmitNone, f.Omitted, f.NewPath)
85			assert.NotEmpty(t, f.Hunks, f.NewPath)
86		}
87	})
88}
89
90func bigContent(n int) string {
91	var b strings.Builder
92	for i := range n {
93		fmt.Fprintf(&b, "line %d\n", i)
94	}
95	return b.String()
96}
97
98func TestCheckLineBeyondFileLimit(t *testing.T) {
99	r := newMRRig(t)
100	r.seed()
101	r.sh("checkout", "-q", "-B", "feat", "main")
102	r.commit(author, "big.txt", bigContent(3000), "big")
103	r.commit(author, "go.sum", "h1:x\nh1:y\n", "sum")
104	r.push("feat")
105	n := r.open("feat").Number
106
107	add := func(path string, line int) error {
108		_, err := r.mrs.AddComment(r.ctx, mrW, r.repo, n, ports.AddCommentParams{Path: path, Line: line, Side: domain.SideNew, Text: "c"})
109		return err
110	}
111	require.NoError(t, add("big.txt", 1))
112	require.NoError(t, add("big.txt", 2500))
113	require.NoError(t, add("big.txt", 3000))
114	require.ErrorIs(t, add("big.txt", 3001), domain.ErrInvalid)
115	// A generated file is collapsed in the web windows, but loadable: its
116	// lines can be commented on.
117	require.NoError(t, add("go.sum", 2))
118}
119
120func TestBrowseGetCommitFullWindow(t *testing.T) {
121	r := newMRRig(t)
122	r.seed()
123	r.commit(author, "a-big.txt", bigContent(3000), "big")
124	id := r.head()
125	r.push("main")
126	b := usecase.NewBrowse(r.store, r.git)
127	rev := domain.RevisionOf(id)
128
129	d, err := b.GetCommit(r.ctx, root, r.repo, rev, ports.GetCommitOptions{})
130	require.NoError(t, err)
131	require.Len(t, d.Diff.Files, 1)
132	assert.Equal(t, domain.OmitLarge, d.Diff.Files[0].Omitted)
133	assert.Empty(t, d.Diff.Files[0].Hunks)
134
135	d, err = b.GetCommit(r.ctx, root, r.repo, rev, ports.GetCommitOptions{Files: &ports.FileWindow{From: 0, To: 1, Full: true}})
136	require.NoError(t, err)
137	require.Len(t, d.Diff.Files, 1)
138	f := d.Diff.Files[0]
139	assert.Equal(t, domain.OmitNone, f.Omitted)
140	require.NotEmpty(t, f.Hunks)
141	assert.Equal(t, 3000, f.Additions)
142
143	_, err = b.GetCommit(r.ctx, root, r.repo, rev, ports.GetCommitOptions{Files: &ports.FileWindow{From: 0, To: 2, Full: true}})
144	require.ErrorIs(t, err, domain.ErrInvalid)
145}
146
147func TestBrowseGetCommitWindowCollapsesGenerated(t *testing.T) {
148	r := newMRRig(t)
149	r.seed()
150	r.commit(author, "go.sum", "h1:x\n", "sum")
151	id := r.head()
152	r.push("main")
153	b := usecase.NewBrowse(r.store, r.git)
154	rev := domain.RevisionOf(id)
155
156	d, err := b.GetCommit(r.ctx, root, r.repo, rev, ports.GetCommitOptions{})
157	require.NoError(t, err)
158	require.Len(t, d.Diff.Files, 1)
159	assert.Equal(t, domain.OmitNone, d.Diff.Files[0].Omitted, "API reads keep generated files")
160
161	d, err = b.GetCommit(r.ctx, root, r.repo, rev, ports.GetCommitOptions{Files: &ports.FileWindow{From: 0, To: 25}})
162	require.NoError(t, err)
163	require.Len(t, d.Diff.Files, 1)
164	assert.Equal(t, domain.OmitGenerated, d.Diff.Files[0].Omitted, "web windows collapse them")
165	assert.Empty(t, d.Diff.Files[0].Hunks)
166}

Keyboard shortcuts

Single-key shortcuts are on. ctrl k and esc work whatever this says.

ctrl k :
go to a section, branch, tag, file or repository
?
this list
j k
move the row cursor down / up
enter
open the row under the cursor; on Files, a directory opens or closes in place
↑ ↓ ← → home end
in the file tree (focus on a row): previous / next row, close / open a directory or go to its parent / first entry, first / last row. They never open a file
l h
on Files: open a directory or a file / close a directory or go up to its parent, then to the parent page. Elsewhere: into the side panel (on a merge request, its files) / back to the content; there j k move between its controls
g g G
first / last row
/
filter the list; on Files, find a file anywhere in the repository (esc clears, enter opens the best match)
b
switch branch or tag
g s g f g c g b g m g i g r g a
tabs: summary, files, commits, branches & tags, merge requests, issues, releases, actions (when available)
g h
home
w
wide mode: use the whole width of the screen, or not (remembered)
y
copy a permalink pinned to the commit
d u x
diff: split / unified / collapse or expand all
e E
diff: the whole file in view / every file, with the lines the hunks hide
n
merge request: next note in the diff
esc
close a dialog, leave a field

Branches 3

Tags 44

↑↓ moveenter openesc close