package topdata import ( "os" "path/filepath" "testing" "git.westgate.pw/ShadowsOverWestgate/sow-tools/internal/project" ) // PARITY GUARD: a bare project (no manifest_file, no NWN_ROOT, no token) with a // cdn_channel consumer must produce accessory rows. If accessory resolution ever // drifts back out of Crucible into a wrapper, this fails. func TestParityGuardCDNChannelProducesAccessoryRows(t *testing.T) { root := testProjectRoot(t) srv := cdnServer(t, map[string]string{ "/releases/haks/channels.json": `{"current":"v1.0.0"}`, "/releases/haks/v1.0.0/vfxs.yml": `assets: - path: vfxs/head_accessories/hat/hfx_bandana.mdl restype: mdl sha256: aaa - path: vfxs/chest_accessories/cape/cfx_cloak.mdl restype: mdl sha256: bbb `, }) t.Setenv("BUNNY_CDN_BASE", srv.URL) t.Setenv("SOW_TOPDATA_ASSET_CHANNEL", "current") // Prove R3: no NWN_ROOT in the environment. t.Setenv("NWN_ROOT", "") c := cdnConsumer() c.Source.CDNBaseEnv = "BUNNY_CDN_BASE" c.Source.ChannelEnv = "SOW_TOPDATA_ASSET_CHANNEL" c.AccessoryVisualeffects.ModelColumn = "Imp_HeadCon_Node" p := testProject(root) p.Config.Autogen.Consumers = []project.AutogenConsumerConfig{c} collected := []nativeCollectedDataset{{ Dataset: nativeDataset{Name: "visualeffects", Kind: nativeDatasetBase}, Columns: []string{"Label", "Imp_HeadCon_Node", "Imp_Root_S_Node"}, Rows: nil, LockData: map[string]int{}, }} got, err := applyAutogenConsumers(p, collected, nil) if err != nil { t.Fatalf("applyAutogenConsumers failed: %v", err) } if len(got[0].Rows) != 2 { t.Fatalf("PARITY GUARD: expected 2 accessory rows, got %d (%#v)", len(got[0].Rows), got[0].Rows) } wantKey := "visualeffects:head_accessories/hat/hfx_bandana.mdl" if _, ok := got[0].LockData[wantKey]; !ok { 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) } }