package updatewords import ( "encoding/json" "os" "path/filepath" "strings" "testing" ) // sectionFor maps a plan-5 draft ID to its review section. An ID that // matches no rule is unmapped and must be added here. func sectionFor(id string) string { switch { case strings.HasPrefix(id, "settings.cli.apps-set-"): return "CLI: carlos apps set" case strings.HasPrefix(id, "settings.cli.instances-usage-"), strings.HasPrefix(id, "settings.cli.set-"), strings.HasPrefix(id, "settings.cli.update-"), id == "settings.cli.direct-mode", id == "settings.cli.bad-major-flag", id == "settings.cli.flag-account-id", id == "settings.cli.none": return "CLI: carlos instances set and update" case id == "settings.cli.not-enforced-reason": return "CLI: carlos instances list" case strings.HasPrefix(id, "settings.page."): return "Console: instance page, Updates section" case strings.HasPrefix(id, "settings.cli.docs-"), id == "settings.cli.roll-note": return "Docs and roll notes" default: return "" } } // TestEverySurfaceDraftHasASection ensures every plan-5 draft is mapped to // a section, so a new draft cannot be silently left out of review. func TestEverySurfaceDraftHasASection(t *testing.T) { for _, d := range All() { if !strings.HasPrefix(d.ID, "settings.cli.") && !strings.HasPrefix(d.ID, "settings.page.") { continue // not a plan-5 draft } sec := sectionFor(d.ID) if sec == "" { t.Errorf("%s: plan-5 draft is unmapped to a section", d.ID) } } } // Astra r1 (R21): the docs lead-in and the shared --account-id help land in // the sections Paul reviews them under. func TestTheR21DraftsLandInTheirSections(t *testing.T) { for id, want := range map[string]string{ "settings.cli.docs-apps-set-lead": "Docs and roll notes", "settings.cli.flag-account-id": "CLI: carlos instances set and update", "settings.page.major-inherited": "Console: instance page, Updates section", } { if got := sectionFor(id); got != want { t.Errorf("%s is in %q, want %q", id, got, want) } } } // TestExportDrafts writes every plan-5 draft to $UPDATEWORDS_EXPORT in JSON // format, organized by section for review. It does nothing unless the // variable is set. func TestExportDrafts(t *testing.T) { out := os.Getenv("UPDATEWORDS_EXPORT") if out == "" { t.Skip("UPDATEWORDS_EXPORT not set") } type entry struct { ID string `json:"id"` Section string `json:"section"` Label string `json:"label"` Text string `json:"text"` Context string `json:"context"` } // Collect all plan-5 drafts, organized by section then by ID. type sectionGroup struct { name string entries []entry } sectionMap := map[string]*sectionGroup{} sectionOrder := []string{ "CLI: carlos apps set", "CLI: carlos instances set and update", "CLI: carlos instances list", "Console: instance page, Updates section", "Docs and roll notes", } for _, sec := range sectionOrder { sectionMap[sec] = §ionGroup{name: sec} } for _, d := range All() { if !strings.HasPrefix(d.ID, "settings.cli.") && !strings.HasPrefix(d.ID, "settings.page.") { continue // not a plan-5 draft } sec := sectionFor(d.ID) if sec == "" { t.Fatalf("%s: plan-5 draft is unmapped to a section", d.ID) } parts := strings.Split(d.ID, ".") label := parts[len(parts)-1] sectionMap[sec].entries = append(sectionMap[sec].entries, entry{ ID: d.ID, Section: sec, Label: label, Text: d.Text, Context: d.Context, }) } // Build the final list in section order. var entries []entry for _, sec := range sectionOrder { if group := sectionMap[sec]; group != nil && len(group.entries) > 0 { entries = append(entries, group.entries...) } } b, err := json.MarshalIndent(entries, "", " ") if err != nil { t.Fatal(err) } if err := os.MkdirAll(filepath.Dir(out), 0o755); err != nil { t.Fatal(err) } if err := os.WriteFile(out, b, 0o644); err != nil { t.Fatal(err) } }