package server import ( "encoding/hex" "encoding/json" "os" "path/filepath" "testing" ) // The spec vectors are shared with the reference implementation // (.reference/opentunnel/spec/vectors). type frameVector struct { Conn uint32 `json:"conn"` Payload string `json:"payload"` Frame string `json:"frame"` } func TestFrameVectors(t *testing.T) { var vectors []frameVector readVector(t, "frames.json", &vectors) for i, v := range vectors { payload, err := hex.DecodeString(v.Payload) if err != nil { t.Fatalf("vector %d payload: %v", i, err) } want, err := hex.DecodeString(v.Frame) if err != nil { t.Fatalf("vector %d frame: %v", i, err) } if got := buildFrame(v.Conn, payload); !equalBytes(got, want) { t.Errorf("vector %d: buildFrame = %x, want %x", i, got, want) } conn, gotPayload, ok := parseFrame(want) if !ok || conn != v.Conn || !equalBytes(gotPayload, payload) { t.Errorf("vector %d: parseFrame = (%d, %x, %v)", i, conn, gotPayload, ok) } } } type controlVector struct { Client []json.RawMessage `json:"client"` Server []json.RawMessage `json:"server"` Ignored []json.RawMessage `json:"ignored"` Invalid []json.RawMessage `json:"invalid"` } func TestControlVectors(t *testing.T) { var vectors controlVector readVector(t, "control.json", &vectors) for i, raw := range vectors.Client { msg, err := parseClientControl(raw) if err != nil || msg.Type == "" { t.Errorf("client vector %d: %v", i, err) } } for i, raw := range vectors.Server { var probe struct { Type string `json:"type"` } if err := json.Unmarshal(raw, &probe); err != nil || probe.Type == "" { t.Errorf("server vector %d should decode", i) } } for i, raw := range vectors.Ignored { if _, err := parseClientControl(raw); err != nil { t.Errorf("ignored vector %d should still decode: %v", i, err) } } // The invalid vectors either fail to decode or miss required fields. for i, raw := range vectors.Invalid { msg, err := parseClientControl(raw) if err != nil { continue } switch msg.Type { case "": // {"conn":1} has no type case "end": // {"type":"end"} misses conn if msg.Conn != nil { t.Errorf("invalid vector %d: end with conn?", i) } } } } func TestAttachedVectorShape(t *testing.T) { // Our attached message must marshal to exactly the vector's key order. var vectors controlVector readVector(t, "control.json", &vectors) var want map[string]any json.Unmarshal(vectors.Server[0], &want) raw, err := json.Marshal(attachedMessage{ Type: "attached", Session: "sess_1", Routes: []string{"api"}, HeartbeatMs: 15000, IdleTimeoutMs: 45000, }) if err != nil { t.Fatal(err) } var got map[string]any json.Unmarshal(raw, &got) if len(got) != len(want) { t.Fatalf("attached fields = %v, want %v", got, want) } for key := range want { if _, ok := got[key]; !ok { t.Errorf("attached misses %q", key) } } } type namesVector struct { Route []struct { Value string `json:"value"` Valid bool `json:"valid"` } `json:"route"` } func TestRouteNameVectors(t *testing.T) { var vectors namesVector readVector(t, "names.json", &vectors) for _, v := range vectors.Route { if got := validRoute(v.Value); got != v.Valid { t.Errorf("validRoute(%q) = %v, want %v", v.Value, got, v.Valid) } } } type routeVector struct { Hostname string `json:"hostname"` SNI string `json:"sni"` Route *string `json:"route"` } func TestRouteVectors(t *testing.T) { var vectors []routeVector readVector(t, "routes.json", &vectors) const domain = "opentunnel.xyz" for _, v := range vectors { id, route, ok := routeForSNI(domain, v.SNI) // The vectors know which tunnel exists; the server closes visitors of // unknown tunnels at lookup, so a foreign ID resolves to no route. tunnelID := v.Hostname[:len(v.Hostname)-len("."+domain)] if ok && id != tunnelID { ok = false } if v.Route == nil { if ok { t.Errorf("sni %q: got route %q, want no match", v.SNI, route) } continue } if !ok || route != *v.Route { t.Errorf("sni %q: got (%q, %q, %v), want route %q", v.SNI, id, route, ok, *v.Route) } } } func readVector(t *testing.T, name string, out any) { t.Helper() raw, err := os.ReadFile(filepath.Join(vectorDir(t), name)) if err != nil { t.Skipf("spec vectors not available: %v", err) } if err := json.Unmarshal(raw, out); err != nil { t.Fatalf("parsing %s: %v", name, err) } } // vectorDir locates the shared vectors: the reference checkout lives in // .reference/opentunnel at the repository root. func vectorDir(t *testing.T) string { t.Helper() return filepath.Join("..", "..", ".reference", "opentunnel", "spec", "vectors") } func equalBytes(a, b []byte) bool { if len(a) != len(b) { return false } for i := range a { if a[i] != b[i] { return false } } return true }