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}