package topdata import ( "testing" "git.westgate.pw/ShadowsOverWestgate/sow-tools/internal/project" ) func accConsumer() project.AutogenConsumerConfig { return project.AutogenConsumerConfig{ ID: "accessory_visualeffects", Producer: "accessory_visualeffects", Dataset: "visualeffects", Mode: "accessory_visualeffects", Optional: true, AccessoryVisualeffects: project.AccessoryVisualeffectsConfig{ Groups: map[string]project.AccessoryVisualeffectGroupConfig{ "head_accessories": {ModelColumns: []string{"Imp_HeadCon_Node"}}, }, GroupTokenSource: "folder_name", CategoryFrom: "immediate_parent", Delimiter: "/", Case: "preserve", StripModelPrefixes: []string{"hfx_"}, KeyFormat: "{dataset}:{group}{delimiter}{category_segment}{stem}", LabelFormat: "{group}{delimiter}{category_segment}{stem}", ModelColumn: "Imp_HeadCon_Node", }, } } func vfxEntry() autogenManifestEntry { return autogenManifestEntry{ Source: "head_accessories/hat/hfx_bandana.mdl", Group: "head_accessories", Subgroup: "hat", ModelStem: "hfx_bandana", } } func vfxDataset(lock map[string]int) nativeCollectedDataset { return nativeCollectedDataset{ Dataset: nativeDataset{Name: "visualeffects", Kind: nativeDatasetBase}, Columns: []string{"Label", "Imp_HeadCon_Node"}, Rows: nil, LockData: lock, } } func TestAccessoryLockKeyIsModelAnchored(t *testing.T) { got := accessoryVisualeffectLockKey("visualeffects", vfxEntry()) want := "visualeffects:head_accessories/hat/hfx_bandana.mdl" if got != want { t.Fatalf("lock key = %q, want %q", got, want) } } // A config-only change (here: a different key_format) must NOT move the ID, // because identity is the model path, not the presentation key. func TestConfigOnlyChangeKeepsLockID(t *testing.T) { lockKey := "visualeffects:head_accessories/hat/hfx_bandana.mdl" collected := []nativeCollectedDataset{vfxDataset(map[string]int{lockKey: 555})} c := accConsumer() c.AccessoryVisualeffects.KeyFormat = "{dataset}:{stem}" // config-only change got, err := augmentWithAutogeneratedAccessoryVisualeffects(collected, []autogenManifestEntry{vfxEntry()}, c, nil) if err != nil { t.Fatalf("augment failed: %v", err) } if got[0].LockData[lockKey] != 555 { t.Fatalf("expected ID preserved at 555, got %#v", got[0].LockData) } } // A model re-export (same path) keeps its ID — content change != identity change. func TestModelReExportKeepsLockID(t *testing.T) { lockKey := "visualeffects:head_accessories/hat/hfx_bandana.mdl" collected := []nativeCollectedDataset{vfxDataset(map[string]int{lockKey: 777})} got, err := augmentWithAutogeneratedAccessoryVisualeffects(collected, []autogenManifestEntry{vfxEntry()}, accConsumer(), nil) if err != nil { t.Fatalf("augment failed: %v", err) } if got[0].LockData[lockKey] != 777 { t.Fatalf("expected ID 777 preserved, got %#v", got[0].LockData) } } // A removed model frees its old ID; the surviving model keeps its lock entry. func TestModelRemovalPrunesStaleLockKey(t *testing.T) { keep := "visualeffects:head_accessories/hat/hfx_bandana.mdl" gone := "visualeffects:head_accessories/hat/hfx_removed.mdl" collected := []nativeCollectedDataset{vfxDataset(map[string]int{keep: 1, gone: 2})} got, err := augmentWithAutogeneratedAccessoryVisualeffects(collected, []autogenManifestEntry{vfxEntry()}, accConsumer(), nil) if err != nil { t.Fatalf("augment failed: %v", err) } if _, present := got[0].LockData[gone]; present { t.Fatalf("expected removed model's lock key pruned, got %#v", got[0].LockData) } if got[0].LockData[keep] != 1 { t.Fatalf("expected surviving model ID 1 kept, got %#v", got[0].LockData) } } // Cutover: a lock that only has the OLD config-derived key adopts that ID under // the new model-path key, with zero shift, and prunes the old key. func TestCutoverRemapPreservesID(t *testing.T) { oldKey := "visualeffects:head_accessories/hat/bandana" // {dataset}:{group}/{category}/{stem-without-hfx_} newKey := "visualeffects:head_accessories/hat/hfx_bandana.mdl" collected := []nativeCollectedDataset{vfxDataset(map[string]int{oldKey: 4242})} got, err := augmentWithAutogeneratedAccessoryVisualeffects(collected, []autogenManifestEntry{vfxEntry()}, accConsumer(), nil) if err != nil { t.Fatalf("augment failed: %v", err) } if got[0].LockData[newKey] != 4242 { t.Fatalf("expected cutover to carry ID 4242 to new key, got %#v", got[0].LockData) } if _, present := got[0].LockData[oldKey]; present { t.Fatalf("expected old config key pruned after cutover, got %#v", got[0].LockData) } }