Publish ITWorx Pulse source
Public source validation / validate (push) Failing after 3m8s

This commit is contained in:
ITWorx Pulse release export
2026-09-03 02:09:19 +02:00
commit bd774932d5
614 changed files with 77116 additions and 0 deletions
+133
View File
@@ -0,0 +1,133 @@
package reverseproxy
import (
"context"
"encoding/json"
"errors"
"fmt"
"io"
"net/http"
"net/url"
"strconv"
"strings"
"time"
)
type TokenSource interface {
Token(context.Context) (string, error)
}
type NPMClient struct {
BaseURL string
Token TokenSource
HTTPClient *http.Client
MaxBody int64
Now func() time.Time
}
func (c NPMClient) ListRoutes(ctx context.Context) ([]RawRoute, error) {
if ctx == nil {
return nil, errors.New("reverse proxy context is nil")
}
if err := ctx.Err(); err != nil {
return nil, err
}
base, err := url.Parse(strings.TrimSpace(c.BaseURL))
if err != nil || (base.Scheme != "http" && base.Scheme != "https") || base.Host == "" || base.User != nil || base.RawQuery != "" || base.Fragment != "" {
return nil, errors.New("nginx proxy manager base URL is invalid")
}
token := ""
if c.Token != nil {
token, err = c.Token.Token(ctx)
if err != nil {
if errors.Is(err, context.Canceled) || errors.Is(err, context.DeadlineExceeded) {
return nil, err
}
return nil, errors.New("nginx proxy manager credential unavailable")
}
}
client := c.HTTPClient
if client == nil {
client = http.DefaultClient
}
requestURL := strings.TrimRight(base.String(), "/") + "/api/nginx/proxy-hosts"
request, err := http.NewRequestWithContext(ctx, http.MethodGet, requestURL, nil)
if err != nil {
return nil, errors.New("create nginx proxy manager request")
}
request.Header.Set("Accept", "application/json")
if strings.TrimSpace(token) != "" {
request.Header.Set("Authorization", "Bearer "+token)
}
response, err := client.Do(request)
if err != nil {
if errors.Is(err, context.Canceled) || errors.Is(err, context.DeadlineExceeded) {
return nil, err
}
return nil, errors.New("nginx proxy manager request failed")
}
defer response.Body.Close()
maxBody := c.MaxBody
if maxBody == 0 {
maxBody = 2 << 20
}
if maxBody < 1024 || maxBody > 8<<20 {
return nil, errors.New("nginx proxy manager body limit is outside bounds")
}
if response.StatusCode < http.StatusOK || response.StatusCode >= http.StatusMultipleChoices {
return nil, fmt.Errorf("nginx proxy manager returned status %d", response.StatusCode)
}
body, err := io.ReadAll(io.LimitReader(response.Body, maxBody+1))
if err != nil {
return nil, errors.New("read nginx proxy manager response")
}
if int64(len(body)) > maxBody {
return nil, errors.New("nginx proxy manager response exceeds bounds")
}
var hosts []npmHost
if err := json.Unmarshal(body, &hosts); err != nil {
return nil, errors.New("decode nginx proxy manager response")
}
now := time.Now().UTC()
if c.Now != nil {
now = c.Now().UTC()
}
routes := make([]RawRoute, 0, len(hosts))
for _, host := range hosts {
if len(host.DomainNames) > 32 {
return nil, errors.New("nginx proxy manager hostnames exceed bounds")
}
id := string(host.ID)
if decoded, decodeErr := strconv.Unquote(id); decodeErr == nil {
id = decoded
}
if strings.TrimSpace(id) == "" || len(id) > 128 {
return nil, errors.New("nginx proxy manager route id is invalid")
}
scheme := strings.ToLower(strings.TrimSpace(host.ForwardScheme))
if scheme == "" {
scheme = "http"
}
targetServiceID := ""
if value, ok := host.Meta["pulse_service_id"].(string); ok {
targetServiceID = value
}
for _, domain := range host.DomainNames {
routes = append(routes, RawRoute{ID: id + ":" + domain, SourceID: "npm", Hostname: domain, Scheme: scheme, Port: host.Port, TargetServiceID: targetServiceID, TargetHost: host.ForwardHost, Enabled: host.Enabled, Description: "Nginx Proxy Manager", ObservedAt: now})
}
}
if len(routes) > 1000 {
return nil, errors.New("nginx proxy manager route count exceeds bounds")
}
return routes, nil
}
type npmHost struct {
ID json.RawMessage `json:"id"`
DomainNames []string `json:"domain_names"`
ForwardScheme string `json:"forward_scheme"`
ForwardHost string `json:"forward_host"`
Port int `json:"forward_port"`
Enabled bool `json:"enabled"`
Meta map[string]any `json:"meta"`
}
+57
View File
@@ -0,0 +1,57 @@
package reverseproxy
import (
"context"
"errors"
"net/http"
"net/http/httptest"
"testing"
"time"
)
type staticToken string
func (s staticToken) Token(context.Context) (string, error) { return string(s), nil }
func TestNPMClientReadsOnlyProxyHostsAndUsesExternalToken(t *testing.T) {
var method, authorization, path string
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
method, authorization, path = r.Method, r.Header.Get("Authorization"), r.URL.Path
w.Header().Set("Content-Type", "application/json")
_, _ = w.Write([]byte("[{\"id\":7,\"domain_names\":[\"pulse.example.test\"],\"forward_scheme\":\"http\",\"forward_host\":\"10.0.0.7\",\"forward_port\":8080,\"enabled\":true,\"meta\":{\"pulse_service_id\":\"svc-api\"}}]"))
}))
defer server.Close()
now := time.Date(2026, 8, 2, 12, 0, 0, 0, time.UTC)
routes, err := (NPMClient{BaseURL: server.URL, Token: staticToken("external-token"), Now: func() time.Time { return now }}).ListRoutes(context.Background())
if err != nil {
t.Fatal(err)
}
if method != http.MethodGet || path != "/api/nginx/proxy-hosts" || authorization != "Bearer external-token" {
t.Fatalf("unexpected request method=%s path=%s authorization=%s", method, path, authorization)
}
if len(routes) != 1 || routes[0].ID != "7:pulse.example.test" || routes[0].TargetServiceID != "svc-api" || routes[0].SourceID != "npm" {
t.Fatalf("unexpected routes: %+v", routes)
}
}
func TestNPMClientRejectsMutationAndOversizedResponses(t *testing.T) {
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if r.Method != http.MethodGet {
t.Fatalf("unexpected method %s", r.Method)
}
_, _ = w.Write([]byte("[]"))
}))
defer server.Close()
client := NPMClient{BaseURL: server.URL, MaxBody: 1024}
if _, err := client.ListRoutes(context.Background()); err != nil {
t.Fatalf("small valid response should pass: %v", err)
}
if _, err := (NPMClient{BaseURL: server.URL, MaxBody: 9 << 20}).ListRoutes(context.Background()); err == nil {
t.Fatal("expected unsafe body bound")
}
ctx, cancel := context.WithCancel(context.Background())
cancel()
if _, err := client.ListRoutes(ctx); !errors.Is(err, context.Canceled) {
t.Fatalf("expected cancellation, got %v", err)
}
}
+433
View File
@@ -0,0 +1,433 @@
package reverseproxy
import (
"context"
"errors"
"fmt"
"net"
"net/url"
"sort"
"strings"
"time"
"github.com/itworx/pulse/internal/datasource"
)
const ContractVersion = "v1"
const (
StateEnabled = "enabled"
StateDisabled = "disabled"
StateUnknown = "unknown"
Fresh = "fresh"
Unavailable = "unavailable"
)
type Limits struct {
MaxRoutes int
MaxHostnames int
MaxDescription int
}
func (l Limits) withDefaults() Limits {
if l.MaxRoutes == 0 {
l.MaxRoutes = 150
}
if l.MaxHostnames == 0 {
l.MaxHostnames = 8
}
if l.MaxDescription == 0 {
l.MaxDescription = 256
}
return l
}
func (l Limits) Validate() error {
if l.MaxRoutes < 1 || l.MaxRoutes > 1000 || l.MaxHostnames < 1 || l.MaxHostnames > 32 || l.MaxDescription < 1 || l.MaxDescription > 1000 {
return errors.New("reverse proxy limits are outside safe bounds")
}
return nil
}
type Source struct {
ID string `json:"id"`
Type string `json:"type"`
CapabilityVersion string `json:"capabilityVersion"`
ObservedAt time.Time `json:"observedAt"`
ReceivedAt time.Time `json:"receivedAt"`
Freshness string `json:"freshness"`
State string `json:"state"`
Reason string `json:"reason,omitempty"`
Capabilities datasource.CapabilitySet `json:"capabilities"`
}
type RawRoute struct {
ID string
SourceID string
Hostname string
Scheme string
Port int
TargetServiceID string
TargetHost string
Enabled bool
Description string
ObservedAt time.Time
}
type Route struct {
ID string `json:"id"`
SourceID string `json:"sourceId"`
Hostname string `json:"hostname"`
Scheme string `json:"scheme"`
Port int `json:"port,omitempty"`
URL string `json:"url"`
TargetServiceID string `json:"targetServiceId,omitempty"`
TargetHost string `json:"targetHost,omitempty"`
Enabled bool `json:"enabled"`
Description string `json:"description,omitempty"`
ObservedAt time.Time `json:"observedAt"`
Overridden bool `json:"overridden"`
OverrideSource string `json:"overrideSource,omitempty"`
}
type Override struct {
Hostname string
TargetServiceID string
UserID string
Confirmed bool
}
type Snapshot struct {
ContractVersion string `json:"contractVersion"`
Source Source `json:"source"`
ObservedAt time.Time `json:"observedAt"`
Routes []Route `json:"routes"`
Total int `json:"total"`
}
type Provider interface {
Snapshot(context.Context) (Snapshot, error)
}
type ReadOnlyClient interface {
ListRoutes(context.Context) ([]RawRoute, error)
}
type Connector struct {
Enabled bool
SourceID string
SourceType string
EndpointURL string
CredentialRef string
Client ReadOnlyClient
Overrides []Override
Limits Limits
Now func() time.Time
}
func (c Connector) Snapshot(ctx context.Context) (Snapshot, error) {
if ctx == nil {
return Snapshot{}, errors.New("reverse proxy context is nil")
}
if err := ctx.Err(); err != nil {
return Snapshot{}, err
}
now := c.now()
if !c.Enabled {
return DisabledSnapshot(now, c.sourceID(), c.sourceType(), "connector_disabled"), nil
}
if c.Client == nil {
return UnknownSnapshot(now, c.sourceID(), c.sourceType(), "connector_client_unavailable"), nil
}
routes, err := c.Client.ListRoutes(ctx)
if err != nil {
if errors.Is(err, context.Canceled) || errors.Is(err, context.DeadlineExceeded) {
return Snapshot{}, err
}
return UnknownSnapshot(now, c.sourceID(), c.sourceType(), "connector_unavailable"), nil
}
return BuildSnapshot(now, Source{ID: c.sourceID(), Type: c.sourceType(), State: StateEnabled, Freshness: Fresh, ObservedAt: now, ReceivedAt: now}, routes, c.Overrides, c.Limits)
}
func (c Connector) Capabilities(ctx context.Context) (datasource.CapabilitySet, error) {
if ctx == nil {
return nil, errors.New("reverse proxy context is nil")
}
if err := ctx.Err(); err != nil {
return nil, err
}
state := datasource.CapabilityDisabled
reason := "connector_disabled"
if c.Enabled {
state = datasource.CapabilityUnavailable
reason = "connector_client_unavailable"
if c.Client != nil {
state = datasource.CapabilityEnabled
reason = ""
}
}
return datasource.CapabilitySet{{ID: "reverse_proxy.routes", Version: ContractVersion, State: state, Description: "Read-only reverse-proxy host to service mapping.", ReasonCode: reason, ObservedAt: c.now()}}, nil
}
type DisabledProvider struct {
SourceID string
SourceType string
Reason string
Now func() time.Time
}
func (p DisabledProvider) Snapshot(ctx context.Context) (Snapshot, error) {
if ctx == nil {
return Snapshot{}, errors.New("reverse proxy context is nil")
}
if err := ctx.Err(); err != nil {
return Snapshot{}, err
}
reason := p.Reason
if strings.TrimSpace(reason) == "" {
reason = "connector_disabled"
}
now := time.Now().UTC()
if p.Now != nil {
now = p.Now().UTC()
}
return DisabledSnapshot(now, p.sourceID(), p.sourceType(), reason), nil
}
func BuildSnapshot(now time.Time, source Source, raw []RawRoute, overrides []Override, limits Limits) (Snapshot, error) {
limits = limits.withDefaults()
if err := limits.Validate(); err != nil {
return Snapshot{}, err
}
if now.IsZero() {
now = time.Now().UTC()
}
now = now.UTC()
if strings.TrimSpace(source.ID) == "" || len(source.ID) > 120 {
return Snapshot{}, errors.New("reverse proxy source id is required and bounded")
}
if strings.TrimSpace(source.Type) == "" || len(source.Type) > 64 {
return Snapshot{}, errors.New("reverse proxy source type is required and bounded")
}
if len(raw) > limits.MaxRoutes {
return Snapshot{}, errors.New("reverse proxy route count exceeds bounds")
}
if source.CapabilityVersion == "" {
source.CapabilityVersion = ContractVersion
}
if source.ReceivedAt.IsZero() {
source.ReceivedAt = now
}
if source.ObservedAt.IsZero() {
source.ObservedAt = source.ReceivedAt
}
source.ObservedAt, source.ReceivedAt = source.ObservedAt.UTC(), source.ReceivedAt.UTC()
source.Capabilities = capabilitySet(source.State, source.Reason, source.ObservedAt)
overrideByHost, err := normalizeOverrides(overrides)
if err != nil {
return Snapshot{}, err
}
routes := make([]Route, 0, len(raw))
seenIDs := make(map[string]struct{}, len(raw))
seenHosts := make(map[string]int, len(raw))
for _, item := range raw {
route, err := normalizeRoute(item, source.ID, limits)
if err != nil {
return Snapshot{}, err
}
if _, exists := seenIDs[route.ID]; exists {
return Snapshot{}, fmt.Errorf("duplicate reverse proxy route %q", route.ID)
}
seenIDs[route.ID] = struct{}{}
if previous, exists := seenHosts[route.Hostname]; exists {
if routes[previous].TargetServiceID != route.TargetServiceID {
override, confirmed := overrideByHost[route.Hostname]
if !confirmed || override.TargetServiceID == "" {
return Snapshot{}, fmt.Errorf("reverse proxy host conflict for %q requires confirmed override", route.Hostname)
}
if err := applyOverride(&routes[previous], override); err != nil {
return Snapshot{}, err
}
continue
}
return Snapshot{}, fmt.Errorf("duplicate reverse proxy hostname %q", route.Hostname)
}
if override, ok := overrideByHost[route.Hostname]; ok {
if err := applyOverride(&route, override); err != nil {
return Snapshot{}, err
}
}
seenHosts[route.Hostname] = len(routes)
routes = append(routes, route)
}
sort.SliceStable(routes, func(i, j int) bool {
if routes[i].Hostname != routes[j].Hostname {
return routes[i].Hostname < routes[j].Hostname
}
return routes[i].ID < routes[j].ID
})
return Snapshot{ContractVersion: ContractVersion, Source: source, ObservedAt: source.ObservedAt, Routes: routes, Total: len(routes)}, nil
}
func DisabledSnapshot(now time.Time, sourceID, sourceType, reason string) Snapshot {
if now.IsZero() {
now = time.Now().UTC()
}
return Snapshot{ContractVersion: ContractVersion, Source: Source{ID: bound(sourceID, 120), Type: bound(sourceType, 64), CapabilityVersion: ContractVersion, ObservedAt: now.UTC(), ReceivedAt: now.UTC(), Freshness: Unavailable, State: StateDisabled, Reason: bound(reason, 128), Capabilities: capabilitySet(StateDisabled, reason, now)}, ObservedAt: now.UTC(), Routes: []Route{}, Total: 0}
}
func UnknownSnapshot(now time.Time, sourceID, sourceType, reason string) Snapshot {
if now.IsZero() {
now = time.Now().UTC()
}
return Snapshot{ContractVersion: ContractVersion, Source: Source{ID: bound(sourceID, 120), Type: bound(sourceType, 64), CapabilityVersion: ContractVersion, ObservedAt: now.UTC(), ReceivedAt: now.UTC(), Freshness: Unavailable, State: StateUnknown, Reason: bound(reason, 128), Capabilities: capabilitySet(StateUnknown, reason, now)}, ObservedAt: now.UTC(), Routes: []Route{}, Total: 0}
}
func normalizeRoute(raw RawRoute, fallbackSource string, limits Limits) (Route, error) {
id := strings.TrimSpace(raw.ID)
if id == "" || len(id) > 128 {
return Route{}, errors.New("reverse proxy route id is required and bounded")
}
sourceID := strings.TrimSpace(raw.SourceID)
if sourceID == "" {
sourceID = fallbackSource
}
hostname, err := normalizeHostname(raw.Hostname)
if err != nil {
return Route{}, err
}
scheme := strings.ToLower(strings.TrimSpace(raw.Scheme))
if scheme != "http" && scheme != "https" {
return Route{}, errors.New("reverse proxy route scheme must be http or https")
}
if raw.Port < 0 || raw.Port > 65535 {
return Route{}, errors.New("reverse proxy route port is invalid")
}
targetHost := strings.TrimSpace(raw.TargetHost)
if len(targetHost) > 253 || strings.ContainsAny(targetHost, "/@?#") {
return Route{}, errors.New("reverse proxy target host is invalid")
}
if targetHost != "" && net.ParseIP(targetHost) == nil {
if _, err := normalizeHostname(targetHost); err != nil {
return Route{}, errors.New("reverse proxy target host is invalid")
}
}
description := strings.TrimSpace(raw.Description)
if len(description) > limits.MaxDescription {
description = description[:limits.MaxDescription]
}
observedAt := raw.ObservedAt.UTC()
if observedAt.IsZero() {
observedAt = time.Now().UTC()
}
return Route{ID: id, SourceID: bound(sourceID, 120), Hostname: hostname, Scheme: scheme, Port: raw.Port, URL: routeURL(scheme, hostname, raw.Port), TargetServiceID: bound(strings.TrimSpace(raw.TargetServiceID), 128), TargetHost: targetHost, Enabled: raw.Enabled, Description: description, ObservedAt: observedAt}, nil
}
func normalizeOverrides(overrides []Override) (map[string]Override, error) {
byHost := make(map[string]Override, len(overrides))
for _, override := range overrides {
hostname, err := normalizeHostname(override.Hostname)
if err != nil {
return nil, err
}
if !override.Confirmed || strings.TrimSpace(override.TargetServiceID) == "" || strings.TrimSpace(override.UserID) == "" {
return nil, fmt.Errorf("reverse proxy override for %q is not user-confirmed", hostname)
}
if _, exists := byHost[hostname]; exists {
return nil, fmt.Errorf("duplicate reverse proxy override for %q", hostname)
}
override.Hostname, override.TargetServiceID, override.UserID = hostname, strings.TrimSpace(override.TargetServiceID), strings.TrimSpace(override.UserID)
byHost[hostname] = override
}
return byHost, nil
}
func applyOverride(route *Route, override Override) error {
if route == nil || !override.Confirmed || override.TargetServiceID == "" || override.UserID == "" {
return errors.New("reverse proxy override is not user-confirmed")
}
route.TargetServiceID = override.TargetServiceID
route.Overridden = true
route.OverrideSource = "user:" + bound(override.UserID, 120)
return nil
}
func normalizeHostname(value string) (string, error) {
value = strings.ToLower(strings.TrimSuffix(strings.TrimSpace(value), "."))
if value == "" || len(value) > 253 || strings.ContainsAny(value, "/@?#: ") {
return "", errors.New("reverse proxy hostname is invalid")
}
value = strings.TrimPrefix(value, "*.")
if value == "" {
return "", errors.New("reverse proxy hostname is invalid")
}
for _, label := range strings.Split(value, ".") {
if label == "" || len(label) > 63 || strings.HasPrefix(label, "-") || strings.HasSuffix(label, "-") {
return "", errors.New("reverse proxy hostname is invalid")
}
for _, character := range label {
if (character < 'a' || character > 'z') && (character < '0' || character > '9') && character != '-' {
return "", errors.New("reverse proxy hostname is invalid")
}
}
}
return value, nil
}
func routeURL(scheme, hostname string, port int) string {
host := hostname
if port > 0 && (scheme != "http" || port != 80) && (scheme != "https" || port != 443) {
host = net.JoinHostPort(hostname, fmt.Sprintf("%d", port))
}
parsed := url.URL{Scheme: scheme, Host: host}
return parsed.String()
}
func capabilitySet(state, reason string, observedAt time.Time) datasource.CapabilitySet {
capabilityState := datasource.CapabilityUnavailable
switch state {
case StateEnabled:
capabilityState = datasource.CapabilityEnabled
case StateDisabled:
capabilityState = datasource.CapabilityDisabled
}
return datasource.CapabilitySet{{ID: "reverse_proxy.routes", Version: ContractVersion, State: capabilityState, Description: "Read-only reverse-proxy host to service mapping.", ReasonCode: bound(reason, 80), ObservedAt: observedAt.UTC()}}
}
func (c Connector) now() time.Time {
if c.Now != nil {
return c.Now().UTC()
}
return time.Now().UTC()
}
func (c Connector) sourceID() string {
if strings.TrimSpace(c.SourceID) != "" {
return bound(c.SourceID, 120)
}
return "reverse-proxy"
}
func (c Connector) sourceType() string {
if strings.TrimSpace(c.SourceType) != "" {
return bound(c.SourceType, 64)
}
return "reverse_proxy"
}
func (p DisabledProvider) sourceID() string {
if strings.TrimSpace(p.SourceID) != "" {
return bound(p.SourceID, 120)
}
return "reverse-proxy"
}
func (p DisabledProvider) sourceType() string {
if strings.TrimSpace(p.SourceType) != "" {
return bound(p.SourceType, 64)
}
return "reverse_proxy"
}
func bound(value string, max int) string {
value = strings.TrimSpace(value)
if len(value) > max {
return value[:max]
}
return value
}
+95
View File
@@ -0,0 +1,95 @@
package reverseproxy
import (
"context"
"encoding/json"
"errors"
"fmt"
"strings"
"testing"
"time"
)
type routeClient struct {
routes []RawRoute
err error
}
func (c routeClient) ListRoutes(context.Context) ([]RawRoute, error) { return c.routes, c.err }
func TestBuildSnapshotNormalizesRoutesAndPreservesSourceOwnership(t *testing.T) {
now := time.Date(2026, 8, 2, 12, 0, 0, 0, time.UTC)
snapshot, err := BuildSnapshot(now, Source{ID: "npm-prod", Type: "nginx_proxy_manager", ObservedAt: now, ReceivedAt: now}, []RawRoute{{ID: "host-2", Hostname: "B.Example.test.", Scheme: "HTTPS", Port: 443, TargetServiceID: "svc-b", TargetHost: "10.0.0.20", Enabled: true, SourceID: "npm-prod", ObservedAt: now}}, nil, Limits{})
if err != nil {
t.Fatal(err)
}
if len(snapshot.Routes) != 1 || snapshot.Routes[0].Hostname != "b.example.test" || snapshot.Routes[0].URL != "https://b.example.test" || snapshot.Routes[0].SourceID != "npm-prod" {
t.Fatalf("unexpected route projection: %+v", snapshot.Routes)
}
if snapshot.Source.Capabilities[0].State != "unavailable" {
t.Fatalf("expected source capability to remain unavailable without a connector state, got %+v", snapshot.Source.Capabilities)
}
}
func TestBuildSnapshotRequiresConfirmedOverrideForHostConflict(t *testing.T) {
now := time.Date(2026, 8, 2, 12, 0, 0, 0, time.UTC)
routes := []RawRoute{{ID: "a", Hostname: "pulse.example.test", Scheme: "https", TargetServiceID: "svc-a", Enabled: true}, {ID: "b", Hostname: "pulse.example.test", Scheme: "https", TargetServiceID: "svc-b", Enabled: true}}
if _, err := BuildSnapshot(now, Source{ID: "npm", Type: "npm"}, routes, nil, Limits{}); err == nil {
t.Fatal("expected unconfirmed route conflict")
}
snapshot, err := BuildSnapshot(now, Source{ID: "npm", Type: "npm"}, routes, []Override{{Hostname: "pulse.example.test", TargetServiceID: "svc-confirmed", UserID: "operator-1", Confirmed: true}}, Limits{})
if err != nil {
t.Fatal(err)
}
if len(snapshot.Routes) != 1 || snapshot.Routes[0].TargetServiceID != "svc-confirmed" || !snapshot.Routes[0].Overridden || snapshot.Routes[0].OverrideSource != "user:operator-1" {
t.Fatalf("unexpected confirmed override: %+v", snapshot.Routes)
}
}
func TestConnectorDisabledAndUnavailableAreNonFatalAndNeverExposeCredentialReference(t *testing.T) {
now := time.Date(2026, 8, 2, 12, 0, 0, 0, time.UTC)
disabled := Connector{SourceID: "npm", CredentialRef: "secret/npm-token", Now: func() time.Time { return now }}
snapshot, err := disabled.Snapshot(context.Background())
if err != nil || snapshot.Source.State != StateDisabled || snapshot.Total != 0 {
t.Fatalf("unexpected disabled snapshot: %+v, %v", snapshot, err)
}
payload, err := json.Marshal(snapshot)
if err != nil {
t.Fatal(err)
}
if strings.Contains(string(payload), "secret/npm-token") {
t.Fatalf("credential reference leaked into snapshot: %s", payload)
}
unavailable := Connector{Enabled: true, SourceID: "npm", Client: routeClient{err: errors.New("connection refused")}, Now: func() time.Time { return now }}
snapshot, err = unavailable.Snapshot(context.Background())
if err != nil || snapshot.Source.State != StateUnknown || snapshot.Source.Reason != "connector_unavailable" {
t.Fatalf("unexpected unavailable snapshot: %+v, %v", snapshot, err)
}
}
func TestConnectorPropagatesCancellation(t *testing.T) {
ctx, cancel := context.WithCancel(context.Background())
cancel()
_, err := (Connector{}).Snapshot(ctx)
if !errors.Is(err, context.Canceled) {
t.Fatalf("expected cancellation, got %v", err)
}
}
func TestBuildSnapshotTargetScaleIsBounded(t *testing.T) {
now := time.Date(2026, 8, 2, 12, 0, 0, 0, time.UTC)
raw := make([]RawRoute, 150)
for index := range raw {
raw[index] = RawRoute{ID: fmt.Sprintf("route-%03d", index), Hostname: fmt.Sprintf("service-%03d.example.test", index), Scheme: "https", TargetServiceID: fmt.Sprintf("svc-%03d", index), Enabled: true, ObservedAt: now}
}
snapshot, err := BuildSnapshot(now, Source{ID: "npm", Type: "npm", State: StateEnabled}, raw, nil, Limits{})
if err != nil {
t.Fatal(err)
}
if snapshot.Total != 150 || len(snapshot.Routes) != 150 {
t.Fatalf("target-scale routes were not preserved within bounds: total=%d len=%d", snapshot.Total, len(snapshot.Routes))
}
if _, err := BuildSnapshot(now, Source{ID: "npm", Type: "npm", State: StateEnabled}, append(raw, RawRoute{ID: "route-over", Hostname: "over.example.test", Scheme: "https", Enabled: true}), nil, Limits{}); err == nil {
t.Fatal("expected route bound rejection above target")
}
}