vevs_v4_golden_test.go RawHistoryFolder

1package snapshotcodec
2
3import (
4	"bytes"
5	"encoding/hex"
6	"testing"
7
8	vt "github.com/bnema/vev-vt"
9	"github.com/bnema/vev/internal/domain/layout"
10)
11
12const compactVEVSGolden = "564556530005000000000104dbb0765f0007726573746f7265000000000000007b0000000100037461620050001800000000000000020001700000017000010001700006737461626c6500042f746d70000000010000003f5654433100000001000000000000002a0000000200000001000000010000000000000000000000290000000100000000410000000000000000200000000000000000105654433100000000000000000000002a000000645654433100000001000000000000002b000000020000000100000002000000000900000000000000000000ffffffffffffffff010203000000000000000000000000000000000000000000002a00000002000000754c000000010000000000000000010100"
13
14func TestVEVSV4CompactGoldenRestore(t *testing.T) {
15	const golden = compactVEVSGolden
16	data, err := hex.DecodeString(golden)
17	if err != nil {
18		t.Fatal(err)
19	}
20
21	got, err := Unmarshal(data)
22	if err != nil {
23		t.Fatalf("Unmarshal golden VEVS: %v", err)
24	}
25	if got.Name != "restore" || got.CreatedAt != 123 || len(got.Tabs) != 1 {
26		t.Fatalf("session header = %#v", got)
27	}
28	tab := got.Tabs[0]
29	if tab.StableID != "tab" || tab.Cols != 80 || tab.Rows != 24 || tab.NextPaneID != 2 || tab.Focus != layout.PaneID("p") || len(tab.Panes) != 1 {
30		t.Fatalf("tab header = %#v", tab)
31	}
32	pane := tab.Panes[0]
33	if pane.ID != layout.PaneID("p") || pane.StableID != "stable" || pane.Cwd != "/tmp" || pane.Process != nil || len(pane.SealedChunks) != 1 {
34		t.Fatalf("pane header = %#v", pane)
35	}
36
37	sealed, err := vt.UnmarshalHistory(pane.SealedChunks[0])
38	if err != nil {
39		t.Fatalf("sealed history: %v", err)
40	}
41	if sealed.RowID(0) != 41 || sealed.Row(0)[0].Rune != 'A' {
42		t.Fatalf("sealed history = row %d %#v", sealed.RowID(0), sealed.Row(0))
43	}
44	tail, err := vt.UnmarshalHistory(pane.Tail)
45	if err != nil {
46		t.Fatalf("history tail: %v", err)
47	}
48	if tail.Len() != 0 || tail.NextRowID() != 42 {
49		t.Fatalf("history tail = len %d next %d", tail.Len(), tail.NextRowID())
50	}
51	transcript, err := vt.UnmarshalHistory(pane.Transcript)
52	if err != nil {
53		t.Fatalf("recovery transcript: %v", err)
54	}
55	if transcript.RowID(0) != 42 || transcript.Row(0)[0].Rune != '界' {
56		t.Fatalf("recovery transcript = row %d %#v", transcript.RowID(0), transcript.Row(0))
57	}
58
59	screen, err := vt.NewScreenWithRecoveryTranscript(8, 2, vt.HistoryConfig{MaxRows: 8, ChunkRows: 2}, pane.SealedChunks, pane.Tail, pane.Transcript)
60	if err != nil {
61		t.Fatalf("restore VT history: %v", err)
62	}
63	view := screen.History().View()
64	if view.Len() != 2 || view.RowID(0) != 41 || view.RowID(1) != 42 || view.NextRowID() != 43 {
65		t.Fatalf("restored history = len %d IDs %d/%d next %d", view.Len(), view.RowID(0), view.RowID(1), view.NextRowID())
66	}
67}
68
69func TestVEVSV4GoldenRejectsMalformedInput(t *testing.T) {
70	const golden = compactVEVSGolden
71	valid, err := hex.DecodeString(golden)
72	if err != nil {
73		t.Fatal(err)
74	}
75	mutate := func(fn func([]byte)) []byte {
76		out := append([]byte(nil), valid...)
77		fn(out)
78		return out
79	}
80	cases := []struct {
81		name string
82		data []byte
83	}{
84		{name: "truncated", data: valid[:len(valid)-1]},
85		{name: "trailing bytes", data: append(append([]byte(nil), valid...), 0)},
86		{name: "bad magic", data: mutate(func(data []byte) { data[0] ^= 0xff })},
87		{name: "bad version", data: mutate(func(data []byte) { data[4] = 3 })},
88		{name: "bad flags", data: mutate(func(data []byte) { data[7] = 1 })},
89		{name: "bad crc", data: mutate(func(data []byte) { data[15] ^= 0xff })},
90	}
91
92	body := append([]byte(nil), valid[16:]...)
93	if offset := bytes.Index(body, []byte("VTC1")); offset < 0 {
94		t.Fatal("golden has no VT blob")
95	} else {
96		body[offset+3] = '2'
97		cases = append(cases, struct {
98			name string
99			data []byte
100		}{name: "bad nested VT version", data: v4Envelope(version, 0, body)})
101	}
102
103	for _, test := range cases {
104		t.Run(test.name, func(t *testing.T) {
105			reject(t, test.data, nil)
106		})
107	}
108	for length := 0; length < len(valid); length++ {
109		reject(t, valid[:length], nil)
110	}
111}

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