v0.33.2 57f21d6

favorite_repo_test.go RawHistoryFolder

1package sqlite
2
3import (
4	"context"
5	"database/sql"
6	"fmt"
7	"testing"
8
9	"github.com/bnema/dumber/internal/domain/entity"
10	_ "github.com/bnema/purego-sqlite/driver"
11)
12
13func openFavoriteRepoTestDB(t *testing.T) *sql.DB {
14	t.Helper()
15	db, err := sql.Open("sqlite3", ":memory:")
16	if err != nil {
17		t.Fatalf("open sqlite: %v", err)
18	}
19	db.SetMaxOpenConns(1)
20	t.Cleanup(func() { _ = db.Close() })
21	if err := RunMigrations(context.Background(), db); err != nil {
22		t.Fatalf("run migrations: %v", err)
23	}
24	return db
25}
26
27func seedFavoriteWithTags(t *testing.T, db *sql.DB) (entity.FavoriteID, entity.TagID) {
28	t.Helper()
29	mustExec(t, db, `INSERT INTO favorites(id, url, title, shortcut_key, position) VALUES (1, 'https://one.example', 'One', 1, 1), (2, 'https://two.example', 'Two', NULL, 2)`)
30	mustExec(t, db, `INSERT INTO favorite_tags(id, name, color) VALUES (1, 'research', '#111111'), (2, 'engines', '#222222')`)
31	mustExec(t, db, `INSERT INTO favorite_tag_assignments(favorite_id, tag_id) VALUES (1, 1), (1, 2), (2, 1)`)
32	return 1, 1
33}
34
35func TestFavoriteRepositoryGetAllHydratesTags(t *testing.T) {
36	db := openFavoriteRepoTestDB(t)
37	seedFavoriteWithTags(t, db)
38	repo := NewFavoriteRepository(db)
39
40	favorites, err := repo.GetAll(context.Background())
41	if err != nil {
42		t.Fatalf("GetAll: %v", err)
43	}
44	if len(favorites) != 2 {
45		t.Fatalf("expected 2 favorites, got %d", len(favorites))
46	}
47	assertTagNames(t, favorites[0], "engines", "research")
48	assertTagNames(t, favorites[1], "research")
49}
50
51func TestFavoriteRepositoryFindByIDHydratesTags(t *testing.T) {
52	db := openFavoriteRepoTestDB(t)
53	favID, _ := seedFavoriteWithTags(t, db)
54	repo := NewFavoriteRepository(db)
55
56	fav, err := repo.FindByID(context.Background(), favID)
57	if err != nil {
58		t.Fatalf("FindByID: %v", err)
59	}
60	assertTagNames(t, fav, "engines", "research")
61}
62
63func TestFavoriteRepositoryFindByURLHydratesTags(t *testing.T) {
64	db := openFavoriteRepoTestDB(t)
65	seedFavoriteWithTags(t, db)
66	repo := NewFavoriteRepository(db)
67
68	fav, err := repo.FindByURL(context.Background(), "https://one.example")
69	if err != nil {
70		t.Fatalf("FindByURL: %v", err)
71	}
72	assertTagNames(t, fav, "engines", "research")
73}
74
75func TestFavoriteRepositoryGetByTagHydratesTags(t *testing.T) {
76	db := openFavoriteRepoTestDB(t)
77	_, tagID := seedFavoriteWithTags(t, db)
78	repo := NewFavoriteRepository(db)
79
80	favorites, err := repo.GetByTag(context.Background(), tagID)
81	if err != nil {
82		t.Fatalf("GetByTag: %v", err)
83	}
84	if len(favorites) != 2 {
85		t.Fatalf("expected 2 tagged favorites, got %d", len(favorites))
86	}
87	assertTagNames(t, favorites[0], "engines", "research")
88	assertTagNames(t, favorites[1], "research")
89}
90
91func TestFavoriteRepositoryGetByShortcutHydratesTags(t *testing.T) {
92	db := openFavoriteRepoTestDB(t)
93	seedFavoriteWithTags(t, db)
94	repo := NewFavoriteRepository(db)
95
96	fav, err := repo.GetByShortcut(context.Background(), 1)
97	if err != nil {
98		t.Fatalf("GetByShortcut: %v", err)
99	}
100	assertTagNames(t, fav, "engines", "research")
101}
102
103func TestFavoriteRepositoryNoTagsHydratesEmptyTags(t *testing.T) {
104	db := openFavoriteRepoTestDB(t)
105	mustExec(t, db, `INSERT INTO favorites(id, url, title, position) VALUES (1, 'https://none.example', 'None', 1)`)
106	repo := NewFavoriteRepository(db)
107
108	fav, err := repo.FindByID(context.Background(), 1)
109	if err != nil {
110		t.Fatalf("FindByID: %v", err)
111	}
112	if fav == nil {
113		t.Fatal("expected favorite")
114	}
115	if fav.Tags == nil || len(fav.Tags) != 0 {
116		t.Fatalf("expected non-nil empty Tags, got %#v", fav.Tags)
117	}
118}
119
120func TestFavoriteRepositorySaveUpdatesShortcutKey(t *testing.T) {
121	db := openFavoriteRepoTestDB(t)
122	mustExec(t, db, `INSERT INTO favorites(id, url, title, position) VALUES (1, 'https://shortcut.example', 'Shortcut', 1)`)
123	repo := NewFavoriteRepository(db)
124
125	fav, err := repo.FindByID(context.Background(), 1)
126	if err != nil {
127		t.Fatalf("FindByID: %v", err)
128	}
129	key := 5
130	fav.ShortcutKey = &key
131	err = repo.Save(context.Background(), fav)
132	if err != nil {
133		t.Fatalf("Save: %v", err)
134	}
135
136	updated, err := repo.GetByShortcut(context.Background(), 5)
137	if err != nil {
138		t.Fatalf("GetByShortcut: %v", err)
139	}
140	if updated == nil || updated.ID != fav.ID {
141		t.Fatalf("expected favorite %d by shortcut, got %#v", fav.ID, updated)
142	}
143
144	updated.ShortcutKey = nil
145	err = repo.Save(context.Background(), updated)
146	if err != nil {
147		t.Fatalf("Save clear shortcut: %v", err)
148	}
149	cleared, err := repo.FindByID(context.Background(), updated.ID)
150	if err != nil {
151		t.Fatalf("FindByID after clear: %v", err)
152	}
153	if cleared.ShortcutKey != nil {
154		t.Fatalf("expected shortcut cleared, got %d", *cleared.ShortcutKey)
155	}
156}
157
158func TestFavoriteRepositoryGetAllHydratesTagsInBatches(t *testing.T) {
159	db := openFavoriteRepoTestDB(t)
160	mustExec(t, db, `INSERT INTO favorite_tags(id, name, color) VALUES (1, 'batched', '#111111')`)
161	for i := int64(1); i <= favoriteTagHydrationBatchSize+5; i++ {
162		_, err := db.Exec(
163			`INSERT INTO favorites(id, url, title, position) VALUES (?, ?, ?, ?)`,
164			i,
165			fmt.Sprintf("https://batch.example/%d", i),
166			"Batch",
167			i,
168		)
169		if err != nil {
170			t.Fatalf("insert favorite %d: %v", i, err)
171		}
172		_, err = db.Exec(`INSERT INTO favorite_tag_assignments(favorite_id, tag_id) VALUES (?, 1)`, i)
173		if err != nil {
174			t.Fatalf("insert assignment %d: %v", i, err)
175		}
176	}
177	repo := NewFavoriteRepository(db)
178
179	favorites, err := repo.GetAll(context.Background())
180	if err != nil {
181		t.Fatalf("GetAll: %v", err)
182	}
183	if len(favorites) != favoriteTagHydrationBatchSize+5 {
184		t.Fatalf("expected %d favorites, got %d", favoriteTagHydrationBatchSize+5, len(favorites))
185	}
186	assertTagNames(t, favorites[0], "batched")
187	assertTagNames(t, favorites[len(favorites)-1], "batched")
188}
189
190func assertTagNames(t *testing.T, fav *entity.Favorite, names ...string) {
191	t.Helper()
192	if fav == nil {
193		t.Fatal("favorite is nil")
194	}
195	if len(fav.Tags) != len(names) {
196		t.Fatalf("expected %d tags, got %d (%#v)", len(names), len(fav.Tags), fav.Tags)
197	}
198	for i, name := range names {
199		if fav.Tags[i].Name != name {
200			t.Fatalf("tag %d: expected %q, got %q", i, name, fav.Tags[i].Name)
201		}
202	}
203}

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