From 1d755d31ed1a714e365f6adab9d66c4b476df920 Mon Sep 17 00:00:00 2001 From: Aly Raffauf Date: Mon, 10 Aug 2026 07:39:39 -0400 Subject: [PATCH] refactor: project initialization and action results --- internal/application/apply/execute_aliases.go | 2 +- internal/application/apply/execute_files.go | 2 +- internal/application/apply/prepare_test.go | 8 ++++---- internal/application/apply/types.go | 4 ++-- internal/application/apply/types_test.go | 8 ++++---- internal/application/initialize/service.go | 4 +++- internal/application/initialize/service_test.go | 2 +- internal/application/initialize/types.go | 12 ++++++++++-- internal/application/initialize/types_test.go | 4 ++-- 9 files changed, 28 insertions(+), 18 deletions(-) diff --git a/internal/application/apply/execute_aliases.go b/internal/application/apply/execute_aliases.go index 6105d07..78e3e36 100644 --- a/internal/application/apply/execute_aliases.go +++ b/internal/application/apply/execute_aliases.go @@ -22,7 +22,7 @@ func (service *Service) ExecuteAliases(ctx context.Context, plan PreparedPlan, c } byPath := candidatesByPath(candidates) results := make([]ItemResult, 0) - for _, action := range plan.Actions().Actions() { + for _, action := range plan.Actions().Items() { if action.Kind != ActionKindRealizeAlias && action.Kind != ActionKindRetireFile && action.Kind != ActionKindRetireAlias { continue } diff --git a/internal/application/apply/execute_files.go b/internal/application/apply/execute_files.go index 27719a1..bf5637c 100644 --- a/internal/application/apply/execute_files.go +++ b/internal/application/apply/execute_files.go @@ -20,7 +20,7 @@ func (service *Service) ExecuteFiles(ctx context.Context, plan PreparedPlan, can } byPath := candidatesByPath(candidates) results := make([]ItemResult, 0) - for _, action := range plan.Actions().Actions() { + for _, action := range plan.Actions().Items() { if action.Kind != ActionKindWriteSource && action.Kind != ActionKindReplaceFile { continue } diff --git a/internal/application/apply/prepare_test.go b/internal/application/apply/prepare_test.go index ea45720..082284f 100644 --- a/internal/application/apply/prepare_test.go +++ b/internal/application/apply/prepare_test.go @@ -80,7 +80,7 @@ func testPrepareDryRun(t *testing.T) { if len(records) != 1 || records[0].Status != StatusPlanned || records[0].Kind != ActionKindWriteSource { t.Fatalf("dry-run records = %+v, want one planned write-source record", records) } - if len(plan.Actions().Actions()) != 0 || plan.WithHooks() { + if len(plan.Actions().Items()) != 0 || plan.WithHooks() { t.Fatal("dry-run must carry no actions and no hooks") } if plan.Summary().Planned != 1 { @@ -114,7 +114,7 @@ func testPrepareSkip(t *testing.T) { if len(records) != 1 || records[0].Status != StatusPlanned { t.Fatalf("skip records = %+v, want one planned record", records) } - if len(plan.Actions().Actions()) != 0 { + if len(plan.Actions().Items()) != 0 { t.Fatal("a skipped plan must carry no actions") } if !plan.WithHooks() { @@ -136,7 +136,7 @@ func testPrepareNoop(t *testing.T) { if err != nil { t.Fatalf("prepare: %v", err) } - if len(plan.Actions().Actions()) != 0 || len(plan.Records()) != 0 || plan.WithHooks() { + if len(plan.Actions().Items()) != 0 || len(plan.Records()) != 0 || plan.WithHooks() { t.Fatal("a converged apply must stay empty") } } @@ -159,7 +159,7 @@ func assertPreparedAction(t *testing.T, pair evalPair, decisions CollectedDecisi if !plan.WithHooks() { t.Fatal("an executing plan with actions must run hooks") } - actions := plan.Actions().Actions() + actions := plan.Actions().Items() if len(actions) != 1 || actions[0].Kind != ActionKindWriteSource || actions[0].TargetPath != "a.conf" { t.Fatalf("actions = %+v, want one write-source action", actions) } diff --git a/internal/application/apply/types.go b/internal/application/apply/types.go index 67f9f64..9a3f040 100644 --- a/internal/application/apply/types.go +++ b/internal/application/apply/types.go @@ -281,8 +281,8 @@ func NewActionPlan(actions []PlanAction) ActionPlan { return ActionPlan{actions: append([]PlanAction(nil), actions...)} } -// Actions returns a defensive copy of the ordered execution actions. -func (p ActionPlan) Actions() []PlanAction { +// Items returns a defensive copy of the ordered execution actions. +func (p ActionPlan) Items() []PlanAction { return append([]PlanAction(nil), p.actions...) } diff --git a/internal/application/apply/types_test.go b/internal/application/apply/types_test.go index 2c942f0..7a0ca94 100644 --- a/internal/application/apply/types_test.go +++ b/internal/application/apply/types_test.go @@ -108,16 +108,16 @@ func testContractActionPlan(t *testing.T) { {TargetPath: "b", Kind: ActionKindReplaceFile, SourcePath: "files/b"}, {TargetPath: "a", Kind: ActionKindWriteSource, SourcePath: "files/a"}, }) - if got := plan.Actions(); len(got) != 2 || got[0].Kind != ActionKindReplaceFile { + if got := plan.Items(); len(got) != 2 || got[0].Kind != ActionKindReplaceFile { t.Fatalf("action plan must freeze the ordered actions, got %+v", got) } - actions := plan.Actions() + actions := plan.Items() actions[0].Kind = ActionKindRetireFile - if plan.Actions()[0].Kind != ActionKindReplaceFile { + if plan.Items()[0].Kind != ActionKindReplaceFile { t.Fatal("mutating an Actions copy must not reach the plan") } empty := NewActionPlan(nil) - if len(empty.Actions()) != 0 { + if len(empty.Items()) != 0 { t.Fatal("an empty action plan must stay empty") } } diff --git a/internal/application/initialize/service.go b/internal/application/initialize/service.go index d85f29d..46e646f 100644 --- a/internal/application/initialize/service.go +++ b/internal/application/initialize/service.go @@ -60,7 +60,9 @@ func (service *Service) Initialize(ctx context.Context, request Request) (Result if err != nil { return Result{}, failure.New(failure.Operational, "initialize: register repository", err) } - return Result{Repository: row}, nil + return Result{Repository: RegisteredRepository{ + RootPath: row.RootPath, HomePath: row.HomePath, IsDefault: row.IsDefault, + }}, nil } // environment bundles the canonical inputs every initialization needs. diff --git a/internal/application/initialize/service_test.go b/internal/application/initialize/service_test.go index 064a808..7e2fb60 100644 --- a/internal/application/initialize/service_test.go +++ b/internal/application/initialize/service_test.go @@ -202,7 +202,7 @@ func newService(t *testing.T) (*database.Fixture, *Service) { return fixture, service } -func registerPath(t *testing.T, fixture *database.Fixture, path string) state.Repository { +func registerPath(t *testing.T, fixture *database.Fixture, path string) RegisteredRepository { t.Helper() service := NewService(Dependencies{Home: fixture.Home, Store: fixture.Store}) result, err := service.Initialize(context.Background(), Request{Path: path}) diff --git a/internal/application/initialize/types.go b/internal/application/initialize/types.go index 1f87fb3..858b2d1 100644 --- a/internal/application/initialize/types.go +++ b/internal/application/initialize/types.go @@ -22,8 +22,16 @@ type Request struct { Path string } +// RegisteredRepository is the application-owned projection of a registered +// repository row. Persistence identifiers and timestamps stay inside state. +type RegisteredRepository struct { + RootPath string + HomePath string + IsDefault bool +} + // Result is the frozen outcome of one initialization, carrying the registered -// repository row so callers can render or chain it without another lookup. +// repository projection so callers can render or chain it without another lookup. type Result struct { - Repository state.Repository + Repository RegisteredRepository } diff --git a/internal/application/initialize/types_test.go b/internal/application/initialize/types_test.go index 1ef7fac..7acb680 100644 --- a/internal/application/initialize/types_test.go +++ b/internal/application/initialize/types_test.go @@ -40,8 +40,8 @@ func testContractResultShape(t *testing.T) { if !found { t.Fatal("Result.Repository field missing") } - if field.Type != reflect.TypeOf(state.Repository{}) { - t.Fatalf("Result.Repository type = %v, want state.Repository", field.Type) + if field.Type != reflect.TypeOf(RegisteredRepository{}) { + t.Fatalf("Result.Repository type = %v, want initialize.RegisteredRepository", field.Type) } } -- 2.51.2