summaryrefslogtreecommitdiff
path: root/internal/resource/repository_test.go
diff options
context:
space:
mode:
authorPaul Buetow <paul@buetow.org>2026-07-05 11:25:57 +0300
committerPaul Buetow <paul@buetow.org>2026-07-05 11:25:57 +0300
commit4c5b95d5e3811025e2148a6f2a059738e79ca2c9 (patch)
treee0aaaef14e1ed6fc0d78c460b671289e39e94c73 /internal/resource/repository_test.go
parent5256a4d1e5a56510758bfac710ba2ad2a45af66e (diff)
add tests for repository's Apply
Diffstat (limited to 'internal/resource/repository_test.go')
-rw-r--r--internal/resource/repository_test.go126
1 files changed, 126 insertions, 0 deletions
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
+ }
+ }
+ }
+ })
+ }
+}