1package daemon
2
3import (
4 "testing"
5
6 "github.com/bnema/vev/internal/domain"
7 "github.com/bnema/vev/internal/ports"
8 "github.com/stretchr/testify/require"
9)
10
11func TestAttachmentStatusUsesClientRouteSnapshot(t *testing.T) {
12 d, sess, ac, _ := newManualSessionWithPTYs(t, nil)
13 ac.setRouteSnapshot(ports.RecentRouteSnapshot{
14 Generation: 2,
15 Active: ports.RouteRef{Key: 3, Generation: 2},
16 Entries: []ports.RecentRouteEntry{
17 {Key: 2, Generation: 1, Name: "logs", HostLabel: "edge", Kind: ports.RouteKindRemote},
18 {Key: 1, Generation: 1, Name: "work", Kind: ports.RouteKindLocal},
19 },
20 })
21
22 state := d.barStateForAttachmentPaletteHintsFor(sess, ac, "", nil, ports.RecentRouteSnapshot{})
23
24 require.Len(t, state.mru, 2)
25 require.Equal(t, []string{"logs@edge", "work"}, []string{state.mru[0].name, state.mru[1].name})
26}
27
28func TestAttachmentStatusResolvesSubscribedRouteAttention(t *testing.T) {
29 for _, tt := range []struct {
30 name string
31 targetID domain.SessionLifecycleID
32 attention bool
33 }{
34 {name: "matching lifecycle", targetID: domain.SessionLifecycleID{9}, attention: true},
35 {name: "stale lifecycle", targetID: domain.SessionLifecycleID{8}, attention: false},
36 } {
37 t.Run(tt.name, func(t *testing.T) {
38 d, sess, ac, _ := newManualSessionWithPTYs(t, nil)
39 sess.mu.Lock()
40 sess.incarnation = domain.SessionLifecycleID{9}
41 sess.tabs[0].attention = true
42 sess.mu.Unlock()
43 ref := ports.RouteRef{Key: 2, Generation: 1}
44 ac.setRouteSnapshot(ports.RecentRouteSnapshot{
45 Generation: 2,
46 Active: ports.RouteRef{Key: 3, Generation: 2},
47 Entries: []ports.RecentRouteEntry{{Key: ref.Key, Generation: ref.Generation, Name: sess.name, Kind: ports.RouteKindLocal}},
48 })
49 ac.setRouteAttentionSubscription(ports.RouteAttentionSubscription{Targets: []ports.RouteAttentionTarget{{
50 Ref: ref,
51 Target: ports.ExactSessionTarget{
52 LifecycleID: tt.targetID,
53 SessionName: sess.name,
54 },
55 }}})
56
57 state := d.barStateForAttachmentPaletteHintsFor(sess, ac, "", nil, ports.RecentRouteSnapshot{})
58
59 require.Len(t, state.mru, 1)
60 require.Equal(t, tt.attention, state.mru[0].attention)
61 })
62 }
63}
64
65func TestRecentRouteSnapshotRepaintsWithoutDeferredIdentity(t *testing.T) {
66 d, sess, ac, sends := newManualSessionWithPTYs(t, nil)
67 token := sess.attachmentToken(ac, ac.transport())
68 ac.publishAttachmentCapability(token)
69 ac.setRouteSnapshot(ports.RecentRouteSnapshot{Generation: 1})
70
71 payload, err := ports.MarshalRecentRouteSnapshot(ports.RecentRouteSnapshot{Generation: 2})
72 require.NoError(t, err)
73 require.False(t, d.handleAttachmentClientFrame(token, ports.Frame{Type: ports.MsgRecentRouteSnapshot, Payload: payload}))
74
75 awaitFrame(t, sends, ports.MsgOutput)
76}
77
78func TestAttachmentRouteSnapshotCopiesPublishedEntries(t *testing.T) {
79 _, _, ac, _ := newManualSessionWithPTYs(t, nil)
80 entries := []ports.RecentRouteEntry{{Key: 1, Generation: 2, Name: "before", Kind: ports.RouteKindLocal}}
81 snapshot := ports.RecentRouteSnapshot{Generation: 3, Entries: entries}
82 ac.setRouteSnapshot(snapshot)
83
84 entries[0].Name = "after"
85 got := ac.routeSnapshotCopy()
86 require.Equal(t, "before", got.Entries[0].Name)
87 got.Entries[0].Name = "mutated copy"
88 require.Equal(t, "before", ac.routeSnapshotCopy().Entries[0].Name)
89}
90
91func TestPaletteRecentRouteSelectionSendsTypedClientAction(t *testing.T) {
92 d, source, ac, _ := newManualSessionWithPTYs(t, nil)
93 transport := &closeTrackingTransport{}
94 ac.replaceTransport(transport)
95 rc := d.attachCoordinator(source, nil, ac, true)
96 token := source.attachmentToken(ac, transport)
97 token.lease = rc.attachmentLease(ac)
98 ac.publishAttachmentCapability(token)
99 effect, admitted := ac.beginAttachmentEffect(token)
100 require.True(t, admitted)
101 defer effect.End()
102 token.effect = effect
103
104 exec := paletteExec{
105 d: d, sess: source, attachment: source, ac: ac,
106 routeSnapshot: ports.RecentRouteSnapshot{
107 Generation: 4,
108 Entries: []ports.RecentRouteEntry{{Key: 2, Generation: 3, Name: "remote", Kind: ports.RouteKindRemote}},
109 },
110 effect: effect,
111 }
112 require.NoError(t, exec.JumpRecentSession(1))
113
114 frames := transport.Sends()
115 var action ports.RouteNavigationAction
116 found := false
117 for _, frame := range frames {
118 if frame.Type != ports.MsgNavigateRecentRoute {
119 continue
120 }
121 var err error
122 action, err = ports.UnmarshalRouteNavigationAction(frame.Payload)
123 require.NoError(t, err)
124 found = true
125 }
126 require.True(t, found)
127 require.Equal(t, ports.RouteNavigationAction{SnapshotGeneration: 4, Key: 2, Generation: 3}, action)
128}
129
130func TestRecentRouteHintsRetainSnapshotSelectionIdentity(t *testing.T) {
131 snapshot := ports.RecentRouteSnapshot{
132 Generation: 8,
133 Entries: []ports.RecentRouteEntry{{
134 Key: 11, Generation: 7, Name: "logs", HostLabel: "edge", Kind: ports.RouteKindRemote,
135 }},
136 }
137
138 hints := recentRouteHints(snapshot, nil)
139
140 require.Len(t, hints.Recent, 1)
141 require.Equal(t, "logs@edge", hints.Recent[0].Name)
142 require.Equal(t, uint64(8), hints.Recent[0].SnapshotGeneration)
143 require.Equal(t, uint64(11), hints.Recent[0].Key)
144 require.Equal(t, uint64(7), hints.Recent[0].Generation)
145}