Skip to content

refactor(management): readability + maintainability sweep#65

Merged
jmgilman merged 5 commits into
masterfrom
session-038/management-sweep
May 27, 2026
Merged

refactor(management): readability + maintainability sweep#65
jmgilman merged 5 commits into
masterfrom
session-038/management-sweep

Conversation

@jmgilman
Copy link
Copy Markdown
Contributor

Summary

Fifth pass of the multi-session refactor sweep (after access/ PR #61, authz/ PR #62, exchange/ PR #63, http/ PR #64). Same 10-criteria bar applied to management/. Non-goals: bugs, correctness, performance.

management/ is the largest target of the sweep so far (1778 LOC) and has zero production consumers — the package is the canonical management facade for app authors (CLIs, seed scripts, admin handlers). The Service composes 15 root-package ports plus a package-local APITokens port.

Changes per commit

  1. refactor(management): split service into per-domain filesservice.go (383 LOC) split into five per-domain files: principal.go, role.go, provisioning.go, identity.go, apitoken.go. service.go now holds only the scaffolding (Options, Service, NewService). The APITokens port + IssueAPITokenRequest + IssuedAPIToken move into apitoken.go; types.go is deleted. Matches the per-domain split precedent from PR refactor(exchange): readability + maintainability sweep #63.
  2. refactor(management): add Service godoc and IssueAPIToken precondition comment — godoc on the Service struct; inline comment on the principal-existence precondition in IssueAPIToken (the one cross-port method, where refusing to mint a token for a missing principal is the security-relevant invariant).
  3. test(management): split service_test into per-domain files — mechanical lift of the 1358-line test file into per-domain files (principal_test.go, role_test.go, provisioning_test.go, identity_test.go, apitoken_test.go, plus helpers_test.go and a tighter service_test.go for cross-cutting tests + integration anchors). Tests still use the hand-rolled fakes after this commit.
  4. test(management): migrate root-port fakes to mockery — adds 13 new entries to .mockery.yaml (the largest mockery expansion in the sweep so far; .mockery.yaml now covers 18 root-package interfaces). Aggregated hand-rolled fakePrincipalCreator, fakeIdentityLinker, fakeRoleStore, fakeProvisioningRuleStore and their grouping interfaces are deleted. fakeAPITokens survives — it backs the package-local management.APITokens port plus the sub-package apikey.TokenMetadataLister, both of which stay hand-rolled per the established pattern. Two integration anchors in service_test.go (real memory.Store + real apikey.Service) retained unchanged.
  5. chore(management): collapse struct field copies into type conversions — staticcheck (S1016) cleanup after the migration; two field-by-field literals become T(src) conversions.

Architecture decisions reaffirmed

Test plan

  • moon run root:check --summary minimal — format, lint, build, unit, Testcontainers integration. All green.
  • All 26 tests pass; none dropped.
  • go doc github.com/meigma/authkit/management — public surface unchanged.
  • No consumer source change required (there are no consumers).

Deliberately deferred

  • IssuedAPIToken.Plaintext rename — waits for the proof/apikey/ pass so the field name realigns with whatever apikey.IssuedToken.Plaintext becomes (Token or Secret), avoiding mid-sweep asymmetry. Carried open thread from PR refactor(exchange): readability + maintainability sweep #63.
  • CI drift-detection for .mockery.yaml — still unwired. The file now has 18 entries; the open thread is becoming more pressing. Future task.

🤖 Generated with Claude Code

jmgilman added 5 commits May 27, 2026 16:22
service.go was 383 LOC spanning five distinct domains (principal, role,
provisioning rule, identity, API token). Split per the exchange/ precedent
from PR #63 so a reader can jump straight to the domain they care about.

Final layout:
- service.go now holds only the scaffolding: Options, Service, NewService.
- principal.go, role.go, provisioning.go, identity.go, apitoken.go each
  hold the methods (and types) for one domain.
- types.go is deleted; its IssueAPITokenRequest and IssuedAPIToken move to
  apitoken.go since they are API-token-specific. The APITokens port moves
  there too — each domain file owns its own port.

No logic changes, no godoc changes (godoc work happens in commit 2).
…n comment

The Service struct lacked a godoc. Add a one-liner that names the sparse-
options pattern and the app-owned setup-flow audience captured in the
package doc.

IssueAPIToken is the only method on the package with cross-port logic: it
calls FindPrincipal first to validate the principal exists before
delegating to the apikey service. Annotate the precondition so the next
reader can see *why* both ports are required and *why* the lookup happens
before delegation.

No per-field godocs on the 15 private Service fields — the field name plus
port type already says the same thing, and 15 lines of `// X is the X port`
would be noise.
Mechanical lift only — no test logic changes, no fakes touched.

service_test.go at 1358 LOC was well past the ~400-line split threshold the
earlier passes used. 26 Test functions naturally group by domain.

Final layout:
- helpers_test.go: all builders (newService, newServiceWithRoles,
  newServiceWithProvisioningRules, newManagementService), grouping
  interfaces (roleStore, principalStore, provisioningRuleStore), all
  fake types and their constructors, fixedTime, provisioningRule, the
  shared const block.
- service_test.go: cross-cutting tests (NewService sparse-options,
  CoreMethodsRequirePorts, IssueAPITokenRequiresPrincipalFinderBeforeIssuing,
  DoesNotRequireRolePorts, DoesNotRequireProvisioningRulePorts) plus the
  two integration anchors (PropagatesContextCancellation,
  IssueAPITokenResolvesThroughMemoryStore).
- principal_test.go, role_test.go, provisioning_test.go, identity_test.go,
  apitoken_test.go: per-domain method tests.

Tests still use the hand-rolled fakes after this commit. The mockery
migration is commit 4, separated to keep the file split mechanical.

Matches the helpers_test.go precedent from PRs #61 (access/jwt), #63
(exchange), and #64 (http/auth, http/compose).
Add 13 new entries to .mockery.yaml — PrincipalCreator, PrincipalLister,
RoleCreator, RoleActionGranter, PrincipalRoleAssigner,
PrincipalRoleUnassigner, PrincipalRoleAssignmentLister,
ProvisioningRuleCreator, ProvisioningRuleUpdater, ProvisioningRuleDeleter,
ProvisioningRuleFinder, ProvisioningRuleLister, IdentityLinker. Each
follows the same shape as the 5 entries from PRs #61-#64. .mockery.yaml
now covers 18 root-package interfaces.

Migrate the 24 mockery-eligible test functions across principal_test.go,
role_test.go, provisioning_test.go, identity_test.go, apitoken_test.go,
and the sparse-options tests in service_test.go to use the generated
mocks with declarative .EXPECT() setups. The "this exact request was
passed" assertions collapse from a separately-tracked field-of-the-fake
into the mock's argument matcher.

Delete the aggregated hand-rolled types fakePrincipalCreator,
fakeIdentityLinker, fakeRoleStore, and fakeProvisioningRuleStore, their
grouping interfaces roleStore/principalStore/provisioningRuleStore, and
the service builders newService/newServiceWithRoles/
newServiceWithProvisioningRules that combined them. helpers_test.go is
now ~115 LOC (down from 408).

Surviving in helpers_test.go: fakeAPITokens (package-local port + the
sub-package apikey.TokenMetadataLister; both stay hand-rolled per the
project's mockery pattern), newFakeAPITokens, fixedTime,
provisioningRule, the shared const block, and newManagementService
(used only by the two integration anchors in service_test.go).

Two integration anchors in service_test.go retained unchanged:
TestServicePropagatesContextCancellation and
TestServiceIssueAPITokenResolvesThroughMemoryStore (real memory.Store +
real apikey.Service).
staticcheck (S1016) flagged two field-by-field copy patterns in the
migrated tests:
- role_test.go: authkit.CreateRoleRequest{want.ID, want.DisplayName, want.Description}
- provisioning_test.go: authkit.CreateProvisioningRuleRequest{rule.ID, ...}

Both source/destination pairs have identical field layouts, so a direct
type conversion (T(src)) replaces the literal. Pure cleanup, no behaviour
change.
@jmgilman jmgilman merged commit 8a60a0b into master May 27, 2026
2 checks passed
@jmgilman jmgilman deleted the session-038/management-sweep branch May 27, 2026 23:43
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant