test(sdk): add zero-cert listener tests for token-only path
cognitive committed
Mar 4, 2026 at 22:21 UTC
5c07de47e7323a6bd627b7f5d696fd2bb0b6b6ae
1 file changed
+109
sdk/listener_test.go
+109
@@ -8,6 +8,7 @@ import (
8
"net"
9
"net/http"
10
"net/url"
11
+ "reflect"
12
"strings"
13
"testing"
14
"time"
@@ -18,6 +19,114 @@ import (
19
20
const testNonTLSStartMarker = byte(0x01)
21
22
+func TestNewListener_ZeroCert_Succeeds(t *testing.T) {
23
+ t.Parallel()
24
+
25
+ relayAddr := "https://localhost:4017"
26
+ lease := &portal.Lease{
27
+ ID: "test-lease",
28
+ Name: "test-app",
29
+ ReverseToken: "test-token",
30
+ }
31
+ tlsConfig := &tls.Config{MinVersion: tls.VersionTLS12}
32
+ zeroCert := tls.Certificate{}
33
+
34
+ listener, err := NewListener(relayAddr, lease, tlsConfig, zeroCert, 0, 0)
35
+ if err != nil {
36
+ t.Fatalf("NewListener with zero cert failed: %v", err)
37
+ }
38
+ if listener == nil {
39
+ t.Fatal("NewListener returned nil listener")
40
+ }
41
+ defer listener.Close()
42
+
43
+ // Use reflection to access unexported controlPlaneCert field
44
+ lValue := reflect.ValueOf(listener).Elem()
45
+ certField := lValue.FieldByName("controlPlaneCert")
46
+ if !certField.IsValid() {
47
+ t.Fatal("could not access controlPlaneCert field")
48
+ }
49
+ // Access the Certificate slice field directly (it's exported from tls.Certificate)
50
+ certSlice := certField.FieldByName("Certificate")
51
+ if !certSlice.IsValid() {
52
+ t.Fatal("could not access Certificate field")
53
+ }
54
+ if certSlice.Len() != 0 {
55
+ t.Fatalf("controlPlaneCert.Certificate has len %d, want 0", certSlice.Len())
56
+ }
57
+}
58
+
59
+func TestOpenReverseConnection_NoCert_NoClientCertPresented(t *testing.T) {
60
+ t.Parallel()
61
+
62
+ // Test the exact condition we care about: when controlPlaneCert is zero-value,
63
+ // the TLS config does NOT include the cert. This is what prevents client cert presentation.
64
+
65
+ tests := []struct {
66
+ cert tls.Certificate
67
+ name string
68
+ wantCertSet bool
69
+ }{
70
+ {
71
+ name: "zero cert → no client cert in TLS config",
72
+ cert: tls.Certificate{},
73
+ wantCertSet: false,
74
+ },
75
+ {
76
+ name: "non-zero cert → client cert in TLS config",
77
+ cert: tls.Certificate{Certificate: [][]byte{{0x01, 0x02, 0x03}}},
78
+ wantCertSet: true,
79
+ },
80
+ }
81
+
82
+ for _, tt := range tests {
83
+ t.Run(tt.name, func(t *testing.T) {
84
+ t.Parallel()
85
+
86
+ l := &Listener{
87
+ controlPlaneCert: tt.cert,
88
+ }
89
+
90
+ // Duplicate the exact logic from listener.go (both HTTP transport and reverse connection)
91
+ // First check the HTTP transport logic (lines 134-136)
92
+ transportTLSConfig := &tls.Config{
93
+ MinVersion: tls.VersionTLS12,
94
+ }
95
+ if len(l.controlPlaneCert.Certificate) > 0 {
96
+ transportTLSConfig.Certificates = []tls.Certificate{l.controlPlaneCert}
97
+ }
98
+
99
+ if tt.wantCertSet {
100
+ if len(transportTLSConfig.Certificates) != 1 {
101
+ t.Fatalf("transportTLSConfig.Certificates has len %d, want 1", len(transportTLSConfig.Certificates))
102
+ }
103
+ } else {
104
+ if len(transportTLSConfig.Certificates) != 0 {
105
+ t.Fatalf("transportTLSConfig.Certificates has len %d, want 0", len(transportTLSConfig.Certificates))
106
+ }
107
+ }
108
+
109
+ // Then check the reverse connection logic (lines 412-414)
110
+ reverseTLSConfig := &tls.Config{
111
+ MinVersion: tls.VersionTLS12,
112
+ }
113
+ if len(l.controlPlaneCert.Certificate) > 0 {
114
+ reverseTLSConfig.Certificates = []tls.Certificate{l.controlPlaneCert}
115
+ }
116
+
117
+ if tt.wantCertSet {
118
+ if len(reverseTLSConfig.Certificates) != 1 {
119
+ t.Fatalf("reverseTLSConfig.Certificates has len %d, want 1", len(reverseTLSConfig.Certificates))
120
+ }
121
+ } else {
122
+ if len(reverseTLSConfig.Certificates) != 0 {
123
+ t.Fatalf("reverseTLSConfig.Certificates has len %d, want 0", len(reverseTLSConfig.Certificates))
124
+ }
125
+ }
126
+ })
127
+ }
128
+}
129
+
130
func TestNormalizeRelayAPIURL(t *testing.T) {
131
t.Parallel()
132