v0.3.0 99dc266

recent_route_snapshot_test.go RawHistoryFolder

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}

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