From 9313769649981bcca9533a42c9bad54e4c0b6173 Mon Sep 17 00:00:00 2001 From: vickydotbat Date: Sat, 4 Apr 2026 09:34:36 +0200 Subject: [PATCH] Force Check Duplicates --- README.md | 2 ++ internal/pipeline/pipeline_test.go | 2 +- internal/validator/validator.go | 2 +- internal/validator/validator_test.go | 48 ++++++++++++++++++++++++++++ 4 files changed, 52 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 1f3e70f..1141977 100644 --- a/README.md +++ b/README.md @@ -35,6 +35,8 @@ tools/ built development binary output - `sow-module` uses `sow-toolkit` for `build-module`, `extract`, `validate`, `compare`, and `apply-hak-manifest` - `sow-assets` uses `sow-toolkit` for `build-haks` and validation + - validation now fails early if duplicate asset resources would collide inside the same generated HAK + - duplicate resources split across different HAK groups remain warnings because later HAK order can still override earlier content at runtime - `sow-module` now also uses `sow-toolkit` for the early topdata bridge: - `validate-topdata` - `build-topdata` diff --git a/internal/pipeline/pipeline_test.go b/internal/pipeline/pipeline_test.go index 3d6e321..0d82aff 100644 --- a/internal/pipeline/pipeline_test.go +++ b/internal/pipeline/pipeline_test.go @@ -1052,7 +1052,7 @@ func TestBuildHAKsFailsForDuplicateResourcesInSameHAK(t *testing.T) { if err == nil { t.Fatal("expected duplicate same-hak build error") } - if !strings.Contains(err.Error(), "conflicts") { + if !strings.Contains(err.Error(), "validation failed with 1 error(s)") { t.Fatalf("unexpected error: %v", err) } } diff --git a/internal/validator/validator.go b/internal/validator/validator.go index dc87a1f..6f80e93 100644 --- a/internal/validator/validator.go +++ b/internal/validator/validator.go @@ -312,7 +312,7 @@ func classifyAssetDuplicates(report *Report, occurrences map[string][]assetOccur slices.Sort(paths) if len(groups) == 1 { - report.add(entries[0].Path, fmt.Sprintf("duplicate asset resource %s appears multiple times in hak group %q (%s); build will fail if duplicates land in the same generated hak", key, groups[0], strings.Join(paths, ", ")), SeverityWarning) + report.add(entries[0].Path, fmt.Sprintf("duplicate asset resource %s appears multiple times in hak group %q (%s); build will fail if duplicates land in the same generated hak", key, groups[0], strings.Join(paths, ", ")), SeverityError) continue } diff --git a/internal/validator/validator_test.go b/internal/validator/validator_test.go index 5dbe3f0..994fa52 100644 --- a/internal/validator/validator_test.go +++ b/internal/validator/validator_test.go @@ -54,6 +54,54 @@ func TestValidateProjectWarnsForCrossHAKDuplicateAssets(t *testing.T) { } } +func TestValidateProjectErrorsForSameHAKDuplicateAssets(t *testing.T) { + root := t.TempDir() + mustMkdir(t, filepath.Join(root, "src")) + mustMkdir(t, filepath.Join(root, "assets", "core", "a")) + mustMkdir(t, filepath.Join(root, "assets", "core", "b")) + mustMkdir(t, filepath.Join(root, "build")) + + mustWriteFile(t, filepath.Join(root, "nwn-tool.json"), `{ + "module": { + "name": "Test Assets", + "resref": "testassets" + }, + "paths": { + "source": "src", + "assets": "assets", + "build": "build" + }, + "haks": [ + { "name": "core", "priority": 1, "max_bytes": 0, "split": false, "include": ["core/**"] } + ] +} +`) + mustWriteFile(t, filepath.Join(root, "assets", "core", "a", "shared.mdl"), "one") + mustWriteFile(t, filepath.Join(root, "assets", "core", "b", "shared.mdl"), "two") + + p, err := project.Load(root) + if err != nil { + t.Fatalf("load project: %v", err) + } + if err := p.ValidateLayout(); err != nil { + t.Fatalf("validate layout: %v", err) + } + if err := p.Scan(); err != nil { + t.Fatalf("scan: %v", err) + } + + report := ValidateProject(p) + if !report.HasErrors() { + t.Fatalf("expected same-hak duplicate to be an error, got %#v", report.Diagnostics) + } + if report.ErrorCount() != 1 { + t.Fatalf("expected 1 error, got %d (%#v)", report.ErrorCount(), report.Diagnostics) + } + if report.WarningCount() != 0 { + t.Fatalf("expected no warnings, got %d (%#v)", report.WarningCount(), report.Diagnostics) + } +} + func TestValidateProjectWarnsForUppercaseResourceNames(t *testing.T) { root := t.TempDir() mustMkdir(t, filepath.Join(root, "src", "blueprints", "items"))