1package domain
2
3import "testing"
4
5func TestRemoteHealthTransition(t *testing.T) {
6 healthy := RemoteHealth{Availability: RemoteAvailabilityReachable}
7 unknown := RemoteHealth{Availability: RemoteAvailabilityUnknown}
8 noDaemon := RemoteHealth{Availability: RemoteAvailabilityNoDaemon, Episode: 1}
9 down := func(episode uint64) RemoteHealth {
10 return RemoteHealth{Availability: RemoteAvailabilityUnreachable, Episode: episode}
11 }
12 tests := []struct {
13 name string
14 prev RemoteHealth
15 seen bool
16 cur RemoteHealth
17 want RemoteHealthEvent
18 wantNext RemoteHealth
19 }{
20 {name: "first healthy sighting is silent", cur: healthy, want: RemoteHealthSilent, wantNext: healthy},
21 {name: "first unknown sighting is silent", cur: unknown, want: RemoteHealthSilent, wantNext: unknown},
22 {name: "healthy to healthy is silent", prev: healthy, seen: true, cur: healthy, want: RemoteHealthSilent, wantNext: healthy},
23 {name: "first failing sighting fails", cur: down(1), want: RemoteHealthFailed, wantNext: down(1)},
24 {name: "healthy to failing fails", prev: healthy, seen: true, cur: down(1), want: RemoteHealthFailed, wantNext: down(1)},
25 {name: "same episode is silent", prev: down(1), seen: true, cur: down(1), want: RemoteHealthSilent, wantNext: down(1)},
26 {name: "new episode fails", prev: down(1), seen: true, cur: down(2), want: RemoteHealthFailed, wantNext: down(2)},
27 {name: "new failure class in same episode fails", prev: down(1), seen: true, cur: noDaemon, want: RemoteHealthFailed, wantNext: noDaemon},
28 {name: "no daemon to unreachable fails", prev: noDaemon, seen: true, cur: down(2), want: RemoteHealthFailed, wantNext: down(2)},
29 {name: "recovery", prev: down(1), seen: true, cur: healthy, want: RemoteHealthRecovered, wantNext: healthy},
30 {name: "unknown keeps the failing state", prev: down(1), seen: true, cur: unknown, want: RemoteHealthSilent, wantNext: down(1)},
31 {name: "daemon started after no daemon is silent", prev: noDaemon, seen: true, cur: healthy, want: RemoteHealthSilent, wantNext: healthy},
32 {name: "version fixed recovers", prev: RemoteHealth{Availability: RemoteAvailabilityReachable, VersionMismatch: true}, seen: true, cur: healthy, want: RemoteHealthRecovered, wantNext: healthy},
33 {name: "version mismatch fails", cur: RemoteHealth{Availability: RemoteAvailabilityReachable, VersionMismatch: true}, want: RemoteHealthFailed, wantNext: RemoteHealth{Availability: RemoteAvailabilityReachable, VersionMismatch: true}},
34 {name: "incompatible fails", cur: RemoteHealth{Availability: RemoteAvailabilityIncompatible}, want: RemoteHealthFailed, wantNext: RemoteHealth{Availability: RemoteAvailabilityIncompatible}},
35 }
36 for _, tt := range tests {
37 t.Run(tt.name, func(t *testing.T) {
38 next, event := RemoteHealthTransition(tt.prev, tt.seen, tt.cur)
39 if event != tt.want || next != tt.wantNext {
40 t.Fatalf("got (%+v, %d), want (%+v, %d)", next, event, tt.wantNext, tt.want)
41 }
42 })
43 }
44}
45
46func TestRemoteHealthRecoveryAcrossUnknown(t *testing.T) {
47 steps := []RemoteHealth{
48 {Availability: RemoteAvailabilityUnreachable, Episode: 1},
49 {Availability: RemoteAvailabilityUnknown, Episode: 1},
50 {Availability: RemoteAvailabilityReachable, Episode: 1},
51 }
52 want := []RemoteHealthEvent{RemoteHealthFailed, RemoteHealthSilent, RemoteHealthRecovered}
53 var prev RemoteHealth
54 seen := false
55 for i, cur := range steps {
56 var event RemoteHealthEvent
57 prev, event = RemoteHealthTransition(prev, seen, cur)
58 seen = true
59 if event != want[i] {
60 t.Fatalf("step %d: event %d, want %d", i, event, want[i])
61 }
62 }
63}
64
65func TestNotificationSubject(t *testing.T) {
66 base := Notification{Code: NoticeRemoteObservation, SessionID: "s", Scope: "h", Message: "down"}
67 tests := []struct {
68 name string
69 other Notification
70 same bool
71 }{
72 {name: "same subject, other message", other: Notification{Code: NoticeRemoteObservation, SessionID: "s", Scope: "h", Message: "reconnected"}, same: true},
73 {name: "other scope", other: Notification{Code: NoticeRemoteObservation, SessionID: "s", Scope: "other"}},
74 {name: "other session", other: Notification{Code: NoticeRemoteObservation, SessionID: "t", Scope: "h"}},
75 {name: "other code", other: Notification{Code: NoticeSessionKill, SessionID: "s", Scope: "h"}},
76 }
77 for _, tt := range tests {
78 t.Run(tt.name, func(t *testing.T) {
79 if got := base.Subject() == tt.other.Subject(); got != tt.same {
80 t.Fatalf("same subject = %v, want %v", got, tt.same)
81 }
82 })
83 }
84}