1package sessionwire
2
3import (
4 "testing"
5
6 "github.com/bnema/vev/internal/domain"
7 "github.com/bnema/vev/internal/protocol"
8 "github.com/bnema/vev/internal/protocol/wire"
9 "github.com/stretchr/testify/require"
10)
11
12func TestUISynchronizationClientDispatch(t *testing.T) {
13 for _, message := range []protocol.ClientMessage{
14 protocol.Input{InputSeq: 3, ActionID: 7, Data: []byte("x")},
15 protocol.UIFence{ActionID: 7},
16 } {
17 clientRaw := &scriptedTransport{recv: []wire.Envelope{mustPreambleResponse(t)}}
18 require.NoError(t, NewClientConnection(clientRaw).SendClient(message))
19 sent := mustSingleAppPayload(t, clientRaw)
20 serverRaw := &scriptedTransport{recv: mustServerPreambleQueue(t, sent)}
21 got, err := NewServerConnection(serverRaw).ReceiveClient()
22 require.NoError(t, err)
23 require.Equal(t, message, got)
24 headerLen := len(sent)
25 if input, ok := message.(protocol.Input); ok {
26 // Input carries opaque bytes after the envelope header; only
27 // the envelope framing has strict prefix truncation semantics.
28 headerLen -= len(input.Data)
29 }
30 for size := range headerLen {
31 raw := &scriptedTransport{recv: mustServerPreambleQueue(t, sent[:size])}
32 _, err := NewServerConnection(raw).ReceiveClient()
33 require.Error(t, err, "prefix %d", size)
34 }
35 }
36}
37
38func testUIOutputContext() *protocol.ViewContext {
39 return &protocol.ViewContext{Publication: 4,
40 Route: protocol.CommittedRouteIdentity{Target: protocol.ExactSessionTarget{LifecycleID: domain.SessionLifecycleID{1}, SessionName: "work"}},
41 TabID: "tab-1", FocusedPaneID: "pane-1"}
42}
43
44func TestUISynchronizationServerDispatch(t *testing.T) {
45 context := *testUIOutputContext()
46 for _, test := range []struct {
47 name string
48 message protocol.ServerMessage
49 }{
50 {"output", protocol.Output{Epoch: 2, New: 1, Full: true, Size: domain.Size{Cols: 80, Rows: 24}, Context: &context}},
51 {"receipt", protocol.UIReceipt{ActionID: 7, Epoch: 2, State: 1, ViewPublication: 4, Outcome: protocol.UIReceiptProcessed}},
52 {"view update", protocol.UIViewUpdate{Epoch: 2, State: 1, Context: context}},
53 {"attach target", protocol.AttachTarget{CauseActionID: 7, Session: "work", Intent: protocol.IntentAttach}},
54 {"route action", protocol.RouteNavigationAction{CauseActionID: 7, SnapshotGeneration: 1, Key: 2, Generation: 3}},
55 {"route create", protocol.RouteCreateSessionAction{CauseActionID: 7, RequestID: 4, SnapshotGeneration: 1, Key: 2, Generation: 3, SessionName: "work"}},
56 } {
57 t.Run(test.name, func(t *testing.T) {
58 for _, async := range []bool{false, true} {
59 serverRaw := &capableTransport{}
60 server := NewServerConnection(serverRaw)
61 var sent []byte
62 if async {
63 require.NoError(t, server.SendServerAsync(test.message))
64 require.Equal(t, 1, len(serverRaw.async))
65 sent = serverRaw.async[0].Payload
66 } else {
67 require.NoError(t, server.SendServer(test.message))
68 require.Equal(t, 1, serverRaw.sentLen())
69 sent = serverRaw.sentPayload(0)
70 }
71 clientRaw := &scriptedTransport{recv: mustClientPreambleQueue(t, sent)}
72 got, err := NewClientConnection(clientRaw).ReceiveServer()
73 require.NoError(t, err)
74 require.Equal(t, test.message, got)
75 for size := range len(sent) {
76 raw := &scriptedTransport{recv: mustClientPreambleQueue(t, sent[:size])}
77 _, err := NewClientConnection(raw).ReceiveServer()
78 require.Error(t, err, "prefix %d", size)
79 }
80 raw := &scriptedTransport{recv: mustClientPreambleQueue(t, append(append([]byte(nil), sent...), 0))}
81 _, err = NewClientConnection(raw).ReceiveServer()
82 require.Error(t, err)
83 }
84 })
85 }
86}