guard.ts: cover every branch for a rule Go's regexp reads otherwise, and hold both matchers to one table (issue 539)
mesh/merge-gate pass: builds gitea → novox; no bus step; every machine composes with the change as it did without (4 of 4 compose)
mesh/repo-check pass: its merge-check.sh passed, each part in its toolchain: merge-check.sh (go), merge-check-typescript.sh (typescript)
mesh/delivery delivered

Review 30 found guard.ts missing branches the forge protects: Go reads \d, \w,
\pL, a class opening with ] or ^, and [[:digit:]] otherwise than JavaScript.
A rule using any of them now covers every branch, so the guard refuses more
than the forge, never less. test/rule-cases.json holds the forge's answers,
read by the Go and TypeScript tests alike.
This commit is contained in:
jochen
2026-10-12 12:51:22 +02:00
parent f2d765320a
commit ac90b35b49
4 changed files with 336 additions and 32 deletions
+21 -18
View File
@@ -398,24 +398,27 @@ func TestACommitAskThatCannotBeReadIsRefused(t *testing.T) {
}
}
func TestRuleCoversIsTheGuardsRule(t *testing.T) {
for _, c := range []struct {
rule, branch string
want bool
}{
{"main", "main", true}, {"main", "mainx", false}, {"release/*", "release/1.0", true},
{"release/*", "release/a/b", false}, {"release/**", "release/a/b", true}, {"v?", "v1", true},
{"a.b", "axb", false},
// As the forge decides it (novox/hq issue 539): classes, alternatives, escapes, a plain name in any case.
{"release-[ab]", "release-a", true}, {"release-[ab]", "release-c", false}, {"release-[!ab]", "release-c", true},
{"v[0-9].x", "v7.x", true}, {"v[0-9].x", "vA.x", false}, {"{main,master}", "master", true},
{"{main,master}", "mainline", false}, {"rel/{a,b}/*", "rel/b/1", true}, {"rel/{a,b}/*", "rel/c/1", false},
{"Main", "main", true}, {"main", "MAIN", true}, {"Feat/*", "feat/x", false}, {`a\*`, "a*", true},
{`a\*`, "ab", false}, {"a+b", "a+b", true}, {"a+b", "aab", false}, {"{open", "{open", true},
{"[open", "[open", true}, {"[open", "o", false},
} {
if got := ruleCovers(c.rule, c.branch); got != c.want {
t.Errorf("%s covers %s: %v", c.rule, c.branch, got)
// The forge's own answers (Gitea 1.27's Match), one table for this bundle and guard.ts alike (novox/hq issue 539).
// `forge` is the forge's answer, which this port must give; `guard` is where guard.ts deliberately covers more.
func TestRuleCoversAnswersAsTheForgeDoes(t *testing.T) {
raw, err := os.ReadFile("../../test/rule-cases.json")
if err != nil {
t.Fatal(err)
}
var cases []struct {
Rule string `json:"rule"`
Branch string `json:"branch"`
Forge bool `json:"forge"`
}
if err := json.Unmarshal(raw, &cases); err != nil {
t.Fatal(err)
}
if len(cases) < 100 {
t.Fatalf("only %d cases", len(cases))
}
for _, c := range cases {
if got := ruleCovers(c.Rule, c.Branch); got != c.Forge {
t.Errorf("%q covers %q: %v, the forge says %v", c.Rule, c.Branch, got, c.Forge)
}
}
}