From ef20ef9a5af947a2d1bd4e2dfb113623d6e4fd0d Mon Sep 17 00:00:00 2001 From: iliya Date: Mon, 7 Sep 2026 15:45:23 +0000 Subject: [PATCH 1/2] test(authoring): use physical roots for mutation fixtures --- .../authoring/commands/public_contract_test.go | 6 +++--- .../internal/authoring/commands/skills_test.go | 17 ++++++++++++++--- .../authoring/commands/vertical_fix_test.go | 2 +- .../internal/authoring/scaffold/apply_test.go | 14 +++++++++++++- .../authoring/scaffold/skill_plan_test.go | 7 ++++++- .../internal/authoring/skills/skills_test.go | 16 ++++++++++++++-- 6 files changed, 51 insertions(+), 11 deletions(-) diff --git a/cli/plugin-kit-ai/internal/authoring/commands/public_contract_test.go b/cli/plugin-kit-ai/internal/authoring/commands/public_contract_test.go index 26234ce1..6bf044fb 100644 --- a/cli/plugin-kit-ai/internal/authoring/commands/public_contract_test.go +++ b/cli/plugin-kit-ai/internal/authoring/commands/public_contract_test.go @@ -342,7 +342,7 @@ func TestPublicLiteralExamplesAndEveryLeaf(t *testing.T) { var first [][]byte var firstTree map[string]string for _, mount := range []bool{false, true} { - parent := t.TempDir() + parent := physicalMutationRoot(t) t.Chdir(parent) a := publicApp(t) args := append([]string{"init", lane.name, "--format=json"}, lane.flags...) @@ -408,7 +408,7 @@ func TestPublicLiteralExamplesAndEveryLeaf(t *testing.T) { } func TestPublicCWDAndExplicitInputs(t *testing.T) { - parent := t.TempDir() + parent := physicalMutationRoot(t) t.Chdir(parent) a := publicApp(t) root := filepath.Join(parent, "explicit-destination") @@ -681,7 +681,7 @@ func TestPublicAnnotatedErrorAndRealCleanup(t *testing.T) { noPolicy(t, e) for _, canceled := range []bool{false, true} { for _, mount := range []bool{false, true} { - parent := t.TempDir() + parent := physicalMutationRoot(t) ctx, cancel := context.WithCancel(context.Background()) stage := "" fault := faultContext{Context: ctx, check: func() { diff --git a/cli/plugin-kit-ai/internal/authoring/commands/skills_test.go b/cli/plugin-kit-ai/internal/authoring/commands/skills_test.go index 45148f9a..5b5b34ce 100644 --- a/cli/plugin-kit-ai/internal/authoring/commands/skills_test.go +++ b/cli/plugin-kit-ai/internal/authoring/commands/skills_test.go @@ -17,9 +17,20 @@ import ( "github.com/777genius/plugin-kit-ai/install/integrationctl/agentplugins/conformance" ) +// physicalMutationRoot resolves only a fresh owned infrastructure directory, +// before fixture inputs (including deliberate symlinks) are constructed. +func physicalMutationRoot(t *testing.T) string { + t.Helper() + root, err := filepath.EvalSymlinks(t.TempDir()) + if err != nil { + t.Fatal(err) + } + return root +} + func TestSkillsFactoriesAndEntrypointParity(t *testing.T) { a := commands.App{Projects: project.Service{Scratch: t.TempDir()}, Revision: "skills-factories"} - roots := []string{t.TempDir(), t.TempDir()} + roots := []string{physicalMutationRoot(t), physicalMutationRoot(t)} for _, root := range roots { write(t, root, "plugin.json", plugin("")) write(t, root, "keep", marker) @@ -105,7 +116,7 @@ func TestSkillsFactoriesAndEntrypointParity(t *testing.T) { } } func TestSkillsExactCWD(t *testing.T) { - root := t.TempDir() + root := physicalMutationRoot(t) write(t, root, "plugin.json", plugin("")) child := filepath.Join(root, "child") if e := os.Mkdir(child, 0700); e != nil { @@ -160,7 +171,7 @@ func TestSkillsHumanAllowlist(t *testing.T) { func TestSkillsPostCommitErrorIsExplicit(t *testing.T) { for _, mount := range []bool{false, true} { for _, jsonOutput := range []bool{false, true} { - root := t.TempDir() + root := physicalMutationRoot(t) write(t, root, "plugin.json", plugin("")) a := commands.App{Projects: project.Service{Scratch: t.TempDir(), Limits: packageview.Limits{Entries: 1}}} args := []string{"skills", "init", "new", "--description", "text", root} diff --git a/cli/plugin-kit-ai/internal/authoring/commands/vertical_fix_test.go b/cli/plugin-kit-ai/internal/authoring/commands/vertical_fix_test.go index 83bdf33d..577e4bc1 100644 --- a/cli/plugin-kit-ai/internal/authoring/commands/vertical_fix_test.go +++ b/cli/plugin-kit-ai/internal/authoring/commands/vertical_fix_test.go @@ -104,7 +104,7 @@ func TestReviewInitCleanupFailurePrecedence(t *testing.T) { boundary = "command" } t.Run(fixture+"/"+boundary, func(t *testing.T) { - parent, scratch := t.TempDir(), t.TempDir() + parent, scratch := physicalMutationRoot(t), t.TempDir() ctx, cancel := context.WithCancel(context.Background()) defer cancel() replaced := "" diff --git a/cli/plugin-kit-ai/internal/authoring/scaffold/apply_test.go b/cli/plugin-kit-ai/internal/authoring/scaffold/apply_test.go index d79dfd12..134249eb 100644 --- a/cli/plugin-kit-ai/internal/authoring/scaffold/apply_test.go +++ b/cli/plugin-kit-ai/internal/authoring/scaffold/apply_test.go @@ -107,7 +107,16 @@ func TestMissingParentSymlinkParentAndSourceOverlap(t *testing.T) { t.Fatal(err) } p := planFor(t, "skill") - validate := realValidation(t) + validationCalls := 0 + realValidate := realValidation(t) + validate := func(ctx context.Context, stage string) error { + validationCalls++ + return realValidate(ctx, stage) + } + // Prove this physical fixture reaches real validation before adding aliases. + if result, err := Apply(context.Background(), p, ApplyOptions{Destination: filepath.Join(root, "positive"), Validate: validate}); err != nil || !result.Committed || validationCalls != 1 { + t.Fatalf("physical root positive control: %+v %v calls=%d", result, err, validationCalls) + } for _, o := range []ApplyOptions{ {Destination: filepath.Join(root, "missing", "out")}, {Destination: filepath.Join(source, "out"), SourceRoots: []string{source}}, @@ -133,6 +142,9 @@ func TestMissingParentSymlinkParentAndSourceOverlap(t *testing.T) { if _, err := Apply(context.Background(), p, ApplyOptions{Destination: filepath.Join(source, "out"), SourceRoots: []string{alias}, Validate: validate}); err == nil { t.Fatal("source alias overlap") } + if validationCalls != 1 { + t.Fatalf("unsafe input reached validation: calls=%d", validationCalls) + } assertOnly(t, source, "sentinel") b, _ := os.ReadFile(filepath.Join(source, "sentinel")) if string(b) != "untouched" { diff --git a/cli/plugin-kit-ai/internal/authoring/scaffold/skill_plan_test.go b/cli/plugin-kit-ai/internal/authoring/scaffold/skill_plan_test.go index b30c8de8..579a1651 100644 --- a/cli/plugin-kit-ai/internal/authoring/scaffold/skill_plan_test.go +++ b/cli/plugin-kit-ai/internal/authoring/scaffold/skill_plan_test.go @@ -21,7 +21,7 @@ import ( func skillFixture(t *testing.T) (string, SkillPlan, SkillSourceGate) { t.Helper() - root, scratch := t.TempDir(), t.TempDir() + root, scratch := tempRoot(t), t.TempDir() body := []byte(`{"$schema":"` + domain.PluginSchemaV1 + `","name":"fixture"}`) if e := os.WriteFile(filepath.Join(root, "plugin.json"), body, 0600); e != nil { t.Fatal(e) @@ -135,7 +135,9 @@ func TestSkillAtomicFailureAndStagedValidation(t *testing.T) { ctx, cancel := context.WithCancel(context.Background()) defer cancel() injected := errors.New("injected I/O failure") + writeReached := false ops := applyOps{write: func(ctx context.Context, r *os.Root, files []File) error { + writeReached = true if mode == "write-failure" { if e := r.Mkdir("partial", 0700); e != nil { return e @@ -189,6 +191,9 @@ func TestSkillAtomicFailureAndStagedValidation(t *testing.T) { return nil }} r, e := applySkill(ctx, p, root, gate, sharedSkillValidation("new-skill"), ops) + if !writeReached { + t.Fatalf("write fault callback not reached: %v", e) + } if e == nil { t.Fatal("injected failure reported success") } diff --git a/cli/plugin-kit-ai/internal/authoring/skills/skills_test.go b/cli/plugin-kit-ai/internal/authoring/skills/skills_test.go index 23c20199..65a17a6b 100644 --- a/cli/plugin-kit-ai/internal/authoring/skills/skills_test.go +++ b/cli/plugin-kit-ai/internal/authoring/skills/skills_test.go @@ -31,9 +31,21 @@ func put(t *testing.T, root, path, body string) { t.Fatal(err) } } + +// physicalMutationRoot resolves only a fresh owned infrastructure directory, +// before fixture inputs (including deliberate symlinks) are constructed. +func physicalMutationRoot(t *testing.T) string { + t.Helper() + root, err := filepath.EvalSymlinks(t.TempDir()) + if err != nil { + t.Fatal(err) + } + return root +} + func setup(t *testing.T) (skills.Service, string) { t.Helper() - root := t.TempDir() + root := physicalMutationRoot(t) put(t, root, "plugin.json", `{"$schema":"`+domain.PluginSchemaV1+`","name":"fixture"}`) return skills.Service{Projects: project.Service{Scratch: t.TempDir()}, Revision: "skills-test"}, root } @@ -212,7 +224,7 @@ func TestSkillContainmentAndSourceGate(t *testing.T) { selected := root switch kind { case "root-link": - selected = filepath.Join(t.TempDir(), "link") + selected = filepath.Join(physicalMutationRoot(t), "link") if e := os.Symlink(root, selected); e != nil { t.Fatal(e) } From 6dab4d3b28a5e65aaa981a3bb869fbc10ec3c7bd Mon Sep 17 00:00:00 2001 From: iliya Date: Fri, 11 Sep 2026 14:11:15 +0300 Subject: [PATCH 2/2] fix(authoring): forward the *os.Root validate argument in apply_test The physical-fixture-root positive control in TestMissingParentSymlinkParentAndSourceOverlap wrapped realValidation(t) (type Validate, now 3 args: ctx, stage, *os.Root) in a local closure still declared with the pre-GeneratedStaging 2-arg signature, breaking go vet/test discovery on every platform (this is why exact-head CI failed test discovery on linux-arm64 and windows-amd64, not a real per-OS regression). Forward the dir argument through. Refs #183 --- cli/plugin-kit-ai/internal/authoring/scaffold/apply_test.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cli/plugin-kit-ai/internal/authoring/scaffold/apply_test.go b/cli/plugin-kit-ai/internal/authoring/scaffold/apply_test.go index 134249eb..2f2e1674 100644 --- a/cli/plugin-kit-ai/internal/authoring/scaffold/apply_test.go +++ b/cli/plugin-kit-ai/internal/authoring/scaffold/apply_test.go @@ -109,9 +109,9 @@ func TestMissingParentSymlinkParentAndSourceOverlap(t *testing.T) { p := planFor(t, "skill") validationCalls := 0 realValidate := realValidation(t) - validate := func(ctx context.Context, stage string) error { + validate := func(ctx context.Context, stage string, dir *os.Root) error { validationCalls++ - return realValidate(ctx, stage) + return realValidate(ctx, stage, dir) } // Prove this physical fixture reaches real validation before adding aliases. if result, err := Apply(context.Background(), p, ApplyOptions{Destination: filepath.Join(root, "positive"), Validate: validate}); err != nil || !result.Committed || validationCalls != 1 {