catalogue_test.go RawHistoryFolder

1package catalogue
2
3import (
4	"encoding/json"
5	"fmt"
6	"strings"
7	"testing"
8	"time"
9
10	"github.com/bnema/vev/internal/protocol"
11	"github.com/stretchr/testify/require"
12)
13
14func TestRemoteCatalogStateContract(t *testing.T) {
15	require.Equal(t, uint16(3), RemoteCatalogSchemaVersion)
16
17	for _, state := range []RemoteCatalogSessionState{RemoteCatalogSessionUp, RemoteCatalogSessionDown, RemoteCatalogSessionBroken} {
18		t.Run(string(state), func(t *testing.T) {
19			err := ValidateRemoteCatalog(RemoteCatalog{
20				ProtocolVersion: protocol.Version,
21				SchemaVersion:   RemoteCatalogSchemaVersion,
22				Sessions: []RemoteCatalogSession{{
23					LifecycleID: [16]byte{1}, Name: "work", State: state,
24					Tabs: []RemoteCatalogTab{{ID: "tab-1"}},
25				}},
26			})
27			require.NoError(t, err)
28		})
29	}
30
31	err := ValidateRemoteCatalog(RemoteCatalog{
32		ProtocolVersion: protocol.Version,
33		SchemaVersion:   RemoteCatalogSchemaVersion,
34		Sessions: []RemoteCatalogSession{{
35			LifecycleID: [16]byte{1}, Name: "work", State: RemoteCatalogSessionState("running"),
36			Tabs: []RemoteCatalogTab{{ID: "tab-1"}},
37		}},
38	})
39	require.ErrorIs(t, err, ErrRemoteCatalogUnknownState)
40
41	err = ValidateRemoteCatalog(RemoteCatalog{ProtocolVersion: protocol.Version, Sessions: []RemoteCatalogSession{}})
42	var mismatch *RemoteCatalogVersionMismatchError
43	require.ErrorAs(t, err, &mismatch)
44	require.Equal(t, "catalog", mismatch.Kind)
45	require.Equal(t, RemoteCatalogSchemaVersion, mismatch.Want)
46}
47
48func TestValidateRemoteCatalogRejectsTerminalUnsafeText(t *testing.T) {
49	for _, tt := range []struct{ name, value string }{
50		{name: "bidi override", value: "\u202eoverride"},
51		{name: "line separator", value: "\u2028line separator"},
52		{name: "paragraph separator", value: "\u2029paragraph separator"},
53	} {
54		t.Run(tt.name, func(t *testing.T) {
55			err := ValidateRemoteCatalog(RemoteCatalog{
56				ProtocolVersion: protocol.Version,
57				SchemaVersion:   RemoteCatalogSchemaVersion,
58				Sessions: []RemoteCatalogSession{{
59					LifecycleID: [16]byte{1}, Name: "work", State: RemoteCatalogSessionUp,
60					Tabs: []RemoteCatalogTab{{ID: "tab-1", Detail: tt.value}},
61				}},
62			})
63			require.ErrorIs(t, err, ErrInvalidRemoteCatalog)
64		})
65	}
66}
67
68func TestValidateRemoteCatalogCacheEntriesRejectsTerminalUnsafeHost(t *testing.T) {
69	err := ValidateRemoteCatalogCacheEntries([]RemoteCatalogCacheEntry{{
70		Host: "host\u202eoverride", FetchedAt: time.Unix(1, 0), Sessions: []RemoteCatalogSession{},
71	}})
72	require.ErrorIs(t, err, ErrInvalidRemoteCatalog)
73}
74
75func TestValidateRemoteCatalogRejectsOversizedEncodedJSON(t *testing.T) {
76	sessions := make([]RemoteCatalogSession, 4)
77	for sessionIndex := range sessions {
78		tabs := make([]RemoteCatalogTab, 100)
79		for tabIndex := range tabs {
80			tabs[tabIndex] = RemoteCatalogTab{
81				ID:     fmt.Sprintf("tab-%d-%d", sessionIndex, tabIndex),
82				Index:  uint16(tabIndex),
83				Detail: strings.Repeat("<", RemoteCatalogMaxDetailBytes),
84			}
85		}
86		sessions[sessionIndex] = RemoteCatalogSession{
87			LifecycleID: [16]byte{byte(sessionIndex + 1)},
88			Name:        fmt.Sprintf("work-%d", sessionIndex),
89			State:       RemoteCatalogSessionUp,
90			Tabs:        tabs,
91		}
92	}
93	catalog := RemoteCatalog{
94		ProtocolVersion: protocol.Version,
95		SchemaVersion:   RemoteCatalogSchemaVersion,
96		Sessions:        sessions,
97	}
98	encoded, err := json.Marshal(catalog)
99	require.NoError(t, err)
100	require.Greater(t, len(encoded)+1, RemoteCatalogMaxCatalogBytes)
101	require.ErrorIs(t, ValidateRemoteCatalog(catalog), ErrRemoteCatalogTooLarge)
102}
103
104func TestValidateRemoteCatalogRejectsDuplicateSessionNames(t *testing.T) {
105	err := ValidateRemoteCatalog(RemoteCatalog{
106		ProtocolVersion: protocol.Version,
107		SchemaVersion:   RemoteCatalogSchemaVersion,
108		Sessions: []RemoteCatalogSession{
109			{LifecycleID: [16]byte{1}, Name: "work", State: RemoteCatalogSessionUp, Tabs: []RemoteCatalogTab{}},
110			{LifecycleID: [16]byte{2}, Name: "work", State: RemoteCatalogSessionDown, Tabs: []RemoteCatalogTab{}},
111		},
112	})
113	require.ErrorIs(t, err, ErrInvalidRemoteCatalog)
114	require.ErrorContains(t, err, "duplicate session name")
115}

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