main 6af0a61

repos_handler_test.go RawHistoryFolder

1package handlers
2
3import (
4	"context"
5	"fmt"
6	"testing"
7
8	"github.com/bnema/archup/internal/application/commands"
9	"github.com/bnema/archup/internal/domain/packages"
10	"github.com/bnema/archup/internal/domain/ports/mocks"
11	"go.uber.org/mock/gomock"
12)
13
14func errNotFound(name string) error {
15	return fmt.Errorf("%s: not found", name)
16}
17
18// chaoticMocks sets up the mock expectations that are always required because
19// Chaotic-AUR is unconditionally enabled.
20func chaoticMocks(mockChrExec *mocks.MockChrootExecutor, mockFS *mocks.MockFileSystem) {
21	mockChrExec.EXPECT().ExecuteInChroot(gomock.Any(), gomock.Any(), "pacman-key", "--recv-key", gomock.Any(), "--keyserver", gomock.Any()).Return([]byte{}, nil).AnyTimes()
22	mockChrExec.EXPECT().ExecuteInChroot(gomock.Any(), gomock.Any(), "pacman-key", "--lsign-key", gomock.Any()).Return([]byte{}, nil).AnyTimes()
23	mockChrExec.EXPECT().ExecuteInChroot(gomock.Any(), gomock.Any(), "pacman", "-U", "--noconfirm", gomock.Any(), gomock.Any()).Return([]byte{}, nil).AnyTimes()
24	mockChrExec.EXPECT().ExecuteInChroot(gomock.Any(), gomock.Any(), "pacman", "-Sy", "--noconfirm").Return([]byte{}, nil).AnyTimes()
25	mockChrExec.EXPECT().ExecuteInChroot(gomock.Any(), gomock.Any(), "pacman", "-S", "--noconfirm", gomock.Any()).Return([]byte{}, nil).AnyTimes()
26	mockFS.EXPECT().ReadFile("/mnt/etc/pacman.conf").Return([]byte("[core]\nInclude = /etc/pacman.d/mirrorlist\n"), nil).AnyTimes()
27	mockFS.EXPECT().WriteFile("/mnt/etc/pacman.conf", gomock.Any(), gomock.Any()).Return(nil).AnyTimes()
28}
29
30func TestReposHandler_Handle_Minimal(t *testing.T) {
31	ctrl := gomock.NewController(t)
32	defer ctrl.Finish()
33
34	mockFS := mocks.NewMockFileSystem(ctrl)
35	mockChrExec := mocks.NewMockChrootExecutor(ctrl)
36	mockLogger := mocks.NewMockLogger(ctrl)
37
38	mockLogger.EXPECT().Info(gomock.Any(), gomock.Any()).AnyTimes()
39	mockLogger.EXPECT().Warn(gomock.Any(), gomock.Any()).AnyTimes()
40
41	chaoticMocks(mockChrExec, mockFS)
42	mockFS.EXPECT().ReadFile(gomock.Any()).Return(nil, errNotFound("extra.packages")).AnyTimes()
43
44	handler := NewReposHandler(mockFS, mockChrExec, mockLogger)
45
46	cmd := commands.SetupRepositoriesCommand{
47		MountPoint:     "/mnt",
48		EnableMultilib: false,
49		AURHelper:      packages.AURHelperParu,
50	}
51
52	result, err := handler.Handle(context.Background(), cmd)
53
54	if err != nil {
55		t.Fatalf("expected no error, got %v", err)
56	}
57	if !result.Success {
58		t.Error("expected success")
59	}
60	if result.Multilib {
61		t.Error("expected multilib to be disabled")
62	}
63	if result.AURHelper != "paru" {
64		t.Errorf("expected paru helper, got %s", result.AURHelper)
65	}
66}
67
68func TestReposHandler_Handle_MultilibEnabled(t *testing.T) {
69	ctrl := gomock.NewController(t)
70	defer ctrl.Finish()
71
72	mockFS := mocks.NewMockFileSystem(ctrl)
73	mockChrExec := mocks.NewMockChrootExecutor(ctrl)
74	mockLogger := mocks.NewMockLogger(ctrl)
75
76	mockLogger.EXPECT().Info(gomock.Any(), gomock.Any()).AnyTimes()
77	mockLogger.EXPECT().Warn(gomock.Any(), gomock.Any()).AnyTimes()
78
79	mockFS.EXPECT().ReadFile("/mnt/etc/pacman.conf").Return(
80		[]byte("[core]\n#[multilib]\n#Include = /etc/pacman.d/mirrorlist\n"), nil,
81	).AnyTimes()
82	mockFS.EXPECT().WriteFile(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil).AnyTimes()
83	mockFS.EXPECT().MkdirAll(gomock.Any(), gomock.Any()).Return(nil).AnyTimes()
84
85	chaoticMocks(mockChrExec, mockFS)
86	mockChrExec.EXPECT().ExecuteInChroot(gomock.Any(), gomock.Any(), "pacman", "-S", "--noconfirm", "--needed", gomock.Any()).Return([]byte{}, nil).AnyTimes()
87	mockFS.EXPECT().ReadFile(gomock.Any()).Return(nil, errNotFound("extra.packages")).AnyTimes()
88
89	handler := NewReposHandler(mockFS, mockChrExec, mockLogger)
90
91	cmd := commands.SetupRepositoriesCommand{
92		MountPoint:     "/mnt",
93		EnableMultilib: true,
94		AURHelper:      packages.AURHelperYay,
95	}
96
97	result, err := handler.Handle(context.Background(), cmd)
98
99	if err != nil {
100		t.Fatalf("expected no error, got %v", err)
101	}
102	if !result.Success {
103		t.Error("expected success")
104	}
105	if !result.Multilib {
106		t.Error("expected multilib to be enabled")
107	}
108	if result.AURHelper != "yay" {
109		t.Errorf("expected yay helper, got %s", result.AURHelper)
110	}
111}
112
113func TestReposHandler_Handle_CachyOSKernel_AddsCachyOSRepo(t *testing.T) {
114	ctrl := gomock.NewController(t)
115	defer ctrl.Finish()
116
117	mockFS := mocks.NewMockFileSystem(ctrl)
118	mockChrExec := mocks.NewMockChrootExecutor(ctrl)
119	mockLogger := mocks.NewMockLogger(ctrl)
120
121	mockLogger.EXPECT().Info(gomock.Any(), gomock.Any()).AnyTimes()
122	mockLogger.EXPECT().Warn(gomock.Any(), gomock.Any()).AnyTimes()
123
124	// pacman.conf read: first call for CachyOS, rest for Chaotic
125	mockFS.EXPECT().ReadFile("/mnt/etc/pacman.conf").Return(
126		[]byte("[core]\nInclude = /etc/pacman.d/mirrorlist\n"), nil,
127	).AnyTimes()
128	mockFS.EXPECT().ReadFile(gomock.Any()).Return(nil, errNotFound("extra.packages")).AnyTimes()
129
130	mockFS.EXPECT().MkdirAll(gomock.Any(), gomock.Any()).Return(nil).AnyTimes()
131
132	// WriteFile: cachyos mirrorlist written once; pacman.conf written multiple times
133	// (once for chaotic-aur, once for cachyos) — verify the final write has [cachyos]
134	mockFS.EXPECT().WriteFile("/mnt/etc/pacman.d/cachyos-mirrorlist", gomock.Any(), gomock.Any()).Return(nil)
135	pacmanConfWriteCount := 0
136	mockFS.EXPECT().WriteFile("/mnt/etc/pacman.conf", gomock.Any(), gomock.Any()).DoAndReturn(
137		func(path string, data []byte, perm interface{}) error {
138			pacmanConfWriteCount++
139			return nil
140		},
141	).AnyTimes()
142	t.Cleanup(func() {
143		if pacmanConfWriteCount == 0 {
144			t.Error("expected pacman.conf to be written at least once")
145		}
146	})
147
148	// CachyOS key ops
149	mockChrExec.EXPECT().ExecuteInChroot(gomock.Any(), "/mnt", "pacman-key", "--init").Return([]byte{}, nil)
150	mockChrExec.EXPECT().ExecuteInChroot(gomock.Any(), "/mnt", "pacman-key", "--list-keys", "F3B607488DB35A47").Return(nil, errNotFound("key"))
151	mockChrExec.EXPECT().ExecuteInChroot(gomock.Any(), "/mnt", "pacman-key", "--recv-keys",
152		"F3B607488DB35A47", "--keyserver", "keyserver.ubuntu.com").Return([]byte{}, nil)
153	mockChrExec.EXPECT().ExecuteInChroot(gomock.Any(), "/mnt", "pacman-key", "--lsign-key",
154		"F3B607488DB35A47").Return([]byte{}, nil)
155
156	// Chaotic AUR key ops (always enabled)
157	mockChrExec.EXPECT().ExecuteInChroot(gomock.Any(), gomock.Any(), "pacman-key", "--recv-key", gomock.Any(), "--keyserver", gomock.Any()).Return([]byte{}, nil).AnyTimes()
158	mockChrExec.EXPECT().ExecuteInChroot(gomock.Any(), gomock.Any(), "pacman-key", "--lsign-key", gomock.Any()).Return([]byte{}, nil).AnyTimes()
159	mockChrExec.EXPECT().ExecuteInChroot(gomock.Any(), gomock.Any(), "pacman", "-U", "--noconfirm", gomock.Any(), gomock.Any()).Return([]byte{}, nil).AnyTimes()
160	mockChrExec.EXPECT().ExecuteInChroot(gomock.Any(), gomock.Any(), "pacman", "-Sy", "--noconfirm").Return([]byte{}, nil).AnyTimes()
161	mockChrExec.EXPECT().ExecuteInChroot(gomock.Any(), gomock.Any(), "pacman", "-S", "--noconfirm", gomock.Any()).Return([]byte{}, nil).AnyTimes()
162
163	handler := NewReposHandler(mockFS, mockChrExec, mockLogger)
164
165	cmd := commands.SetupRepositoriesCommand{
166		MountPoint:    "/mnt",
167		AURHelper:     packages.AURHelperParu,
168		KernelVariant: packages.KernelCachyOS,
169	}
170
171	result, err := handler.Handle(context.Background(), cmd)
172
173	if err != nil {
174		t.Fatalf("expected no error, got %v", err)
175	}
176	if !result.Success {
177		t.Error("expected success")
178	}
179}
180
181func TestReposHandler_Handle_WithAdditionalRepos(t *testing.T) {
182	ctrl := gomock.NewController(t)
183	defer ctrl.Finish()
184
185	mockFS := mocks.NewMockFileSystem(ctrl)
186	mockChrExec := mocks.NewMockChrootExecutor(ctrl)
187	mockLogger := mocks.NewMockLogger(ctrl)
188
189	mockLogger.EXPECT().Info(gomock.Any(), gomock.Any()).AnyTimes()
190	mockLogger.EXPECT().Warn(gomock.Any(), gomock.Any()).AnyTimes()
191
192	chaoticMocks(mockChrExec, mockFS)
193	mockFS.EXPECT().ReadFile(gomock.Any()).Return(nil, errNotFound("extra.packages")).AnyTimes()
194
195	handler := NewReposHandler(mockFS, mockChrExec, mockLogger)
196
197	cmd := commands.SetupRepositoriesCommand{
198		MountPoint: "/mnt",
199		AURHelper:  packages.AURHelperParu,
200		AdditionalRepos: []string{
201			"https://example.com/archup",
202			"https://custom.repo/packages",
203		},
204	}
205
206	result, err := handler.Handle(context.Background(), cmd)
207
208	if err != nil {
209		t.Fatalf("expected no error, got %v", err)
210	}
211	if !result.Success {
212		t.Error("expected success")
213	}
214}

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 1

Tags 0

    ↑↓ moveenter openesc close