1package daemon
2
3import (
4 "testing"
5
6 "github.com/bnema/vev/internal/domain"
7 "github.com/stretchr/testify/require"
8)
9
10func TestValidateAttachmentTransitionLocksSourceCoordinatorBeforeLeaseValidation(t *testing.T) {
11 d := newTestDaemon(t, nil, stubClock{})
12 transport := &closeTrackingTransport{}
13 source := &session{sessionCore: sessionCore{id: domain.SessionID("source")}}
14 target := &session{sessionCore: sessionCore{id: domain.SessionID("target")}}
15 ac := &attachedClient{tr: transport}
16 ac.setSession(source)
17 source.mu.Lock()
18 registered := source.registerAttachmentLocked(ac)
19 source.mu.Unlock()
20 require.True(t, registered)
21 sourceCoordinator := newRenderCoordinator(renderCoordinatorOptions{})
22 source.installRenderCoordinator(sourceCoordinator)
23 sourceCoordinator.attach(ac)
24 token := source.captureAttachmentCapability(ac, transport)
25 require.NotNil(t, token.lease)
26 d.sessions[source.id] = source
27 d.sessions[target.id] = target
28
29 checked := false
30 coordinatorUnlocked := false
31 d.afterAttachmentTransitionCoordinatorsLocked = func() {
32 coordinatorUnlocked = sourceCoordinator.mu.TryLock()
33 if coordinatorUnlocked {
34 sourceCoordinator.mu.Unlock()
35 }
36 checked = true
37 }
38 req := attachmentTransitionRequest{
39 source: source, target: target, next: ac,
40 expectedTransport: ac.transportSnapshot(), sourceCapability: &token,
41 preflighted: true, attachmentEffectsFrozen: true,
42 }
43 d.mu.Lock()
44 d.notices.routingMu.Lock()
45 unlockSessions := lockAttachmentSessions(source, target)
46 publication, err := d.validateAttachmentTransitionPrelocked(req)
47 unlockSessions()
48 d.notices.routingMu.Unlock()
49 d.mu.Unlock()
50 if publication != nil {
51 publication.unlockCoordinators()
52 }
53 require.NoError(t, err)
54 require.False(t, coordinatorUnlocked, "source coordinator was not held during lease validation")
55 require.True(t, checked)
56 require.NotNil(t, publication)
57}
58
59func TestAttachmentCapabilityRevalidatesTheCapturedTransportIncarnation(t *testing.T) {
60 first := &closeTrackingTransport{}
61 second := &closeTrackingTransport{}
62 ac := &attachedClient{tr: first}
63 sess := &session{sessionCore: sessionCore{id: domain.SessionID("work")}}
64 ac.setSession(sess)
65 sess.mu.Lock()
66 registered := sess.registerAttachmentLocked(ac)
67 sess.mu.Unlock()
68 require.True(t, registered)
69
70 captured := make(chan struct{})
71 release := make(chan struct{})
72 ac.beforeAttachmentCapabilityValidation = func() {
73 close(captured)
74 <-release
75 }
76 result := make(chan attachmentCapability, 1)
77 go func() { result <- sess.captureAttachmentCapability(ac, first) }()
78 <-captured
79 ac.replaceTransport(second)
80 close(release)
81
82 token := <-result
83 require.Nil(t, token.ac, "a token must not bind a replacement transport")
84 require.Equal(t, uint64(1), ac.transportSnapshot().incarnation)
85 require.Same(t, second, ac.transport())
86}