migration_test.go RawHistoryFolder

1package persist
2
3import (
4	"os"
5	"testing"
6
7	"github.com/stretchr/testify/require"
8
9	"github.com/bnema/vev/internal/domain"
10	"github.com/bnema/vev/pkg/kv"
11)
12
13const legacyRecordWireVersion = 5
14
15func TestOpenOrCreateMigrationPreservesBackupAndIsIdempotent(t *testing.T) {
16	t.Parallel()
17	record := domain.CatalogueRecord{
18		Name: "work", IncarnationID: domain.IncarnationID{1}, Cwd: "/workspace",
19		CreatedAt: 11, UpdatedAt: 22, LastUsedSeq: 33,
20		TabNames: []string{"shell"}, TabRecords: []domain.CatalogueTabRecord{{StableID: "tab-1", Name: "shell"}},
21		Committed: &domain.CheckpointRef{Generation: 3, ManifestDigest: [32]byte{3}},
22	}
23	legacy, err := encodeRecordValueForFormat(record, protocolRecordVersion, legacyRecordWireVersion)
24	require.NoError(t, err)
25	dir := privateDir(t)
26	store, err := kv.Open(StorePath(dir))
27	require.NoError(t, err)
28	require.NoError(t, store.Set([]byte(record.Name), legacy))
29	require.NoError(t, store.Close())
30	original, err := os.ReadFile(StorePath(dir))
31	require.NoError(t, err)
32	require.NoError(t, os.WriteFile(StorePath(dir)+legacyCatalogueBackupSuffix+".tmp", []byte("stale"), 0o600))
33
34	opened, err := OpenOrCreate(dir)
35	require.NoError(t, err)
36	require.Equal(t, []domain.CatalogueRecord{record}, opened.Records)
37	require.True(t, opened.Migration.Performed)
38	require.Equal(t, []uint16{protocolRecordVersion}, opened.Migration.SourceFormats)
39	require.Equal(t, catalogueRecordVersion, opened.Migration.TargetFormat)
40	require.Equal(t, 1, opened.Migration.RecordCount)
41	require.NoError(t, opened.Catalogue.Close())
42
43	backup, err := os.ReadFile(StorePath(dir) + legacyCatalogueBackupSuffix)
44	require.NoError(t, err)
45	require.Equal(t, original, backup)
46	info, err := os.Stat(StorePath(dir) + legacyCatalogueBackupSuffix)
47	require.NoError(t, err)
48	require.Equal(t, os.FileMode(0o600), info.Mode().Perm())
49	migrated, err := os.ReadFile(StorePath(dir))
50	require.NoError(t, err)
51
52	reopened, err := OpenOrCreate(dir)
53	require.NoError(t, err)
54	require.False(t, reopened.Migration.Performed)
55	require.Equal(t, []domain.CatalogueRecord{record}, reopened.Records)
56	require.NoError(t, reopened.Catalogue.Close())
57	after, err := os.ReadFile(StorePath(dir))
58	require.NoError(t, err)
59	require.Equal(t, migrated, after)
60}
61
62func TestOpenOrCreateMigratesV3Records(t *testing.T) {
63	t.Parallel()
64	legacyRecord := domain.CatalogueRecord{
65		Name: "work", IncarnationID: domain.IncarnationID{1}, Cwd: "/workspace",
66		CreatedAt: 11, UpdatedAt: 22, LastUsedSeq: 33, TabNames: []string{"shell"},
67		Committed: &domain.CheckpointRef{Generation: 3, ManifestDigest: [32]byte{3}},
68	}
69	legacy, err := encodeRecordValueForFormat(legacyRecord, legacyCatalogueRecordVersion, 0)
70	require.NoError(t, err)
71	dir := privateDir(t)
72	store, err := kv.Open(StorePath(dir))
73	require.NoError(t, err)
74	require.NoError(t, store.Set([]byte(legacyRecord.Name), legacy))
75	require.NoError(t, store.Close())
76	original, err := os.ReadFile(StorePath(dir))
77	require.NoError(t, err)
78
79	opened, err := OpenOrCreate(dir)
80	require.NoError(t, err)
81	want := legacyRecord
82	want.TabRecords = []domain.CatalogueTabRecord{{Name: "shell"}}
83	require.Equal(t, []domain.CatalogueRecord{want}, opened.Records)
84	require.Equal(t, []uint16{legacyCatalogueRecordVersion}, opened.Migration.SourceFormats)
85	require.NoError(t, opened.Catalogue.Close())
86	backup, err := os.ReadFile(StorePath(dir) + legacyCatalogueBackupSuffix)
87	require.NoError(t, err)
88	require.Equal(t, original, backup)
89}
90
91func TestOpenOrCreateMigrationRejectsConflictingBackupWithoutChangingCatalogue(t *testing.T) {
92	t.Parallel()
93	record := domain.CatalogueRecord{Name: "work", IncarnationID: domain.IncarnationID{1}}
94	legacy, err := encodeRecordValueForFormat(record, protocolRecordVersion, legacyRecordWireVersion)
95	require.NoError(t, err)
96	dir := privateDir(t)
97	store, err := kv.Open(StorePath(dir))
98	require.NoError(t, err)
99	require.NoError(t, store.Set([]byte(record.Name), legacy))
100	require.NoError(t, store.Close())
101	original, err := os.ReadFile(StorePath(dir))
102	require.NoError(t, err)
103	require.NoError(t, os.WriteFile(StorePath(dir)+legacyCatalogueBackupSuffix, []byte("different"), 0o600))
104
105	_, err = OpenOrCreate(dir)
106	require.Error(t, err)
107	after, readErr := os.ReadFile(StorePath(dir))
108	require.NoError(t, readErr)
109	require.Equal(t, original, after)
110}
111
112func TestOpenOrCreateMigrationRejectsSymlinkBackupWithoutChangingCatalogue(t *testing.T) {
113	t.Parallel()
114	record := domain.CatalogueRecord{Name: "work", IncarnationID: domain.IncarnationID{1}}
115	legacy, err := encodeRecordValueForFormat(record, protocolRecordVersion, legacyRecordWireVersion)
116	require.NoError(t, err)
117	dir := privateDir(t)
118	store, err := kv.Open(StorePath(dir))
119	require.NoError(t, err)
120	require.NoError(t, store.Set([]byte(record.Name), legacy))
121	require.NoError(t, store.Close())
122	original, err := os.ReadFile(StorePath(dir))
123	require.NoError(t, err)
124	require.NoError(t, os.Symlink(StorePath(dir), StorePath(dir)+legacyCatalogueBackupSuffix))
125
126	_, err = OpenOrCreate(dir)
127	require.Error(t, err)
128	after, readErr := os.ReadFile(StorePath(dir))
129	require.NoError(t, readErr)
130	require.Equal(t, original, after)
131}

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