configure_system_handler_test.go RawHistoryFolder
1package handlers
2
3import (
4 "context"
5 "testing"
6
7 "github.com/bnema/archup/internal/application/commands"
8 "github.com/bnema/archup/internal/domain/ports/mocks"
9 "go.uber.org/mock/gomock"
10)
11
12func TestXkbLayoutFromKeymap(t *testing.T) {
13 tests := []struct {
14 keymap string
15 expected string
16 }{
17 {"us", "us"},
18 {"fr", "fr"},
19 {"de", "de"},
20 {"de-nodeadkeys", "de"},
21 {"fr-latin9", "fr"},
22 {"", ""},
23 }
24 for _, tt := range tests {
25 got := xkbLayoutFromKeymap(tt.keymap)
26 if got != tt.expected {
27 t.Errorf("xkbLayoutFromKeymap(%q) = %q, want %q", tt.keymap, got, tt.expected)
28 }
29 }
30}
31
32func TestConfigureSystemHandler_Handle_FrenchKeymap(t *testing.T) {
33 ctrl := gomock.NewController(t)
34 defer ctrl.Finish()
35
36 mockFS := mocks.NewMockFileSystem(ctrl)
37 mockChrExec := mocks.NewMockChrootExecutor(ctrl)
38 mockLogger := mocks.NewMockLogger(ctrl)
39
40 mockLogger.EXPECT().Info(gomock.Any(), gomock.Any()).AnyTimes()
41 mockLogger.EXPECT().Warn(gomock.Any(), gomock.Any()).AnyTimes()
42 mockLogger.EXPECT().LogPath().Return("/var/log/archup-install.log").AnyTimes()
43 mockChrExec.EXPECT().ExecuteInChroot(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return([]byte{}, nil).AnyTimes()
44 mockChrExec.EXPECT().ExecuteInChrootWithStdin(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(nil).AnyTimes()
45 mockChrExec.EXPECT().ChrootSystemctl(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(nil).AnyTimes()
46 mockFS.EXPECT().MkdirAll(gomock.Any(), gomock.Any()).Return(nil).AnyTimes()
47
48 // Expect 00-keyboard.conf to be written with "fr" layout
49 x11ConfPath := "/mnt/etc/X11/xorg.conf.d/00-keyboard.conf"
50 mockFS.EXPECT().WriteFile(gomock.Eq(x11ConfPath), gomock.Any(), gomock.Any()).DoAndReturn(
51 func(path string, content []byte, perm interface{}) error {
52 if !containsString(string(content), `Option "XkbLayout" "fr"`) {
53 t.Errorf("expected XkbLayout fr in 00-keyboard.conf, got: %s", string(content))
54 }
55 return nil
56 },
57 )
58 // All other WriteFile calls
59 mockFS.EXPECT().WriteFile(gomock.Not(gomock.Eq(x11ConfPath)), gomock.Any(), gomock.Any()).Return(nil).AnyTimes()
60
61 handler := NewConfigureSystemHandler(mockFS, mockChrExec, mockLogger)
62
63 cmd := commands.ConfigureSystemCommand{
64 MountPoint: "/mnt",
65 Hostname: "myarch",
66 Timezone: "Europe/Paris",
67 Locale: "fr_FR.UTF-8",
68 Keymap: "fr",
69 Username: "testuser",
70 UserShell: "/bin/bash",
71 UserPassword: "testpass123",
72 RootPassword: "rootpass456",
73 }
74
75 result, err := handler.Handle(context.Background(), cmd)
76 if err != nil {
77 t.Fatalf("expected no error, got %v", err)
78 }
79 if !result.Success {
80 t.Error("expected success")
81 }
82}
83
84func containsString(s, substr string) bool {
85 return len(s) >= len(substr) && (s == substr || len(s) > 0 && containsSubstring(s, substr))
86}
87
88func containsSubstring(s, substr string) bool {
89 for i := 0; i <= len(s)-len(substr); i++ {
90 if s[i:i+len(substr)] == substr {
91 return true
92 }
93 }
94 return false
95}
96
97func TestConfigureSystemHandler_Handle_Success(t *testing.T) {
98 ctrl := gomock.NewController(t)
99 defer ctrl.Finish()
100
101 mockFS := mocks.NewMockFileSystem(ctrl)
102 mockChrExec := mocks.NewMockChrootExecutor(ctrl)
103 mockLogger := mocks.NewMockLogger(ctrl)
104
105 mockLogger.EXPECT().Info(gomock.Any(), gomock.Any()).AnyTimes()
106 mockLogger.EXPECT().Warn(gomock.Any(), gomock.Any()).AnyTimes()
107 mockLogger.EXPECT().LogPath().Return("/var/log/archup-install.log").AnyTimes()
108 mockFS.EXPECT().WriteFile(gomock.Any(), gomock.Any(), gomock.Any()).AnyTimes()
109 mockFS.EXPECT().MkdirAll(gomock.Any(), gomock.Any()).Return(nil).AnyTimes()
110 mockChrExec.EXPECT().ExecuteInChroot(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return([]byte{}, nil).AnyTimes()
111 mockChrExec.EXPECT().ExecuteInChrootWithStdin(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(nil).AnyTimes()
112 mockChrExec.EXPECT().ChrootSystemctl(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(nil).AnyTimes()
113
114 handler := NewConfigureSystemHandler(mockFS, mockChrExec, mockLogger)
115
116 cmd := commands.ConfigureSystemCommand{
117 MountPoint: "/mnt",
118 Hostname: "myarch",
119 Timezone: "UTC",
120 Locale: "en_US.UTF-8",
121 Keymap: "us",
122 Username: "testuser",
123 UserShell: "/bin/bash",
124 UserPassword: "testpass123",
125 RootPassword: "rootpass456",
126 }
127
128 result, err := handler.Handle(context.Background(), cmd)
129
130 if err != nil {
131 t.Fatalf("expected no error, got %v", err)
132 }
133
134 if !result.Success {
135 t.Error("expected success")
136 }
137
138 if result.Hostname != "myarch" {
139 t.Errorf("expected hostname myarch, got %s", result.Hostname)
140 }
141
142 if result.Timezone != "UTC" {
143 t.Errorf("expected timezone UTC, got %s", result.Timezone)
144 }
145
146 if result.Username != "testuser" {
147 t.Errorf("expected username testuser, got %s", result.Username)
148 }
149}
150
151func TestConfigureSystemHandler_Handle_InvalidHostname(t *testing.T) {
152 ctrl := gomock.NewController(t)
153 defer ctrl.Finish()
154
155 mockFS := mocks.NewMockFileSystem(ctrl)
156 mockChrExec := mocks.NewMockChrootExecutor(ctrl)
157 mockLogger := mocks.NewMockLogger(ctrl)
158
159 mockLogger.EXPECT().Info(gomock.Any(), gomock.Any()).AnyTimes()
160 mockLogger.EXPECT().Error(gomock.Any(), gomock.Any(), gomock.Any()).Times(1)
161
162 handler := NewConfigureSystemHandler(mockFS, mockChrExec, mockLogger)
163
164 cmd := commands.ConfigureSystemCommand{
165 MountPoint: "/mnt",
166 Hostname: "", // Empty hostname
167 Timezone: "UTC",
168 Locale: "en_US.UTF-8",
169 Keymap: "us",
170 Username: "testuser",
171 UserShell: "/bin/bash",
172 UserPassword: "testpass123",
173 RootPassword: "rootpass456",
174 }
175
176 result, err := handler.Handle(context.Background(), cmd)
177
178 if err == nil {
179 t.Error("expected error for invalid hostname")
180 }
181
182 if result.Success {
183 t.Error("expected failure for invalid hostname")
184 }
185}
186
187func TestConfigureSystemHandler_Handle_InvalidUser(t *testing.T) {
188 ctrl := gomock.NewController(t)
189 defer ctrl.Finish()
190
191 mockFS := mocks.NewMockFileSystem(ctrl)
192 mockChrExec := mocks.NewMockChrootExecutor(ctrl)
193 mockLogger := mocks.NewMockLogger(ctrl)
194
195 mockLogger.EXPECT().Info(gomock.Any(), gomock.Any()).AnyTimes()
196 mockLogger.EXPECT().Error(gomock.Any(), gomock.Any(), gomock.Any()).Times(1)
197
198 handler := NewConfigureSystemHandler(mockFS, mockChrExec, mockLogger)
199
200 cmd := commands.ConfigureSystemCommand{
201 MountPoint: "/mnt",
202 Hostname: "myarch",
203 Timezone: "UTC",
204 Locale: "en_US.UTF-8",
205 Keymap: "us",
206 Username: "", // Empty username
207 UserShell: "/bin/bash",
208 UserPassword: "testpass123",
209 RootPassword: "rootpass456",
210 }
211
212 result, err := handler.Handle(context.Background(), cmd)
213
214 if err == nil {
215 t.Error("expected error for invalid username")
216 }
217
218 if result.Success {
219 t.Error("expected failure for invalid username")
220 }
221}
222
223func TestConfigureSystemHandler_Handle_InvalidPassword(t *testing.T) {
224 ctrl := gomock.NewController(t)
225 defer ctrl.Finish()
226
227 mockFS := mocks.NewMockFileSystem(ctrl)
228 mockChrExec := mocks.NewMockChrootExecutor(ctrl)
229 mockLogger := mocks.NewMockLogger(ctrl)
230
231 mockLogger.EXPECT().Info(gomock.Any(), gomock.Any()).AnyTimes()
232 mockLogger.EXPECT().Error(gomock.Any(), gomock.Any(), gomock.Any()).Times(1)
233
234 handler := NewConfigureSystemHandler(mockFS, mockChrExec, mockLogger)
235
236 cmd := commands.ConfigureSystemCommand{
237 MountPoint: "/mnt",
238 Hostname: "myarch",
239 Timezone: "UTC",
240 Locale: "en_US.UTF-8",
241 Keymap: "us",
242 Username: "testuser",
243 UserShell: "/bin/bash",
244 UserPassword: "testpass123",
245 RootPassword: "testpass123", // Same as user password
246 }
247
248 result, err := handler.Handle(context.Background(), cmd)
249
250 if err == nil {
251 t.Error("expected error for same passwords")
252 }
253
254 if result.Success {
255 t.Error("expected failure for same passwords")
256 }
257}