From a53aba8b430a1a5acc6e749501aad50fc3762e85 Mon Sep 17 00:00:00 2001 From: matst80 Date: Sat, 25 Jul 2026 15:24:27 +0200 Subject: [PATCH] chore: pre-existing uncommitted platform/event changes --- event/event.go | 31 ++++++++++++++++ event/event_test.go | 90 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 121 insertions(+) create mode 100644 event/event_test.go diff --git a/event/event.go b/event/event.go index 85a0d03..8663d57 100644 --- a/event/event.go +++ b/event/event.go @@ -76,7 +76,38 @@ const ( TrackPageEngaged Type = "track.page_engaged" TrackPageAction Type = "track.page_action" TrackExperiment Type = "track.experiment" +// TrackPurchase — cut 23. Post-checkout funnel closure; emitted by +// event_handlers.go::TrackPurchase on a purchase request. Replaces +// 5 hand-written `pevent.Type("track.purchase")` literals with this +// typed constant. + TrackPurchase Type = "track.purchase" ) +// KnownTypes lists every event that has a declared Track* constant above. +// New pevent constants MUST also be added here so IsKnownType stays in +// sync. Used by HTTP handlers' forward-validation (e.g. SessionsPanel +// ?event_types= param, cut 25) so caller's typo's surface as a 400 +// rather than silently dropping unknown tokens. +var KnownTypes = []Type{ + TrackSession, TrackSearch, TrackClick, TrackCart, TrackPurchase, + TrackImpression, TrackAction, TrackPageView, TrackPageEngaged, + TrackPageAction, TrackExperiment, +} + +// IsKnownType reports whether s matches one of the declared Track* +// constants. Case-sensitive + exact-match. Callers MUST pass the canonical +// lowercase form (e.g. "track.search", NOT "Track.Search" or +// "TRACK.SEARCH"). Track* constants are declared lowercase in this file, +// and this function matches the literal string value of each constant. +// Callers should validate with this BEFORE casting untrusted input to Type. +func IsKnownType(s string) bool { + for _, t := range KnownTypes { + if string(t) == s { + return true + } + } + return false +} + // Event is the one envelope. Payload is per-Type, agreed between producer and // consumer (json.RawMessage, not a god-struct). A nil Payload means "the subject diff --git a/event/event_test.go b/event/event_test.go new file mode 100644 index 0000000..1f6a7b3 --- /dev/null +++ b/event/event_test.go @@ -0,0 +1,90 @@ +package event + +import ( + "go/ast" + "go/parser" + "go/token" + "os" + "strings" + "testing" +) + +// TestKnownTypes_ContainsAllTrackConstants is a build-time drift guard. +// It walks event.go's Analytics const block via go/parser + go/ast and +// asserts every `Track*` constant declared there appears in +// event.KnownTypes. Self-deriving means: adding TrackNew requires +// updating ONLY KnownTypes, not also the test. So the test cannot +// itself drift. +// +// If you add a new Track* constant and accidentally forget +// KnownTypes, this test fails. Update KnownTypes and re-run. +func TestKnownTypes_ContainsAllTrackConstants(t *testing.T) { + const srcPath = "event.go" + raw, err := os.ReadFile(srcPath) + if err != nil { +t.Skipf("cannot read %q (running under go test in a different layout) — drift check deferred: %v", srcPath, err) + } + + fset := token.NewFileSet() + file, err := parser.ParseFile(fset, srcPath, string(raw), parser.ParseComments) + if err != nil { + t.Fatalf("parser.ParseFile failed: %v", err) + } + + var trackConsts []Type + ast.Inspect(file, func(n ast.Node) bool { + gen, ok := n.(*ast.GenDecl) + if !ok || gen.Tok != token.CONST { + return true + } + for _, spec := range gen.Specs { + vs, ok := spec.(*ast.ValueSpec) + if !ok { + continue + } + for i, name := range vs.Names { + if !strings.HasPrefix(name.Name, "Track") { + continue + } + if vs.Values == nil || i >= len(vs.Values) { + continue + } + bl, ok := vs.Values[i].(*ast.BasicLit) + if !ok || bl.Kind != token.STRING { + continue + } + trackConsts = append(trackConsts, Type(strings.Trim(bl.Value, `""`))) + } + } + return true + }) + + if len(trackConsts) == 0 { + t.Fatalf("AST scan did not find any Track* constants in event.go — check the AST walker or the file layout") + } + + for _, c := range KnownTypes { + found := false + for _, tc := range trackConsts { + if tc == c { + found = true + break + } + } + if !found { + t.Errorf("KnownTypes contains %q but the const block in event.go does NOT declare a Track* with that value", string(c)) + } + } + for _, tc := range trackConsts { + found := false + for _, c := range KnownTypes { + if c == tc { + found = true + break + } + } + if !found { + t.Errorf("event.go declares Track* = %q but KnownTypes is missing it — add it to KnownTypes and re-run", string(tc)) + } + } +}