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:
@@ -1,6 +1,8 @@
|
||||
package topdata
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
"git.westgate.pw/ShadowsOverWestgate/sow-tools/internal/project"
|
||||
@@ -54,3 +56,45 @@ func TestParityGuardCDNChannelProducesAccessoryRows(t *testing.T) {
|
||||
t.Fatalf("PARITY GUARD: expected lock id for %s, got %#v", wantKey, got[0].LockData)
|
||||
}
|
||||
}
|
||||
|
||||
// PARITY GUARD (parts): a bare project whose only parts source is the offline
|
||||
// CDN-equivalent (SOW_PART_MANIFEST -> a part.yml), with no NWN_ROOT, no token,
|
||||
// no asset checkout, must produce parts rows from the filtered inventory. If
|
||||
// parts resolution ever drifts back into a wrapper, this fails.
|
||||
func TestParityGuardCDNChannelProducesPartsRows(t *testing.T) {
|
||||
root := testProjectRoot(t)
|
||||
checkout := filepath.Join(root, "manifest-checkout")
|
||||
if err := os.MkdirAll(filepath.Join(checkout, "assets"), 0o755); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
writeFile(t, filepath.Join(checkout, "assets", "part.yml"),
|
||||
"assets:\n - {path: part/belt/m/pfa0_belt017.mdl, restype: mdl}\n - {path: part/belt/m/pfa0_belt018.mdl, restype: mdl}\n")
|
||||
t.Setenv("SOW_PART_MANIFEST", checkout)
|
||||
t.Setenv("NWN_ROOT", "")
|
||||
|
||||
c := projectPartsConsumer()
|
||||
c.Source = project.AutogenSourceConfig{
|
||||
Kind: "cdn_channel",
|
||||
ChannelsPath: "releases/haks/channels.json",
|
||||
ManifestPath: "releases/haks/{tag}/part.yml",
|
||||
ReleaseMarkerPath: "releases/haks/{tag}/haks.json",
|
||||
OfflineOverrideEnv: "SOW_PART_MANIFEST",
|
||||
}
|
||||
|
||||
p := testProject(root)
|
||||
p.Config.Autogen.Consumers = []project.AutogenConsumerConfig{c}
|
||||
|
||||
collected := []nativeCollectedDataset{partsDatasetWithIDs("parts/belt", nil)}
|
||||
|
||||
got, err := applyAutogenConsumers(p, collected, nil)
|
||||
if err != nil {
|
||||
t.Fatalf("applyAutogenConsumers failed: %v", err)
|
||||
}
|
||||
ids := map[int]bool{}
|
||||
for _, row := range got[0].Rows {
|
||||
ids[row["id"].(int)] = true
|
||||
}
|
||||
if !ids[17] || !ids[18] {
|
||||
t.Fatalf("PARITY GUARD: expected belt rows 17 and 18 from inventory, got %#v", got[0].Rows)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user