exact_route_test.go RawHistoryFolder

1package daemon
2
3import (
4	"context"
5	"testing"
6
7	"github.com/bnema/vev/internal/domain"
8	"github.com/bnema/vev/internal/protocol"
9	"github.com/stretchr/testify/require"
10)
11
12func TestRouteExactSessionTargetSelectsLifecycle(t *testing.T) {
13	d := newTestDaemon(t, nil, stubClock{})
14	sess := addControlSession(d, "work", "tab-1", "pane-1")
15	sess.ephemeral = false
16	sess.incarnation = newTestLifecycle(t)
17	tr, _ := newCapturingTransport(t)
18	target := protocol.ExactSessionTarget{LifecycleID: sess.incarnation, SessionName: sess.name}
19	hello := protocol.Hello{
20		Version:     protocol.Version,
21		Intent:      protocol.IntentAttach,
22		Name:        target.SessionName,
23		Size:        domain.Size{Cols: 80, Rows: 24},
24		ExactTarget: &target,
25	}
26
27	routed, ac, err := d.routeWithContext(context.Background(), hello, tr)
28	require.NoError(t, err)
29	require.Same(t, sess, routed)
30	require.Same(t, sess, ac.currentAttachmentSession())
31	d.clientGone(sess, ac, tr, false)
32}
33
34func TestStoppedSessionAttachTargetAcceptsSameLifecycleNowLive(t *testing.T) {
35	d := newTestDaemon(t, nil, stubClock{})
36	sess := addControlSession(d, "work", "tab-1", "pane-1")
37	sess.ephemeral = false
38	sess.incarnation = newTestLifecycle(t)
39	tr, _ := newCapturingTransport(t)
40	target := protocol.SessionAttachTarget{LifecycleID: sess.incarnation, SessionName: sess.name, TabID: domain.TabStableID(sess.tabs[0].stableID), TabIndex: protocol.NoTabIndex, Stopped: true}
41	hello := protocol.Hello{Version: protocol.Version, Intent: protocol.IntentAttach, Name: sess.name, Size: domain.Size{Cols: 80, Rows: 24}, SessionTarget: &target, EnvironmentPolicy: protocol.EnvironmentPolicyDaemonOwned}
42
43	routed, ac, err := d.routeWithContext(context.Background(), hello, tr)
44	require.NoError(t, err)
45	require.Same(t, sess, routed)
46	d.mu.Lock()
47	require.Len(t, d.sessions, 1)
48	d.mu.Unlock()
49	d.clientGone(sess, ac, tr, false)
50}
51
52func TestSessionAttachTargetRejectsLifecycleReplacement(t *testing.T) {
53	d := newTestDaemon(t, nil, stubClock{})
54	sess := addControlSession(d, "work", "tab-1", "pane-1")
55	sess.incarnation = newTestLifecycle(t)
56	stale := sess.incarnation
57	sess.incarnation[0]++
58	tr, _ := newCapturingTransport(t)
59	target := protocol.SessionAttachTarget{LifecycleID: stale, SessionName: sess.name, TabID: domain.TabStableID(sess.tabs[0].stableID), TabIndex: protocol.NoTabIndex}
60	hello := protocol.Hello{Version: protocol.Version, Intent: protocol.IntentAttach, Name: sess.name, Size: domain.Size{Cols: 80, Rows: 24}, SessionTarget: &target, EnvironmentPolicy: protocol.EnvironmentPolicyDaemonOwned}
61
62	_, _, err := d.routeWithContext(context.Background(), hello, tr)
63	var protocolErr *protoErr
64	require.ErrorAs(t, err, &protocolErr)
65	require.Equal(t, protocol.ErrNoSuchTarget, protocolErr.code)
66}
67
68func TestRouteExactSessionTargetRejectsNameMismatch(t *testing.T) {
69	d := newTestDaemon(t, nil, stubClock{})
70	sess := addControlSession(d, "work", "tab-1", "pane-1")
71	sess.ephemeral = false
72	sess.incarnation = newTestLifecycle(t)
73	tr, _ := newCapturingTransport(t)
74	hello := protocol.Hello{
75		Version:     protocol.Version,
76		Intent:      protocol.IntentAttach,
77		Name:        "other",
78		Size:        domain.Size{Cols: 80, Rows: 24},
79		ExactTarget: &protocol.ExactSessionTarget{LifecycleID: sess.incarnation, SessionName: sess.name},
80	}
81
82	_, _, err := d.routeWithContext(context.Background(), hello, tr)
83	var protocolErr *protoErr
84	require.ErrorAs(t, err, &protocolErr)
85	require.Equal(t, protocol.ErrNoSuchSession, protocolErr.code)
86}
87
88func TestLockedExactSessionTargetRejectsReplacement(t *testing.T) {
89	d := newTestDaemon(t, nil, stubClock{})
90	sess := addControlSession(d, "work", "tab-1", "pane-1")
91	sess.ephemeral = false
92	sess.incarnation = newTestLifecycle(t)
93	target := protocol.ExactSessionTarget{LifecycleID: sess.incarnation, SessionName: sess.name}
94	replacement := sess.incarnation
95	replacement[0]++
96	sess.incarnation = replacement
97
98	d.mu.Lock()
99	err := d.validateExactSessionTargetLocked(target)
100	d.mu.Unlock()
101
102	var protocolErr *protoErr
103	require.ErrorAs(t, err, &protocolErr)
104	require.Equal(t, protocol.ErrNoSuchSession, protocolErr.code)
105}
106
107func TestRouteExactSessionTargetRejectsLifecycleReplacement(t *testing.T) {
108	d := newTestDaemon(t, nil, stubClock{})
109	sess := addControlSession(d, "work", "tab-1", "pane-1")
110	sess.ephemeral = false
111	sess.incarnation = newTestLifecycle(t)
112	tr, _ := newCapturingTransport(t)
113	wrong := sess.incarnation
114	wrong[0]++
115	hello := protocol.Hello{
116		Version:     protocol.Version,
117		Intent:      protocol.IntentAttach,
118		Name:        sess.name,
119		Size:        domain.Size{Cols: 80, Rows: 24},
120		ExactTarget: &protocol.ExactSessionTarget{LifecycleID: wrong, SessionName: sess.name},
121	}
122
123	_, _, err := d.routeWithContext(context.Background(), hello, tr)
124	var protocolErr *protoErr
125	require.ErrorAs(t, err, &protocolErr)
126	require.Equal(t, protocol.ErrNoSuchSession, protocolErr.code)
127}

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