1package client
2
3import (
4 "testing"
5
6 "github.com/bnema/vev/internal/domain"
7 "github.com/bnema/vev/internal/protocol"
8)
9
10func TestOutputApplyStateTransitions(t *testing.T) {
11 full := protocol.Output{Epoch: 1, Base: 0, New: 1, Full: true, ViewRevision: 2, Size: domain.Size{Cols: 80, Rows: 24}}
12 tests := []struct {
13 name string
14 state outputApplyState
15 output protocol.Output
16 accepted bool
17 want outputApplyState
18 }{
19 {name: "first full", output: full, accepted: true, want: outputApplyState{epoch: 1, state: 1, viewRevision: 2, initialized: true}},
20 {name: "first gap", output: protocol.Output{Epoch: 1, Base: 1, New: 2, ViewRevision: 2}, accepted: false},
21 {name: "increment", state: outputApplyState{epoch: 1, state: 1, viewRevision: 2, initialized: true}, output: protocol.Output{Epoch: 1, Base: 1, New: 2, ViewRevision: 2}, accepted: true, want: outputApplyState{epoch: 1, state: 2, viewRevision: 2, initialized: true}},
22 {name: "base gap", state: outputApplyState{epoch: 1, state: 1, viewRevision: 2, initialized: true}, output: protocol.Output{Epoch: 1, Base: 0, New: 2, Full: true, ViewRevision: 2}, accepted: false},
23 {name: "revision gap", state: outputApplyState{epoch: 1, state: 1, viewRevision: 2, initialized: true}, output: protocol.Output{Epoch: 1, Base: 1, New: 2, ViewRevision: 3}, accepted: false},
24 {name: "handoff side effect crosses newer epoch replay gate", state: outputApplyState{epoch: 1, state: 1, viewRevision: 2, initialized: true}, output: protocol.Output{Epoch: 2, ViewRevision: 3}, accepted: true, want: outputApplyState{epoch: 1, state: 1, viewRevision: 2, initialized: true}},
25 {name: "handoff side effect crosses stale epoch replay gate", state: outputApplyState{epoch: 2, state: 4, viewRevision: 5, initialized: true}, output: protocol.Output{Epoch: 1, ViewRevision: 2}, accepted: true, want: outputApplyState{epoch: 2, state: 4, viewRevision: 5, initialized: true}},
26 {name: "side effect cannot invent state", state: outputApplyState{epoch: 1, state: 1, viewRevision: 2, initialized: true}, output: protocol.Output{Epoch: 2, Base: 1}, accepted: false},
27 {name: "side effect cannot claim full replay", state: outputApplyState{epoch: 1, state: 1, viewRevision: 2, initialized: true}, output: protocol.Output{Epoch: 2, Full: true}, accepted: false},
28 {name: "new epoch reset", state: outputApplyState{epoch: 1, state: 1, viewRevision: 2, initialized: true}, output: protocol.Output{Epoch: 2, Base: 0, New: 1, Full: true, ViewRevision: 3}, accepted: true, want: outputApplyState{epoch: 2, state: 1, viewRevision: 3, initialized: true}},
29 }
30 for _, tt := range tests {
31 t.Run(tt.name, func(t *testing.T) {
32 if tt.state.initialized {
33 tt.state.context = testOutputView(1)
34 }
35 if tt.output.New != 0 {
36 context := testOutputView(2)
37 tt.output.Context = &context
38 tt.want.context = context
39 } else {
40 tt.want.context = tt.state.context
41 }
42 got, accepted := tt.state.next(tt.output)
43 if accepted != tt.accepted {
44 t.Fatalf("accepted = %v, want %v", accepted, tt.accepted)
45 }
46 if accepted && got != tt.want {
47 t.Fatalf("state = %+v, want %+v", got, tt.want)
48 }
49 })
50 }
51}