test(daemon): cover fresh wake before welcome
- commit
- 97fb27acf187ba444249d5811e51fb8d46a2305f
- author
- brice · · 2026-07-11 21:20 UTC
- parent
- 7c216c5
- signature
- GPG Verified
CD88F9E564E0102646FB6077A1A07C7C7C3E349D· arch-gpg Verified for the committer email
1 file · +55 -0
A internal/usecase/daemon/render_coordinator_handshake_test.go +55 -0
| @@ -0,0 +1,55 @@ | |||
| 1 | + | package daemon | |
| 2 | + | ||
| 3 | + | import ( | |
| 4 | + | "testing" | |
| 5 | + | "time" | |
| 6 | + | ||
| 7 | + | "github.com/stretchr/testify/require" | |
| 8 | + | ) | |
| 9 | + | ||
| 10 | + | // Fresh producer state may arrive after route has published an attachment but | |
| 11 | + | // before the transport has accepted Welcome. It must remain pending until the | |
| 12 | + | // exact attachment incarnation completes that handshake. | |
| 13 | + | func TestRenderCoordinatorFreshWakeWaitsForWelcome(t *testing.T) { | |
| 14 | + | h := newCoordinatorHarness(t) | |
| 15 | + | ac := &attachedClient{} | |
| 16 | + | h.rc.attach(ac) | |
| 17 | + | ||
| 18 | + | h.rc.invalidate(renderInvalidation{class: invalidateOutput, reset: true, producer: "pty"}) | |
| 19 | + | timers := h.armedTimers(t) | |
| 20 | + | require.Len(t, timers, 1) | |
| 21 | + | timers[0].ch <- time.Time{} | |
| 22 | + | requireNoWake(t, h.wakes) | |
| 23 | + | ||
| 24 | + | require.True(t, h.rc.markAttachmentReady(ac)) | |
| 25 | + | h.rc.fireCurrent(false) | |
| 26 | + | wake := awaitWake(t, h.wakes) | |
| 27 | + | require.True(t, wake.reset) | |
| 28 | + | require.Equal(t, 1, wake.coalesced) | |
| 29 | + | require.Same(t, ac, wake.attachment) | |
| 30 | + | requireNoWake(t, h.wakes) | |
| 31 | + | } | |
| 32 | + | ||
| 33 | + | // Parking and resuming the same client object is a new incarnation: a failed | |
| 34 | + | // Welcome on the replacement transport must not revive readiness from before | |
| 35 | + | // the park. | |
| 36 | + | func TestRenderCoordinatorParkedResumeRequiresNewWelcome(t *testing.T) { | |
| 37 | + | h := newCoordinatorHarness(t) | |
| 38 | + | ac := &attachedClient{} | |
| 39 | + | h.rc.attach(ac) | |
| 40 | + | require.True(t, h.rc.markAttachmentReady(ac)) | |
| 41 | + | h.rc.notePark(ac) | |
| 42 | + | h.rc.attach(ac) | |
| 43 | + | ||
| 44 | + | h.rc.invalidate(renderInvalidation{class: invalidateUrgent, reset: true, producer: "session"}) | |
| 45 | + | timers := h.armedTimers(t) | |
| 46 | + | require.Len(t, timers, 1) | |
| 47 | + | timers[0].ch <- time.Time{} | |
| 48 | + | requireNoWake(t, h.wakes) | |
| 49 | + | ||
| 50 | + | require.True(t, h.rc.markAttachmentReady(ac)) | |
| 51 | + | h.rc.fireCurrent(false) | |
| 52 | + | wake := awaitWake(t, h.wakes) | |
| 53 | + | require.True(t, wake.reset) | |
| 54 | + | require.Equal(t, 1, wake.coalesced) | |
| 55 | + | } | |
| @@ -0,0 +1,55 @@ | |||||
| 1 | + | package daemon | |||
| 2 | + | ||||
| 3 | + | import ( | |||
| 4 | + | "testing" | |||
| 5 | + | "time" | |||
| 6 | + | ||||
| 7 | + | "github.com/stretchr/testify/require" | |||
| 8 | + | ) | |||
| 9 | + | ||||
| 10 | + | // Fresh producer state may arrive after route has published an attachment but | |||
| 11 | + | // before the transport has accepted Welcome. It must remain pending until the | |||
| 12 | + | // exact attachment incarnation completes that handshake. | |||
| 13 | + | func TestRenderCoordinatorFreshWakeWaitsForWelcome(t *testing.T) { | |||
| 14 | + | h := newCoordinatorHarness(t) | |||
| 15 | + | ac := &attachedClient{} | |||
| 16 | + | h.rc.attach(ac) | |||
| 17 | + | ||||
| 18 | + | h.rc.invalidate(renderInvalidation{class: invalidateOutput, reset: true, producer: "pty"}) | |||
| 19 | + | timers := h.armedTimers(t) | |||
| 20 | + | require.Len(t, timers, 1) | |||
| 21 | + | timers[0].ch <- time.Time{} | |||
| 22 | + | requireNoWake(t, h.wakes) | |||
| 23 | + | ||||
| 24 | + | require.True(t, h.rc.markAttachmentReady(ac)) | |||
| 25 | + | h.rc.fireCurrent(false) | |||
| 26 | + | wake := awaitWake(t, h.wakes) | |||
| 27 | + | require.True(t, wake.reset) | |||
| 28 | + | require.Equal(t, 1, wake.coalesced) | |||
| 29 | + | require.Same(t, ac, wake.attachment) | |||
| 30 | + | requireNoWake(t, h.wakes) | |||
| 31 | + | } | |||
| 32 | + | ||||
| 33 | + | // Parking and resuming the same client object is a new incarnation: a failed | |||
| 34 | + | // Welcome on the replacement transport must not revive readiness from before | |||
| 35 | + | // the park. | |||
| 36 | + | func TestRenderCoordinatorParkedResumeRequiresNewWelcome(t *testing.T) { | |||
| 37 | + | h := newCoordinatorHarness(t) | |||
| 38 | + | ac := &attachedClient{} | |||
| 39 | + | h.rc.attach(ac) | |||
| 40 | + | require.True(t, h.rc.markAttachmentReady(ac)) | |||
| 41 | + | h.rc.notePark(ac) | |||
| 42 | + | h.rc.attach(ac) | |||
| 43 | + | ||||
| 44 | + | h.rc.invalidate(renderInvalidation{class: invalidateUrgent, reset: true, producer: "session"}) | |||
| 45 | + | timers := h.armedTimers(t) | |||
| 46 | + | require.Len(t, timers, 1) | |||
| 47 | + | timers[0].ch <- time.Time{} | |||
| 48 | + | requireNoWake(t, h.wakes) | |||
| 49 | + | ||||
| 50 | + | require.True(t, h.rc.markAttachmentReady(ac)) | |||
| 51 | + | h.rc.fireCurrent(false) | |||
| 52 | + | wake := awaitWake(t, h.wakes) | |||
| 53 | + | require.True(t, wake.reset) | |||
| 54 | + | require.Equal(t, 1, wake.coalesced) | |||
| 55 | + | } | |||