package main import ( "encoding/json" "sort" "strings" "testing" "time" stdio "git.novox.be/novox/mesh-sdk/go" "route-proxy/internal/routestate" ) // held is a state as the bus holds it, read-only. type held map[string]any func (h held) Keys() ([]string, error) { out := []string{} for k := range h { out = append(out, k) } sort.Strings(out) return out, nil } func (h held) Get(key string) (*stdio.StateEntry, error) { v, ok := h[key] if !ok { return nil, nil } raw, err := json.Marshal(v) return &stdio.StateEntry{Key: key, Value: raw}, err } // The web front's tools answer from the bus, in one call each (issue 774): every site with its target, certificate // expiry and last answer; one site in full, or that it is not served and when it was asked for; and the names the // proxy answered "no route for". func TestTheWebFrontsToolsAnswerFromTheBus(t *testing.T) { now := time.Date(2026, 10, 30, 21, 0, 0, 0, time.UTC) expires := now.Add(30 * 24 * time.Hour) routes := held{ "anchor.grafana.example": routestate.Route{Name: "grafana.example", Proxy: "anchor", Reach: routestate.ReachPublic, Targets: []routestate.Target{{Module: "grafana", Node: "home-server", Target: "http://10.0.0.2:3000"}}, Certificate: routestate.Certificate{Authority: routestate.ReachPublic, Issuer: "Let's Encrypt R11", NotAfter: &expires, Outcome: routestate.Issued}, LastAnswer: &routestate.Answer{Status: 502, At: now, Error: "dial tcp 10.0.0.2:3000: connect: connection refused"}}, "anchor.drive.example": routestate.Route{Name: "drive.example", Proxy: "anchor", Reach: routestate.ReachPublic, Targets: []routestate.Target{{Module: "nextcloud", Node: "home-server", Target: "http://10.0.0.2:8080"}}, Certificate: routestate.Certificate{Authority: routestate.ReachPublic}, LastAnswer: &routestate.Answer{Status: 200, At: now}}, } unrouted := held{ "anchor.old.example": routestate.Unrouted{Name: "old.example", Proxy: "anchor", AskedAt: now.Add(-time.Hour), Times: 3}, "anchor.wiki.example": routestate.Unrouted{Name: "wiki.example", Proxy: "anchor", AskedAt: now, Times: 1}, } tools := map[string]stdio.Tool{} for _, tool := range (front{routes: routes, unrouted: unrouted, now: func() time.Time { return now }}).tools() { tools[tool.Name] = tool } for _, row := range []struct { tool string args map[string]any want []string // what the answer says, in its JSON }{ {RoutesTool, nil, []string{`"served":2`, `"unreachable":1`, `"name":"grafana.example"`, `"reachable":"no"`, `"expires_in_days":30`, `/ → grafana@home-server at http://10.0.0.2:3000`, `"name":"drive.example"`, `"reachable":"yes"`, `public: none seen yet`}}, {RoutesTool, map[string]any{"node": "home-server"}, []string{`"served":0`}}, {RouteTool, map[string]any{"name": "Grafana.Example"}, []string{`"routed":true`, `"reachable":"no"`, `connection refused`, `"issuer":"Let's Encrypt R11"`}}, {RouteTool, map[string]any{"name": "wiki.example"}, []string{`"routed":false`, `answered \"no route\" for it, last at 2026-10-30T21:00:00Z`}}, {RouteTool, map[string]any{"name": "nothing.example"}, []string{`"routed":false`, `none was asked for it`}}, {UnroutedTool, nil, []string{`"names":2`, `"unrouted":[{"name":"wiki.example"`}}, } { t.Run(row.tool, func(t *testing.T) { tool, ok := tools[row.tool] if !ok { t.Fatalf("%s is not served", row.tool) } if !strings.Contains(tool.Description, "route-proxy") { t.Errorf("%s's description does not name the web front, so a search does not find it", row.tool) } answer, err := tool.Run(row.args) if err != nil { t.Fatal(err) } raw, _ := json.Marshal(answer) for _, w := range row.want { if !strings.Contains(string(raw), w) { t.Errorf("%s(%v) does not say %s:\n%s", row.tool, row.args, w, raw) } } }) } }