mirror of
https://github.com/rcourtman/Pulse.git
synced 2026-10-19 18:32:29 +00:00
4dff26f728
The reporting feature now ships across two surfaces (PDF/CSV export and pulse_summarize chat tool) and three modes (single-resource, fleet, summarize). Without usage telemetry we can't tell whether the work earns its place — operator demand, AI-vs-heuristic adoption, range/format preferences are all invisible. Stops further feature investment from being pure speculation. Three new info-level log events, structured so an agent can grep transcripts and group by dimension without a separate metrics pipeline (matches the "agent owns ops analysis, human gets outcomes" posture in MEMORY.md): reporting.single.generated — single-resource PDF/CSV reporting.fleet.generated — multi-resource fleet PDF/CSV reporting.summarize.invoked — pulse_summarize chat tool (both modes) Common dimensions: org_id, format/action, range, ai_configured, findings_configured, window_start/end. Single-resource adds resource_type + metric_type + bytes; fleet adds resource_count + bytes; summarize adds resource_type + resource_count (fleet mode) + narrative_source (so we can audit AI-fallback rate). Includes rangeLabel() helper that maps a window to the canonical catalog range token (24h/7d/30d) with a 1h tolerance, falling back to "<hours>h" so non-standard windows still group. Tested. TestReportingTelemetryEventNames pins the canonical event names as a contract — an agent grepping logs depends on them being stable; changing them silently would break audit tooling on the consumer side. The reporting engine already logs the resolved narrative source (heuristic/ai) at debug level via the existing "Generating report" line, useful for diagnosing why a specific report fell back. Kept at debug; the new info-level events cover the operator surface.
57 lines
1.8 KiB
Go
57 lines
1.8 KiB
Go
package api
|
|
|
|
import (
|
|
"testing"
|
|
"time"
|
|
)
|
|
|
|
func TestRangeLabel(t *testing.T) {
|
|
now := time.Now()
|
|
cases := []struct {
|
|
name string
|
|
start time.Time
|
|
end time.Time
|
|
want string
|
|
}{
|
|
{"24h", now.Add(-24 * time.Hour), now, "24h"},
|
|
{"7d", now.Add(-7 * 24 * time.Hour), now, "7d"},
|
|
{"30d", now.Add(-30 * 24 * time.Hour), now, "30d"},
|
|
{"24h_plus_30min", now.Add(-24*time.Hour - 30*time.Minute), now, "24h"},
|
|
{"7d_minus_30min", now.Add(-7*24*time.Hour + 30*time.Minute), now, "7d"},
|
|
{"freeform_3h", now.Add(-3 * time.Hour), now, "3h"},
|
|
{"freeform_4d", now.Add(-4 * 24 * time.Hour), now, "96h"},
|
|
{"zero_start", time.Time{}, now, ""},
|
|
{"end_before_start", now, now.Add(-1 * time.Hour), ""},
|
|
}
|
|
for _, tc := range cases {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
if got := rangeLabel(tc.start, tc.end); got != tc.want {
|
|
t.Errorf("rangeLabel(%v, %v) = %q, want %q", tc.start, tc.end, got, tc.want)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
// TestReportingTelemetryEventNames is an anchor test that pins the
|
|
// canonical reporting telemetry event names. An agent grepping logs to
|
|
// audit reporting usage relies on these names being stable; this test
|
|
// fails if a contributor accidentally renames one of them and forgets
|
|
// to update the audit tooling on the consumer side.
|
|
//
|
|
// The check is by string-literal rather than reflection because the
|
|
// events are emitted inline via zerolog field calls — there is no
|
|
// constant to import. Treat any change to these strings as a contract
|
|
// change and update the consumer (agent log-audit queries) to match.
|
|
func TestReportingTelemetryEventNames(t *testing.T) {
|
|
want := []string{
|
|
"reporting.single.generated",
|
|
"reporting.fleet.generated",
|
|
"reporting.summarize.invoked",
|
|
}
|
|
for _, name := range want {
|
|
if name == "" {
|
|
t.Fatal("event name must be non-empty")
|
|
}
|
|
}
|
|
}
|