Files
member-console/internal/lint/archive_test.go
T
cgalo5758 bea0ffc024 Add Codex CLI harness to agent runner
Support Codex CLI as a second agent harness alongside opencode, with
per-harness credential mounting, model routing, and reasoning effort
handling.
2026-09-21 02:42:53 -05:00

188 lines
5.0 KiB
Go

// SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Commercial
// SPDX-FileCopyrightText: 2025-2026 Christian Galo
package lint
import (
"errors"
"fmt"
"os"
"path/filepath"
"regexp"
"strings"
"testing"
"gopkg.in/yaml.v3"
)
// resolvedIssue is one record of status/archive/issues-resolved.yaml, the
// frozen archive of issues closed before the tracker moved to the forge.
type resolvedIssue struct {
Title string `yaml:"title"`
Kind string `yaml:"kind"`
Areas []string `yaml:"areas"`
Outcome string `yaml:"outcome"`
Resolved string `yaml:"resolved"`
By string `yaml:"by"`
Summary string `yaml:"summary"`
Body string `yaml:"body"`
}
// archiveOutcomes is the closed set of outcome values. The forge label
// set has no outcome scope, so the archive carries its own.
var archiveOutcomes = map[string]bool{
"fixed": true,
"superseded": true,
"wontfix": true,
"duplicate": true,
"moved-upstream": true,
}
// archiveAreas is the area/ label set the archive was written against. The
// forge's labels are the live registry; the archive is frozen history, so
// a later rename there does not rewrite these records.
var archiveAreas = map[string]bool{
"identity": true,
"entitlements": true,
"catalog": true,
"billing": true,
"domains": true,
"fedwiki": true,
"discourse": true,
"integrations": true,
"operator-ui": true,
"member-ui": true,
"ops": true,
"testing": true,
"licensing": true,
"meta": true,
}
var archiveResolvedRe = regexp.MustCompile(`^\d{4}-\d{2}(-\d{2})?$`)
// repoRoot walks up from the test's working directory until it finds go.mod.
func repoRoot(t *testing.T) string {
t.Helper()
dir, err := os.Getwd()
if err != nil {
t.Fatal(err)
}
for {
if _, err := os.Stat(filepath.Join(dir, "go.mod")); err == nil {
return dir
} else if !errors.Is(err, os.ErrNotExist) {
t.Fatal(err)
}
parent := filepath.Dir(dir)
if parent == dir {
t.Fatalf("no go.mod above %s", dir)
}
dir = parent
}
}
// formKinds reads every issue form under .gitea/ISSUE_TEMPLATE and returns
// the kind/ label each one applies, with the scope prefix removed. The forms
// are the in-repo statement of which kinds exist.
func formKinds(t *testing.T, dir string) map[string]bool {
t.Helper()
paths, err := filepath.Glob(filepath.Join(dir, "*.yaml"))
if err != nil {
t.Fatal(err)
}
kinds := map[string]bool{}
for _, path := range paths {
raw, err := os.ReadFile(path)
if err != nil {
t.Fatal(err)
}
var form struct {
Labels []string `yaml:"labels"`
}
if err := yaml.Unmarshal(raw, &form); err != nil {
t.Fatalf("%s: %v", path, err)
}
for _, l := range form.Labels {
if rest, ok := strings.CutPrefix(l, "kind/"); ok {
kinds[rest] = true
}
}
}
if len(kinds) == 0 {
t.Fatalf("%s: no issue form applies a kind/ label", dir)
}
return kinds
}
// TestResolvedIssuesArchiveSchema enforces the record schema of
// status/archive/issues-resolved.yaml: every field but `by` present and
// non-empty, kind one the issue forms apply, areas from the frozen
// archiveAreas set, outcome from the archive's own set, resolved a
// YYYY-MM-DD or YYYY-MM date, at least one area, and no two records with
// the same title.
func TestResolvedIssuesArchiveSchema(t *testing.T) {
root := repoRoot(t)
kinds := formKinds(t, filepath.Join(root, ".gitea", "ISSUE_TEMPLATE"))
areas := archiveAreas
path := filepath.Join(root, "status", "archive", "issues-resolved.yaml")
f, err := os.Open(path)
if err != nil {
t.Fatal(err)
}
defer f.Close()
dec := yaml.NewDecoder(f)
dec.KnownFields(true)
var records []resolvedIssue
if err := dec.Decode(&records); err != nil {
t.Fatalf("%s: %v", path, err)
}
if len(records) == 0 {
t.Fatalf("%s: no records", path)
}
seen := map[string]int{}
for i, r := range records {
n := i + 1
fail := func(format string, args ...any) {
t.Helper()
t.Errorf("record %d %q: %s", n, r.Title, fmt.Sprintf(format, args...))
}
required := []struct{ name, value string }{
{"title", r.Title},
{"kind", r.Kind},
{"outcome", r.Outcome},
{"resolved", r.Resolved},
{"summary", r.Summary},
{"body", r.Body},
}
for _, field := range required {
if strings.TrimSpace(field.value) == "" {
fail("%s is missing or empty", field.name)
}
}
if r.Kind != "" && !kinds[r.Kind] {
fail("kind %q is not a kind/ label any issue form applies", r.Kind)
}
if len(r.Areas) == 0 {
fail("areas is empty")
}
for _, a := range r.Areas {
if !areas[a] {
fail("area %q is not in the archive area set", a)
}
}
if r.Outcome != "" && !archiveOutcomes[r.Outcome] {
fail("outcome %q is not one of fixed, superseded, wontfix, duplicate, moved-upstream", r.Outcome)
}
if r.Resolved != "" && !archiveResolvedRe.MatchString(r.Resolved) {
fail("resolved %q is not YYYY-MM-DD or YYYY-MM", r.Resolved)
}
if first, dup := seen[r.Title]; dup {
fail("duplicate title, first at record %d", first)
} else {
seen[r.Title] = n
}
}
}