detached ec1815e

test(snapshot): cover complete generation restore

commit
ec1815e0587d02f823b2bf2f07da45f7236a9dc4
author
brice · · 2026-07-21 19:52 UTC
committer
brice · · 2026-07-21 19:53 UTC
parent
ef93e42
signature
GPG VerifiedCD88F9E564E0102646FB6077A1A07C7C7C3E349D · arch-gpg Verified for the committer email
1 file · +59 -0
A internal/adapters/snapshot/repository_fault_test.go +59 -0
@@ -0,0 +1,59 @@
1+package snapshot
2+
3+import (
4+ "context"
5+ "errors"
6+ "os"
7+ "testing"
8+
9+ codec "github.com/bnema/vev/internal/usecase/snapshot"
10+)
11+
12+func TestRepositoryHeadFailureKeepsOldCompleteGeneration(t *testing.T) {
13+ dir := privateDir(t)
14+ repo := NewRepository(dir)
15+ first := repositoryPublication(t, "named", 1, []byte("one"))
16+ if err := repo.Publish(context.Background(), first); err != nil {
17+ t.Fatal(err)
18+ }
19+ second := repositoryPublication(t, "named", 2, []byte("two"))
20+ repo.hooks.beforeHeadWrite = func(string) error { return errors.New("injected HEAD failure") }
21+ if err := repo.Publish(context.Background(), second); err == nil {
22+ t.Fatal("Publish succeeded with injected HEAD failure")
23+ }
24+ repo.hooks.beforeHeadWrite = nil
25+ got, err := repo.Load(context.Background(), "named")
26+ if err != nil {
27+ t.Fatal(err)
28+ }
29+ if got.Generation != 1 {
30+ t.Fatalf("generation after failed publish = %d, want 1", got.Generation)
31+ }
32+}
33+
34+func TestRepositoryLoadFallsBackFromIncompleteNewestGeneration(t *testing.T) {
35+ dir := privateDir(t)
36+ repo := NewRepository(dir)
37+ first := repositoryPublication(t, "named", 1, []byte("one"))
38+ if err := repo.Publish(context.Background(), first); err != nil {
39+ t.Fatal(err)
40+ }
41+ second := repositoryPublication(t, "named", 2, []byte("two"))
42+ if err := repo.Publish(context.Background(), second); err != nil {
43+ t.Fatal(err)
44+ }
45+ manifest, err := codec.UnmarshalManifest(second.Manifest)
46+ if err != nil {
47+ t.Fatal(err)
48+ }
49+ if err := os.Remove(repo.objectPath(sessionKey("named"), manifest.Tabs[0].Panes[0].Tail.Digest)); err != nil {
50+ t.Fatal(err)
51+ }
52+ got, err := repo.Load(context.Background(), "named")
53+ if err != nil {
54+ t.Fatal(err)
55+ }
56+ if got.Generation != 1 || !got.Fallback {
57+ t.Fatalf("Load = generation %d fallback %v, want generation 1 fallback true", got.Generation, got.Fallback)
58+ }
59+}
@@ -0,0 +1,59 @@
1+package snapshot
2+
3+import (
4+ "context"
5+ "errors"
6+ "os"
7+ "testing"
8+
9+ codec "github.com/bnema/vev/internal/usecase/snapshot"
10+)
11+
12+func TestRepositoryHeadFailureKeepsOldCompleteGeneration(t *testing.T) {
13+ dir := privateDir(t)
14+ repo := NewRepository(dir)
15+ first := repositoryPublication(t, "named", 1, []byte("one"))
16+ if err := repo.Publish(context.Background(), first); err != nil {
17+ t.Fatal(err)
18+ }
19+ second := repositoryPublication(t, "named", 2, []byte("two"))
20+ repo.hooks.beforeHeadWrite = func(string) error { return errors.New("injected HEAD failure") }
21+ if err := repo.Publish(context.Background(), second); err == nil {
22+ t.Fatal("Publish succeeded with injected HEAD failure")
23+ }
24+ repo.hooks.beforeHeadWrite = nil
25+ got, err := repo.Load(context.Background(), "named")
26+ if err != nil {
27+ t.Fatal(err)
28+ }
29+ if got.Generation != 1 {
30+ t.Fatalf("generation after failed publish = %d, want 1", got.Generation)
31+ }
32+}
33+
34+func TestRepositoryLoadFallsBackFromIncompleteNewestGeneration(t *testing.T) {
35+ dir := privateDir(t)
36+ repo := NewRepository(dir)
37+ first := repositoryPublication(t, "named", 1, []byte("one"))
38+ if err := repo.Publish(context.Background(), first); err != nil {
39+ t.Fatal(err)
40+ }
41+ second := repositoryPublication(t, "named", 2, []byte("two"))
42+ if err := repo.Publish(context.Background(), second); err != nil {
43+ t.Fatal(err)
44+ }
45+ manifest, err := codec.UnmarshalManifest(second.Manifest)
46+ if err != nil {
47+ t.Fatal(err)
48+ }
49+ if err := os.Remove(repo.objectPath(sessionKey("named"), manifest.Tabs[0].Panes[0].Tail.Digest)); err != nil {
50+ t.Fatal(err)
51+ }
52+ got, err := repo.Load(context.Background(), "named")
53+ if err != nil {
54+ t.Fatal(err)
55+ }
56+ if got.Generation != 1 || !got.Fallback {
57+ t.Fatalf("Load = generation %d fallback %v, want generation 1 fallback true", got.Generation, got.Fallback)
58+ }
59+}

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 12

Tags 11

↑↓ moveenter openesc close