main ece7682

remote_notice_test.go RawHistoryFolder

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}

Keyboard shortcuts

Single-key shortcuts are on. ctrl k and esc work whatever this says.

ctrl k :
go to a section, branch, tag, file or repository
?
this list
j k
move the row cursor down / up
enter
open the row under the cursor; on Files, a directory opens or closes in place
↑ ↓ ← → home end
in the file tree (focus on a row): previous / next row, close / open a directory or go to its parent / first entry, first / last row. They never open a file
l h
on Files: open a directory or a file / close a directory or go up to its parent, then to the parent page. Elsewhere: into the side panel (on a merge request, its files) / back to the content; there j k move between its controls
g g G
first / last row
/
filter the list; on Files, find a file anywhere in the repository (esc clears, enter opens the best match)
b
switch branch or tag
g s g f g c g b g m g i g r g a
tabs: summary, files, commits, branches & tags, merge requests, issues, releases, actions (when available)
g h
home
w
wide mode: use the whole width of the screen, or not (remembered)
y
copy a permalink pinned to the commit
d u x
diff: split / unified / collapse or expand all
e E
diff: the whole file in view / every file, with the lines the hunks hide
n
merge request: next note in the diff
esc
close a dialog, leave a field

Branches 12

Tags 11

↑↓ moveenter openesc close