package server import ( "reflect" "testing" "time" "sultan/pkg/accountant" "sultan/pkg/registry" ) // Aus dem gemeldeten Zustand eines Knotens entstehen die Berichtszeilen: // Hosts, Ziel (IP und FQDN), Compose-Angaben, Produkt, down/aktiv. func TestReportRoutes(t *testing.T) { started := time.Date(2026, 10, 17, 8, 0, 0, 0, time.UTC) st := registry.NodeState{ Host: ®istry.HostInfo{FQDN: "webdocker11.lan.r25dc.de"}, Workloads: []registry.Workload{ { Name: "kunde-a-wordpress-1", Host: "10.0.0.11", Labels: map[string]string{ "traefik.enable": "true", "traefik.http.routers.wp.rule": "Host(`www.kunde.de`) || Host(`kunde.de`)", "traefik.http.services.wp.loadbalancer.server.port": "8081", "sultan.accountant.billing": "web-hosting", }, Meta: ®istry.WorkloadMeta{ComposeProject: "kunde-a", ComposeService: "wordpress", ComposeDir: "/srv/kunde-a", State: "running", StartedAt: started, Health: "healthy"}, }, { Name: "olat", Host: "10.0.0.11", Labels: map[string]string{ "traefik.enable": "true", "traefik.http.routers.olat.rule": "Host(`olat.kunde.de`)", "traefik.http.services.olat.loadbalancer.server.port": "8090", }, Meta: ®istry.WorkloadMeta{State: "exited"}, }, { Name: "ca", Host: "10.0.0.11", Labels: map[string]string{ "traefik.enable": "true", "traefik.tcp.routers.ca.rule": "HostSNI(`ca.kunde.de`)", "traefik.tcp.routers.ca.entrypoints": "websecure", "traefik.tcp.routers.ca.tls.passthrough": "true", "traefik.tcp.services.ca.loadbalancer.server.port": "9000", }, }, {Name: "kaputt", Host: "10.0.0.11", Labels: map[string]string{"traefik.enable": "vielleicht"}}, }, DownServices: []string{"olat"}, } active := map[string]bool{"http:kunde__webdocker11__wp": true, "tcp:kunde__webdocker11__ca": true} got := reportRoutes("kunde", "webdocker11", st, func(proto, name string) bool { return active[proto+":"+name] }) want := []accountant.ReportRoute{ {Tenant: "kunde", Node: "webdocker11", Workload: "ca", Router: "ca", Protocol: "tcp", Hosts: []string{"ca.kunde.de"}, Backend: "10.0.0.11:9000", BackendFQDN: "webdocker11.lan.r25dc.de:9000", Active: true}, {Tenant: "kunde", Node: "webdocker11", Workload: "kunde-a-wordpress-1", Router: "wp", Protocol: "http", Hosts: []string{"kunde.de", "www.kunde.de"}, Backend: "http://10.0.0.11:8081", BackendFQDN: "webdocker11.lan.r25dc.de:8081", Active: true, Product: "web-hosting", ComposeProject: "kunde-a", ComposeService: "wordpress", ComposeDir: "/srv/kunde-a", State: "running", StartedAt: started, Health: "healthy"}, {Tenant: "kunde", Node: "webdocker11", Workload: "olat", Router: "olat", Protocol: "http", Hosts: []string{"olat.kunde.de"}, Backend: "http://10.0.0.11:8090", BackendFQDN: "webdocker11.lan.r25dc.de:8090", Active: false, Down: true, State: "exited"}, } if !reflect.DeepEqual(got, want) { t.Errorf("reportRoutes =\n%+v\nerwartet\n%+v", got, want) } // Ohne Host-Infos (älterer Agent): kein FQDN-Ziel, Rest bleibt. st.Host = nil for _, r := range reportRoutes("kunde", "webdocker11", st, func(string, string) bool { return true }) { if r.BackendFQDN != "" || r.Backend == "" { t.Errorf("ohne Host-Infos: %+v", r) } } }