]> git.giacomolli.uk - onehouse/blob - driver/ledger_test.go
Share the Alamo gateway request budget
[onehouse] / driver / ledger_test.go
1 package driver
2
3 import (
4 "errors"
5 "testing"
6 "time"
7
8 "github.com/stretchr/testify/require"
9
10 "onehouse/protocol"
11 )
12
13 func TestLedgerReplaysFirstResultWithoutAnotherAction(t *testing.T) {
14 ledger, err := NewLedger(testDeviceID)
15 require.NoError(t, err)
16
17 transport := &recordingTransport{}
18 publisher := NewPublisher(transport)
19 actions := 0
20
21 var first ResultRecord
22
23 for attempt := range 3 {
24 result, exists := ledger.Lookup(testCommandID)
25 if !exists {
26 actions++
27 result, err = ledger.Remember(testCommandID, protocol.ResultPayload{Status: "completed"})
28 require.NoError(t, err)
29
30 first = result
31 }
32
33 if attempt == 0 {
34 transport.setError(errors.New("delivery uncertain"))
35 require.Error(t, publisher.Result(t.Context(), testDeviceID, testCommandID, result.Metadata, result.Payload))
36 transport.setError(nil)
37 } else {
38 require.NoError(t, publisher.Result(t.Context(), testDeviceID, testCommandID, result.Metadata, result.Payload))
39 }
40
41 require.Equal(t, first, result)
42 }
43
44 require.Equal(t, 1, actions)
45
46 packets := transport.messages()
47 require.Len(t, packets, 3)
48 require.Equal(t, packets[0], packets[1])
49 require.Equal(t, packets[0], packets[2])
50
51 ignored, err := ledger.Remember(testCommandID, protocol.ResultPayload{
52 Status: "failed",
53 Reason: "different outcome",
54 })
55 require.NoError(t, err)
56 require.Equal(t, first, ignored)
57 }
58
59 func TestLedgerEvictsOldestAndKeepsDevicesIndependent(t *testing.T) {
60 ledger, err := NewLedger(testDeviceID)
61 require.NoError(t, err)
62
63 first, err := ledger.Remember(testCommandID, protocol.ResultPayload{
64 Status: "rejected",
65 Reason: "invalid output",
66 })
67 require.NoError(t, err)
68
69 other, err := NewLedger(testOtherDeviceID)
70 require.NoError(t, err)
71
72 independent, err := other.Remember(testCommandID, protocol.ResultPayload{
73 Status: "failed",
74 Reason: "unavailable",
75 })
76 require.NoError(t, err)
77 require.NotEqual(t, first.Payload, independent.Payload)
78
79 lastID := ""
80
81 for range commandResultCapacity {
82 lastID, err = protocol.NewID(time.Now().UTC())
83 require.NoError(t, err)
84
85 _, err = ledger.Remember(lastID, protocol.ResultPayload{Status: "completed"})
86 require.NoError(t, err)
87 }
88
89 _, exists := ledger.Lookup(testCommandID)
90 require.False(t, exists)
91
92 _, exists = ledger.Lookup(lastID)
93 require.True(t, exists)
94
95 _, exists = other.Lookup(testCommandID)
96 require.True(t, exists)
97 }
98
99 func TestLedgerRejectsInvalidResultsAndCopiesReturnedTimes(t *testing.T) {
100 _, err := NewLedger("invalid")
101 require.Error(t, err)
102
103 ledger, err := NewLedger(testDeviceID)
104 require.NoError(t, err)
105
106 testCases := []struct {
107 name string
108 payload protocol.ResultPayload
109 }{
110 {
111 name: "failure requires a reason",
112 payload: protocol.ResultPayload{Status: "failed"},
113 },
114 {
115 name: "completion rejects a reason",
116 payload: protocol.ResultPayload{
117 Status: "completed",
118 Reason: "not allowed",
119 },
120 },
121 {
122 name: "unknown status rejects",
123 payload: protocol.ResultPayload{Status: "invalid"},
124 },
125 }
126
127 for _, tc := range testCases {
128 t.Run(tc.name, func(t *testing.T) {
129 _, err = ledger.Remember(testCommandID, tc.payload)
130 require.Error(t, err)
131
132 _, exists := ledger.Lookup(testCommandID)
133 require.False(t, exists)
134 })
135 }
136
137 _, err = ledger.Remember("invalid", protocol.ResultPayload{Status: "completed"})
138 require.Error(t, err)
139
140 result, err := ledger.Remember(testCommandID, protocol.ResultPayload{Status: "completed"})
141 require.NoError(t, err)
142
143 observation := *result.Metadata.SentAt
144 *result.Metadata.SentAt = observation.Add(time.Hour)
145 first, exists := ledger.Lookup(testCommandID)
146 require.True(t, exists)
147 require.Equal(t, observation, *first.Metadata.SentAt)
148
149 *first.Metadata.SentAt = observation.Add(time.Hour)
150 second, err := ledger.Remember(testCommandID, protocol.ResultPayload{})
151 require.NoError(t, err)
152 require.Equal(t, observation, *second.Metadata.SentAt)
153 }