Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions docs/fixtures.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,16 @@ Committed fixtures may include:

Generated artifacts outside intentional fixture paths should not be committed.

## Fixture Integrity Validation

Run the focused integrity check after adding, removing, or renaming committed fixture files:

```bash
go test ./pkg/prmaven -run TestFixtureIntegrity -v
```

This validation ensures expected demo and `testdata` files exist, intentionally committed `target` report and log artifacts remain present, and unexpected generated files are not added under fixture `target` directories.

## Compatibility Expectations

When adding or updating fixtures:
Expand Down
12 changes: 12 additions & 0 deletions docs/testing.md
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,18 @@ The `target/*-reports` directories and selected `target/*.log` files are intenti

Fixture compatibility notes, including the Maven 3.9.x production baseline and Maven 4 tracking boundary, are documented in [Fixture Notes](fixtures.md).

### Fixture Integrity

Fixture integrity is validated by `TestFixtureIntegrity` in `pkg/prmaven`.

Run the focused validation after adding, removing, or renaming committed fixture files:

```bash
go test ./pkg/prmaven -run TestFixtureIntegrity -v
```

The test checks that expected demo and `testdata` files exist, that intentionally committed `target` report and log artifacts remain present, and that unexpected generated files are not added under fixture `target` directories.

### Golden Files

Golden files live under:
Expand Down
133 changes: 133 additions & 0 deletions pkg/prmaven/fixture_integrity_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
package prmaven

import (
"io/fs"
"os"
"path/filepath"
"strings"
"testing"
)

func TestFixtureIntegrity(t *testing.T) {
expectedFixtureFiles := []fixtureFile{
{path: "../../demo/multi-module-failure/pom.xml", description: "multi-module failure demo root POM"},
{path: "../../demo/multi-module-failure/payment-api/pom.xml", description: "payment-api module POM"},
{
path: "../../demo/multi-module-failure/payment-api/target/failsafe-reports/TEST-dev.prmaven.demo.PaymentApiIT.xml",
description: "Failsafe demo report",
},
{path: "../../demo/multi-module-failure/payment-core/pom.xml", description: "payment-core module POM"},
{
path: "../../demo/multi-module-failure/payment-core/target/surefire-reports/TEST-dev.prmaven.demo.PaymentRoundingTest.xml",
description: "Surefire demo report",
},
{path: "../../demo/no-failure/pom.xml", description: "no-failure demo root POM"},
{path: "../../demo/no-failure/inventory-core/pom.xml", description: "inventory-core module POM"},
{
path: "../../demo/no-failure/inventory-core/target/surefire-reports/TEST-dev.prmaven.demo.InventoryServiceTest.xml",
description: "no-failure Surefire report",
},
{path: "testdata/checkstyle-project/pom.xml", description: "Checkstyle fixture root POM"},
{path: "testdata/checkstyle-project/service-core/pom.xml", description: "Checkstyle fixture module POM"},
{path: "testdata/checkstyle-project/service-core/target/checkstyle-result.xml", description: "Checkstyle report fixture"},
{path: "testdata/spotbugs-project/pom.xml", description: "SpotBugs fixture root POM"},
{path: "testdata/spotbugs-project/service-core/pom.xml", description: "SpotBugs fixture module POM"},
{path: "testdata/spotbugs-project/service-core/target/spotbugsXml.xml", description: "SpotBugs report fixture"},
{path: "testdata/enforcer-project/pom.xml", description: "Enforcer fixture root POM"},
{path: "testdata/enforcer-project/service-core/pom.xml", description: "Enforcer fixture module POM"},
{path: "testdata/enforcer-project/service-core/target/maven-enforcer.log", description: "Enforcer log fixture"},
{path: "testdata/jacoco-project/pom.xml", description: "JaCoCo fixture root POM"},
{path: "testdata/jacoco-project/service-core/pom.xml", description: "JaCoCo fixture module POM"},
{path: "testdata/jacoco-project/service-core/target/jacoco.log", description: "JaCoCo log fixture"},
{path: "testdata/nested-module-project/pom.xml", description: "nested fixture root POM"},
{path: "testdata/nested-module-project/platform/pom.xml", description: "nested platform POM"},
{path: "testdata/nested-module-project/platform/service-core/pom.xml", description: "nested service-core POM"},
{
path: "testdata/nested-module-project/platform/service-core/target/surefire-reports/TEST-dev.prmaven.demo.NestedPaymentTest.xml",
description: "nested Surefire report",
},
{path: "testdata/golden/multi-module-failure.txt", description: "multi-module text golden file"},
{path: "testdata/golden/no-failure.txt", description: "no-failure text golden file"},
}

for _, file := range expectedFixtureFiles {
assertFixtureFile(t, file.path, file.description)
}

expectedTargetFiles := map[string]bool{}
for _, file := range expectedFixtureFiles {
if containsTargetSegment(file.path) {
expectedTargetFiles[fixturePathKey(file.path)] = true
}
}

seenTargetFiles := map[string]bool{}
for _, root := range []string{"../../demo", "testdata"} {
walkFixtureRoot(t, root, func(path string, entry fs.DirEntry) {
if entry.Type()&fs.ModeSymlink != 0 {
t.Fatalf("fixture %s is a symlink; fixtures must be self-contained committed files", fixturePathKey(path))
}
if entry.IsDir() || !containsTargetSegment(path) {
return
}

key := fixturePathKey(path)
if !expectedTargetFiles[key] {
t.Fatalf("unexpected committed target artifact %s; add it to TestFixtureIntegrity if it is an intentional sanitized fixture", key)
}
seenTargetFiles[key] = true
})
}

for path := range expectedTargetFiles {
if !seenTargetFiles[path] {
t.Fatalf("expected committed target fixture %s was not found during fixture scan", path)
}
}
}

type fixtureFile struct {
path string
description string
}

func assertFixtureFile(t *testing.T, path, description string) {
t.Helper()

info, err := os.Stat(path)
if err != nil {
t.Fatalf("missing %s at %s: %v", description, fixturePathKey(path), err)
}
if !info.Mode().IsRegular() {
t.Fatalf("%s at %s is not a regular file", description, fixturePathKey(path))
}
if info.Size() == 0 {
t.Fatalf("%s at %s is empty", description, fixturePathKey(path))
}
}

func walkFixtureRoot(t *testing.T, root string, visit func(path string, entry fs.DirEntry)) {
t.Helper()

err := filepath.WalkDir(root, func(path string, entry fs.DirEntry, walkErr error) error {
if walkErr != nil {
return walkErr
}
visit(path, entry)
return nil
})
if err != nil {
t.Fatalf("walk fixture root %s: %v", fixturePathKey(root), err)
}
}

func containsTargetSegment(path string) bool {
normalized := filepath.ToSlash(filepath.Clean(path))
return normalized == "target" ||
strings.HasPrefix(normalized, "target/") ||
strings.Contains(normalized, "/target/")
}

func fixturePathKey(path string) string {
return filepath.ToSlash(filepath.Clean(path))
}
Loading