Ctrl K
ComunidadGuías…

pkg/models/passthrough_test.go

Rama main · réplica de lectura del repositorio original.

Archivo original ↗Preguntar a KiBot
package models

import (
	"encoding/json"
	"testing"
)

// MatchPassThrough is single-tier: host/port/path/method must all match, most
// specific wins. Covers segment-boundary matching and the method gate.
func TestMatchPassThrough_SingleTier(t *testing.T) {
	rules := []PassThroughRule{
		{Port: 8126, Mode: PassThroughRecordOne},
		{Host: "*.datadoghq.com", Mode: PassThroughSkip},
		{Host: "api.datadoghq.com", Port: 443, Mode: PassThroughRecordOne},
	}
	cases := []struct {
		name     string
		host     string
		port     uint32
		method   string
		path     string
		wantOK   bool
		wantMode PassThroughMode
	}{
		{"port only", "", 8126, "POST", "/v0.4/traces", true, PassThroughRecordOne},
		{"host glob", "intake.datadoghq.com", 443, "POST", "/api", true, PassThroughSkip},
		{"host+port most specific wins", "api.datadoghq.com", 443, "POST", "/api", true, PassThroughRecordOne},
		{"no match", "example.com", 9999, "POST", "/x", false, ""},
		{"host with port suffix normalized", "intake.datadoghq.com:443", 0, "GET", "/api", true, PassThroughSkip},
	}
	for _, c := range cases {
		t.Run(c.name, func(t *testing.T) {
			got, ok := MatchPassThrough(rules, c.host, c.port, c.method, c.path)
			if ok != c.wantOK {
				t.Fatalf("ok=%v want %v", ok, c.wantOK)
			}
			if ok && got.Mode != c.wantMode {
				t.Fatalf("mode=%q want %q", got.Mode, c.wantMode)
			}
		})
	}
}

// Segment-boundary matching (#1): a rule for "/ingest" must match "/ingest" and
// "/ingest/x" but NOT a same-prefixed app route "/ingestData". PathPrefix opts
// into raw-prefix for the gRPC service path.
func TestMatchPassThrough_SegmentBoundaryAndPrefix(t *testing.T) {
	seg := []PassThroughRule{{Path: "/ingest", Mode: PassThroughSkip}}
	if _, ok := MatchPassThrough(seg, "", 0, "POST", "/ingest"); !ok {
		t.Fatal("/ingest should match")
	}
	if _, ok := MatchPassThrough(seg, "", 0, "POST", "/ingest/status"); !ok {
		t.Fatal("/ingest/status should match (segment)")
	}
	if _, ok := MatchPassThrough(seg, "", 0, "POST", "/ingestData"); ok {
		t.Fatal("/ingestData must NOT match (not a segment boundary)")
	}
	pre := []PassThroughRule{{Path: "/opentelemetry.proto.collector.", PathPrefix: true, Mode: PassThroughSkip}}
	if _, ok := MatchPassThrough(pre, "", 0, "POST", "/opentelemetry.proto.collector.trace.v1.TraceService/Export"); !ok {
		t.Fatal("gRPC OTLP :path prefix should match with PathPrefix")
	}
}

// The method gate: a built-in POST rule must not swallow a GET to the same path.
func TestMatchPassThrough_MethodGate(t *testing.T) {
	r := []PassThroughRule{{Path: "/v1/traces", Method: "POST", Mode: PassThroughSkip}}
	if _, ok := MatchPassThrough(r, "", 0, "POST", "/v1/traces"); !ok {
		t.Fatal("POST /v1/traces should match")
	}
	if _, ok := MatchPassThrough(r, "", 0, "GET", "/v1/traces"); ok {
		t.Fatal("GET /v1/traces must NOT match a POST-only rule")
	}
}

// NormalizeMode: empty→recordOne (default), known→self, UNKNOWN→off (fail closed).
func TestNormalizeMode(t *testing.T) {
	if (PassThroughRule{}).NormalizeMode() != DefaultPassThroughMode {
		t.Fatal("empty mode should default to recordOne")
	}
	if (PassThroughRule{Mode: PassThroughSkip}).NormalizeMode() != PassThroughSkip {
		t.Fatal("skip should be preserved")
	}
	if (PassThroughRule{Mode: PassThroughOff}).NormalizeMode() != PassThroughOff {
		t.Fatal("off should be preserved")
	}
	for _, m := range []PassThroughMode{"bogus", "OFF", "Skip", "of"} {
		if (PassThroughRule{Mode: m}).NormalizeMode() != PassThroughOff {
			t.Fatalf("unknown mode %q must fail closed to off, got %q", m, (PassThroughRule{Mode: m}).NormalizeMode())
		}
	}
}

// ResolvePassThrough is the off-aware, two-tier entry point.
func TestResolvePassThrough(t *testing.T) {
	// Zero config: New Relic built-in applies (recordOne + queryKeys), POST only.
	nr, ok := ResolvePassThrough(nil, nil, "collector.newrelic.com", 443, "POST", "/agent_listener/invoke_raw_method")
	if !ok || nr.Mode != PassThroughRecordOne {
		t.Fatalf("NR built-in should resolve to recordOne: ok=%v mode=%q", ok, nr.Mode)
	}
	if len(nr.QueryKeys) != 1 || nr.QueryKeys[0] != "method" {
		t.Fatalf("NR queryKeys = %v, want [method]", nr.QueryKeys)
	}
	// Prometheus remote_write built-in skips (POST).
	if r, ok := ResolvePassThrough(nil, nil, "", 0, "POST", "/api/v1/write"); !ok || r.Mode != PassThroughSkip {
		t.Fatalf("prometheus built-in should skip: ok=%v mode=%q", ok, r.Mode)
	}

	// #1 regression guards: app routes / wrong method are NOT passthrough.
	for _, c := range []struct{ method, path string }{
		{"GET", "/ingestData"}, {"POST", "/ingestData"}, {"POST", "/v1/tracesearch"},
		{"GET", "/v1/traces"}, {"POST", "/api/v1/writeReview"},
	} {
		if _, ok := ResolvePassThrough(nil, nil, "", 0, c.method, c.path); ok {
			t.Fatalf("%s %s must not be passthrough", c.method, c.path)
		}
	}

	// #4 two-tier: a user rule beats a MORE-SPECIFIC built-in host rule.
	// off on the datadog host → record normally, even though *.datadoghq.com skips.
	userOff := []PassThroughRule{{Host: "*.datadoghq.com", Mode: PassThroughOff}}
	if _, ok := ResolvePassThrough(nil, userOff, "trace.agent.datadoghq.com", 443, "POST", "/api/v2/series"); ok {
		t.Fatal("user off rule must beat the *.datadoghq.com built-in (record normally)")
	}
	// A path-only user off beats the built-in for that path too.
	if _, ok := ResolvePassThrough(nil, []PassThroughRule{{Path: "/v1/traces", Mode: PassThroughOff}}, "", 0, "POST", "/v1/traces"); ok {
		t.Fatal("user off on /v1/traces must beat the built-in skip")
	}
	// A user recordOne beats a built-in skip on the same target.
	if r, ok := ResolvePassThrough([]PassThroughRule{{Port: 4318, Mode: PassThroughRecordOne}}, nil, "", 4318, "POST", "/v1/traces"); !ok || r.Mode != PassThroughRecordOne {
		t.Fatalf("user recordOne must beat built-in skip: ok=%v mode=%q", ok, r.Mode)
	}
}

// matchHost covers the apex of a "*.x" glob; normalizeHost must not eat a
// bracket-less IPv6 group as a port.
func TestMatchHostApexAndNormalizeIPv6(t *testing.T) {
	if !matchHost("*.datadoghq.com", "datadoghq.com") {
		t.Fatal("apex host should match *.datadoghq.com")
	}
	if !matchHost("*.datadoghq.com", "intake.datadoghq.com") {
		t.Fatal("subdomain should still match")
	}
	if matchHost("*.datadoghq.com", "evil.com") {
		t.Fatal("unrelated host must not match")
	}
	if got := normalizeHost("::1"); got != "::1" {
		t.Fatalf("normalizeHost(::1) = %q, want ::1 (must not strip a v6 group as a port)", got)
	}
	if got := normalizeHost("api.datadoghq.com:443"); got != "api.datadoghq.com" {
		t.Fatalf("normalizeHost should strip a real :port, got %q", got)
	}
}

// An {id, mode:"off"} override disables a built-in by identity — robust even for
// the PathPrefix OTLP/gRPC rule that a field-reconstructed override would miss.
func TestResolvePassThrough_DisableByID(t *testing.T) {
	grpcPath := "/opentelemetry.proto.collector.trace.v1.TraceService/Export"
	if _, ok := ResolvePassThrough(nil, nil, "", 0, "POST", grpcPath); !ok {
		t.Fatal("OTLP/gRPC should be a built-in passthrough by default")
	}
	off := []PassThroughRule{{ID: "otlp-grpc", Mode: PassThroughOff}}
	if _, ok := ResolvePassThrough(nil, off, "", 0, "POST", grpcPath); ok {
		t.Fatal("id-based off must disable the OTLP/gRPC built-in (field override would miss it)")
	}
	// Disabling one built-in must not affect another.
	if _, ok := ResolvePassThrough(nil, off, "", 0, "POST", "/v1/traces"); !ok {
		t.Fatal("disabling otlp-grpc must not disable otlp-http-traces")
	}
	// Every built-in has a non-empty, unique ID.
	seen := map[string]bool{}
	for _, d := range TelemetryDefaults() {
		if d.Rule.ID == "" {
			t.Fatalf("built-in %q/%q missing ID", d.Provider, d.Label)
		}
		if seen[d.Rule.ID] {
			t.Fatalf("duplicate built-in ID %q", d.Rule.ID)
		}
		seen[d.Rule.ID] = true
	}
}

// UnmarshalJSON scalar shorthand is accepted on the JSON agent wire (NOT yaml).
func TestPassThroughRule_UnmarshalJSON_Shorthand(t *testing.T) {
	var list []PassThroughRule
	if err := json.Unmarshal([]byte(`[8126, "4317", {"port":4318,"mode":"skip"}, {"host":"*.sentry.io","mode":"skip"}]`), &list); err != nil {
		t.Fatalf("unmarshal: %v", err)
	}
	if len(list) != 4 || list[0].Port != 8126 || list[1].Port != 4317 {
		t.Fatalf("scalar shorthand not parsed: %+v", list)
	}
	if list[2].Port != 4318 || list[2].Mode != PassThroughSkip || list[3].Host != "*.sentry.io" {
		t.Fatalf("object form not parsed: %+v", list[2:])
	}
}

// TelemetryDefaults is the single source of truth; BuiltinTelemetryDefaults must
// be exactly its rules, and the version is exposed. Every path built-in is POST.
func TestTelemetryDefaults_SingleSource(t *testing.T) {
	defs := TelemetryDefaults()
	rules := BuiltinTelemetryDefaults()
	if len(defs) != len(rules) || len(defs) == 0 {
		t.Fatalf("defs/rules length mismatch: %d vs %d", len(defs), len(rules))
	}
	for i := range defs {
		r := defs[i].Rule
		if r.Port != rules[i].Port || r.Host != rules[i].Host || r.Path != rules[i].Path ||
			r.Mode != rules[i].Mode || r.Method != rules[i].Method || r.PathPrefix != rules[i].PathPrefix ||
			len(r.QueryKeys) != len(rules[i].QueryKeys) {
			t.Fatalf("default %d: BuiltinTelemetryDefaults not derived from TelemetryDefaults", i)
		}
		if defs[i].Provider == "" || defs[i].Label == "" {
			t.Fatalf("default %d missing provider/label", i)
		}
		if r.Path != "" && r.Method != "POST" {
			t.Fatalf("path built-in %q should be POST-gated, got method %q", r.Path, r.Method)
		}
	}
	if TelemetryDefaultsVersion < 1 {
		t.Fatal("TelemetryDefaultsVersion must be >= 1")
	}
}

// Under the proven model a built-in path row matches only with its protocol's
// fingerprint (design v14 §4): any other call on such a path is an ordinary
// dependency. Host rows and the OTLP/gRPC prefix are fingerprints already.
func TestResolveProvenNeedsTheFingerprint(t *testing.T) {
	proto := map[string][]string{"Content-Type": {"application/x-protobuf"}}
	cases := []struct {
		name   string
		host   string
		port   uint32
		path   string
		header map[string][]string
		want   string
	}{
		{"OTLP on its port", "collector", 4318, "/v1/traces", nil, "otlp-http-traces"},
		{"OTLP by its content type", "collector", 8080, "/v1/traces", proto, "otlp-http-traces"},
		{"an app route on an OTLP path", "app", 8080, "/v1/traces", map[string][]string{"Content-Type": {"application/json"}}, ""},
		{"Pyroscope on its port", "pyro", 4040, "/ingest", nil, "pyroscope-ingest"},
		{"an app's /ingest", "app", 8080, "/ingest", nil, ""},
		{"Datadog by its header", "dd", 9000, "/v0.4/traces", map[string][]string{"datadog-meta-lang": {"go"}}, "datadog-v04-traces"},
		{"Datadog on its port", "dd", 8126, "/v0.7/traces", nil, "datadog-v07-traces"},
		{"remote_write by its header", "prom", 9090, "/api/v1/write", map[string][]string{"X-Prometheus-Remote-Write-Version": {"0.1.0"}}, "prometheus-remote-write"},
		{"an app's /api/v1/write", "app", 9090, "/api/v1/write", nil, ""},
		{"a SaaS host", "intake.datadoghq.com", 443, "/anything", nil, "datadog-saas"},
		{"the OTLP/gRPC service", "collector", 4317, "/opentelemetry.proto.collector.trace.v1.TraceService/Export", nil, "otlp-grpc"},
	}
	for _, c := range cases {
		rule, ok := ResolveProven(nil, nil, c.host, c.port, "POST", c.path, c.header)
		if c.want == "" {
			if ok {
				t.Errorf("%s: matched %s, want an ordinary dependency", c.name, rule.ID)
			}
			continue
		}
		if !ok || rule.ID != c.want {
			t.Errorf("%s: got %q (%v), want %s", c.name, rule.ID, ok, c.want)
		}
	}
}

// Under the proven model the user's rules are read as ResolvePassThrough
// reads them (keploy record and keploy test keep theirs until they read
// mock.real; keploy mock's CLI clears them): a user rule beats the built-in
// rows, and an off marker turns a built-in row off, fingerprint or not.
func TestResolveProvenReadsTheUsersRules(t *testing.T) {
	user := []PassThroughRule{{Host: "telemetry.example.com", Mode: PassThroughSkip}}
	rule, ok := ResolveProven(nil, user, "telemetry.example.com", 443, "POST", "/x", nil)
	if !ok || rule.Host != "telemetry.example.com" {
		t.Fatalf("a user's host rule was not read: %+v %v", rule, ok)
	}
	off := []PassThroughRule{{ID: "otlp-http-traces", Mode: PassThroughOff}}
	if rule, ok := ResolveProven(off, nil, "collector", 4318, "POST", "/v1/traces", nil); ok {
		t.Fatalf("a built-in row the user turned off answered: %+v", rule)
	}
	if rule, ok := ResolveProven(nil, nil, "collector", 4318, "POST", "/v1/traces", nil); !ok || rule.ID != "otlp-http-traces" {
		t.Fatalf("the fingerprinted built-in did not answer: %+v %v", rule, ok)
	}
}

// Every fingerprinted built-in path row, one by one (design v14 §4): its
// path alone is an app's route and an ordinary dependency; on its protocol's
// default port, or with its protocol's header (and that header's value, when
// the fingerprint names one), it is the protocol's, and answered.
func TestEveryFingerprintedTelemetryRowNeedsItsFingerprint(t *testing.T) {
	rows := map[string]PassThroughRule{}
	for _, r := range BuiltinTelemetryDefaults() {
		rows[r.ID] = r
	}
	for id, fp := range telemetryFingerprints {
		row, ok := rows[id]
		if !ok {
			t.Fatalf("fingerprint %s names no built-in row", id)
		}
		t.Run(id, func(t *testing.T) {
			port := uint32(18080)
			if fp.port == port {
				t.Fatalf("the test's own port is %s's", id)
			}
			if r, ok := ResolveProven(nil, nil, "app", port, row.Method, row.Path, nil); ok && r.ID == id {
				t.Fatalf("%s answered a call on its path alone", id)
			}
			if fp.port == 0 && fp.header == "" {
				t.Fatalf("%s has no fingerprint", id)
			}
			if fp.port != 0 {
				if r, ok := ResolveProven(nil, nil, "collector", fp.port, row.Method, row.Path, nil); !ok || r.ID != id {
					t.Fatalf("%s did not answer on its port %d: %+v %v", id, fp.port, r, ok)
				}
			}
			if fp.header == "" {
				return
			}
			value := fp.value
			if value == "" {
				value = "1"
			}
			header := map[string][]string{fp.header: {value}}
			if r, ok := ResolveProven(nil, nil, "collector", port, row.Method, row.Path, header); !ok || r.ID != id {
				t.Fatalf("%s did not answer with its header %s: %+v %v", id, fp.header, r, ok)
			}
			if fp.value != "" {
				other := map[string][]string{fp.header: {"application/json"}}
				if r, ok := ResolveProven(nil, nil, "app", port, row.Method, row.Path, other); ok && r.ID == id {
					t.Fatalf("%s answered with its header but another value", id)
				}
			}
		})
	}
	// Every built-in row a path alone would match is fingerprinted: a row
	// with a host, or the OTLP/gRPC service prefix, is one already.
	for _, r := range BuiltinTelemetryDefaults() {
		if r.Host == "" && !r.PathPrefix && r.Port == 0 {
			if _, ok := telemetryFingerprints[r.ID]; !ok {
				t.Errorf("built-in path row %s has no fingerprint", r.ID)
			}
		}
	}
}