navigation_attachment_incarnation_test.go RawHistoryFolder
1package daemon
2
3import (
4 "testing"
5
6 "github.com/bnema/vev/internal/domain"
7 "github.com/bnema/vev/internal/usecase/picker"
8 "github.com/stretchr/testify/require"
9)
10
11func TestNavigationHandoffDoesNotMutateAfterInitiatorIncarnationChanges(t *testing.T) {
12 tests := []struct {
13 name string
14 rebind func(*testing.T, *Daemon, *session, *attachedClient)
15 }{
16 {
17 name: "role generation and lease replaced on same attachment",
18 rebind: func(t *testing.T, d *Daemon, source *session, ac *attachedClient) {
19 result, err := d.transitionAttachment(attachmentTransitionRequest{
20 source: source, target: source, next: ac,
21
22 expectedTransport: ac.transportSnapshot(), ready: true,
23 })
24 require.NoError(t, err)
25 d.deferAttachmentTransitionCleanups(result)
26 },
27 },
28 {
29 name: "transport incarnation rebound on same attachment",
30 rebind: func(t *testing.T, _ *Daemon, source *session, ac *attachedClient) {
31 ac.replaceTransport(&closeTrackingTransport{})
32 current := source.attachmentToken(ac, ac.transport())
33 current.lease = source.renderCoordinator().attachmentLease(ac)
34 ac.publishAttachmentCapability(current)
35 },
36 },
37 }
38
39 for _, tt := range tests {
40 t.Run(tt.name, func(t *testing.T) {
41 d, source, ac, _ := newManualSessionWithPTYs(t, nil)
42 target := &session{sessionCore: sessionCore{id: "target", name: "target", incarnation: domain.SessionLifecycleID{1}}, ctx: source.ctx, cancel: func() {}, tabs: []*tab{
43 newTab(nil, domain.Size{Cols: 80, Rows: 23}),
44 newTab(nil, domain.Size{Cols: 80, Rows: 23}),
45 }}
46 d.mu.Lock()
47 d.sessions[target.id] = target
48 d.mu.Unlock()
49
50 rc := d.attachCoordinator(source, nil, ac, true)
51 token := source.attachmentToken(ac, ac.transport())
52 token.lease = rc.attachmentLease(ac)
53 ac.publishAttachmentCapability(token)
54 effect, admitted := ac.beginAttachmentEffect(token)
55 require.True(t, admitted)
56
57 ended := make(chan struct{})
58 release := make(chan struct{})
59 d.afterActionAttachmentEffectEnded = func(action string) {
60 if action == "detach" {
61 close(ended)
62 <-release
63 }
64 }
65 done := make(chan error, 1)
66 go func() {
67 done <- d.switchToTargetForAttachment(effect.connectionToken(), picker.Target{Session: target.id, TabIndex: 1}, sessionHandoffGuard{}, "test-handoff")
68 }()
69 awaitTestCompletion(t, ended, "test handoff did not release its role ticket")
70 tt.rebind(t, d, source, ac)
71 close(release)
72 require.NoError(t, awaitTestValue(t, done, "test handoff did not finish"))
73
74 require.Same(t, source, ac.currentSession())
75 target.mu.Lock()
76 require.Empty(t, target.snapshotAttachmentsLocked())
77 require.Zero(t, testAttachmentTabIndexLocked(target))
78 target.mu.Unlock()
79 })
80 }
81}