v0.3.0 99dc266

remote_attach_test.go RawHistoryFolder

1package daemon
2
3import (
4	"testing"
5	"time"
6
7	"github.com/stretchr/testify/require"
8
9	"github.com/bnema/vev/internal/domain"
10	"github.com/bnema/vev/internal/ports"
11	"github.com/bnema/vev/internal/usecase/picker"
12)
13
14func remoteLifecycleForTest() domain.SessionLifecycleID {
15	var id domain.SessionLifecycleID
16	id[0] = 7
17	return id
18}
19
20func TestLocalPickerHandoffCarriesExactLifecycle(t *testing.T) {
21	d := newRemotePickerDaemon(nil)
22	source, ac, sends := addRemoteRefreshPickerOwner(t, d, "source")
23	target, _, _ := addRemoteRefreshPickerOwner(t, d, "target")
24	target.incarnation = remoteLifecycleForTest()
25	token := source.attachmentToken(ac, ac.transport())
26	effect, admitted := ac.beginAttachmentEffect(token)
27	require.True(t, admitted)
28	token.effect = effect
29	defer effect.End()
30
31	require.NoError(t, d.switchToTargetForAttachment(token, picker.Target{Session: target.id}, sessionHandoffGuard{}, "picker-select"))
32	frame := receiveRemotePicker(t, sends, "local attach target")
33	got, err := ports.UnmarshalAttachTarget(frame.Payload)
34	require.NoError(t, err)
35	require.Empty(t, got.Endpoint)
36	require.Equal(t, ports.IntentAttach, got.Intent)
37	require.Equal(t, &ports.ExactSessionTarget{LifecycleID: target.incarnation, SessionName: target.name}, got.ExactTarget)
38	require.Nil(t, ac.currentAttachmentSession())
39}
40
41func TestRemotePickerRichHandoffCarriesLifecycleTabAndPolicy(t *testing.T) {
42	d := newRemotePickerDaemon(nil)
43	lifecycle := remoteLifecycleForTest()
44	d.remoteCatalog.replaceCache([]ports.RemoteCatalogCacheEntry{{
45		Host: "arch", FetchedAt: time.Unix(10, 0), Sessions: []ports.RemoteCatalogSession{{
46			LifecycleID: lifecycle, Name: "work", State: "up", Tabs: []ports.RemoteCatalogTab{{ID: "tab-1", Index: 0, Name: "main"}},
47		}},
48	}})
49	d.remoteCatalog.mu.Lock()
50	d.remoteCatalog.status["arch"] = remoteHostFresh
51	d.remoteCatalog.mu.Unlock()
52	sess, ac, sends := addRemoteRefreshPickerOwner(t, d, "local")
53	token := sess.attachmentToken(ac, ac.transport())
54	effect, admitted := ac.beginAttachmentEffect(token)
55	require.True(t, admitted)
56	token.effect = effect
57	defer effect.End()
58	key := domain.RemoteSessionKey{Host: "arch", Name: "work", LifecycleID: lifecycle, DisplayOrigin: "arch"}
59	remoteTarget := domain.RemoteSessionTarget{Endpoint: "arch", DisplayOrigin: "arch", LifecycleID: lifecycle, SessionName: "work", LiveTabID: "tab-1"}
60	target := picker.Target{Session: key.ID(), RemoteKey: &key, RemoteTarget: &remoteTarget, TabID: "tab-1"}
61	require.NoError(t, d.sendRemoteAttachTargetForAttachment(token, target, key, sessionHandoffGuard{closePicker: true}, "picker-select"))
62	frame := receiveRemotePicker(t, sends, "rich attach target")
63	got, err := ports.UnmarshalAttachTarget(frame.Payload)
64	require.NoError(t, err)
65	require.NotNil(t, got.RemoteTarget)
66	require.Equal(t, remoteTarget, *got.RemoteTarget)
67	require.Equal(t, ports.EnvironmentPolicyDaemonOwned, got.EnvironmentPolicy)
68}
69
70func TestRemotePickerRichHandoffRejectsReplacedLifecycle(t *testing.T) {
71	d := newRemotePickerDaemon(nil)
72	lifecycle := remoteLifecycleForTest()
73	cachedLifecycle := lifecycle
74	cachedLifecycle[0]++
75	d.remoteCatalog.replaceCache([]ports.RemoteCatalogCacheEntry{{
76		Host: "arch", FetchedAt: time.Unix(10, 0), Sessions: []ports.RemoteCatalogSession{{
77			LifecycleID: cachedLifecycle, Name: "work", State: "up", Tabs: []ports.RemoteCatalogTab{{ID: "tab-1", Index: 0, Name: "main"}},
78		}},
79	}})
80	d.remoteCatalog.mu.Lock()
81	d.remoteCatalog.status["arch"] = remoteHostFresh
82	d.remoteCatalog.mu.Unlock()
83	sess, ac, sends := addRemoteRefreshPickerOwner(t, d, "local")
84	token := sess.attachmentToken(ac, ac.transport())
85	effect, admitted := ac.beginAttachmentEffect(token)
86	require.True(t, admitted)
87	token.effect = effect
88	defer effect.End()
89	key := domain.RemoteSessionKey{Host: "arch", Name: "work", LifecycleID: lifecycle, DisplayOrigin: "arch"}
90	remoteTarget := domain.RemoteSessionTarget{Endpoint: "arch", DisplayOrigin: "arch", LifecycleID: lifecycle, SessionName: "work", LiveTabID: "tab-1"}
91	target := picker.Target{Session: key.ID(), RemoteKey: &key, RemoteTarget: &remoteTarget, TabID: "tab-1"}
92	require.ErrorIs(t, d.sendRemoteAttachTargetForAttachment(token, target, key, sessionHandoffGuard{}, "picker-select"), errAttachmentTransition)
93	for {
94		select {
95		case frame := <-sends:
96			require.NotEqual(t, ports.MsgAttachTarget, frame.Type, "rejected handoff must not send an attach target")
97		default:
98			return
99		}
100	}
101}

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