remote_state_test.go RawHistoryFolder

1package domain
2
3import (
4	"errors"
5	"fmt"
6	"testing"
7)
8
9func TestRemoteAvailabilityString(t *testing.T) {
10	tests := []struct {
11		name  string
12		value RemoteAvailability
13		want  string
14	}{
15		{name: "unknown", value: RemoteAvailabilityUnknown, want: "unknown"},
16		{name: "reachable", value: RemoteAvailabilityReachable, want: "reachable"},
17		{name: "unreachable", value: RemoteAvailabilityUnreachable, want: "unreachable"},
18		{name: "incompatible", value: RemoteAvailabilityIncompatible, want: "incompatible"},
19		{name: "auth failed", value: RemoteAvailabilityAuthFailed, want: "authentication_failed"},
20		{name: "invalid response", value: RemoteAvailabilityInvalidResponse, want: "invalid_response"},
21		{name: "zero maps to unknown", value: RemoteAvailability(0), want: "unknown"},
22		{name: "out of range maps to unknown", value: RemoteAvailability(99), want: "unknown"},
23	}
24	for _, test := range tests {
25		t.Run(test.name, func(t *testing.T) {
26			if got := test.value.String(); got != test.want {
27				t.Fatalf("String() = %q, want %q", got, test.want)
28			}
29		})
30	}
31}
32
33func TestRemoteFailureKindString(t *testing.T) {
34	tests := []struct {
35		name  string
36		value RemoteFailureKind
37		want  string
38	}{
39		{name: "none", value: RemoteFailureNone, want: "none"},
40		{name: "transport", value: RemoteFailureTransport, want: "transport"},
41		{name: "timeout", value: RemoteFailureTimeout, want: "timeout"},
42		{name: "trust", value: RemoteFailureTrust, want: "trust"},
43		{name: "authentication", value: RemoteFailureAuthentication, want: "authentication"},
44		{name: "incompatible", value: RemoteFailureIncompatible, want: "incompatible"},
45		{name: "invalid response", value: RemoteFailureInvalidResponse, want: "invalid_response"},
46		{name: "out of range stays transport", value: RemoteFailureKind(99), want: "transport"},
47	}
48	for _, test := range tests {
49		t.Run(test.name, func(t *testing.T) {
50			if got := test.value.String(); got != test.want {
51				t.Fatalf("String() = %q, want %q", got, test.want)
52			}
53		})
54	}
55}
56
57func TestRemoteFailurePreservesCause(t *testing.T) {
58	cause := errors.New("ssh: connection refused")
59	failure := RemoteFailure{Kind: RemoteFailureTimeout, Err: cause}
60	if !errors.Is(failure, cause) {
61		t.Fatalf("errors.Is(failure, cause) = false, want true")
62	}
63	var asFailure RemoteFailure
64	if !errors.As(fmt.Errorf("observe: %w", failure), &asFailure) || asFailure.Kind != RemoteFailureTimeout {
65		t.Fatalf("errors.As did not recover %+v", failure)
66	}
67	if failure.Error() == "" || failure.Error() == cause.Error() {
68		t.Fatalf("Error() must report the sanitized kind, got %q", failure.Error())
69	}
70}
71
72func TestNewRemoteRegistration(t *testing.T) {
73	first, err := NewRemoteRegistration("user@arch", [16]byte{1})
74	if err != nil {
75		t.Fatalf("NewRemoteRegistration: %v", err)
76	}
77	if err := first.Validate(); err != nil {
78		t.Fatalf("Validate: %v", err)
79	}
80	if first.IsZero() {
81		t.Fatal("fresh registration must not be zero")
82	}
83	second, err := NewRemoteRegistration("user@arch", [16]byte{2})
84	if err != nil {
85		t.Fatalf("NewRemoteRegistration: %v", err)
86	}
87	if first.Incarnation == second.Incarnation {
88		t.Fatal("incarnations must be unique per registration")
89	}
90	if first.Equal(second) {
91		t.Fatal("distinct incarnations must not compare equal")
92	}
93	if _, err := NewRemoteRegistration("not a host", [16]byte{1}); err == nil {
94		t.Fatal("invalid endpoint must be rejected")
95	}
96}
97
98func TestRemoteRegistrationIdentity(t *testing.T) {
99	valid := RemoteRegistration{Endpoint: "user@arch", Incarnation: [16]byte{1}, Generation: 2}
100	tests := []struct {
101		name     string
102		value    RemoteRegistration
103		zero     bool
104		validate bool
105	}{
106		{name: "valid", value: valid, zero: false, validate: true},
107		{name: "empty", value: RemoteRegistration{}, zero: true, validate: false},
108		{name: "zero incarnation", value: RemoteRegistration{Endpoint: "user@arch", Generation: 1}, zero: true, validate: false},
109		{name: "zero generation", value: RemoteRegistration{Endpoint: "user@arch", Incarnation: [16]byte{1}}, zero: false, validate: false},
110		{name: "bad endpoint", value: RemoteRegistration{Endpoint: "not a host", Incarnation: [16]byte{1}, Generation: 1}, zero: false, validate: false},
111	}
112	for _, test := range tests {
113		t.Run(test.name, func(t *testing.T) {
114			if got := test.value.IsZero(); got != test.zero {
115				t.Fatalf("IsZero() = %t, want %t", got, test.zero)
116			}
117			if err := test.value.Validate(); (err == nil) != test.validate {
118				t.Fatalf("Validate() err = %v, want valid=%t", err, test.validate)
119			}
120		})
121	}
122	if !valid.Equal(valid) {
123		t.Fatal("registration must equal itself")
124	}
125	renamed := valid
126	renamed.Endpoint = "user@mule"
127	if valid.Equal(renamed) {
128		t.Fatal("different endpoints must not compare equal")
129	}
130	advanced := valid
131	advanced.Generation++
132	if valid.Equal(advanced) {
133		t.Fatal("different generations must not compare equal")
134	}
135}

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