detached 725d1f4

test(sqlite): add unit tests for LazyDB and DatabaseProvider mock

commit
725d1f48cb3791799cb05eb8aedb7be6fa28abf8
author
bnema · · 2025-12-27 21:18 UTC
parent
3232426
signature
GPG Unverified2B82207D370127A0 The key is unknown
3 files · +304 -0
M .mockery.yaml +1 -0
@@ -9,6 +9,7 @@ packages:
99 pkgname: mocks
1010 interfaces:
1111 ConfigMigrator: {}
12+ DatabaseProvider: {}
1213 SessionLogger: {}
1314 SessionSpawner: {}
1415 TabListProvider: {}
@@ -9,6 +9,7 @@ packages:
9 pkgname: mocks9 pkgname: mocks
10 interfaces:10 interfaces:
11 ConfigMigrator: {}11 ConfigMigrator: {}
12+ DatabaseProvider: {}
12 SessionLogger: {}13 SessionLogger: {}
13 SessionSpawner: {}14 SessionSpawner: {}
14 TabListProvider: {}15 TabListProvider: {}
M internal/application/port/mocks/mocks.go +178 -0
@@ -6,6 +6,7 @@ package mocks
66
77 import (
88 "context"
9+ "database/sql"
910
1011 "github.com/bnema/dumber/internal/application/port"
1112 "github.com/bnema/dumber/internal/domain/entity"
@@ -201,6 +202,183 @@ func (_c *MockConfigMigrator_Migrate_Call) RunAndReturn(run func() ([]string, er
201202 return _c
202203 }
203204
205+// NewMockDatabaseProvider creates a new instance of MockDatabaseProvider. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
206+// The first argument is typically a *testing.T value.
207+func NewMockDatabaseProvider(t interface {
208+ mock.TestingT
209+ Cleanup(func())
210+}) *MockDatabaseProvider {
211+ mock := &MockDatabaseProvider{}
212+ mock.Mock.Test(t)
213+
214+ t.Cleanup(func() { mock.AssertExpectations(t) })
215+
216+ return mock
217+}
218+
219+// MockDatabaseProvider is an autogenerated mock type for the DatabaseProvider type
220+type MockDatabaseProvider struct {
221+ mock.Mock
222+}
223+
224+type MockDatabaseProvider_Expecter struct {
225+ mock *mock.Mock
226+}
227+
228+func (_m *MockDatabaseProvider) EXPECT() *MockDatabaseProvider_Expecter {
229+ return &MockDatabaseProvider_Expecter{mock: &_m.Mock}
230+}
231+
232+// Close provides a mock function for the type MockDatabaseProvider
233+func (_mock *MockDatabaseProvider) Close() error {
234+ ret := _mock.Called()
235+
236+ if len(ret) == 0 {
237+ panic("no return value specified for Close")
238+ }
239+
240+ var r0 error
241+ if returnFunc, ok := ret.Get(0).(func() error); ok {
242+ r0 = returnFunc()
243+ } else {
244+ r0 = ret.Error(0)
245+ }
246+ return r0
247+}
248+
249+// MockDatabaseProvider_Close_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Close'
250+type MockDatabaseProvider_Close_Call struct {
251+ *mock.Call
252+}
253+
254+// Close is a helper method to define mock.On call
255+func (_e *MockDatabaseProvider_Expecter) Close() *MockDatabaseProvider_Close_Call {
256+ return &MockDatabaseProvider_Close_Call{Call: _e.mock.On("Close")}
257+}
258+
259+func (_c *MockDatabaseProvider_Close_Call) Run(run func()) *MockDatabaseProvider_Close_Call {
260+ _c.Call.Run(func(args mock.Arguments) {
261+ run()
262+ })
263+ return _c
264+}
265+
266+func (_c *MockDatabaseProvider_Close_Call) Return(err error) *MockDatabaseProvider_Close_Call {
267+ _c.Call.Return(err)
268+ return _c
269+}
270+
271+func (_c *MockDatabaseProvider_Close_Call) RunAndReturn(run func() error) *MockDatabaseProvider_Close_Call {
272+ _c.Call.Return(run)
273+ return _c
274+}
275+
276+// DB provides a mock function for the type MockDatabaseProvider
277+func (_mock *MockDatabaseProvider) DB(ctx context.Context) (*sql.DB, error) {
278+ ret := _mock.Called(ctx)
279+
280+ if len(ret) == 0 {
281+ panic("no return value specified for DB")
282+ }
283+
284+ var r0 *sql.DB
285+ var r1 error
286+ if returnFunc, ok := ret.Get(0).(func(context.Context) (*sql.DB, error)); ok {
287+ return returnFunc(ctx)
288+ }
289+ if returnFunc, ok := ret.Get(0).(func(context.Context) *sql.DB); ok {
290+ r0 = returnFunc(ctx)
291+ } else {
292+ if ret.Get(0) != nil {
293+ r0 = ret.Get(0).(*sql.DB)
294+ }
295+ }
296+ if returnFunc, ok := ret.Get(1).(func(context.Context) error); ok {
297+ r1 = returnFunc(ctx)
298+ } else {
299+ r1 = ret.Error(1)
300+ }
301+ return r0, r1
302+}
303+
304+// MockDatabaseProvider_DB_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'DB'
305+type MockDatabaseProvider_DB_Call struct {
306+ *mock.Call
307+}
308+
309+// DB is a helper method to define mock.On call
310+// - ctx context.Context
311+func (_e *MockDatabaseProvider_Expecter) DB(ctx interface{}) *MockDatabaseProvider_DB_Call {
312+ return &MockDatabaseProvider_DB_Call{Call: _e.mock.On("DB", ctx)}
313+}
314+
315+func (_c *MockDatabaseProvider_DB_Call) Run(run func(ctx context.Context)) *MockDatabaseProvider_DB_Call {
316+ _c.Call.Run(func(args mock.Arguments) {
317+ var arg0 context.Context
318+ if args[0] != nil {
319+ arg0 = args[0].(context.Context)
320+ }
321+ run(
322+ arg0,
323+ )
324+ })
325+ return _c
326+}
327+
328+func (_c *MockDatabaseProvider_DB_Call) Return(dB *sql.DB, err error) *MockDatabaseProvider_DB_Call {
329+ _c.Call.Return(dB, err)
330+ return _c
331+}
332+
333+func (_c *MockDatabaseProvider_DB_Call) RunAndReturn(run func(ctx context.Context) (*sql.DB, error)) *MockDatabaseProvider_DB_Call {
334+ _c.Call.Return(run)
335+ return _c
336+}
337+
338+// IsInitialized provides a mock function for the type MockDatabaseProvider
339+func (_mock *MockDatabaseProvider) IsInitialized() bool {
340+ ret := _mock.Called()
341+
342+ if len(ret) == 0 {
343+ panic("no return value specified for IsInitialized")
344+ }
345+
346+ var r0 bool
347+ if returnFunc, ok := ret.Get(0).(func() bool); ok {
348+ r0 = returnFunc()
349+ } else {
350+ r0 = ret.Get(0).(bool)
351+ }
352+ return r0
353+}
354+
355+// MockDatabaseProvider_IsInitialized_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'IsInitialized'
356+type MockDatabaseProvider_IsInitialized_Call struct {
357+ *mock.Call
358+}
359+
360+// IsInitialized is a helper method to define mock.On call
361+func (_e *MockDatabaseProvider_Expecter) IsInitialized() *MockDatabaseProvider_IsInitialized_Call {
362+ return &MockDatabaseProvider_IsInitialized_Call{Call: _e.mock.On("IsInitialized")}
363+}
364+
365+func (_c *MockDatabaseProvider_IsInitialized_Call) Run(run func()) *MockDatabaseProvider_IsInitialized_Call {
366+ _c.Call.Run(func(args mock.Arguments) {
367+ run()
368+ })
369+ return _c
370+}
371+
372+func (_c *MockDatabaseProvider_IsInitialized_Call) Return(b bool) *MockDatabaseProvider_IsInitialized_Call {
373+ _c.Call.Return(b)
374+ return _c
375+}
376+
377+func (_c *MockDatabaseProvider_IsInitialized_Call) RunAndReturn(run func() bool) *MockDatabaseProvider_IsInitialized_Call {
378+ _c.Call.Return(run)
379+ return _c
380+}
381+
204382 // NewMockSessionLogger creates a new instance of MockSessionLogger. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
205383 // The first argument is typically a *testing.T value.
206384 func NewMockSessionLogger(t interface {
@@ -6,6 +6,7 @@ package mocks
6 6
7 import (7 import (
8 "context"8 "context"
9+ "database/sql"
9 10
10 "github.com/bnema/dumber/internal/application/port"11 "github.com/bnema/dumber/internal/application/port"
11 "github.com/bnema/dumber/internal/domain/entity"12 "github.com/bnema/dumber/internal/domain/entity"
@@ -201,6 +202,183 @@ func (_c *MockConfigMigrator_Migrate_Call) RunAndReturn(run func() ([]string, er
201 return _c202 return _c
202 }203 }
203 204
205+// NewMockDatabaseProvider creates a new instance of MockDatabaseProvider. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
206+// The first argument is typically a *testing.T value.
207+func NewMockDatabaseProvider(t interface {
208+ mock.TestingT
209+ Cleanup(func())
210+}) *MockDatabaseProvider {
211+ mock := &MockDatabaseProvider{}
212+ mock.Mock.Test(t)
213+
214+ t.Cleanup(func() { mock.AssertExpectations(t) })
215+
216+ return mock
217+}
218+
219+// MockDatabaseProvider is an autogenerated mock type for the DatabaseProvider type
220+type MockDatabaseProvider struct {
221+ mock.Mock
222+}
223+
224+type MockDatabaseProvider_Expecter struct {
225+ mock *mock.Mock
226+}
227+
228+func (_m *MockDatabaseProvider) EXPECT() *MockDatabaseProvider_Expecter {
229+ return &MockDatabaseProvider_Expecter{mock: &_m.Mock}
230+}
231+
232+// Close provides a mock function for the type MockDatabaseProvider
233+func (_mock *MockDatabaseProvider) Close() error {
234+ ret := _mock.Called()
235+
236+ if len(ret) == 0 {
237+ panic("no return value specified for Close")
238+ }
239+
240+ var r0 error
241+ if returnFunc, ok := ret.Get(0).(func() error); ok {
242+ r0 = returnFunc()
243+ } else {
244+ r0 = ret.Error(0)
245+ }
246+ return r0
247+}
248+
249+// MockDatabaseProvider_Close_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Close'
250+type MockDatabaseProvider_Close_Call struct {
251+ *mock.Call
252+}
253+
254+// Close is a helper method to define mock.On call
255+func (_e *MockDatabaseProvider_Expecter) Close() *MockDatabaseProvider_Close_Call {
256+ return &MockDatabaseProvider_Close_Call{Call: _e.mock.On("Close")}
257+}
258+
259+func (_c *MockDatabaseProvider_Close_Call) Run(run func()) *MockDatabaseProvider_Close_Call {
260+ _c.Call.Run(func(args mock.Arguments) {
261+ run()
262+ })
263+ return _c
264+}
265+
266+func (_c *MockDatabaseProvider_Close_Call) Return(err error) *MockDatabaseProvider_Close_Call {
267+ _c.Call.Return(err)
268+ return _c
269+}
270+
271+func (_c *MockDatabaseProvider_Close_Call) RunAndReturn(run func() error) *MockDatabaseProvider_Close_Call {
272+ _c.Call.Return(run)
273+ return _c
274+}
275+
276+// DB provides a mock function for the type MockDatabaseProvider
277+func (_mock *MockDatabaseProvider) DB(ctx context.Context) (*sql.DB, error) {
278+ ret := _mock.Called(ctx)
279+
280+ if len(ret) == 0 {
281+ panic("no return value specified for DB")
282+ }
283+
284+ var r0 *sql.DB
285+ var r1 error
286+ if returnFunc, ok := ret.Get(0).(func(context.Context) (*sql.DB, error)); ok {
287+ return returnFunc(ctx)
288+ }
289+ if returnFunc, ok := ret.Get(0).(func(context.Context) *sql.DB); ok {
290+ r0 = returnFunc(ctx)
291+ } else {
292+ if ret.Get(0) != nil {
293+ r0 = ret.Get(0).(*sql.DB)
294+ }
295+ }
296+ if returnFunc, ok := ret.Get(1).(func(context.Context) error); ok {
297+ r1 = returnFunc(ctx)
298+ } else {
299+ r1 = ret.Error(1)
300+ }
301+ return r0, r1
302+}
303+
304+// MockDatabaseProvider_DB_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'DB'
305+type MockDatabaseProvider_DB_Call struct {
306+ *mock.Call
307+}
308+
309+// DB is a helper method to define mock.On call
310+// - ctx context.Context
311+func (_e *MockDatabaseProvider_Expecter) DB(ctx interface{}) *MockDatabaseProvider_DB_Call {
312+ return &MockDatabaseProvider_DB_Call{Call: _e.mock.On("DB", ctx)}
313+}
314+
315+func (_c *MockDatabaseProvider_DB_Call) Run(run func(ctx context.Context)) *MockDatabaseProvider_DB_Call {
316+ _c.Call.Run(func(args mock.Arguments) {
317+ var arg0 context.Context
318+ if args[0] != nil {
319+ arg0 = args[0].(context.Context)
320+ }
321+ run(
322+ arg0,
323+ )
324+ })
325+ return _c
326+}
327+
328+func (_c *MockDatabaseProvider_DB_Call) Return(dB *sql.DB, err error) *MockDatabaseProvider_DB_Call {
329+ _c.Call.Return(dB, err)
330+ return _c
331+}
332+
333+func (_c *MockDatabaseProvider_DB_Call) RunAndReturn(run func(ctx context.Context) (*sql.DB, error)) *MockDatabaseProvider_DB_Call {
334+ _c.Call.Return(run)
335+ return _c
336+}
337+
338+// IsInitialized provides a mock function for the type MockDatabaseProvider
339+func (_mock *MockDatabaseProvider) IsInitialized() bool {
340+ ret := _mock.Called()
341+
342+ if len(ret) == 0 {
343+ panic("no return value specified for IsInitialized")
344+ }
345+
346+ var r0 bool
347+ if returnFunc, ok := ret.Get(0).(func() bool); ok {
348+ r0 = returnFunc()
349+ } else {
350+ r0 = ret.Get(0).(bool)
351+ }
352+ return r0
353+}
354+
355+// MockDatabaseProvider_IsInitialized_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'IsInitialized'
356+type MockDatabaseProvider_IsInitialized_Call struct {
357+ *mock.Call
358+}
359+
360+// IsInitialized is a helper method to define mock.On call
361+func (_e *MockDatabaseProvider_Expecter) IsInitialized() *MockDatabaseProvider_IsInitialized_Call {
362+ return &MockDatabaseProvider_IsInitialized_Call{Call: _e.mock.On("IsInitialized")}
363+}
364+
365+func (_c *MockDatabaseProvider_IsInitialized_Call) Run(run func()) *MockDatabaseProvider_IsInitialized_Call {
366+ _c.Call.Run(func(args mock.Arguments) {
367+ run()
368+ })
369+ return _c
370+}
371+
372+func (_c *MockDatabaseProvider_IsInitialized_Call) Return(b bool) *MockDatabaseProvider_IsInitialized_Call {
373+ _c.Call.Return(b)
374+ return _c
375+}
376+
377+func (_c *MockDatabaseProvider_IsInitialized_Call) RunAndReturn(run func() bool) *MockDatabaseProvider_IsInitialized_Call {
378+ _c.Call.Return(run)
379+ return _c
380+}
381+
204 // NewMockSessionLogger creates a new instance of MockSessionLogger. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.382 // NewMockSessionLogger creates a new instance of MockSessionLogger. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
205 // The first argument is typically a *testing.T value.383 // The first argument is typically a *testing.T value.
206 func NewMockSessionLogger(t interface {384 func NewMockSessionLogger(t interface {
A internal/infrastructure/persistence/sqlite/lazy_db_test.go +125 -0
@@ -0,0 +1,125 @@
1+package sqlite_test
2+
3+import (
4+ "path/filepath"
5+ "sync"
6+ "testing"
7+
8+ "github.com/bnema/dumber/internal/infrastructure/persistence/sqlite"
9+ "github.com/stretchr/testify/assert"
10+ "github.com/stretchr/testify/require"
11+)
12+
13+func TestLazyDB_NotInitializedByDefault(t *testing.T) {
14+ dbPath := filepath.Join(t.TempDir(), "test.db")
15+ lazy := sqlite.NewLazyDB(dbPath)
16+
17+ assert.False(t, lazy.IsInitialized(), "LazyDB should not be initialized before DB() is called")
18+}
19+
20+func TestLazyDB_InitializesOnFirstAccess(t *testing.T) {
21+ ctx := testCtx()
22+ dbPath := filepath.Join(t.TempDir(), "test.db")
23+ lazy := sqlite.NewLazyDB(dbPath)
24+
25+ db, err := lazy.DB(ctx)
26+ require.NoError(t, err)
27+ require.NotNil(t, db)
28+
29+ assert.True(t, lazy.IsInitialized(), "LazyDB should be initialized after DB() is called")
30+
31+ // Cleanup
32+ require.NoError(t, lazy.Close())
33+}
34+
35+func TestLazyDB_ReturnsSameConnection(t *testing.T) {
36+ ctx := testCtx()
37+ dbPath := filepath.Join(t.TempDir(), "test.db")
38+ lazy := sqlite.NewLazyDB(dbPath)
39+
40+ db1, err := lazy.DB(ctx)
41+ require.NoError(t, err)
42+
43+ db2, err := lazy.DB(ctx)
44+ require.NoError(t, err)
45+
46+ assert.Same(t, db1, db2, "DB() should return the same connection instance")
47+
48+ require.NoError(t, lazy.Close())
49+}
50+
51+func TestLazyDB_ConcurrentAccess(t *testing.T) {
52+ ctx := testCtx()
53+ dbPath := filepath.Join(t.TempDir(), "test.db")
54+ lazy := sqlite.NewLazyDB(dbPath)
55+
56+ const goroutines = 10
57+ var wg sync.WaitGroup
58+ wg.Add(goroutines)
59+
60+ results := make(chan *struct {
61+ db interface{}
62+ err error
63+ }, goroutines)
64+
65+ for range goroutines {
66+ go func() {
67+ defer wg.Done()
68+ db, err := lazy.DB(ctx)
69+ results <- &struct {
70+ db interface{}
71+ err error
72+ }{db, err}
73+ }()
74+ }
75+
76+ wg.Wait()
77+ close(results)
78+
79+ var firstDB interface{}
80+ for result := range results {
81+ require.NoError(t, result.err)
82+ require.NotNil(t, result.db)
83+
84+ if firstDB == nil {
85+ firstDB = result.db
86+ } else {
87+ assert.Same(t, firstDB, result.db, "All goroutines should receive the same DB instance")
88+ }
89+ }
90+
91+ require.NoError(t, lazy.Close())
92+}
93+
94+func TestLazyDB_CloseBeforeInit(t *testing.T) {
95+ dbPath := filepath.Join(t.TempDir(), "test.db")
96+ lazy := sqlite.NewLazyDB(dbPath)
97+
98+ // Close without ever calling DB() should not error
99+ err := lazy.Close()
100+ assert.NoError(t, err)
101+}
102+
103+func TestLazyDB_Path(t *testing.T) {
104+ dbPath := "/some/path/to/db.sqlite"
105+ lazy := sqlite.NewLazyDB(dbPath)
106+
107+ assert.Equal(t, dbPath, lazy.Path())
108+}
109+
110+func TestLazyDB_DBIsUsable(t *testing.T) {
111+ ctx := testCtx()
112+ dbPath := filepath.Join(t.TempDir(), "test.db")
113+ lazy := sqlite.NewLazyDB(dbPath)
114+
115+ db, err := lazy.DB(ctx)
116+ require.NoError(t, err)
117+
118+ // Verify we can execute a simple query
119+ var result int
120+ err = db.QueryRowContext(ctx, "SELECT 1").Scan(&result)
121+ require.NoError(t, err)
122+ assert.Equal(t, 1, result)
123+
124+ require.NoError(t, lazy.Close())
125+}
@@ -0,0 +1,125 @@
1+package sqlite_test
2+
3+import (
4+ "path/filepath"
5+ "sync"
6+ "testing"
7+
8+ "github.com/bnema/dumber/internal/infrastructure/persistence/sqlite"
9+ "github.com/stretchr/testify/assert"
10+ "github.com/stretchr/testify/require"
11+)
12+
13+func TestLazyDB_NotInitializedByDefault(t *testing.T) {
14+ dbPath := filepath.Join(t.TempDir(), "test.db")
15+ lazy := sqlite.NewLazyDB(dbPath)
16+
17+ assert.False(t, lazy.IsInitialized(), "LazyDB should not be initialized before DB() is called")
18+}
19+
20+func TestLazyDB_InitializesOnFirstAccess(t *testing.T) {
21+ ctx := testCtx()
22+ dbPath := filepath.Join(t.TempDir(), "test.db")
23+ lazy := sqlite.NewLazyDB(dbPath)
24+
25+ db, err := lazy.DB(ctx)
26+ require.NoError(t, err)
27+ require.NotNil(t, db)
28+
29+ assert.True(t, lazy.IsInitialized(), "LazyDB should be initialized after DB() is called")
30+
31+ // Cleanup
32+ require.NoError(t, lazy.Close())
33+}
34+
35+func TestLazyDB_ReturnsSameConnection(t *testing.T) {
36+ ctx := testCtx()
37+ dbPath := filepath.Join(t.TempDir(), "test.db")
38+ lazy := sqlite.NewLazyDB(dbPath)
39+
40+ db1, err := lazy.DB(ctx)
41+ require.NoError(t, err)
42+
43+ db2, err := lazy.DB(ctx)
44+ require.NoError(t, err)
45+
46+ assert.Same(t, db1, db2, "DB() should return the same connection instance")
47+
48+ require.NoError(t, lazy.Close())
49+}
50+
51+func TestLazyDB_ConcurrentAccess(t *testing.T) {
52+ ctx := testCtx()
53+ dbPath := filepath.Join(t.TempDir(), "test.db")
54+ lazy := sqlite.NewLazyDB(dbPath)
55+
56+ const goroutines = 10
57+ var wg sync.WaitGroup
58+ wg.Add(goroutines)
59+
60+ results := make(chan *struct {
61+ db interface{}
62+ err error
63+ }, goroutines)
64+
65+ for range goroutines {
66+ go func() {
67+ defer wg.Done()
68+ db, err := lazy.DB(ctx)
69+ results <- &struct {
70+ db interface{}
71+ err error
72+ }{db, err}
73+ }()
74+ }
75+
76+ wg.Wait()
77+ close(results)
78+
79+ var firstDB interface{}
80+ for result := range results {
81+ require.NoError(t, result.err)
82+ require.NotNil(t, result.db)
83+
84+ if firstDB == nil {
85+ firstDB = result.db
86+ } else {
87+ assert.Same(t, firstDB, result.db, "All goroutines should receive the same DB instance")
88+ }
89+ }
90+
91+ require.NoError(t, lazy.Close())
92+}
93+
94+func TestLazyDB_CloseBeforeInit(t *testing.T) {
95+ dbPath := filepath.Join(t.TempDir(), "test.db")
96+ lazy := sqlite.NewLazyDB(dbPath)
97+
98+ // Close without ever calling DB() should not error
99+ err := lazy.Close()
100+ assert.NoError(t, err)
101+}
102+
103+func TestLazyDB_Path(t *testing.T) {
104+ dbPath := "/some/path/to/db.sqlite"
105+ lazy := sqlite.NewLazyDB(dbPath)
106+
107+ assert.Equal(t, dbPath, lazy.Path())
108+}
109+
110+func TestLazyDB_DBIsUsable(t *testing.T) {
111+ ctx := testCtx()
112+ dbPath := filepath.Join(t.TempDir(), "test.db")
113+ lazy := sqlite.NewLazyDB(dbPath)
114+
115+ db, err := lazy.DB(ctx)
116+ require.NoError(t, err)
117+
118+ // Verify we can execute a simple query
119+ var result int
120+ err = db.QueryRowContext(ctx, "SELECT 1").Scan(&result)
121+ require.NoError(t, err)
122+ assert.Equal(t, 1, result)
123+
124+ require.NoError(t, lazy.Close())
125+}

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 8

Tags 42

↑↓ moveenter openesc close