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}