feat(topdata): parts autogen from CDN part.yml channel
Phase 2 of the parts-row autogen design: Crucible resolves parts 2DA rows from the immutable part.yml published with the asset HAK release, over the anonymous Bunny CDN channel, with the parts consumer required (no fail-open). - generalize resolveCDNChannelManifest: derive manifest basename + provenance label from config; drop hardcoded assets/vfxs.yml so part.yml resolves too - add filterPartsCDNChannelEntries + mode dispatch implementing the inventory contract (restype mdl under part/<cat>/, trailing-digit row id, dedup l/r/race/gender variants, sort by source, zero accepted rows = hard fail) - support hand category + parts/hand dataset mapping (12 datasets total) - native pipeline: single augment -> normalize -> override sequence with the configured parts_rows policy; overrides may update but never synthesize a row - reject multiple parts_rows consumers in project validation - tests: parts filter contract, category coverage, offline basename, orphan override reject, and a parity guard proving a bare CDN-only parts build Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,51 @@
|
||||
package topdata
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
"git.westgate.pw/ShadowsOverWestgate/sow-tools/internal/project"
|
||||
)
|
||||
|
||||
func partsDatasetWithIDs(name string, ids []int) nativeCollectedDataset {
|
||||
rows := make([]map[string]any, 0, len(ids))
|
||||
for _, id := range ids {
|
||||
rows = append(rows, map[string]any{"id": id, "COSTMODIFIER": "0", "ACBONUS": "0.00"})
|
||||
}
|
||||
return nativeCollectedDataset{
|
||||
Dataset: nativeDataset{Name: name, Kind: nativeDatasetBase},
|
||||
Columns: []string{"COSTMODIFIER", "ACBONUS"},
|
||||
Rows: rows,
|
||||
}
|
||||
}
|
||||
|
||||
func TestApplyPartOverridesRejectsOrphan(t *testing.T) {
|
||||
dir := t.TempDir()
|
||||
if err := os.MkdirAll(filepath.Join(dir, "data", "parts", "overrides"), 0o755); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
writeFile(t, filepath.Join(dir, "data", "parts", "overrides", "belt.json"),
|
||||
`{"overrides": [{"id": 999, "COSTMODIFIER": "5"}]}`)
|
||||
collected := []nativeCollectedDataset{partsDatasetWithIDs("parts/belt", []int{17})}
|
||||
if _, err := applyPartOverridesWithConfig(dir, collected, project.PartsRowsConfig{}); err == nil {
|
||||
t.Fatal("expected orphan override to fail, not synthesize a row")
|
||||
}
|
||||
}
|
||||
|
||||
func TestApplyPartOverridesUpdatesDiscoveredRow(t *testing.T) {
|
||||
dir := t.TempDir()
|
||||
if err := os.MkdirAll(filepath.Join(dir, "data", "parts", "overrides"), 0o755); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
writeFile(t, filepath.Join(dir, "data", "parts", "overrides", "belt.json"),
|
||||
`{"overrides": [{"id": 17, "COSTMODIFIER": "5"}]}`)
|
||||
collected := []nativeCollectedDataset{partsDatasetWithIDs("parts/belt", []int{17})}
|
||||
out, err := applyPartOverridesWithConfig(dir, collected, project.PartsRowsConfig{})
|
||||
if err != nil {
|
||||
t.Fatalf("override: %v", err)
|
||||
}
|
||||
if got := out[0].Rows[0]["COSTMODIFIER"]; got != "5" {
|
||||
t.Fatalf("row 17 COSTMODIFIER want 5, got %v", got)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user