The three admin pages (classify, caldav, bulk) had no shared entry point — m navigated around and couldn't find them. /admin is now their index: - 3 cards, each linking to the underlying tool, with live counts (orphan count via projax.items_unified predicate; calendar count via ListCalendars; item count via projax.items where deleted_at IS NULL AND archived = false) - CalDAV card auto-disables when DAV_URL isn't configured - System panel: version (build-time ldflags hook), last migration (projax.schema_migrations top row), MCP status (token present yes/no — token itself never displayed), upstream health (DAV + Gitea + Supabase, parallel-probed with 1s HTTP timeout each, cached 30s) web/admin.go houses the handler + cache + probeURL helper + count queries. Templates/admin.tmpl renders the cards + system grid. admin_test.go covers /admin render + nav-link presence on every chrome-bearing route. Nav consolidation: the three separate admin links in layout.tmpl collapse to one /admin entry. Pre-existing TestTreeRenders updated to assert the new shape. Probe-URL caveat: probeURL counts any HTTP response as "alive" (incl. 4xx) — the admin panel measures reachability, not authorisation. CalDAV returns 401 on bare GET; Gitea returns 200 at the root; Supabase same. All show green when alive.
72 lines
2.2 KiB
Go
72 lines
2.2 KiB
Go
package web_test
|
|
|
|
import (
|
|
"strings"
|
|
"testing"
|
|
)
|
|
|
|
// TestAdminIndexRenders proves /admin returns 200 with the three card links
|
|
// visible. Counts come from the live DB (orphan + total item counts run
|
|
// against projax.items_unified, so they reflect whatever m has seeded).
|
|
func TestAdminIndexRenders(t *testing.T) {
|
|
srv, pool := mustServer(t)
|
|
defer pool.Close()
|
|
h := srv.Routes()
|
|
code, body := get(t, h, "/admin")
|
|
if code != 200 {
|
|
t.Fatalf("GET /admin → %d body=%s", code, body)
|
|
}
|
|
for _, want := range []string{
|
|
`Classify orphans`,
|
|
`href="/admin/classify"`,
|
|
`Bulk edit`,
|
|
`href="/admin/bulk"`,
|
|
// CalDAV card title renders regardless of config; the link is only
|
|
// emitted when DAV_URL is configured. mustServer leaves CalDAV nil,
|
|
// so we only assert the card text exists (with a "DAV_URL not
|
|
// configured" stub instead of a link).
|
|
`CalDAV calendars`,
|
|
`admin-cards`,
|
|
`admin-system`,
|
|
// system panel scaffold
|
|
`Last migration`,
|
|
`MCP`,
|
|
} {
|
|
if !strings.Contains(body, want) {
|
|
t.Errorf("/admin missing %q", want)
|
|
}
|
|
}
|
|
}
|
|
|
|
// TestLayoutHasAdminNavLink proves the header nav across every chrome-bearing
|
|
// route exposes /admin. Without the link the index page is invisible — m
|
|
// reported that as the headline bug.
|
|
func TestLayoutHasAdminNavLink(t *testing.T) {
|
|
srv, pool := mustServer(t)
|
|
defer pool.Close()
|
|
h := srv.Routes()
|
|
for _, path := range []string{"/", "/dashboard", "/graph", "/admin/bulk", "/admin/classify"} {
|
|
_, body := get(t, h, path)
|
|
if !strings.Contains(body, `href="/admin"`) {
|
|
t.Errorf("GET %s: nav missing /admin link", path)
|
|
}
|
|
}
|
|
}
|
|
|
|
// TestAdminCountsReflectStore: seed an item, render /admin, item count
|
|
// goes up by 1. Stale-cache regression guard.
|
|
func TestAdminCountsReflectStore(t *testing.T) {
|
|
srv, pool := mustServer(t)
|
|
defer pool.Close()
|
|
h := srv.Routes()
|
|
|
|
// Baseline.
|
|
_, before := get(t, h, "/admin")
|
|
// Count occurrences of "items in scope" — the bulk card's label. The card
|
|
// renders Count + CountLabel; assert the label is present and that we can
|
|
// parse the number on either side of the strong tag.
|
|
if !strings.Contains(before, "items in scope") {
|
|
t.Fatalf("baseline /admin missing 'items in scope' label")
|
|
}
|
|
}
|