package project import ( "errors" "os" "path/filepath" "slices" "testing" ) func TestTopDataAssetsDirPrefersExplicitConfig(t *testing.T) { root := t.TempDir() mkdirAll(t, filepath.Join(root, "src")) mkdirAll(t, filepath.Join(root, "assets")) mkdirAll(t, filepath.Join(root, "build")) mkdirAll(t, filepath.Join(root, "custom-assets")) proj := &Project{ Root: root, Config: Config{ Module: ModuleConfig{Name: "Test", ResRef: "test"}, Paths: PathConfig{Source: "src", Assets: "assets", Build: "build"}, TopData: TopDataConfig{ Assets: "custom-assets", }, }, } result := proj.TopDataAssetsDir() expected := filepath.Join(root, "custom-assets") if result != expected { t.Errorf("expected %s, got %s", expected, result) } } func TestTopDataAssetsDirDoesNotAssumeSiblingRepo(t *testing.T) { root := t.TempDir() mkdirAll(t, filepath.Join(root, "src")) mkdirAll(t, filepath.Join(root, "assets")) mkdirAll(t, filepath.Join(root, "build")) parentDir := filepath.Dir(root) siblingPath := filepath.Join(parentDir, "sow-assets") mkdirAll(t, siblingPath) mkdirAll(t, filepath.Join(siblingPath, "assets", "part", "belt")) proj := &Project{ Root: root, Config: Config{ Module: ModuleConfig{Name: "Test", ResRef: "test"}, Paths: PathConfig{Source: "src", Assets: "assets", Build: "build"}, TopData: TopDataConfig{ Assets: "sow-assets", }, }, } result := proj.TopDataAssetsDir() expected := filepath.Join(root, "sow-assets") if result != expected { t.Errorf("expected unresolved local path %s, got %s", expected, result) } } func TestTopDataAssetsDirReturnsEmptyForNoConfig(t *testing.T) { root := t.TempDir() mkdirAll(t, filepath.Join(root, "src")) mkdirAll(t, filepath.Join(root, "assets")) mkdirAll(t, filepath.Join(root, "build")) proj := &Project{ Root: root, Config: Config{ Module: ModuleConfig{Name: "Test", ResRef: "test"}, Paths: PathConfig{Source: "src", Assets: "assets", Build: "build"}, TopData: TopDataConfig{}, }, } result := proj.TopDataAssetsDir() if result != "" { t.Errorf("expected empty string, got %s", result) } } func TestValidateLayoutAllowsMissingAssetsDir(t *testing.T) { root := t.TempDir() mkdirAll(t, filepath.Join(root, "src")) mkdirAll(t, filepath.Join(root, "build")) proj := &Project{ Root: root, Config: Config{ Module: ModuleConfig{Name: "Test", ResRef: "test"}, Paths: PathConfig{Source: "src", Assets: "assets", Build: "build"}, }, } if err := proj.ValidateLayout(); err != nil { t.Fatalf("ValidateLayout returned error: %v", err) } if _, err := os.Stat(filepath.Join(root, "assets")); !errors.Is(err, os.ErrNotExist) { t.Fatalf("expected missing assets dir to remain absent, got err=%v", err) } } func TestScanAllowsMissingAssetsDir(t *testing.T) { root := t.TempDir() mkdirAll(t, filepath.Join(root, "src")) mkdirAll(t, filepath.Join(root, "build")) proj := &Project{ Root: root, Config: Config{ Module: ModuleConfig{Name: "Test", ResRef: "test"}, Paths: PathConfig{Source: "src", Assets: "assets", Build: "build"}, }, } if err := proj.Scan(); err != nil { t.Fatalf("Scan returned error: %v", err) } if len(proj.Inventory.AssetFiles) != 0 { t.Fatalf("expected no asset files, got %#v", proj.Inventory.AssetFiles) } } func mkdirAll(t *testing.T, path string) { t.Helper() if err := os.MkdirAll(path, 0o755); err != nil { t.Fatalf("mkdir %s: %v", path, err) } } func TestCloneWithHAKNamesFiltersConfiguredHAKs(t *testing.T) { proj := &Project{ Root: "/tmp/test", Config: Config{ Module: ModuleConfig{Name: "Test", ResRef: "test"}, Paths: PathConfig{Source: "src", Assets: "assets", Build: "build"}, HAKs: []HAKConfig{ {Name: "sow_over", Priority: 1, Include: []string{"over/**"}}, {Name: "sow_core", Priority: 2, Include: []string{"core/**"}}, {Name: "sow_item", Priority: 3, Include: []string{"item/**"}}, }, }, } filtered, err := proj.CloneWithHAKNames([]string{"sow_core", "sow_item"}) if err != nil { t.Fatalf("CloneWithHAKNames returned error: %v", err) } if filtered == proj { t.Fatalf("expected filtered clone, got original pointer") } got := make([]string, 0, len(filtered.Config.HAKs)) for _, hak := range filtered.Config.HAKs { got = append(got, hak.Name) } want := []string{"sow_core", "sow_item"} if !slices.Equal(got, want) { t.Fatalf("unexpected filtered haks: got %v want %v", got, want) } if len(proj.Config.HAKs) != 3 { t.Fatalf("original project config should remain unchanged, got %d haks", len(proj.Config.HAKs)) } } func TestCloneWithHAKNamesRejectsUnknownHAKs(t *testing.T) { proj := &Project{ Config: Config{ HAKs: []HAKConfig{ {Name: "sow_core", Priority: 1, Include: []string{"core/**"}}, }, }, } if _, err := proj.CloneWithHAKNames([]string{"missing_hak"}); err == nil { t.Fatal("expected error for unknown hak name") } }