8 "github.com/stretchr/testify/require"
13 func TestLedgerReplaysFirstResultWithoutAnotherAction(t *testing.T) {
14 ledger, err := NewLedger(testDeviceID)
15 require.NoError(t, err)
17 transport := &recordingTransport{}
18 publisher := NewPublisher(transport)
21 var first ResultRecord
23 for attempt := range 3 {
24 result, exists := ledger.Lookup(testCommandID)
27 result, err = ledger.Remember(testCommandID, protocol.ResultPayload{Status: "completed"})
28 require.NoError(t, err)
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)
38 require.NoError(t, publisher.Result(t.Context(), testDeviceID, testCommandID, result.Metadata, result.Payload))
41 require.Equal(t, first, result)
44 require.Equal(t, 1, actions)
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])
51 ignored, err := ledger.Remember(testCommandID, protocol.ResultPayload{
53 Reason: "different outcome",
55 require.NoError(t, err)
56 require.Equal(t, first, ignored)
59 func TestLedgerEvictsOldestAndKeepsDevicesIndependent(t *testing.T) {
60 ledger, err := NewLedger(testDeviceID)
61 require.NoError(t, err)
63 first, err := ledger.Remember(testCommandID, protocol.ResultPayload{
65 Reason: "invalid output",
67 require.NoError(t, err)
69 other, err := NewLedger(testOtherDeviceID)
70 require.NoError(t, err)
72 independent, err := other.Remember(testCommandID, protocol.ResultPayload{
74 Reason: "unavailable",
76 require.NoError(t, err)
77 require.NotEqual(t, first.Payload, independent.Payload)
81 for range commandResultCapacity {
82 lastID, err = protocol.NewID(time.Now().UTC())
83 require.NoError(t, err)
85 _, err = ledger.Remember(lastID, protocol.ResultPayload{Status: "completed"})
86 require.NoError(t, err)
89 _, exists := ledger.Lookup(testCommandID)
90 require.False(t, exists)
92 _, exists = ledger.Lookup(lastID)
93 require.True(t, exists)
95 _, exists = other.Lookup(testCommandID)
96 require.True(t, exists)
99 func TestLedgerRejectsInvalidResultsAndCopiesReturnedTimes(t *testing.T) {
100 _, err := NewLedger("invalid")
101 require.Error(t, err)
103 ledger, err := NewLedger(testDeviceID)
104 require.NoError(t, err)
106 testCases := []struct {
108 payload protocol.ResultPayload
111 name: "failure requires a reason",
112 payload: protocol.ResultPayload{Status: "failed"},
115 name: "completion rejects a reason",
116 payload: protocol.ResultPayload{
118 Reason: "not allowed",
122 name: "unknown status rejects",
123 payload: protocol.ResultPayload{Status: "invalid"},
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)
132 _, exists := ledger.Lookup(testCommandID)
133 require.False(t, exists)
137 _, err = ledger.Remember("invalid", protocol.ResultPayload{Status: "completed"})
138 require.Error(t, err)
140 result, err := ledger.Remember(testCommandID, protocol.ResultPayload{Status: "completed"})
141 require.NoError(t, err)
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)
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)