diff options
| author | Paul Buetow <paul@buetow.org> | 2026-07-05 11:25:57 +0300 |
|---|---|---|
| committer | Paul Buetow <paul@buetow.org> | 2026-07-05 11:25:57 +0300 |
| commit | 4c5b95d5e3811025e2148a6f2a059738e79ca2c9 (patch) | |
| tree | e0aaaef14e1ed6fc0d78c460b671289e39e94c73 /internal/resource | |
| parent | 5256a4d1e5a56510758bfac710ba2ad2a45af66e (diff) | |
add tests for repository's Apply
Diffstat (limited to 'internal/resource')
| -rw-r--r-- | internal/resource/dir/dir.go | 7 | ||||
| -rw-r--r-- | internal/resource/dir/dir_test.go | 49 | ||||
| -rw-r--r-- | internal/resource/file/file.go | 7 | ||||
| -rw-r--r-- | internal/resource/file/file_test.go | 32 | ||||
| -rw-r--r-- | internal/resource/link/link.go | 3 | ||||
| -rw-r--r-- | internal/resource/link/link_test.go | 32 | ||||
| -rw-r--r-- | internal/resource/repository.go | 2 | ||||
| -rw-r--r-- | internal/resource/repository_test.go | 126 | ||||
| -rw-r--r-- | internal/resource/resource_test.go | 12 |
9 files changed, 255 insertions, 15 deletions
diff --git a/internal/resource/dir/dir.go b/internal/resource/dir/dir.go index 1934272..ba50caf 100644 --- a/internal/resource/dir/dir.go +++ b/internal/resource/dir/dir.go @@ -180,8 +180,11 @@ func applyAttributesTo(path string, mode os.FileMode, usr, group string) error { // Ensure builds and applies the directory resource described by opts, // without registering it. func Ensure(path string, opts ...opt.Option) error { - _, err := build(path, opts...) - return err + d, err := build(path, opts...) + if err != nil { + return err + } + return d.apply() } func Have(path string, opts ...opt.Option) resource.Resource { diff --git a/internal/resource/dir/dir_test.go b/internal/resource/dir/dir_test.go index 5ffb5cf..8b90fac 100644 --- a/internal/resource/dir/dir_test.go +++ b/internal/resource/dir/dir_test.go @@ -5,15 +5,21 @@ import ( "path/filepath" "testing" + resource "codeberg.org/snonux/gonf/internal/resource" "codeberg.org/snonux/gonf/internal/resource/file" + . "codeberg.org/snonux/gonf/internal/resource/opt" ) func TestHaveDirectoryCreate(t *testing.T) { + resource.ResetRepository() tmp := t.TempDir() path := filepath.Join(tmp, "sub", "nested") Have(path) + if err := resource.Apply(); err != nil { + t.Fatalf("Apply failed: %v", err) + } info, err := os.Stat(path) if err != nil { @@ -28,6 +34,7 @@ func TestHaveDirectoryCreate(t *testing.T) { } func TestHaveDirectoryIdempotentWithMode(t *testing.T) { + resource.ResetRepository() tmp := t.TempDir() path := filepath.Join(tmp, "d") @@ -52,6 +59,7 @@ func TestHaveDirectoryIdempotentWithMode(t *testing.T) { } func TestHaveDirectoryFailsWhenFileExists(t *testing.T) { + resource.ResetRepository() tmp := t.TempDir() path := filepath.Join(tmp, "afile") if err := os.WriteFile(path, []byte("x"), 0o644); err != nil { @@ -64,6 +72,7 @@ func TestHaveDirectoryFailsWhenFileExists(t *testing.T) { } func TestHaveAbsentNonEmptyDirWithoutPruneFails(t *testing.T) { + resource.ResetRepository() tmp := t.TempDir() target := filepath.Join(tmp, "d") if err := os.MkdirAll(filepath.Join(target, "sub"), 0o755); err != nil { @@ -79,6 +88,7 @@ func TestHaveAbsentNonEmptyDirWithoutPruneFails(t *testing.T) { } func TestHaveAbsentPruneDirectoryRecursive(t *testing.T) { + resource.ResetRepository() tmp := t.TempDir() target := filepath.Join(tmp, "d") if err := os.MkdirAll(filepath.Join(target, "sub", "deep"), 0o755); err != nil { @@ -89,6 +99,9 @@ func TestHaveAbsentPruneDirectoryRecursive(t *testing.T) { } Have(target, IsAbsent(), WithPrune()) + if err := resource.Apply(); err != nil { + t.Fatalf("Apply failed: %v", err) + } if _, err := os.Stat(target); !os.IsNotExist(err) { t.Errorf("expected %s to be removed recursively", target) } @@ -101,6 +114,7 @@ func TestHaveAbsentPruneDirectoryRecursive(t *testing.T) { } func TestAbsentPruneDirectoryRecursive(t *testing.T) { + resource.ResetRepository() tmp := t.TempDir() target := filepath.Join(tmp, "d") if err := os.MkdirAll(filepath.Join(target, "sub", "deep"), 0o755); err != nil { @@ -111,6 +125,9 @@ func TestAbsentPruneDirectoryRecursive(t *testing.T) { } Absent(target, WithPrune()) + if err := resource.Apply(); err != nil { + t.Fatalf("Apply failed: %v", err) + } if _, err := os.Stat(target); !os.IsNotExist(err) { t.Errorf("expected %s to be removed recursively", target) } @@ -124,6 +141,7 @@ func TestAbsentPruneDirectoryRecursive(t *testing.T) { func TestHaveDirectoryWithSource(t *testing.T) { t.Run("recursive copy", func(t *testing.T) { + resource.ResetRepository() tmp := t.TempDir() src := t.TempDir() dst := filepath.Join(tmp, "dst") @@ -142,6 +160,9 @@ func TestHaveDirectoryWithSource(t *testing.T) { } Have(dst, WithSource(src)) + if err := resource.Apply(); err != nil { + t.Fatalf("Apply failed: %v", err) + } if data, err := os.ReadFile(filepath.Join(dst, "file.txt")); err != nil || string(data) != "hello" { t.Errorf("expected 'hello' at %s, got %q err %v", filepath.Join(dst, "file.txt"), string(data), err) @@ -152,6 +173,7 @@ func TestHaveDirectoryWithSource(t *testing.T) { }) t.Run("pruning", func(t *testing.T) { + resource.ResetRepository() tmp := t.TempDir() src := t.TempDir() dst := filepath.Join(tmp, "dst") @@ -174,6 +196,9 @@ func TestHaveDirectoryWithSource(t *testing.T) { } Have(dst, WithSource(src), WithPrune()) + if err := resource.Apply(); err != nil { + t.Fatalf("Apply failed: %v", err) + } if _, err := os.Stat(extra); !os.IsNotExist(err) { t.Errorf("expected %s to be pruned", extra) @@ -188,6 +213,7 @@ func TestHaveDirectoryWithSource(t *testing.T) { } func TestSourceCopyUsesFileModeDefaultNotDirMode(t *testing.T) { + resource.ResetRepository() tmp := t.TempDir() src := t.TempDir() dst := filepath.Join(tmp, "dst") @@ -197,6 +223,9 @@ func TestSourceCopyUsesFileModeDefaultNotDirMode(t *testing.T) { } Have(dst, WithSource(src)) + if err := resource.Apply(); err != nil { + t.Fatalf("Apply failed: %v", err) + } dirInfo, err := os.Stat(dst) if err != nil { @@ -216,6 +245,7 @@ func TestSourceCopyUsesFileModeDefaultNotDirMode(t *testing.T) { } func TestSourceCopyRespectsExplicitWithFileMode(t *testing.T) { + resource.ResetRepository() tmp := t.TempDir() src := t.TempDir() dst := filepath.Join(tmp, "dst") @@ -225,6 +255,9 @@ func TestSourceCopyRespectsExplicitWithFileMode(t *testing.T) { } Have(dst, WithSource(src), WithMode(0o755), WithFileMode(0o600)) + if err := resource.Apply(); err != nil { + t.Fatalf("Apply failed: %v", err) + } dirInfo, err := os.Stat(dst) if err != nil { @@ -244,6 +277,7 @@ func TestSourceCopyRespectsExplicitWithFileMode(t *testing.T) { } func TestSourceCopyStripsTmplSuffixOnCopiedFile(t *testing.T) { + resource.ResetRepository() tmp := t.TempDir() src := t.TempDir() dst := filepath.Join(tmp, "dst") @@ -253,6 +287,9 @@ func TestSourceCopyStripsTmplSuffixOnCopiedFile(t *testing.T) { } Have(dst, WithSource(src)) + if err := resource.Apply(); err != nil { + t.Fatalf("Apply failed: %v", err) + } if _, err := os.Stat(filepath.Join(dst, "foo.conf")); err != nil { t.Errorf("expected de-suffixed foo.conf to exist: %v", err) @@ -263,6 +300,7 @@ func TestSourceCopyStripsTmplSuffixOnCopiedFile(t *testing.T) { } func TestSourceCopyWithPruneKeepsTemplatedFile(t *testing.T) { + resource.ResetRepository() tmp := t.TempDir() src := t.TempDir() dst := filepath.Join(tmp, "dst") @@ -275,6 +313,9 @@ func TestSourceCopyWithPruneKeepsTemplatedFile(t *testing.T) { // apply; a de-suffixed templated file must not be pruned just because // its own name has no direct match in the source tree. Have(dst, WithSource(src), WithPrune()) + if err := resource.Apply(); err != nil { + t.Fatalf("Apply failed: %v", err) + } if _, err := os.Stat(filepath.Join(dst, "foo.conf")); err != nil { t.Errorf("expected foo.conf to survive pruning, got %v", err) @@ -282,6 +323,7 @@ func TestSourceCopyWithPruneKeepsTemplatedFile(t *testing.T) { } func TestSourceCopyParamMatchesSingleFilePath(t *testing.T) { + resource.ResetRepository() tmp := t.TempDir() src := t.TempDir() dst := filepath.Join(tmp, "dst") @@ -291,6 +333,9 @@ func TestSourceCopyParamMatchesSingleFilePath(t *testing.T) { } Have(dst, WithSource(src)) + if err := resource.Apply(); err != nil { + t.Fatalf("Apply failed: %v", err) + } viaDir, err := os.ReadFile(filepath.Join(dst, "foo.conf")) if err != nil { t.Fatal(err) @@ -311,6 +356,7 @@ func TestSourceCopyParamMatchesSingleFilePath(t *testing.T) { } func TestSourceCopyRecreatesSymlinkNotContent(t *testing.T) { + resource.ResetRepository() tmp := t.TempDir() src := t.TempDir() dst := filepath.Join(tmp, "dst") @@ -323,6 +369,9 @@ func TestSourceCopyRecreatesSymlinkNotContent(t *testing.T) { } Have(dst, WithSource(src)) + if err := resource.Apply(); err != nil { + t.Fatalf("Apply failed: %v", err) + } linkPath := filepath.Join(dst, "link.txt") info, err := os.Lstat(linkPath) diff --git a/internal/resource/file/file.go b/internal/resource/file/file.go index ea443a9..50ba6e3 100644 --- a/internal/resource/file/file.go +++ b/internal/resource/file/file.go @@ -211,8 +211,11 @@ func ensureAbsent(path string) error { // registering it. Used by other resource packages (e.g. dir) to write an // individual file without it becoming its own top-level resource. func Ensure(path string, opts ...opt.Option) error { - _, err := build(path, opts...) - return err + f, err := build(path, opts...) + if err != nil { + return err + } + return f.apply() } func Have(path string, opts ...opt.Option) resource.Resource { diff --git a/internal/resource/file/file_test.go b/internal/resource/file/file_test.go index 49deb4c..0669b88 100644 --- a/internal/resource/file/file_test.go +++ b/internal/resource/file/file_test.go @@ -6,10 +6,12 @@ import ( "strings" "testing" + "codeberg.org/snonux/gonf/internal/resource" . "codeberg.org/snonux/gonf/internal/resource/opt" ) func TestGetChecksum(t *testing.T) { + resource.ResetRepository() dir := t.TempDir() path := filepath.Join(dir, "test.txt") @@ -29,6 +31,7 @@ func TestGetChecksum(t *testing.T) { } func TestWriteTmpFile(t *testing.T) { + resource.ResetRepository() dir := t.TempDir() tmpPath := filepath.Join(dir, "test.tmp") content := []byte("temp content") @@ -47,6 +50,7 @@ func TestWriteTmpFile(t *testing.T) { } func TestUpdateFromTmpChecksumChanged(t *testing.T) { + resource.ResetRepository() dir := t.TempDir() tmpPath := filepath.Join(dir, "test.tmp") path := filepath.Join(dir, "test.txt") @@ -72,6 +76,7 @@ func TestUpdateFromTmpChecksumChanged(t *testing.T) { } func TestUpdateFromTmpChecksumUnchanged(t *testing.T) { + resource.ResetRepository() dir := t.TempDir() tmpPath := filepath.Join(dir, "test.tmp") path := filepath.Join(dir, "test.txt") @@ -100,10 +105,14 @@ func TestUpdateFromTmpChecksumUnchanged(t *testing.T) { } func TestHaveStringCreateNewFile(t *testing.T) { + resource.ResetRepository() dir := t.TempDir() path := filepath.Join(dir, "new.txt") Have(path, WithContent("hello world")) + if err := resource.Apply(); err != nil { + t.Fatalf("Apply failed: %v", err) + } got, err := os.ReadFile(path) if err != nil { @@ -115,11 +124,15 @@ func TestHaveStringCreateNewFile(t *testing.T) { } func TestHaveMode(t *testing.T) { + resource.ResetRepository() dir := t.TempDir() path := filepath.Join(dir, "mode.txt") mode := os.FileMode(0o600) Have(path, WithContent("mode test"), WithMode(mode)) + if err := resource.Apply(); err != nil { + t.Fatalf("Apply failed: %v", err) + } info, err := os.Stat(path) if err != nil { @@ -131,11 +144,15 @@ func TestHaveMode(t *testing.T) { } func TestHaveSourceFile(t *testing.T) { + resource.ResetRepository() dir := t.TempDir() sourcePath := filepath.Join("..", "..", "..", "assets", "testfiles", "test.txt") targetPath := filepath.Join(dir, "target.txt") Have(targetPath, WithSource(sourcePath)) + if err := resource.Apply(); err != nil { + t.Fatalf("Apply failed: %v", err) + } got, err := os.ReadFile(targetPath) if err != nil { @@ -148,11 +165,15 @@ func TestHaveSourceFile(t *testing.T) { } func TestHaveTemplateFile(t *testing.T) { + resource.ResetRepository() dir := t.TempDir() sourcePath := filepath.Join("..", "..", "..", "assets", "testfiles", "test.tmpl") targetPath := filepath.Join(dir, "target.conf") Have(targetPath, WithSource(sourcePath)) + if err := resource.Apply(); err != nil { + t.Fatalf("Apply failed: %v", err) + } got, err := os.ReadFile(targetPath) if err != nil { @@ -166,6 +187,7 @@ func TestHaveTemplateFile(t *testing.T) { } func TestHaveAbsent(t *testing.T) { + resource.ResetRepository() dir := t.TempDir() path := filepath.Join(dir, "gone.txt") if err := os.WriteFile(path, []byte("bye"), 0o644); err != nil { @@ -173,6 +195,9 @@ func TestHaveAbsent(t *testing.T) { } Have(path, IsAbsent()) + if err := resource.Apply(); err != nil { + t.Fatalf("Apply failed: %v", err) + } if _, err := os.Stat(path); !os.IsNotExist(err) { t.Errorf("expected %s to be removed", path) } @@ -185,6 +210,7 @@ func TestHaveAbsent(t *testing.T) { } func TestAbsent(t *testing.T) { + resource.ResetRepository() dir := t.TempDir() path := filepath.Join(dir, "gone.txt") if err := os.WriteFile(path, []byte("bye"), 0o644); err != nil { @@ -192,6 +218,9 @@ func TestAbsent(t *testing.T) { } Absent(path) + if err := resource.Apply(); err != nil { + t.Fatalf("Apply failed: %v", err) + } if _, err := os.Stat(path); !os.IsNotExist(err) { t.Errorf("expected %s to be removed", path) } @@ -204,6 +233,7 @@ func TestAbsent(t *testing.T) { } func TestResolveStripsTmplSuffixWhenSourceHasTmplSuffix(t *testing.T) { + resource.ResetRepository() dir := t.TempDir() sourcePath := filepath.Join(dir, "foo.conf.tmpl") if err := os.WriteFile(sourcePath, []byte("hello {{.Param}}"), 0o644); err != nil { @@ -231,6 +261,7 @@ func TestResolveStripsTmplSuffixWhenSourceHasTmplSuffix(t *testing.T) { } func TestResolveDoesNotStripTmplWhenOnlyContentTriggersTemplate(t *testing.T) { + resource.ResetRepository() dir := t.TempDir() targetPath := filepath.Join(dir, "foo.conf.tmpl") @@ -244,6 +275,7 @@ func TestResolveDoesNotStripTmplWhenOnlyContentTriggersTemplate(t *testing.T) { } func TestParamIsBareSourcePathNoPrefix(t *testing.T) { + resource.ResetRepository() dir := t.TempDir() sourcePath := filepath.Join(dir, "src.tmpl") if err := os.WriteFile(sourcePath, []byte("{{.Param}}"), 0o644); err != nil { diff --git a/internal/resource/link/link.go b/internal/resource/link/link.go index 8600166..0e5582c 100644 --- a/internal/resource/link/link.go +++ b/internal/resource/link/link.go @@ -83,8 +83,7 @@ func (l *Link) resourceType() string { // registering it. Used by other resource packages (e.g. dir) to recreate an // individual symlink without it becoming its own top-level resource. func Ensure(path string, opts ...opt.Option) error { - build(path, opts...) - return nil + return build(path, opts...).apply() } func Have(path string, opts ...opt.Option) resource.Resource { diff --git a/internal/resource/link/link_test.go b/internal/resource/link/link_test.go index a892527..20542bd 100644 --- a/internal/resource/link/link_test.go +++ b/internal/resource/link/link_test.go @@ -5,10 +5,12 @@ import ( "path/filepath" "testing" + "codeberg.org/snonux/gonf/internal/resource" . "codeberg.org/snonux/gonf/internal/resource/opt" ) func TestHaveSymlinkCreateAndIdempotent(t *testing.T) { + resource.ResetRepository() dir := t.TempDir() target := filepath.Join(dir, "target.txt") link := filepath.Join(dir, "link.txt") @@ -17,6 +19,9 @@ func TestHaveSymlinkCreateAndIdempotent(t *testing.T) { } Have(link, WithSymlink(target)) + if err := resource.Apply(); err != nil { + t.Fatalf("Apply failed: %v", err) + } got, err := os.Readlink(link) if err != nil { t.Fatalf("readlink: %v", err) @@ -34,6 +39,7 @@ func TestHaveSymlinkCreateAndIdempotent(t *testing.T) { } func TestHaveSymlinkRepoints(t *testing.T) { + resource.ResetRepository() dir := t.TempDir() old := filepath.Join(dir, "old.txt") newT := filepath.Join(dir, "new.txt") @@ -48,6 +54,9 @@ func TestHaveSymlinkRepoints(t *testing.T) { } Have(link, WithSymlink(newT)) + if err := resource.Apply(); err != nil { + t.Fatalf("Apply failed: %v", err) + } got, err := os.Readlink(link) if err != nil { t.Fatal(err) @@ -58,6 +67,7 @@ func TestHaveSymlinkRepoints(t *testing.T) { } func TestHaveSymlinkMovesRealFileAside(t *testing.T) { + resource.ResetRepository() dir := t.TempDir() target := filepath.Join(dir, "target.txt") link := filepath.Join(dir, "real") @@ -69,6 +79,9 @@ func TestHaveSymlinkMovesRealFileAside(t *testing.T) { } Have(link, WithSymlink(target)) + if err := resource.Apply(); err != nil { + t.Fatalf("Apply failed: %v", err) + } got, err := os.Readlink(link) if err != nil { @@ -83,6 +96,7 @@ func TestHaveSymlinkMovesRealFileAside(t *testing.T) { } func TestHaveHardlinkCreateAndIdempotent(t *testing.T) { + resource.ResetRepository() dir := t.TempDir() target := filepath.Join(dir, "target.txt") link := filepath.Join(dir, "link.txt") @@ -91,6 +105,9 @@ func TestHaveHardlinkCreateAndIdempotent(t *testing.T) { } Have(link, WithHardlink(target)) + if err := resource.Apply(); err != nil { + t.Fatalf("Apply failed: %v", err) + } ti, err := os.Stat(target) if err != nil { @@ -113,6 +130,7 @@ func TestHaveHardlinkCreateAndIdempotent(t *testing.T) { } func TestHaveHardlinkMovesRealFileAside(t *testing.T) { + resource.ResetRepository() dir := t.TempDir() target := filepath.Join(dir, "target.txt") link := filepath.Join(dir, "real") @@ -124,6 +142,9 @@ func TestHaveHardlinkMovesRealFileAside(t *testing.T) { } Have(link, WithHardlink(target)) + if err := resource.Apply(); err != nil { + t.Fatalf("Apply failed: %v", err) + } ti, err := os.Stat(target) if err != nil { @@ -142,6 +163,7 @@ func TestHaveHardlinkMovesRealFileAside(t *testing.T) { } func TestHaveHardlinkMissingTarget(t *testing.T) { + resource.ResetRepository() dir := t.TempDir() link := filepath.Join(dir, "link") if err := Ensure(link, WithHardlink(filepath.Join(dir, "nope"))); err == nil { @@ -150,6 +172,7 @@ func TestHaveHardlinkMissingTarget(t *testing.T) { } func TestHaveAbsentSymlink(t *testing.T) { + resource.ResetRepository() dir := t.TempDir() target := filepath.Join(dir, "target.txt") link := filepath.Join(dir, "link.txt") @@ -161,6 +184,9 @@ func TestHaveAbsentSymlink(t *testing.T) { } Have(link, IsAbsent()) + if err := resource.Apply(); err != nil { + t.Fatalf("Apply failed: %v", err) + } if _, err := os.Lstat(link); !os.IsNotExist(err) { t.Errorf("expected %s to be removed", link) } @@ -173,6 +199,7 @@ func TestHaveAbsentSymlink(t *testing.T) { } func TestAbsentSymlink(t *testing.T) { + resource.ResetRepository() dir := t.TempDir() target := filepath.Join(dir, "target.txt") link := filepath.Join(dir, "link.txt") @@ -184,6 +211,9 @@ func TestAbsentSymlink(t *testing.T) { } Absent(link) + if err := resource.Apply(); err != nil { + t.Fatalf("Apply failed: %v", err) + } if _, err := os.Lstat(link); !os.IsNotExist(err) { t.Errorf("expected %s to be removed", link) } @@ -196,6 +226,7 @@ func TestAbsentSymlink(t *testing.T) { } func TestHaveAbsentWithoutKindRegistersGenericLink(t *testing.T) { + resource.ResetRepository() dir := t.TempDir() path := filepath.Join(dir, "whatever") @@ -206,6 +237,7 @@ func TestHaveAbsentWithoutKindRegistersGenericLink(t *testing.T) { } func TestBuildRequiresKindOrAbsent(t *testing.T) { + resource.ResetRepository() dir := t.TempDir() path := filepath.Join(dir, "nope") if err := Ensure(path); err == nil { diff --git a/internal/resource/repository.go b/internal/resource/repository.go index 69617d4..215f7e1 100644 --- a/internal/resource/repository.go +++ b/internal/resource/repository.go @@ -18,7 +18,7 @@ func getRepository() *repository { return &repo } -func resetRepository() { +func ResetRepository() { repo = newRepository() } diff --git a/internal/resource/repository_test.go b/internal/resource/repository_test.go new file mode 100644 index 0000000..8f1aef6 --- /dev/null +++ b/internal/resource/repository_test.go @@ -0,0 +1,126 @@ +package resource + +import ( + "errors" + "strings" + "testing" +) + +// mockApplier records the order in which Apply was called. +type mockApplier struct { + name string + err error + logs *[]string +} + +func (m *mockApplier) Apply() error { + *m.logs = append(*m.logs, m.name) + return m.err +} + +func TestApply(t *testing.T) { + tests := []struct { + name string + setup func(r *repository, logs *[]string) + wantOrder []string + wantError bool + errorString string + }{ + { + name: "dependency chain", + setup: func(r *repository, logs *[]string) { + // A depends on B, B depends on C + r.registered["A"] = Resource{ + Type: "T", Name: "A", + Apply: &mockApplier{name: "A", logs: logs}, + dependsOn: map[string]struct{}{"B": {}}, + } + r.registered["B"] = Resource{ + Type: "T", Name: "B", + Apply: &mockApplier{name: "B", logs: logs}, + dependsOn: map[string]struct{}{"C": {}}, + } + r.registered["C"] = Resource{ + Type: "T", Name: "C", + Apply: &mockApplier{name: "C", logs: logs}, + } + }, + wantOrder: []string{"C", "B", "A"}, + }, + { + name: "circular dependency", + setup: func(r *repository, logs *[]string) { + r.registered["A"] = Resource{ + Type: "T", Name: "A", + Apply: &mockApplier{name: "A", logs: logs}, + dependsOn: map[string]struct{}{"B": {}}, + } + r.registered["B"] = Resource{ + Type: "T", Name: "B", + Apply: &mockApplier{name: "B", logs: logs}, + dependsOn: map[string]struct{}{"A": {}}, + } + }, + wantError: true, + errorString: "circular dependency", + }, + { + name: "missing dependency", + setup: func(r *repository, logs *[]string) { + r.registered["A"] = Resource{ + Type: "T", Name: "A", + Apply: &mockApplier{name: "A", logs: logs}, + dependsOn: map[string]struct{}{"Missing": {}}, + } + }, + wantError: true, + errorString: "not registered", + }, + { + name: "execution failure", + setup: func(r *repository, logs *[]string) { + r.registered["A"] = Resource{ + Type: "T", Name: "A", + Apply: &mockApplier{name: "A", err: errors.New("fail A"), logs: logs}, + } + }, + wantError: true, + errorString: "failed to apply", + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + ResetRepository() + logs := make([]string, 0) + r := getRepository() + tt.setup(r, &logs) + + err := Apply() + + if (err != nil) != tt.wantError { + t.Errorf("Apply() error = %v, wantError %v", err, tt.wantError) + return + } + + if tt.wantError && tt.errorString != "" { + if err == nil || !strings.Contains(err.Error(), tt.errorString) { + t.Errorf("Apply() error = %v, want error containing %q", err, tt.errorString) + } + } + + if !tt.wantError && tt.wantOrder != nil { + if len(logs) != len(tt.wantOrder) { + t.Errorf("Apply() log length = %d, want %d", len(logs), len(tt.wantOrder)) + return + } + for i := range logs { + if logs[i] != tt.wantOrder[i] { + t.Errorf("Apply() order = %v, want %v", logs, tt.wantOrder) + break + } + } + } + }) + } +} diff --git a/internal/resource/resource_test.go b/internal/resource/resource_test.go index 8741f2a..23be8cc 100644 --- a/internal/resource/resource_test.go +++ b/internal/resource/resource_test.go @@ -5,7 +5,7 @@ import ( ) func TestResourceID(t *testing.T) { - resetRepository() + ResetRepository() res := Register("File", "/tmp/foo.txt", &mockApplier{}) expected := "File[/tmp/foo.txt]" if res.ID() != expected { @@ -14,7 +14,7 @@ func TestResourceID(t *testing.T) { } func TestResourceString(t *testing.T) { - resetRepository() + ResetRepository() res := Register("File", "/tmp/foo.txt", &mockApplier{}) expected := "File[/tmp/foo.txt]" if res.String() != expected { @@ -23,7 +23,7 @@ func TestResourceString(t *testing.T) { } func TestNew(t *testing.T) { - resetRepository() + ResetRepository() type_ := "File" name := "/tmp/foo.txt" res := Register(type_, name, &mockApplier{}) @@ -40,7 +40,7 @@ func TestNew(t *testing.T) { } func TestRepositoryRegister(t *testing.T) { - resetRepository() + ResetRepository() repo := getRepository() res := Register("File", "/tmp/foo.txt", &mockApplier{}) @@ -56,7 +56,3 @@ func TestRepositoryRegister(t *testing.T) { t.Error("expected error when registering the same resource twice, got nil") } } - -type mockApplier struct{} - -func (m *mockApplier) Apply() error { return nil } |
