diff options
| author | Paul Buetow <paul@buetow.org> | 2026-04-14 10:01:43 +0300 |
|---|---|---|
| committer | Paul Buetow <paul@buetow.org> | 2026-04-14 10:01:43 +0300 |
| commit | 4d9004ab31a6064af741fbb73758bd8844964c6f (patch) | |
| tree | 38a7f09840b6cd8774ba5b4ce24b373d4cc1de26 | |
| parent | 385cc685ab8f5312225342c66d29aaa3bf008a45 (diff) | |
z2: HTTP report query aliases and Gemtext Content-Type
Accept Category, Metric, and OutputFormat as alternate query keys
alongside category, metric, output-format. Serve Gemtext reports
with text/gemini; assert Content-Type in daemon tests.
Builds on y2 daemon GET /report reusing ParseReportQuery and
WriteReports.
Made-with: Cursor
| -rw-r--r-- | internal/daemon/daemon.go | 2 | ||||
| -rw-r--r-- | internal/daemon/daemon_test.go | 42 | ||||
| -rw-r--r-- | internal/goprecords/report.go | 18 | ||||
| -rw-r--r-- | internal/goprecords/report_test.go | 13 |
4 files changed, 71 insertions, 4 deletions
diff --git a/internal/daemon/daemon.go b/internal/daemon/daemon.go index 4e2402f..a4b1a3e 100644 --- a/internal/daemon/daemon.go +++ b/internal/daemon/daemon.go @@ -101,6 +101,8 @@ func reportContentType(f goprecords.OutputFormat) string { switch f { case goprecords.FormatMarkdown: return "text/markdown; charset=utf-8" + case goprecords.FormatGemtext: + return "text/gemini; charset=utf-8" default: return "text/plain; charset=utf-8" } diff --git a/internal/daemon/daemon_test.go b/internal/daemon/daemon_test.go index 38878fa..b140c9e 100644 --- a/internal/daemon/daemon_test.go +++ b/internal/daemon/daemon_test.go @@ -54,6 +54,9 @@ func TestReport(t *testing.T) { if res.StatusCode != http.StatusOK { t.Fatalf("status %d", res.StatusCode) } + if ct := res.Header.Get("Content-Type"); ct != "text/plain; charset=utf-8" { + t.Fatalf("Content-Type %q", ct) + } b, err := io.ReadAll(res.Body) if err != nil { t.Fatal(err) @@ -63,6 +66,45 @@ func TestReport(t *testing.T) { } } +func TestReportQueryAliases(t *testing.T) { + fixtures := filepath.Join("..", "..", "fixtures") + srv := httptest.NewServer(Handler(fixtures)) + defer srv.Close() + res, err := http.Get(srv.URL + "/report?Category=Host&Metric=Uptime&limit=2&OutputFormat=Markdown") + if err != nil { + t.Fatal(err) + } + defer res.Body.Close() + if res.StatusCode != http.StatusOK { + t.Fatalf("status %d", res.StatusCode) + } + if ct := res.Header.Get("Content-Type"); ct != "text/markdown; charset=utf-8" { + t.Fatalf("Content-Type %q", ct) + } + b, _ := io.ReadAll(res.Body) + body := string(b) + if !strings.Contains(body, "# Top") || !strings.Contains(body, "```") { + t.Fatalf("expected markdown heading and fence, got %q", b) + } +} + +func TestReportGemtextContentType(t *testing.T) { + fixtures := filepath.Join("..", "..", "fixtures") + srv := httptest.NewServer(Handler(fixtures)) + defer srv.Close() + res, err := http.Get(srv.URL + "/report?output-format=Gemtext&limit=2") + if err != nil { + t.Fatal(err) + } + defer res.Body.Close() + if res.StatusCode != http.StatusOK { + t.Fatalf("status %d", res.StatusCode) + } + if ct := res.Header.Get("Content-Type"); ct != "text/gemini; charset=utf-8" { + t.Fatalf("Content-Type %q", ct) + } +} + func TestReportBadQuery(t *testing.T) { srv := httptest.NewServer(Handler(t.TempDir())) defer srv.Close() diff --git a/internal/goprecords/report.go b/internal/goprecords/report.go index 939f0e9..b0403e7 100644 --- a/internal/goprecords/report.go +++ b/internal/goprecords/report.go @@ -72,9 +72,10 @@ func (rf *ReportFlags) Parse() (ReportConfig, error) { // ParseReportQuery builds a ReportConfig from URL query parameters using the // same names and defaults as RegisterReportFlags (category, metric, limit, -// output-format, all, include-kernel, stats-order). +// output-format, all, include-kernel, stats-order). It also accepts Category, +// Metric, and OutputFormat as alternate keys (same values as the CLI). func ParseReportQuery(q url.Values) (ReportConfig, error) { - catStr := q.Get("category") + catStr := firstQuery(q, "category", "Category") if catStr == "" { catStr = "Host" } @@ -82,7 +83,7 @@ func ParseReportQuery(q url.Values) (ReportConfig, error) { if err != nil { return ReportConfig{}, err } - metStr := q.Get("metric") + metStr := firstQuery(q, "metric", "Metric") if metStr == "" { metStr = "Uptime" } @@ -98,7 +99,7 @@ func ParseReportQuery(q url.Values) (ReportConfig, error) { } limit = uint(v) } - outStr := q.Get("output-format") + outStr := firstQuery(q, "output-format", "OutputFormat") if outStr == "" { outStr = "Plaintext" } @@ -131,6 +132,15 @@ func ParseReportQuery(q url.Values) (ReportConfig, error) { }, nil } +func firstQuery(q url.Values, keys ...string) string { + for _, k := range keys { + if v := q.Get(k); v != "" { + return v + } + } + return "" +} + func parseQueryBool(s string) (bool, error) { switch strings.ToLower(strings.TrimSpace(s)) { case "true", "1", "yes": diff --git a/internal/goprecords/report_test.go b/internal/goprecords/report_test.go index c068ce9..d6b884d 100644 --- a/internal/goprecords/report_test.go +++ b/internal/goprecords/report_test.go @@ -346,6 +346,19 @@ func TestParseReportQuery(t *testing.T) { if err == nil { t.Fatal("expected error") } + + q = url.Values{ + "Category": []string{"KernelName"}, + "Metric": []string{"Score"}, + "OutputFormat": []string{"Gemtext"}, + } + cfg, err = ParseReportQuery(q) + if err != nil { + t.Fatal(err) + } + if cfg.Category != CategoryKernelName || cfg.Metric != MetricScore || cfg.OutputFormat != FormatGemtext { + t.Fatalf("PascalCase keys: %+v", cfg) + } } func hostName(i int) string { |
