From fb3a025d848ea6ae458f1a481aad7817defc75bf Mon Sep 17 00:00:00 2001 From: Joshua Gilman Date: Wed, 27 May 2026 16:22:21 -0700 Subject: [PATCH 1/5] refactor(management): split service into per-domain files MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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). --- management/apitoken.go | 107 ++++++++++++++ management/identity.go | 26 ++++ management/principal.go | 54 +++++++ management/provisioning.go | 84 +++++++++++ management/role.go | 82 +++++++++++ management/service.go | 284 ------------------------------------- management/types.go | 30 ---- 7 files changed, 353 insertions(+), 314 deletions(-) create mode 100644 management/apitoken.go create mode 100644 management/identity.go create mode 100644 management/principal.go create mode 100644 management/provisioning.go create mode 100644 management/role.go delete mode 100644 management/types.go diff --git a/management/apitoken.go b/management/apitoken.go new file mode 100644 index 0000000..213da9a --- /dev/null +++ b/management/apitoken.go @@ -0,0 +1,107 @@ +package management + +import ( + "context" + "errors" + "fmt" + "time" + + "github.com/meigma/authkit/proof/apikey" +) + +// APITokens issues and revokes opaque API tokens. +type APITokens interface { + // IssueToken issues an opaque API token for an existing principal. + IssueToken(ctx context.Context, req apikey.IssueRequest) (apikey.IssuedToken, error) + + // RevokeToken revokes tokenID. + RevokeToken(ctx context.Context, tokenID string) error +} + +// IssueAPITokenRequest describes a request to issue an API token. +type IssueAPITokenRequest struct { + // PrincipalID identifies the principal the token should authenticate as. + PrincipalID string + + // Name is an optional human-readable token label. + Name string + + // ExpiresAt is the time after which the token must no longer authenticate. + ExpiresAt time.Time +} + +// IssuedAPIToken describes an issued API token. +type IssuedAPIToken struct { + // ID is the stable lookup identifier embedded in the token. + ID string + + // PrincipalID identifies the principal the token authenticates as. + PrincipalID string + + // Plaintext is the full token secret shown once to the caller. + Plaintext string + + // ExpiresAt is the time after which the token must no longer authenticate. + ExpiresAt time.Time +} + +// IssueAPIToken issues an API token for an existing principal. +func (s *Service) IssueAPIToken(ctx context.Context, req IssueAPITokenRequest) (IssuedAPIToken, error) { + if s.apiTokens == nil { + return IssuedAPIToken{}, errors.New("management: API tokens service is required") + } + if s.principalFinder == nil { + return IssuedAPIToken{}, errors.New("management: principal finder is required") + } + + principal, err := s.principalFinder.FindPrincipal(ctx, req.PrincipalID) + if err != nil { + return IssuedAPIToken{}, fmt.Errorf("management: find API token principal: %w", err) + } + + issued, err := s.apiTokens.IssueToken(ctx, apikey.IssueRequest{ + PrincipalID: principal.ID, + Name: req.Name, + ExpiresAt: req.ExpiresAt, + }) + if err != nil { + return IssuedAPIToken{}, fmt.Errorf("management: issue API token: %w", err) + } + + return IssuedAPIToken{ + ID: issued.ID, + PrincipalID: principal.ID, + Plaintext: issued.Plaintext, + ExpiresAt: issued.ExpiresAt, + }, nil +} + +// RevokeAPIToken revokes tokenID. +func (s *Service) RevokeAPIToken(ctx context.Context, tokenID string) error { + if s.apiTokens == nil { + return errors.New("management: API tokens service is required") + } + + if err := s.apiTokens.RevokeToken(ctx, tokenID); err != nil { + return fmt.Errorf("management: revoke API token: %w", err) + } + + return nil +} + +// ListPrincipalAPITokenMetadata returns API-token metadata for a principal. +func (s *Service) ListPrincipalAPITokenMetadata( + ctx context.Context, + principalID string, +) ([]apikey.TokenMetadata, error) { + if s.apiTokenMetadataLister == nil { + return nil, errors.New("management: API token metadata lister is required") + } + + tokens, err := s.apiTokenMetadataLister.ListPrincipalTokenMetadata(ctx, principalID) + if err != nil { + return nil, fmt.Errorf("management: list principal API token metadata: %w", err) + } + + return tokens, nil +} diff --git a/management/identity.go b/management/identity.go new file mode 100644 index 0000000..9d67095 --- /dev/null +++ b/management/identity.go @@ -0,0 +1,26 @@ +package management + +import ( + "context" + "errors" + "fmt" + + "github.com/meigma/authkit" +) + +// LinkIdentity links an external identity to an internal principal. +func (s *Service) LinkIdentity( + ctx context.Context, + req authkit.LinkIdentityRequest, +) (authkit.ExternalIdentity, error) { + if s.identityLinker == nil { + return authkit.ExternalIdentity{}, errors.New("management: identity linker is required") + } + + identity, err := s.identityLinker.LinkIdentity(ctx, req) + if err != nil { + return authkit.ExternalIdentity{}, fmt.Errorf("management: link identity: %w", err) + } + + return identity, nil +} diff --git a/management/principal.go b/management/principal.go new file mode 100644 index 0000000..5a575a5 --- /dev/null +++ b/management/principal.go @@ -0,0 +1,54 @@ +package management + +import ( + "context" + "errors" + "fmt" + + "github.com/meigma/authkit" +) + +// CreatePrincipal creates an internal principal. +func (s *Service) CreatePrincipal( + ctx context.Context, + req authkit.CreatePrincipalRequest, +) (authkit.Principal, error) { + if s.principalCreator == nil { + return authkit.Principal{}, errors.New("management: principal creator is required") + } + + principal, err := s.principalCreator.CreatePrincipal(ctx, req) + if err != nil { + return authkit.Principal{}, fmt.Errorf("management: create principal: %w", err) + } + + return principal, nil +} + +// FindPrincipal returns a principal by ID. +func (s *Service) FindPrincipal(ctx context.Context, id string) (authkit.Principal, error) { + if s.principalFinder == nil { + return authkit.Principal{}, errors.New("management: principal finder is required") + } + + principal, err := s.principalFinder.FindPrincipal(ctx, id) + if err != nil { + return authkit.Principal{}, fmt.Errorf("management: find principal: %w", err) + } + + return principal, nil +} + +// ListPrincipals returns principals. +func (s *Service) ListPrincipals(ctx context.Context) ([]authkit.Principal, error) { + if s.principalLister == nil { + return nil, errors.New("management: principal lister is required") + } + + principals, err := s.principalLister.ListPrincipals(ctx) + if err != nil { + return nil, fmt.Errorf("management: list principals: %w", err) + } + + return principals, nil +} diff --git a/management/provisioning.go b/management/provisioning.go new file mode 100644 index 0000000..c76b66d --- /dev/null +++ b/management/provisioning.go @@ -0,0 +1,84 @@ +package management + +import ( + "context" + "errors" + "fmt" + + "github.com/meigma/authkit" +) + +// CreateProvisioningRule creates a provisioning rule. +func (s *Service) CreateProvisioningRule( + ctx context.Context, + req authkit.CreateProvisioningRuleRequest, +) (authkit.ProvisioningRule, error) { + if s.provisioningRuleCreator == nil { + return authkit.ProvisioningRule{}, errors.New("management: provisioning rule creator is required") + } + + rule, err := s.provisioningRuleCreator.CreateProvisioningRule(ctx, req) + if err != nil { + return authkit.ProvisioningRule{}, fmt.Errorf("management: create provisioning rule: %w", err) + } + + return rule, nil +} + +// UpdateProvisioningRule updates a provisioning rule. +func (s *Service) UpdateProvisioningRule( + ctx context.Context, + req authkit.UpdateProvisioningRuleRequest, +) (authkit.ProvisioningRule, error) { + if s.provisioningRuleUpdater == nil { + return authkit.ProvisioningRule{}, errors.New("management: provisioning rule updater is required") + } + + rule, err := s.provisioningRuleUpdater.UpdateProvisioningRule(ctx, req) + if err != nil { + return authkit.ProvisioningRule{}, fmt.Errorf("management: update provisioning rule: %w", err) + } + + return rule, nil +} + +// DeleteProvisioningRule deletes a provisioning rule. +func (s *Service) DeleteProvisioningRule(ctx context.Context, id string) error { + if s.provisioningRuleDeleter == nil { + return errors.New("management: provisioning rule deleter is required") + } + + if err := s.provisioningRuleDeleter.DeleteProvisioningRule(ctx, id); err != nil { + return fmt.Errorf("management: delete provisioning rule: %w", err) + } + + return nil +} + +// FindProvisioningRule returns a provisioning rule by ID. +func (s *Service) FindProvisioningRule(ctx context.Context, id string) (authkit.ProvisioningRule, error) { + if s.provisioningRuleFinder == nil { + return authkit.ProvisioningRule{}, errors.New("management: provisioning rule finder is required") + } + + rule, err := s.provisioningRuleFinder.FindProvisioningRule(ctx, id) + if err != nil { + return authkit.ProvisioningRule{}, fmt.Errorf("management: find provisioning rule: %w", err) + } + + return rule, nil +} + +// ListProvisioningRules returns provisioning rules. +func (s *Service) ListProvisioningRules(ctx context.Context) ([]authkit.ProvisioningRule, error) { + if s.provisioningRuleLister == nil { + return nil, errors.New("management: provisioning rule lister is required") + } + + rules, err := s.provisioningRuleLister.ListProvisioningRules(ctx) + if err != nil { + return nil, fmt.Errorf("management: list provisioning rules: %w", err) + } + + return rules, nil +} diff --git a/management/role.go b/management/role.go new file mode 100644 index 0000000..cfa3588 --- /dev/null +++ b/management/role.go @@ -0,0 +1,82 @@ +package management + +import ( + "context" + "errors" + "fmt" + + "github.com/meigma/authkit" +) + +// CreateRole creates a local role. +func (s *Service) CreateRole( + ctx context.Context, + req authkit.CreateRoleRequest, +) (authkit.Role, error) { + if s.roleCreator == nil { + return authkit.Role{}, errors.New("management: role creator is required") + } + + role, err := s.roleCreator.CreateRole(ctx, req) + if err != nil { + return authkit.Role{}, fmt.Errorf("management: create role: %w", err) + } + + return role, nil +} + +// GrantRoleAction grants an authorization action to a local role. +func (s *Service) GrantRoleAction(ctx context.Context, req authkit.GrantRoleActionRequest) error { + if s.roleActionGranter == nil { + return errors.New("management: role action granter is required") + } + + if err := s.roleActionGranter.GrantRoleAction(ctx, req); err != nil { + return fmt.Errorf("management: grant role action: %w", err) + } + + return nil +} + +// AssignPrincipalRole assigns a principal to a local role. +func (s *Service) AssignPrincipalRole(ctx context.Context, req authkit.AssignPrincipalRoleRequest) error { + if s.principalRoleAssigner == nil { + return errors.New("management: principal role assigner is required") + } + + if err := s.principalRoleAssigner.AssignPrincipalRole(ctx, req); err != nil { + return fmt.Errorf("management: assign principal role: %w", err) + } + + return nil +} + +// UnassignPrincipalRole removes a principal from a local role. +func (s *Service) UnassignPrincipalRole(ctx context.Context, req authkit.UnassignPrincipalRoleRequest) error { + if s.principalRoleUnassigner == nil { + return errors.New("management: principal role unassigner is required") + } + + if err := s.principalRoleUnassigner.UnassignPrincipalRole(ctx, req); err != nil { + return fmt.Errorf("management: unassign principal role: %w", err) + } + + return nil +} + +// ListPrincipalRoleAssignments returns role assignments for a principal. +func (s *Service) ListPrincipalRoleAssignments( + ctx context.Context, + principalID string, +) ([]authkit.PrincipalRoleAssignment, error) { + if s.roleAssignmentLister == nil { + return nil, errors.New("management: principal role assignment lister is required") + } + + assignments, err := s.roleAssignmentLister.ListPrincipalRoleAssignments(ctx, principalID) + if err != nil { + return nil, fmt.Errorf("management: list principal role assignments: %w", err) + } + + return assignments, nil +} diff --git a/management/service.go b/management/service.go index ff6780f..3e2cebd 100644 --- a/management/service.go +++ b/management/service.go @@ -1,23 +1,10 @@ package management import ( - "context" - "errors" - "fmt" - "github.com/meigma/authkit" "github.com/meigma/authkit/proof/apikey" ) -// APITokens issues and revokes opaque API tokens. -type APITokens interface { - // IssueToken issues an opaque API token for an existing principal. - IssueToken(ctx context.Context, req apikey.IssueRequest) (apikey.IssuedToken, error) - - // RevokeToken revokes tokenID. - RevokeToken(ctx context.Context, tokenID string) error -} - // Options configures a Service. type Options struct { // PrincipalCreator creates internal principals. @@ -110,274 +97,3 @@ func NewService(opts Options) *Service { apiTokenMetadataLister: opts.APITokenMetadataLister, } } - -// CreatePrincipal creates an internal principal. -func (s *Service) CreatePrincipal( - ctx context.Context, - req authkit.CreatePrincipalRequest, -) (authkit.Principal, error) { - if s.principalCreator == nil { - return authkit.Principal{}, errors.New("management: principal creator is required") - } - - principal, err := s.principalCreator.CreatePrincipal(ctx, req) - if err != nil { - return authkit.Principal{}, fmt.Errorf("management: create principal: %w", err) - } - - return principal, nil -} - -// FindPrincipal returns a principal by ID. -func (s *Service) FindPrincipal(ctx context.Context, id string) (authkit.Principal, error) { - if s.principalFinder == nil { - return authkit.Principal{}, errors.New("management: principal finder is required") - } - - principal, err := s.principalFinder.FindPrincipal(ctx, id) - if err != nil { - return authkit.Principal{}, fmt.Errorf("management: find principal: %w", err) - } - - return principal, nil -} - -// ListPrincipals returns principals. -func (s *Service) ListPrincipals(ctx context.Context) ([]authkit.Principal, error) { - if s.principalLister == nil { - return nil, errors.New("management: principal lister is required") - } - - principals, err := s.principalLister.ListPrincipals(ctx) - if err != nil { - return nil, fmt.Errorf("management: list principals: %w", err) - } - - return principals, nil -} - -// CreateRole creates a local role. -func (s *Service) CreateRole( - ctx context.Context, - req authkit.CreateRoleRequest, -) (authkit.Role, error) { - if s.roleCreator == nil { - return authkit.Role{}, errors.New("management: role creator is required") - } - - role, err := s.roleCreator.CreateRole(ctx, req) - if err != nil { - return authkit.Role{}, fmt.Errorf("management: create role: %w", err) - } - - return role, nil -} - -// GrantRoleAction grants an authorization action to a local role. -func (s *Service) GrantRoleAction(ctx context.Context, req authkit.GrantRoleActionRequest) error { - if s.roleActionGranter == nil { - return errors.New("management: role action granter is required") - } - - if err := s.roleActionGranter.GrantRoleAction(ctx, req); err != nil { - return fmt.Errorf("management: grant role action: %w", err) - } - - return nil -} - -// AssignPrincipalRole assigns a principal to a local role. -func (s *Service) AssignPrincipalRole(ctx context.Context, req authkit.AssignPrincipalRoleRequest) error { - if s.principalRoleAssigner == nil { - return errors.New("management: principal role assigner is required") - } - - if err := s.principalRoleAssigner.AssignPrincipalRole(ctx, req); err != nil { - return fmt.Errorf("management: assign principal role: %w", err) - } - - return nil -} - -// UnassignPrincipalRole removes a principal from a local role. -func (s *Service) UnassignPrincipalRole(ctx context.Context, req authkit.UnassignPrincipalRoleRequest) error { - if s.principalRoleUnassigner == nil { - return errors.New("management: principal role unassigner is required") - } - - if err := s.principalRoleUnassigner.UnassignPrincipalRole(ctx, req); err != nil { - return fmt.Errorf("management: unassign principal role: %w", err) - } - - return nil -} - -// ListPrincipalRoleAssignments returns role assignments for a principal. -func (s *Service) ListPrincipalRoleAssignments( - ctx context.Context, - principalID string, -) ([]authkit.PrincipalRoleAssignment, error) { - if s.roleAssignmentLister == nil { - return nil, errors.New("management: principal role assignment lister is required") - } - - assignments, err := s.roleAssignmentLister.ListPrincipalRoleAssignments(ctx, principalID) - if err != nil { - return nil, fmt.Errorf("management: list principal role assignments: %w", err) - } - - return assignments, nil -} - -// CreateProvisioningRule creates a provisioning rule. -func (s *Service) CreateProvisioningRule( - ctx context.Context, - req authkit.CreateProvisioningRuleRequest, -) (authkit.ProvisioningRule, error) { - if s.provisioningRuleCreator == nil { - return authkit.ProvisioningRule{}, errors.New("management: provisioning rule creator is required") - } - - rule, err := s.provisioningRuleCreator.CreateProvisioningRule(ctx, req) - if err != nil { - return authkit.ProvisioningRule{}, fmt.Errorf("management: create provisioning rule: %w", err) - } - - return rule, nil -} - -// UpdateProvisioningRule updates a provisioning rule. -func (s *Service) UpdateProvisioningRule( - ctx context.Context, - req authkit.UpdateProvisioningRuleRequest, -) (authkit.ProvisioningRule, error) { - if s.provisioningRuleUpdater == nil { - return authkit.ProvisioningRule{}, errors.New("management: provisioning rule updater is required") - } - - rule, err := s.provisioningRuleUpdater.UpdateProvisioningRule(ctx, req) - if err != nil { - return authkit.ProvisioningRule{}, fmt.Errorf("management: update provisioning rule: %w", err) - } - - return rule, nil -} - -// DeleteProvisioningRule deletes a provisioning rule. -func (s *Service) DeleteProvisioningRule(ctx context.Context, id string) error { - if s.provisioningRuleDeleter == nil { - return errors.New("management: provisioning rule deleter is required") - } - - if err := s.provisioningRuleDeleter.DeleteProvisioningRule(ctx, id); err != nil { - return fmt.Errorf("management: delete provisioning rule: %w", err) - } - - return nil -} - -// FindProvisioningRule returns a provisioning rule by ID. -func (s *Service) FindProvisioningRule(ctx context.Context, id string) (authkit.ProvisioningRule, error) { - if s.provisioningRuleFinder == nil { - return authkit.ProvisioningRule{}, errors.New("management: provisioning rule finder is required") - } - - rule, err := s.provisioningRuleFinder.FindProvisioningRule(ctx, id) - if err != nil { - return authkit.ProvisioningRule{}, fmt.Errorf("management: find provisioning rule: %w", err) - } - - return rule, nil -} - -// ListProvisioningRules returns provisioning rules. -func (s *Service) ListProvisioningRules(ctx context.Context) ([]authkit.ProvisioningRule, error) { - if s.provisioningRuleLister == nil { - return nil, errors.New("management: provisioning rule lister is required") - } - - rules, err := s.provisioningRuleLister.ListProvisioningRules(ctx) - if err != nil { - return nil, fmt.Errorf("management: list provisioning rules: %w", err) - } - - return rules, nil -} - -// LinkIdentity links an external identity to an internal principal. -func (s *Service) LinkIdentity( - ctx context.Context, - req authkit.LinkIdentityRequest, -) (authkit.ExternalIdentity, error) { - if s.identityLinker == nil { - return authkit.ExternalIdentity{}, errors.New("management: identity linker is required") - } - - identity, err := s.identityLinker.LinkIdentity(ctx, req) - if err != nil { - return authkit.ExternalIdentity{}, fmt.Errorf("management: link identity: %w", err) - } - - return identity, nil -} - -// IssueAPIToken issues an API token for an existing principal. -func (s *Service) IssueAPIToken(ctx context.Context, req IssueAPITokenRequest) (IssuedAPIToken, error) { - if s.apiTokens == nil { - return IssuedAPIToken{}, errors.New("management: API tokens service is required") - } - if s.principalFinder == nil { - return IssuedAPIToken{}, errors.New("management: principal finder is required") - } - - principal, err := s.principalFinder.FindPrincipal(ctx, req.PrincipalID) - if err != nil { - return IssuedAPIToken{}, fmt.Errorf("management: find API token principal: %w", err) - } - - issued, err := s.apiTokens.IssueToken(ctx, apikey.IssueRequest{ - PrincipalID: principal.ID, - Name: req.Name, - ExpiresAt: req.ExpiresAt, - }) - if err != nil { - return IssuedAPIToken{}, fmt.Errorf("management: issue API token: %w", err) - } - - return IssuedAPIToken{ - ID: issued.ID, - PrincipalID: principal.ID, - Plaintext: issued.Plaintext, - ExpiresAt: issued.ExpiresAt, - }, nil -} - -// RevokeAPIToken revokes tokenID. -func (s *Service) RevokeAPIToken(ctx context.Context, tokenID string) error { - if s.apiTokens == nil { - return errors.New("management: API tokens service is required") - } - - if err := s.apiTokens.RevokeToken(ctx, tokenID); err != nil { - return fmt.Errorf("management: revoke API token: %w", err) - } - - return nil -} - -// ListPrincipalAPITokenMetadata returns API-token metadata for a principal. -func (s *Service) ListPrincipalAPITokenMetadata( - ctx context.Context, - principalID string, -) ([]apikey.TokenMetadata, error) { - if s.apiTokenMetadataLister == nil { - return nil, errors.New("management: API token metadata lister is required") - } - - tokens, err := s.apiTokenMetadataLister.ListPrincipalTokenMetadata(ctx, principalID) - if err != nil { - return nil, fmt.Errorf("management: list principal API token metadata: %w", err) - } - - return tokens, nil -} diff --git a/management/types.go b/management/types.go deleted file mode 100644 index f815108..0000000 --- a/management/types.go +++ /dev/null @@ -1,30 +0,0 @@ -package management - -import "time" - -// IssueAPITokenRequest describes a request to issue an API token. -type IssueAPITokenRequest struct { - // PrincipalID identifies the principal the token should authenticate as. - PrincipalID string - - // Name is an optional human-readable token label. - Name string - - // ExpiresAt is the time after which the token must no longer authenticate. - ExpiresAt time.Time -} - -// IssuedAPIToken describes an issued API token. -type IssuedAPIToken struct { - // ID is the stable lookup identifier embedded in the token. - ID string - - // PrincipalID identifies the principal the token authenticates as. - PrincipalID string - - // Plaintext is the full token secret shown once to the caller. - Plaintext string - - // ExpiresAt is the time after which the token must no longer authenticate. - ExpiresAt time.Time -} From 410cfb682ece8e4f979e2a69700e38a7a0b24767 Mon Sep 17 00:00:00 2001 From: Joshua Gilman Date: Wed, 27 May 2026 16:22:52 -0700 Subject: [PATCH 2/5] refactor(management): add Service godoc and IssueAPIToken precondition comment MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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. --- management/apitoken.go | 3 +++ management/service.go | 4 +++- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/management/apitoken.go b/management/apitoken.go index 213da9a..ca207f6 100644 --- a/management/apitoken.go +++ b/management/apitoken.go @@ -54,6 +54,9 @@ func (s *Service) IssueAPIToken(ctx context.Context, req IssueAPITokenRequest) ( return IssuedAPIToken{}, errors.New("management: principal finder is required") } + // Resolve the principal before delegating to the apikey service so we never mint a token bound + // to an identifier that does not exist; without this check, a typo or deleted principal would + // produce a token that authenticates to nothing. principal, err := s.principalFinder.FindPrincipal(ctx, req.PrincipalID) if err != nil { return IssuedAPIToken{}, fmt.Errorf("management: find API token principal: %w", err) diff --git a/management/service.go b/management/service.go index 3e2cebd..38b0093 100644 --- a/management/service.go +++ b/management/service.go @@ -56,7 +56,9 @@ type Options struct { APITokenMetadataLister apikey.TokenMetadataLister } -// Service composes common authkit management operations. +// Service composes common authkit management operations into a single facade for app-owned setup +// flows (CLIs, seed scripts, migrations, admin handlers). Options accepts a sparse set of ports; +// each method validates the ports it needs and returns an error when one is missing. type Service struct { principalCreator authkit.PrincipalCreator principalFinder authkit.PrincipalFinder From dc3f059a89efd9be1ccf00029deb7d840f14c450 Mon Sep 17 00:00:00 2001 From: Joshua Gilman Date: Wed, 27 May 2026 16:26:48 -0700 Subject: [PATCH 3/5] test(management): split service_test into per-domain files MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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). --- management/apitoken_test.go | 131 +++++ management/helpers_test.go | 408 ++++++++++++++ management/identity_test.go | 28 + management/principal_test.go | 56 ++ management/provisioning_test.go | 198 +++++++ management/role_test.go | 224 ++++++++ management/service_test.go | 970 -------------------------------- 7 files changed, 1045 insertions(+), 970 deletions(-) create mode 100644 management/apitoken_test.go create mode 100644 management/helpers_test.go create mode 100644 management/identity_test.go create mode 100644 management/principal_test.go create mode 100644 management/provisioning_test.go create mode 100644 management/role_test.go diff --git a/management/apitoken_test.go b/management/apitoken_test.go new file mode 100644 index 0000000..07a4458 --- /dev/null +++ b/management/apitoken_test.go @@ -0,0 +1,131 @@ +package management_test + +import ( + "context" + "errors" + "testing" + "time" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + "github.com/meigma/authkit" + "github.com/meigma/authkit/management" + "github.com/meigma/authkit/proof/apikey" +) + +func TestServiceIssueAPITokenIssuesForExistingPrincipal(t *testing.T) { + now := fixedTime() + expiresAt := now.Add(time.Hour) + apiTokens := newFakeAPITokens() + apiTokens.issued = apikey.IssuedToken{ + ID: testTokenID, + Plaintext: testTokenSecret, + ExpiresAt: expiresAt, + } + principals := newFakePrincipalCreator() + principals.principal = authkit.Principal{ + ID: testPrincipalID, + Kind: authkit.PrincipalKindService, + DisplayName: testPrincipalName, + } + service := newService(t, principals, newFakeIdentityLinker(), apiTokens) + req := management.IssueAPITokenRequest{ + PrincipalID: testPrincipalID, + Name: testTokenName, + ExpiresAt: expiresAt, + } + + issued, err := service.IssueAPIToken(context.Background(), req) + + require.NoError(t, err) + assert.Equal(t, []string{testPrincipalID}, principals.findIDs) + assert.Equal(t, []apikey.IssueRequest{{ + PrincipalID: testPrincipalID, + Name: testTokenName, + ExpiresAt: expiresAt, + }}, apiTokens.issueRequests) + assert.Equal(t, management.IssuedAPIToken{ + ID: testTokenID, + PrincipalID: testPrincipalID, + Plaintext: testTokenSecret, + ExpiresAt: expiresAt, + }, issued) +} + +func TestServiceIssueAPITokenRejectsMissingPrincipal(t *testing.T) { + principals := newFakePrincipalCreator() + principals.err = authkit.ErrPrincipalNotFound + apiTokens := newFakeAPITokens() + service := newService(t, principals, newFakeIdentityLinker(), apiTokens) + + issued, err := service.IssueAPIToken(context.Background(), management.IssueAPITokenRequest{ + PrincipalID: testPrincipalID, + ExpiresAt: fixedTime().Add(time.Hour), + }) + + require.ErrorIs(t, err, authkit.ErrPrincipalNotFound) + assert.Empty(t, issued) + assert.Equal(t, []string{testPrincipalID}, principals.findIDs) + assert.Empty(t, apiTokens.issueRequests) +} + +func TestServiceIssueAPITokenReturnsIssueErrorWithoutLinkingIdentity(t *testing.T) { + issueErr := errors.New("issue failed") + apiTokens := newFakeAPITokens() + apiTokens.issueErr = issueErr + linker := newFakeIdentityLinker() + principals := newFakePrincipalCreator() + principals.principal = authkit.Principal{ID: testPrincipalID} + service := newService(t, principals, linker, apiTokens) + + issued, err := service.IssueAPIToken(context.Background(), management.IssueAPITokenRequest{ + PrincipalID: testPrincipalID, + ExpiresAt: fixedTime().Add(time.Hour), + }) + + require.ErrorIs(t, err, issueErr) + assert.Empty(t, issued) + assert.Empty(t, linker.requests) + assert.Empty(t, apiTokens.revokedIDs) +} + +func TestServiceRevokeAPIToken(t *testing.T) { + apiTokens := newFakeAPITokens() + service := newService(t, newFakePrincipalCreator(), newFakeIdentityLinker(), apiTokens) + + err := service.RevokeAPIToken(context.Background(), testTokenID) + + require.NoError(t, err) + assert.Equal(t, []string{testTokenID}, apiTokens.revokedIDs) +} + +func TestServiceRevokeAPITokenReturnsError(t *testing.T) { + revokeErr := errors.New("revoke failed") + apiTokens := newFakeAPITokens() + apiTokens.revokeErr = revokeErr + service := newService(t, newFakePrincipalCreator(), newFakeIdentityLinker(), apiTokens) + + err := service.RevokeAPIToken(context.Background(), testTokenID) + + require.ErrorIs(t, err, revokeErr) +} + +func TestServiceListPrincipalAPITokenMetadata(t *testing.T) { + apiTokens := newFakeAPITokens() + apiTokens.metadata = []apikey.TokenMetadata{{ + ID: testTokenID, + PrincipalID: testPrincipalID, + Name: testTokenName, + ExpiresAt: fixedTime().Add(time.Hour), + }} + service := management.NewService(management.Options{ + APITokenMetadataLister: apiTokens, + }) + + tokens, err := service.ListPrincipalAPITokenMetadata(context.Background(), testPrincipalID) + + require.NoError(t, err) + assert.Equal(t, apiTokens.metadata, tokens) + assert.Equal(t, []string{testPrincipalID}, apiTokens.metadataPrincipalIDs) +} diff --git a/management/helpers_test.go b/management/helpers_test.go new file mode 100644 index 0000000..c52603d --- /dev/null +++ b/management/helpers_test.go @@ -0,0 +1,408 @@ +package management_test + +import ( + "context" + "testing" + "time" + + "github.com/meigma/authkit" + "github.com/meigma/authkit/management" + "github.com/meigma/authkit/proof/apikey" + "github.com/meigma/authkit/store/memory" +) + +const ( + testPrincipalID = "principal_1" + testTokenID = "token_1" + testTokenName = "deploy token" + testTokenSecret = "ak_token_1_secret" + testProvider = "oidc" + testSubject = "user-123" + testPrincipalName = "deploy service" +) + +func newService( + t *testing.T, + principals principalStore, + linker authkit.IdentityLinker, + apiTokens management.APITokens, +) *management.Service { + t.Helper() + + return newServiceWithRoles(t, principals, newFakeRoleStore(), linker, apiTokens) +} + +func newServiceWithRoles( + t *testing.T, + principals principalStore, + roles roleStore, + linker authkit.IdentityLinker, + apiTokens management.APITokens, +) *management.Service { + t.Helper() + + service := management.NewService(management.Options{ + PrincipalCreator: principals, + PrincipalFinder: principals, + RoleCreator: roles, + RoleActionGranter: roles, + PrincipalRoleAssigner: roles, + PrincipalRoleUnassigner: roles, + PrincipalRoleAssignmentLister: roles, + IdentityLinker: linker, + APITokens: apiTokens, + }) + + return service +} + +func newServiceWithProvisioningRules( + t *testing.T, + principals principalStore, + roles roleStore, + rules provisioningRuleStore, + linker authkit.IdentityLinker, + apiTokens management.APITokens, +) *management.Service { + t.Helper() + + service := management.NewService(management.Options{ + PrincipalCreator: principals, + PrincipalFinder: principals, + RoleCreator: roles, + RoleActionGranter: roles, + PrincipalRoleAssigner: roles, + PrincipalRoleUnassigner: roles, + PrincipalRoleAssignmentLister: roles, + ProvisioningRuleCreator: rules, + ProvisioningRuleUpdater: rules, + ProvisioningRuleDeleter: rules, + ProvisioningRuleFinder: rules, + ProvisioningRuleLister: rules, + IdentityLinker: linker, + APITokens: apiTokens, + }) + + return service +} + +func newManagementService( + t *testing.T, + store *memory.Store, + tokenService *apikey.Service, +) *management.Service { + t.Helper() + + return management.NewService(management.Options{ + PrincipalCreator: store, + PrincipalFinder: store, + PrincipalLister: store, + RoleCreator: store, + RoleActionGranter: store, + PrincipalRoleAssigner: store, + PrincipalRoleUnassigner: store, + PrincipalRoleAssignmentLister: store, + ProvisioningRuleCreator: store, + ProvisioningRuleUpdater: store, + ProvisioningRuleDeleter: store, + ProvisioningRuleFinder: store, + ProvisioningRuleLister: store, + IdentityLinker: store, + APITokens: tokenService, + APITokenMetadataLister: store, + }) +} + +type roleStore interface { + authkit.RoleCreator + authkit.RoleActionGranter + authkit.PrincipalRoleAssigner + authkit.PrincipalRoleUnassigner + authkit.PrincipalRoleAssignmentLister +} + +type principalStore interface { + authkit.PrincipalCreator + authkit.PrincipalFinder +} + +type provisioningRuleStore interface { + authkit.ProvisioningRuleCreator + authkit.ProvisioningRuleUpdater + authkit.ProvisioningRuleDeleter + authkit.ProvisioningRuleFinder + authkit.ProvisioningRuleLister +} + +func newFakePrincipalCreator() *fakePrincipalCreator { + return &fakePrincipalCreator{} +} + +func newFakeIdentityLinker() *fakeIdentityLinker { + return &fakeIdentityLinker{} +} + +func newFakeRoleStore() *fakeRoleStore { + return &fakeRoleStore{} +} + +func newFakeProvisioningRuleStore() *fakeProvisioningRuleStore { + return &fakeProvisioningRuleStore{} +} + +func newFakeAPITokens() *fakeAPITokens { + return &fakeAPITokens{} +} + +func fixedTime() time.Time { + return time.Date(2026, time.May, 8, 12, 0, 0, 0, time.UTC) +} + +func provisioningRule() authkit.ProvisioningRule { + return authkit.ProvisioningRule{ + ID: "engineering-readers", + DisplayName: "Engineering readers", + Provider: "https://issuer.example", + Condition: `hasAny(claims.groups, ["/engineering"])`, + AssignRoleIDs: []string{"notes-reader"}, + Enabled: true, + } +} + +type fakePrincipalCreator struct { + requests []authkit.CreatePrincipalRequest + findIDs []string + listCalls int + principal authkit.Principal + principals []authkit.Principal + err error +} + +func (f *fakePrincipalCreator) CreatePrincipal( + _ context.Context, + req authkit.CreatePrincipalRequest, +) (authkit.Principal, error) { + f.requests = append(f.requests, req) + if f.err != nil { + return authkit.Principal{}, f.err + } + + return f.principal, nil +} + +func (f *fakePrincipalCreator) FindPrincipal(_ context.Context, id string) (authkit.Principal, error) { + f.findIDs = append(f.findIDs, id) + if f.err != nil { + return authkit.Principal{}, f.err + } + + return f.principal, nil +} + +func (f *fakePrincipalCreator) ListPrincipals(_ context.Context) ([]authkit.Principal, error) { + f.listCalls++ + if f.err != nil { + return nil, f.err + } + + return f.principals, nil +} + +type fakeIdentityLinker struct { + requests []authkit.LinkIdentityRequest + identity authkit.ExternalIdentity + err error +} + +func (f *fakeIdentityLinker) LinkIdentity( + _ context.Context, + req authkit.LinkIdentityRequest, +) (authkit.ExternalIdentity, error) { + f.requests = append(f.requests, req) + if f.err != nil { + return authkit.ExternalIdentity{}, f.err + } + + return f.identity, nil +} + +type fakeRoleStore struct { + createRequests []authkit.CreateRoleRequest + grantRequests []authkit.GrantRoleActionRequest + assignRequests []authkit.AssignPrincipalRoleRequest + unassignRequests []authkit.UnassignPrincipalRoleRequest + listPrincipalIDs []string + role authkit.Role + assignments []authkit.PrincipalRoleAssignment + err error +} + +func (f *fakeRoleStore) CreateRole( + _ context.Context, + req authkit.CreateRoleRequest, +) (authkit.Role, error) { + f.createRequests = append(f.createRequests, req) + if f.err != nil { + return authkit.Role{}, f.err + } + + return f.role, nil +} + +func (f *fakeRoleStore) GrantRoleAction( + _ context.Context, + req authkit.GrantRoleActionRequest, +) error { + f.grantRequests = append(f.grantRequests, req) + if f.err != nil { + return f.err + } + + return nil +} + +func (f *fakeRoleStore) AssignPrincipalRole( + _ context.Context, + req authkit.AssignPrincipalRoleRequest, +) error { + f.assignRequests = append(f.assignRequests, req) + if f.err != nil { + return f.err + } + + return nil +} + +func (f *fakeRoleStore) UnassignPrincipalRole( + _ context.Context, + req authkit.UnassignPrincipalRoleRequest, +) error { + f.unassignRequests = append(f.unassignRequests, req) + if f.err != nil { + return f.err + } + + return nil +} + +func (f *fakeRoleStore) ListPrincipalRoleAssignments( + _ context.Context, + principalID string, +) ([]authkit.PrincipalRoleAssignment, error) { + f.listPrincipalIDs = append(f.listPrincipalIDs, principalID) + if f.err != nil { + return nil, f.err + } + + return f.assignments, nil +} + +type fakeProvisioningRuleStore struct { + createRequests []authkit.CreateProvisioningRuleRequest + updateRequests []authkit.UpdateProvisioningRuleRequest + deleteIDs []string + findIDs []string + listCalls int + rule authkit.ProvisioningRule + err error +} + +func (f *fakeProvisioningRuleStore) CreateProvisioningRule( + _ context.Context, + req authkit.CreateProvisioningRuleRequest, +) (authkit.ProvisioningRule, error) { + f.createRequests = append(f.createRequests, req) + if f.err != nil { + return authkit.ProvisioningRule{}, f.err + } + + return f.rule, nil +} + +func (f *fakeProvisioningRuleStore) UpdateProvisioningRule( + _ context.Context, + req authkit.UpdateProvisioningRuleRequest, +) (authkit.ProvisioningRule, error) { + f.updateRequests = append(f.updateRequests, req) + if f.err != nil { + return authkit.ProvisioningRule{}, f.err + } + + return f.rule, nil +} + +func (f *fakeProvisioningRuleStore) DeleteProvisioningRule(_ context.Context, id string) error { + f.deleteIDs = append(f.deleteIDs, id) + if f.err != nil { + return f.err + } + + return nil +} + +func (f *fakeProvisioningRuleStore) FindProvisioningRule( + _ context.Context, + id string, +) (authkit.ProvisioningRule, error) { + f.findIDs = append(f.findIDs, id) + if f.err != nil { + return authkit.ProvisioningRule{}, f.err + } + + return f.rule, nil +} + +func (f *fakeProvisioningRuleStore) ListProvisioningRules( + context.Context, +) ([]authkit.ProvisioningRule, error) { + f.listCalls++ + if f.err != nil { + return nil, f.err + } + + return []authkit.ProvisioningRule{f.rule}, nil +} + +type fakeAPITokens struct { + issueRequests []apikey.IssueRequest + issued apikey.IssuedToken + issueErr error + revokedIDs []string + revokeErr error + metadataPrincipalIDs []string + metadata []apikey.TokenMetadata + metadataErr error +} + +func (f *fakeAPITokens) IssueToken( + _ context.Context, + req apikey.IssueRequest, +) (apikey.IssuedToken, error) { + f.issueRequests = append(f.issueRequests, req) + if f.issueErr != nil { + return apikey.IssuedToken{}, f.issueErr + } + + return f.issued, nil +} + +func (f *fakeAPITokens) RevokeToken(_ context.Context, tokenID string) error { + f.revokedIDs = append(f.revokedIDs, tokenID) + if f.revokeErr != nil { + return f.revokeErr + } + + return nil +} + +func (f *fakeAPITokens) ListPrincipalTokenMetadata( + _ context.Context, + principalID string, +) ([]apikey.TokenMetadata, error) { + f.metadataPrincipalIDs = append(f.metadataPrincipalIDs, principalID) + if f.metadataErr != nil { + return nil, f.metadataErr + } + + return f.metadata, nil +} diff --git a/management/identity_test.go b/management/identity_test.go new file mode 100644 index 0000000..1e804aa --- /dev/null +++ b/management/identity_test.go @@ -0,0 +1,28 @@ +package management_test + +import ( + "context" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + "github.com/meigma/authkit" +) + +func TestServiceLinkIdentity(t *testing.T) { + linker := newFakeIdentityLinker() + linker.identity = authkit.ExternalIdentity{ + Provider: testProvider, + Subject: testSubject, + PrincipalID: testPrincipalID, + } + service := newService(t, newFakePrincipalCreator(), linker, newFakeAPITokens()) + req := authkit.LinkIdentityRequest(linker.identity) + + identity, err := service.LinkIdentity(context.Background(), req) + + require.NoError(t, err) + assert.Equal(t, linker.identity, identity) + assert.Equal(t, []authkit.LinkIdentityRequest{req}, linker.requests) +} diff --git a/management/principal_test.go b/management/principal_test.go new file mode 100644 index 0000000..f61543f --- /dev/null +++ b/management/principal_test.go @@ -0,0 +1,56 @@ +package management_test + +import ( + "context" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + "github.com/meigma/authkit" + "github.com/meigma/authkit/management" +) + +func TestServiceCreatePrincipal(t *testing.T) { + creator := newFakePrincipalCreator() + creator.principal = authkit.Principal{ + ID: testPrincipalID, + Kind: authkit.PrincipalKindService, + DisplayName: testPrincipalName, + } + service := newService(t, creator, newFakeIdentityLinker(), newFakeAPITokens()) + req := authkit.CreatePrincipalRequest{ + Kind: authkit.PrincipalKindService, + DisplayName: testPrincipalName, + } + + principal, err := service.CreatePrincipal(context.Background(), req) + + require.NoError(t, err) + assert.Equal(t, creator.principal, principal) + assert.Equal(t, []authkit.CreatePrincipalRequest{req}, creator.requests) +} + +func TestServiceFindAndListPrincipals(t *testing.T) { + principals := newFakePrincipalCreator() + principals.principal = authkit.Principal{ + ID: testPrincipalID, + Kind: authkit.PrincipalKindService, + DisplayName: testPrincipalName, + } + principals.principals = []authkit.Principal{principals.principal} + service := management.NewService(management.Options{ + PrincipalFinder: principals, + PrincipalLister: principals, + }) + + found, err := service.FindPrincipal(context.Background(), testPrincipalID) + require.NoError(t, err) + assert.Equal(t, principals.principal, found) + assert.Equal(t, []string{testPrincipalID}, principals.findIDs) + + listed, err := service.ListPrincipals(context.Background()) + require.NoError(t, err) + assert.Equal(t, principals.principals, listed) + assert.Equal(t, 1, principals.listCalls) +} diff --git a/management/provisioning_test.go b/management/provisioning_test.go new file mode 100644 index 0000000..dec890b --- /dev/null +++ b/management/provisioning_test.go @@ -0,0 +1,198 @@ +package management_test + +import ( + "context" + "errors" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + "github.com/meigma/authkit" + "github.com/meigma/authkit/management" +) + +func TestServiceProvisioningRuleMethods(t *testing.T) { + rules := newFakeProvisioningRuleStore() + rules.rule = provisioningRule() + service := newServiceWithProvisioningRules( + t, + newFakePrincipalCreator(), + newFakeRoleStore(), + rules, + newFakeIdentityLinker(), + newFakeAPITokens(), + ) + createReq := authkit.CreateProvisioningRuleRequest{ + ID: rules.rule.ID, + DisplayName: rules.rule.DisplayName, + Provider: rules.rule.Provider, + Condition: rules.rule.Condition, + AssignRoleIDs: rules.rule.AssignRoleIDs, + Enabled: rules.rule.Enabled, + } + updateReq := authkit.UpdateProvisioningRuleRequest{ + ID: rules.rule.ID, + DisplayName: "Updated", + Provider: rules.rule.Provider, + Condition: rules.rule.Condition, + AssignRoleIDs: rules.rule.AssignRoleIDs, + Enabled: false, + } + + created, err := service.CreateProvisioningRule(context.Background(), createReq) + require.NoError(t, err) + assert.Equal(t, rules.rule, created) + assert.Equal(t, []authkit.CreateProvisioningRuleRequest{createReq}, rules.createRequests) + + updated, err := service.UpdateProvisioningRule(context.Background(), updateReq) + require.NoError(t, err) + assert.Equal(t, rules.rule, updated) + assert.Equal(t, []authkit.UpdateProvisioningRuleRequest{updateReq}, rules.updateRequests) + + found, err := service.FindProvisioningRule(context.Background(), rules.rule.ID) + require.NoError(t, err) + assert.Equal(t, rules.rule, found) + assert.Equal(t, []string{rules.rule.ID}, rules.findIDs) + + listed, err := service.ListProvisioningRules(context.Background()) + require.NoError(t, err) + assert.Equal(t, []authkit.ProvisioningRule{rules.rule}, listed) + assert.Equal(t, 1, rules.listCalls) + + require.NoError(t, service.DeleteProvisioningRule(context.Background(), rules.rule.ID)) + assert.Equal(t, []string{rules.rule.ID}, rules.deleteIDs) +} + +func TestServiceWrapsProvisioningRuleErrors(t *testing.T) { + ruleErr := errors.New("rule failed") + + tests := []struct { + name string + run func(*management.Service) error + }{ + { + name: "create", + run: func(service *management.Service) error { + _, err := service.CreateProvisioningRule(context.Background(), authkit.CreateProvisioningRuleRequest{ + ID: "engineering-readers", + }) + + return err + }, + }, + { + name: "update", + run: func(service *management.Service) error { + _, err := service.UpdateProvisioningRule(context.Background(), authkit.UpdateProvisioningRuleRequest{ + ID: "engineering-readers", + }) + + return err + }, + }, + { + name: "delete", + run: func(service *management.Service) error { + return service.DeleteProvisioningRule(context.Background(), "engineering-readers") + }, + }, + { + name: "find", + run: func(service *management.Service) error { + _, err := service.FindProvisioningRule(context.Background(), "engineering-readers") + + return err + }, + }, + { + name: "list", + run: func(service *management.Service) error { + _, err := service.ListProvisioningRules(context.Background()) + + return err + }, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + rules := newFakeProvisioningRuleStore() + rules.err = ruleErr + service := newServiceWithProvisioningRules( + t, + newFakePrincipalCreator(), + newFakeRoleStore(), + rules, + newFakeIdentityLinker(), + newFakeAPITokens(), + ) + + require.ErrorIs(t, tt.run(service), ruleErr) + }) + } +} + +func TestServiceProvisioningRuleMethodsRequirePorts(t *testing.T) { + service := management.NewService(management.Options{ + PrincipalCreator: newFakePrincipalCreator(), + IdentityLinker: newFakeIdentityLinker(), + APITokens: newFakeAPITokens(), + }) + + tests := []struct { + name string + run func() error + }{ + { + name: "create", + run: func() error { + _, runErr := service.CreateProvisioningRule( + context.Background(), + authkit.CreateProvisioningRuleRequest{}, + ) + + return runErr + }, + }, + { + name: "update", + run: func() error { + _, runErr := service.UpdateProvisioningRule( + context.Background(), + authkit.UpdateProvisioningRuleRequest{}, + ) + + return runErr + }, + }, + { + name: "delete", + run: func() error { + return service.DeleteProvisioningRule(context.Background(), "engineering-readers") + }, + }, + { + name: "find", + run: func() error { + _, runErr := service.FindProvisioningRule(context.Background(), "engineering-readers") + + return runErr + }, + }, + { + name: "list", + run: func() error { + _, runErr := service.ListProvisioningRules(context.Background()) + + return runErr + }, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + require.Error(t, tt.run()) + }) + } +} diff --git a/management/role_test.go b/management/role_test.go new file mode 100644 index 0000000..52643e5 --- /dev/null +++ b/management/role_test.go @@ -0,0 +1,224 @@ +package management_test + +import ( + "context" + "errors" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + "github.com/meigma/authkit" + "github.com/meigma/authkit/management" +) + +func TestServiceCreateRole(t *testing.T) { + roles := newFakeRoleStore() + roles.role = authkit.Role{ + ID: "notes-reader", + DisplayName: "Notes reader", + Description: "Can read notes.", + } + service := newServiceWithRoles(t, newFakePrincipalCreator(), roles, newFakeIdentityLinker(), newFakeAPITokens()) + req := authkit.CreateRoleRequest{ + ID: roles.role.ID, + DisplayName: roles.role.DisplayName, + Description: roles.role.Description, + } + + role, err := service.CreateRole(context.Background(), req) + + require.NoError(t, err) + assert.Equal(t, roles.role, role) + assert.Equal(t, []authkit.CreateRoleRequest{req}, roles.createRequests) +} + +func TestServiceGrantRoleAction(t *testing.T) { + roles := newFakeRoleStore() + service := newServiceWithRoles(t, newFakePrincipalCreator(), roles, newFakeIdentityLinker(), newFakeAPITokens()) + req := authkit.GrantRoleActionRequest{ + RoleID: "notes-reader", + Action: "notes:read", + } + + err := service.GrantRoleAction(context.Background(), req) + + require.NoError(t, err) + assert.Equal(t, []authkit.GrantRoleActionRequest{req}, roles.grantRequests) +} + +func TestServiceAssignPrincipalRole(t *testing.T) { + roles := newFakeRoleStore() + service := newServiceWithRoles(t, newFakePrincipalCreator(), roles, newFakeIdentityLinker(), newFakeAPITokens()) + req := authkit.AssignPrincipalRoleRequest{ + PrincipalID: testPrincipalID, + RoleID: "notes-reader", + } + + err := service.AssignPrincipalRole(context.Background(), req) + + require.NoError(t, err) + assert.Equal(t, []authkit.AssignPrincipalRoleRequest{req}, roles.assignRequests) +} + +func TestServiceUnassignPrincipalRole(t *testing.T) { + roles := newFakeRoleStore() + service := newServiceWithRoles(t, newFakePrincipalCreator(), roles, newFakeIdentityLinker(), newFakeAPITokens()) + req := authkit.UnassignPrincipalRoleRequest{ + PrincipalID: testPrincipalID, + RoleID: "notes-reader", + } + + err := service.UnassignPrincipalRole(context.Background(), req) + + require.NoError(t, err) + assert.Equal(t, []authkit.UnassignPrincipalRoleRequest{req}, roles.unassignRequests) +} + +func TestServiceListPrincipalRoleAssignments(t *testing.T) { + roles := newFakeRoleStore() + roles.assignments = []authkit.PrincipalRoleAssignment{ + {PrincipalID: testPrincipalID, RoleID: "notes-reader"}, + } + service := newServiceWithRoles(t, newFakePrincipalCreator(), roles, newFakeIdentityLinker(), newFakeAPITokens()) + + assignments, err := service.ListPrincipalRoleAssignments(context.Background(), testPrincipalID) + + require.NoError(t, err) + assert.Equal(t, roles.assignments, assignments) + assert.Equal(t, []string{testPrincipalID}, roles.listPrincipalIDs) +} + +func TestServiceWrapsRoleErrors(t *testing.T) { + roleErr := errors.New("role failed") + + tests := []struct { + name string + run func(*management.Service) error + }{ + { + name: "create role", + run: func(service *management.Service) error { + _, err := service.CreateRole(context.Background(), authkit.CreateRoleRequest{ID: "notes-reader"}) + + return err + }, + }, + { + name: "grant role action", + run: func(service *management.Service) error { + return service.GrantRoleAction(context.Background(), authkit.GrantRoleActionRequest{ + RoleID: "notes-reader", + Action: "notes:read", + }) + }, + }, + { + name: "assign principal role", + run: func(service *management.Service) error { + return service.AssignPrincipalRole(context.Background(), authkit.AssignPrincipalRoleRequest{ + PrincipalID: testPrincipalID, + RoleID: "notes-reader", + }) + }, + }, + { + name: "unassign principal role", + run: func(service *management.Service) error { + return service.UnassignPrincipalRole(context.Background(), authkit.UnassignPrincipalRoleRequest{ + PrincipalID: testPrincipalID, + RoleID: "notes-reader", + }) + }, + }, + { + name: "list principal role assignments", + run: func(service *management.Service) error { + _, err := service.ListPrincipalRoleAssignments(context.Background(), testPrincipalID) + + return err + }, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + roles := newFakeRoleStore() + roles.err = roleErr + service := newServiceWithRoles( + t, + newFakePrincipalCreator(), + roles, + newFakeIdentityLinker(), + newFakeAPITokens(), + ) + + require.ErrorIs(t, tt.run(service), roleErr) + }) + } +} + +func TestServiceRoleMethodsRequireRolePorts(t *testing.T) { + service := management.NewService(management.Options{ + PrincipalCreator: newFakePrincipalCreator(), + IdentityLinker: newFakeIdentityLinker(), + APITokens: newFakeAPITokens(), + }) + + tests := []struct { + name string + run func() error + }{ + { + name: "create role", + run: func() error { + _, runErr := service.CreateRole(context.Background(), authkit.CreateRoleRequest{ + ID: "notes-reader", + }) + + return runErr + }, + }, + { + name: "grant role action", + run: func() error { + return service.GrantRoleAction(context.Background(), authkit.GrantRoleActionRequest{ + RoleID: "notes-reader", + Action: "notes:read", + }) + }, + }, + { + name: "assign principal role", + run: func() error { + return service.AssignPrincipalRole(context.Background(), authkit.AssignPrincipalRoleRequest{ + PrincipalID: testPrincipalID, + RoleID: "notes-reader", + }) + }, + }, + { + name: "unassign principal role", + run: func() error { + return service.UnassignPrincipalRole(context.Background(), authkit.UnassignPrincipalRoleRequest{ + PrincipalID: testPrincipalID, + RoleID: "notes-reader", + }) + }, + }, + { + name: "list principal role assignments", + run: func() error { + _, runErr := service.ListPrincipalRoleAssignments(context.Background(), testPrincipalID) + + return runErr + }, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + require.Error(t, tt.run()) + }) + } +} diff --git a/management/service_test.go b/management/service_test.go index 032efb2..f9f1ced 100644 --- a/management/service_test.go +++ b/management/service_test.go @@ -2,7 +2,6 @@ package management_test import ( "context" - "errors" "testing" "time" @@ -15,16 +14,6 @@ import ( "github.com/meigma/authkit/store/memory" ) -const ( - testPrincipalID = "principal_1" - testTokenID = "token_1" - testTokenName = "deploy token" - testTokenSecret = "ak_token_1_secret" - testProvider = "oidc" - testSubject = "user-123" - testPrincipalName = "deploy service" -) - func TestNewServiceAllowsSparseOptions(t *testing.T) { tests := []struct { name string @@ -171,50 +160,6 @@ func TestServiceIssueAPITokenRequiresPrincipalFinderBeforeIssuing(t *testing.T) assert.Empty(t, apiTokens.issueRequests) } -func TestServiceCreatePrincipal(t *testing.T) { - creator := newFakePrincipalCreator() - creator.principal = authkit.Principal{ - ID: testPrincipalID, - Kind: authkit.PrincipalKindService, - DisplayName: testPrincipalName, - } - service := newService(t, creator, newFakeIdentityLinker(), newFakeAPITokens()) - req := authkit.CreatePrincipalRequest{ - Kind: authkit.PrincipalKindService, - DisplayName: testPrincipalName, - } - - principal, err := service.CreatePrincipal(context.Background(), req) - - require.NoError(t, err) - assert.Equal(t, creator.principal, principal) - assert.Equal(t, []authkit.CreatePrincipalRequest{req}, creator.requests) -} - -func TestServiceFindAndListPrincipals(t *testing.T) { - principals := newFakePrincipalCreator() - principals.principal = authkit.Principal{ - ID: testPrincipalID, - Kind: authkit.PrincipalKindService, - DisplayName: testPrincipalName, - } - principals.principals = []authkit.Principal{principals.principal} - service := management.NewService(management.Options{ - PrincipalFinder: principals, - PrincipalLister: principals, - }) - - found, err := service.FindPrincipal(context.Background(), testPrincipalID) - require.NoError(t, err) - assert.Equal(t, principals.principal, found) - assert.Equal(t, []string{testPrincipalID}, principals.findIDs) - - listed, err := service.ListPrincipals(context.Background()) - require.NoError(t, err) - assert.Equal(t, principals.principals, listed) - assert.Equal(t, 1, principals.listCalls) -} - func TestNewServiceDoesNotRequireRolePorts(t *testing.T) { service := management.NewService(management.Options{ PrincipalCreator: newFakePrincipalCreator(), @@ -233,535 +178,6 @@ func TestNewServiceDoesNotRequireProvisioningRulePorts(t *testing.T) { assert.NotNil(t, service) } -func TestServiceCreateRole(t *testing.T) { - roles := newFakeRoleStore() - roles.role = authkit.Role{ - ID: "notes-reader", - DisplayName: "Notes reader", - Description: "Can read notes.", - } - service := newServiceWithRoles(t, newFakePrincipalCreator(), roles, newFakeIdentityLinker(), newFakeAPITokens()) - req := authkit.CreateRoleRequest{ - ID: roles.role.ID, - DisplayName: roles.role.DisplayName, - Description: roles.role.Description, - } - - role, err := service.CreateRole(context.Background(), req) - - require.NoError(t, err) - assert.Equal(t, roles.role, role) - assert.Equal(t, []authkit.CreateRoleRequest{req}, roles.createRequests) -} - -func TestServiceGrantRoleAction(t *testing.T) { - roles := newFakeRoleStore() - service := newServiceWithRoles(t, newFakePrincipalCreator(), roles, newFakeIdentityLinker(), newFakeAPITokens()) - req := authkit.GrantRoleActionRequest{ - RoleID: "notes-reader", - Action: "notes:read", - } - - err := service.GrantRoleAction(context.Background(), req) - - require.NoError(t, err) - assert.Equal(t, []authkit.GrantRoleActionRequest{req}, roles.grantRequests) -} - -func TestServiceAssignPrincipalRole(t *testing.T) { - roles := newFakeRoleStore() - service := newServiceWithRoles(t, newFakePrincipalCreator(), roles, newFakeIdentityLinker(), newFakeAPITokens()) - req := authkit.AssignPrincipalRoleRequest{ - PrincipalID: testPrincipalID, - RoleID: "notes-reader", - } - - err := service.AssignPrincipalRole(context.Background(), req) - - require.NoError(t, err) - assert.Equal(t, []authkit.AssignPrincipalRoleRequest{req}, roles.assignRequests) -} - -func TestServiceUnassignPrincipalRole(t *testing.T) { - roles := newFakeRoleStore() - service := newServiceWithRoles(t, newFakePrincipalCreator(), roles, newFakeIdentityLinker(), newFakeAPITokens()) - req := authkit.UnassignPrincipalRoleRequest{ - PrincipalID: testPrincipalID, - RoleID: "notes-reader", - } - - err := service.UnassignPrincipalRole(context.Background(), req) - - require.NoError(t, err) - assert.Equal(t, []authkit.UnassignPrincipalRoleRequest{req}, roles.unassignRequests) -} - -func TestServiceListPrincipalRoleAssignments(t *testing.T) { - roles := newFakeRoleStore() - roles.assignments = []authkit.PrincipalRoleAssignment{ - {PrincipalID: testPrincipalID, RoleID: "notes-reader"}, - } - service := newServiceWithRoles(t, newFakePrincipalCreator(), roles, newFakeIdentityLinker(), newFakeAPITokens()) - - assignments, err := service.ListPrincipalRoleAssignments(context.Background(), testPrincipalID) - - require.NoError(t, err) - assert.Equal(t, roles.assignments, assignments) - assert.Equal(t, []string{testPrincipalID}, roles.listPrincipalIDs) -} - -func TestServiceWrapsRoleErrors(t *testing.T) { - roleErr := errors.New("role failed") - - tests := []struct { - name string - run func(*management.Service) error - }{ - { - name: "create role", - run: func(service *management.Service) error { - _, err := service.CreateRole(context.Background(), authkit.CreateRoleRequest{ID: "notes-reader"}) - - return err - }, - }, - { - name: "grant role action", - run: func(service *management.Service) error { - return service.GrantRoleAction(context.Background(), authkit.GrantRoleActionRequest{ - RoleID: "notes-reader", - Action: "notes:read", - }) - }, - }, - { - name: "assign principal role", - run: func(service *management.Service) error { - return service.AssignPrincipalRole(context.Background(), authkit.AssignPrincipalRoleRequest{ - PrincipalID: testPrincipalID, - RoleID: "notes-reader", - }) - }, - }, - { - name: "unassign principal role", - run: func(service *management.Service) error { - return service.UnassignPrincipalRole(context.Background(), authkit.UnassignPrincipalRoleRequest{ - PrincipalID: testPrincipalID, - RoleID: "notes-reader", - }) - }, - }, - { - name: "list principal role assignments", - run: func(service *management.Service) error { - _, err := service.ListPrincipalRoleAssignments(context.Background(), testPrincipalID) - - return err - }, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - roles := newFakeRoleStore() - roles.err = roleErr - service := newServiceWithRoles( - t, - newFakePrincipalCreator(), - roles, - newFakeIdentityLinker(), - newFakeAPITokens(), - ) - - require.ErrorIs(t, tt.run(service), roleErr) - }) - } -} - -func TestServiceRoleMethodsRequireRolePorts(t *testing.T) { - service := management.NewService(management.Options{ - PrincipalCreator: newFakePrincipalCreator(), - IdentityLinker: newFakeIdentityLinker(), - APITokens: newFakeAPITokens(), - }) - - tests := []struct { - name string - run func() error - }{ - { - name: "create role", - run: func() error { - _, runErr := service.CreateRole(context.Background(), authkit.CreateRoleRequest{ - ID: "notes-reader", - }) - - return runErr - }, - }, - { - name: "grant role action", - run: func() error { - return service.GrantRoleAction(context.Background(), authkit.GrantRoleActionRequest{ - RoleID: "notes-reader", - Action: "notes:read", - }) - }, - }, - { - name: "assign principal role", - run: func() error { - return service.AssignPrincipalRole(context.Background(), authkit.AssignPrincipalRoleRequest{ - PrincipalID: testPrincipalID, - RoleID: "notes-reader", - }) - }, - }, - { - name: "unassign principal role", - run: func() error { - return service.UnassignPrincipalRole(context.Background(), authkit.UnassignPrincipalRoleRequest{ - PrincipalID: testPrincipalID, - RoleID: "notes-reader", - }) - }, - }, - { - name: "list principal role assignments", - run: func() error { - _, runErr := service.ListPrincipalRoleAssignments(context.Background(), testPrincipalID) - - return runErr - }, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - require.Error(t, tt.run()) - }) - } -} - -func TestServiceProvisioningRuleMethods(t *testing.T) { - rules := newFakeProvisioningRuleStore() - rules.rule = provisioningRule() - service := newServiceWithProvisioningRules( - t, - newFakePrincipalCreator(), - newFakeRoleStore(), - rules, - newFakeIdentityLinker(), - newFakeAPITokens(), - ) - createReq := authkit.CreateProvisioningRuleRequest{ - ID: rules.rule.ID, - DisplayName: rules.rule.DisplayName, - Provider: rules.rule.Provider, - Condition: rules.rule.Condition, - AssignRoleIDs: rules.rule.AssignRoleIDs, - Enabled: rules.rule.Enabled, - } - updateReq := authkit.UpdateProvisioningRuleRequest{ - ID: rules.rule.ID, - DisplayName: "Updated", - Provider: rules.rule.Provider, - Condition: rules.rule.Condition, - AssignRoleIDs: rules.rule.AssignRoleIDs, - Enabled: false, - } - - created, err := service.CreateProvisioningRule(context.Background(), createReq) - require.NoError(t, err) - assert.Equal(t, rules.rule, created) - assert.Equal(t, []authkit.CreateProvisioningRuleRequest{createReq}, rules.createRequests) - - updated, err := service.UpdateProvisioningRule(context.Background(), updateReq) - require.NoError(t, err) - assert.Equal(t, rules.rule, updated) - assert.Equal(t, []authkit.UpdateProvisioningRuleRequest{updateReq}, rules.updateRequests) - - found, err := service.FindProvisioningRule(context.Background(), rules.rule.ID) - require.NoError(t, err) - assert.Equal(t, rules.rule, found) - assert.Equal(t, []string{rules.rule.ID}, rules.findIDs) - - listed, err := service.ListProvisioningRules(context.Background()) - require.NoError(t, err) - assert.Equal(t, []authkit.ProvisioningRule{rules.rule}, listed) - assert.Equal(t, 1, rules.listCalls) - - require.NoError(t, service.DeleteProvisioningRule(context.Background(), rules.rule.ID)) - assert.Equal(t, []string{rules.rule.ID}, rules.deleteIDs) -} - -func TestServiceWrapsProvisioningRuleErrors(t *testing.T) { - ruleErr := errors.New("rule failed") - - tests := []struct { - name string - run func(*management.Service) error - }{ - { - name: "create", - run: func(service *management.Service) error { - _, err := service.CreateProvisioningRule(context.Background(), authkit.CreateProvisioningRuleRequest{ - ID: "engineering-readers", - }) - - return err - }, - }, - { - name: "update", - run: func(service *management.Service) error { - _, err := service.UpdateProvisioningRule(context.Background(), authkit.UpdateProvisioningRuleRequest{ - ID: "engineering-readers", - }) - - return err - }, - }, - { - name: "delete", - run: func(service *management.Service) error { - return service.DeleteProvisioningRule(context.Background(), "engineering-readers") - }, - }, - { - name: "find", - run: func(service *management.Service) error { - _, err := service.FindProvisioningRule(context.Background(), "engineering-readers") - - return err - }, - }, - { - name: "list", - run: func(service *management.Service) error { - _, err := service.ListProvisioningRules(context.Background()) - - return err - }, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - rules := newFakeProvisioningRuleStore() - rules.err = ruleErr - service := newServiceWithProvisioningRules( - t, - newFakePrincipalCreator(), - newFakeRoleStore(), - rules, - newFakeIdentityLinker(), - newFakeAPITokens(), - ) - - require.ErrorIs(t, tt.run(service), ruleErr) - }) - } -} - -func TestServiceProvisioningRuleMethodsRequirePorts(t *testing.T) { - service := management.NewService(management.Options{ - PrincipalCreator: newFakePrincipalCreator(), - IdentityLinker: newFakeIdentityLinker(), - APITokens: newFakeAPITokens(), - }) - - tests := []struct { - name string - run func() error - }{ - { - name: "create", - run: func() error { - _, runErr := service.CreateProvisioningRule( - context.Background(), - authkit.CreateProvisioningRuleRequest{}, - ) - - return runErr - }, - }, - { - name: "update", - run: func() error { - _, runErr := service.UpdateProvisioningRule( - context.Background(), - authkit.UpdateProvisioningRuleRequest{}, - ) - - return runErr - }, - }, - { - name: "delete", - run: func() error { - return service.DeleteProvisioningRule(context.Background(), "engineering-readers") - }, - }, - { - name: "find", - run: func() error { - _, runErr := service.FindProvisioningRule(context.Background(), "engineering-readers") - - return runErr - }, - }, - { - name: "list", - run: func() error { - _, runErr := service.ListProvisioningRules(context.Background()) - - return runErr - }, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - require.Error(t, tt.run()) - }) - } -} - -func TestServiceLinkIdentity(t *testing.T) { - linker := newFakeIdentityLinker() - linker.identity = authkit.ExternalIdentity{ - Provider: testProvider, - Subject: testSubject, - PrincipalID: testPrincipalID, - } - service := newService(t, newFakePrincipalCreator(), linker, newFakeAPITokens()) - req := authkit.LinkIdentityRequest(linker.identity) - - identity, err := service.LinkIdentity(context.Background(), req) - - require.NoError(t, err) - assert.Equal(t, linker.identity, identity) - assert.Equal(t, []authkit.LinkIdentityRequest{req}, linker.requests) -} - -func TestServiceIssueAPITokenIssuesForExistingPrincipal(t *testing.T) { - now := fixedTime() - expiresAt := now.Add(time.Hour) - apiTokens := newFakeAPITokens() - apiTokens.issued = apikey.IssuedToken{ - ID: testTokenID, - Plaintext: testTokenSecret, - ExpiresAt: expiresAt, - } - principals := newFakePrincipalCreator() - principals.principal = authkit.Principal{ - ID: testPrincipalID, - Kind: authkit.PrincipalKindService, - DisplayName: testPrincipalName, - } - service := newService(t, principals, newFakeIdentityLinker(), apiTokens) - req := management.IssueAPITokenRequest{ - PrincipalID: testPrincipalID, - Name: testTokenName, - ExpiresAt: expiresAt, - } - - issued, err := service.IssueAPIToken(context.Background(), req) - - require.NoError(t, err) - assert.Equal(t, []string{testPrincipalID}, principals.findIDs) - assert.Equal(t, []apikey.IssueRequest{{ - PrincipalID: testPrincipalID, - Name: testTokenName, - ExpiresAt: expiresAt, - }}, apiTokens.issueRequests) - assert.Equal(t, management.IssuedAPIToken{ - ID: testTokenID, - PrincipalID: testPrincipalID, - Plaintext: testTokenSecret, - ExpiresAt: expiresAt, - }, issued) -} - -func TestServiceIssueAPITokenRejectsMissingPrincipal(t *testing.T) { - principals := newFakePrincipalCreator() - principals.err = authkit.ErrPrincipalNotFound - apiTokens := newFakeAPITokens() - service := newService(t, principals, newFakeIdentityLinker(), apiTokens) - - issued, err := service.IssueAPIToken(context.Background(), management.IssueAPITokenRequest{ - PrincipalID: testPrincipalID, - ExpiresAt: fixedTime().Add(time.Hour), - }) - - require.ErrorIs(t, err, authkit.ErrPrincipalNotFound) - assert.Empty(t, issued) - assert.Equal(t, []string{testPrincipalID}, principals.findIDs) - assert.Empty(t, apiTokens.issueRequests) -} - -func TestServiceIssueAPITokenReturnsIssueErrorWithoutLinkingIdentity(t *testing.T) { - issueErr := errors.New("issue failed") - apiTokens := newFakeAPITokens() - apiTokens.issueErr = issueErr - linker := newFakeIdentityLinker() - principals := newFakePrincipalCreator() - principals.principal = authkit.Principal{ID: testPrincipalID} - service := newService(t, principals, linker, apiTokens) - - issued, err := service.IssueAPIToken(context.Background(), management.IssueAPITokenRequest{ - PrincipalID: testPrincipalID, - ExpiresAt: fixedTime().Add(time.Hour), - }) - - require.ErrorIs(t, err, issueErr) - assert.Empty(t, issued) - assert.Empty(t, linker.requests) - assert.Empty(t, apiTokens.revokedIDs) -} - -func TestServiceRevokeAPIToken(t *testing.T) { - apiTokens := newFakeAPITokens() - service := newService(t, newFakePrincipalCreator(), newFakeIdentityLinker(), apiTokens) - - err := service.RevokeAPIToken(context.Background(), testTokenID) - - require.NoError(t, err) - assert.Equal(t, []string{testTokenID}, apiTokens.revokedIDs) -} - -func TestServiceRevokeAPITokenReturnsError(t *testing.T) { - revokeErr := errors.New("revoke failed") - apiTokens := newFakeAPITokens() - apiTokens.revokeErr = revokeErr - service := newService(t, newFakePrincipalCreator(), newFakeIdentityLinker(), apiTokens) - - err := service.RevokeAPIToken(context.Background(), testTokenID) - - require.ErrorIs(t, err, revokeErr) -} - -func TestServiceListPrincipalAPITokenMetadata(t *testing.T) { - apiTokens := newFakeAPITokens() - apiTokens.metadata = []apikey.TokenMetadata{{ - ID: testTokenID, - PrincipalID: testPrincipalID, - Name: testTokenName, - ExpiresAt: fixedTime().Add(time.Hour), - }} - service := management.NewService(management.Options{ - APITokenMetadataLister: apiTokens, - }) - - tokens, err := service.ListPrincipalAPITokenMetadata(context.Background(), testPrincipalID) - - require.NoError(t, err) - assert.Equal(t, apiTokens.metadata, tokens) - assert.Equal(t, []string{testPrincipalID}, apiTokens.metadataPrincipalIDs) -} - func TestServicePropagatesContextCancellation(t *testing.T) { now := fixedTime() store := memory.NewStore() @@ -970,389 +386,3 @@ func TestServiceIssueAPITokenResolvesThroughMemoryStore(t *testing.T) { ExpiresAt: issued.ExpiresAt, }, verified) } - -func newService( - t *testing.T, - principals principalStore, - linker authkit.IdentityLinker, - apiTokens management.APITokens, -) *management.Service { - t.Helper() - - return newServiceWithRoles(t, principals, newFakeRoleStore(), linker, apiTokens) -} - -func newServiceWithRoles( - t *testing.T, - principals principalStore, - roles roleStore, - linker authkit.IdentityLinker, - apiTokens management.APITokens, -) *management.Service { - t.Helper() - - service := management.NewService(management.Options{ - PrincipalCreator: principals, - PrincipalFinder: principals, - RoleCreator: roles, - RoleActionGranter: roles, - PrincipalRoleAssigner: roles, - PrincipalRoleUnassigner: roles, - PrincipalRoleAssignmentLister: roles, - IdentityLinker: linker, - APITokens: apiTokens, - }) - - return service -} - -func newServiceWithProvisioningRules( - t *testing.T, - principals principalStore, - roles roleStore, - rules provisioningRuleStore, - linker authkit.IdentityLinker, - apiTokens management.APITokens, -) *management.Service { - t.Helper() - - service := management.NewService(management.Options{ - PrincipalCreator: principals, - PrincipalFinder: principals, - RoleCreator: roles, - RoleActionGranter: roles, - PrincipalRoleAssigner: roles, - PrincipalRoleUnassigner: roles, - PrincipalRoleAssignmentLister: roles, - ProvisioningRuleCreator: rules, - ProvisioningRuleUpdater: rules, - ProvisioningRuleDeleter: rules, - ProvisioningRuleFinder: rules, - ProvisioningRuleLister: rules, - IdentityLinker: linker, - APITokens: apiTokens, - }) - - return service -} - -func newManagementService( - t *testing.T, - store *memory.Store, - tokenService *apikey.Service, -) *management.Service { - t.Helper() - - return management.NewService(management.Options{ - PrincipalCreator: store, - PrincipalFinder: store, - PrincipalLister: store, - RoleCreator: store, - RoleActionGranter: store, - PrincipalRoleAssigner: store, - PrincipalRoleUnassigner: store, - PrincipalRoleAssignmentLister: store, - ProvisioningRuleCreator: store, - ProvisioningRuleUpdater: store, - ProvisioningRuleDeleter: store, - ProvisioningRuleFinder: store, - ProvisioningRuleLister: store, - IdentityLinker: store, - APITokens: tokenService, - APITokenMetadataLister: store, - }) -} - -type roleStore interface { - authkit.RoleCreator - authkit.RoleActionGranter - authkit.PrincipalRoleAssigner - authkit.PrincipalRoleUnassigner - authkit.PrincipalRoleAssignmentLister -} - -type principalStore interface { - authkit.PrincipalCreator - authkit.PrincipalFinder -} - -type provisioningRuleStore interface { - authkit.ProvisioningRuleCreator - authkit.ProvisioningRuleUpdater - authkit.ProvisioningRuleDeleter - authkit.ProvisioningRuleFinder - authkit.ProvisioningRuleLister -} - -func newFakePrincipalCreator() *fakePrincipalCreator { - return &fakePrincipalCreator{} -} - -func newFakeIdentityLinker() *fakeIdentityLinker { - return &fakeIdentityLinker{} -} - -func newFakeRoleStore() *fakeRoleStore { - return &fakeRoleStore{} -} - -func newFakeProvisioningRuleStore() *fakeProvisioningRuleStore { - return &fakeProvisioningRuleStore{} -} - -func newFakeAPITokens() *fakeAPITokens { - return &fakeAPITokens{} -} - -func fixedTime() time.Time { - return time.Date(2026, time.May, 8, 12, 0, 0, 0, time.UTC) -} - -func provisioningRule() authkit.ProvisioningRule { - return authkit.ProvisioningRule{ - ID: "engineering-readers", - DisplayName: "Engineering readers", - Provider: "https://issuer.example", - Condition: `hasAny(claims.groups, ["/engineering"])`, - AssignRoleIDs: []string{"notes-reader"}, - Enabled: true, - } -} - -type fakePrincipalCreator struct { - requests []authkit.CreatePrincipalRequest - findIDs []string - listCalls int - principal authkit.Principal - principals []authkit.Principal - err error -} - -func (f *fakePrincipalCreator) CreatePrincipal( - _ context.Context, - req authkit.CreatePrincipalRequest, -) (authkit.Principal, error) { - f.requests = append(f.requests, req) - if f.err != nil { - return authkit.Principal{}, f.err - } - - return f.principal, nil -} - -func (f *fakePrincipalCreator) FindPrincipal(_ context.Context, id string) (authkit.Principal, error) { - f.findIDs = append(f.findIDs, id) - if f.err != nil { - return authkit.Principal{}, f.err - } - - return f.principal, nil -} - -func (f *fakePrincipalCreator) ListPrincipals(_ context.Context) ([]authkit.Principal, error) { - f.listCalls++ - if f.err != nil { - return nil, f.err - } - - return f.principals, nil -} - -type fakeIdentityLinker struct { - requests []authkit.LinkIdentityRequest - identity authkit.ExternalIdentity - err error -} - -func (f *fakeIdentityLinker) LinkIdentity( - _ context.Context, - req authkit.LinkIdentityRequest, -) (authkit.ExternalIdentity, error) { - f.requests = append(f.requests, req) - if f.err != nil { - return authkit.ExternalIdentity{}, f.err - } - - return f.identity, nil -} - -type fakeRoleStore struct { - createRequests []authkit.CreateRoleRequest - grantRequests []authkit.GrantRoleActionRequest - assignRequests []authkit.AssignPrincipalRoleRequest - unassignRequests []authkit.UnassignPrincipalRoleRequest - listPrincipalIDs []string - role authkit.Role - assignments []authkit.PrincipalRoleAssignment - err error -} - -func (f *fakeRoleStore) CreateRole( - _ context.Context, - req authkit.CreateRoleRequest, -) (authkit.Role, error) { - f.createRequests = append(f.createRequests, req) - if f.err != nil { - return authkit.Role{}, f.err - } - - return f.role, nil -} - -func (f *fakeRoleStore) GrantRoleAction( - _ context.Context, - req authkit.GrantRoleActionRequest, -) error { - f.grantRequests = append(f.grantRequests, req) - if f.err != nil { - return f.err - } - - return nil -} - -func (f *fakeRoleStore) AssignPrincipalRole( - _ context.Context, - req authkit.AssignPrincipalRoleRequest, -) error { - f.assignRequests = append(f.assignRequests, req) - if f.err != nil { - return f.err - } - - return nil -} - -func (f *fakeRoleStore) UnassignPrincipalRole( - _ context.Context, - req authkit.UnassignPrincipalRoleRequest, -) error { - f.unassignRequests = append(f.unassignRequests, req) - if f.err != nil { - return f.err - } - - return nil -} - -func (f *fakeRoleStore) ListPrincipalRoleAssignments( - _ context.Context, - principalID string, -) ([]authkit.PrincipalRoleAssignment, error) { - f.listPrincipalIDs = append(f.listPrincipalIDs, principalID) - if f.err != nil { - return nil, f.err - } - - return f.assignments, nil -} - -type fakeProvisioningRuleStore struct { - createRequests []authkit.CreateProvisioningRuleRequest - updateRequests []authkit.UpdateProvisioningRuleRequest - deleteIDs []string - findIDs []string - listCalls int - rule authkit.ProvisioningRule - err error -} - -func (f *fakeProvisioningRuleStore) CreateProvisioningRule( - _ context.Context, - req authkit.CreateProvisioningRuleRequest, -) (authkit.ProvisioningRule, error) { - f.createRequests = append(f.createRequests, req) - if f.err != nil { - return authkit.ProvisioningRule{}, f.err - } - - return f.rule, nil -} - -func (f *fakeProvisioningRuleStore) UpdateProvisioningRule( - _ context.Context, - req authkit.UpdateProvisioningRuleRequest, -) (authkit.ProvisioningRule, error) { - f.updateRequests = append(f.updateRequests, req) - if f.err != nil { - return authkit.ProvisioningRule{}, f.err - } - - return f.rule, nil -} - -func (f *fakeProvisioningRuleStore) DeleteProvisioningRule(_ context.Context, id string) error { - f.deleteIDs = append(f.deleteIDs, id) - if f.err != nil { - return f.err - } - - return nil -} - -func (f *fakeProvisioningRuleStore) FindProvisioningRule( - _ context.Context, - id string, -) (authkit.ProvisioningRule, error) { - f.findIDs = append(f.findIDs, id) - if f.err != nil { - return authkit.ProvisioningRule{}, f.err - } - - return f.rule, nil -} - -func (f *fakeProvisioningRuleStore) ListProvisioningRules( - context.Context, -) ([]authkit.ProvisioningRule, error) { - f.listCalls++ - if f.err != nil { - return nil, f.err - } - - return []authkit.ProvisioningRule{f.rule}, nil -} - -type fakeAPITokens struct { - issueRequests []apikey.IssueRequest - issued apikey.IssuedToken - issueErr error - revokedIDs []string - revokeErr error - metadataPrincipalIDs []string - metadata []apikey.TokenMetadata - metadataErr error -} - -func (f *fakeAPITokens) IssueToken( - _ context.Context, - req apikey.IssueRequest, -) (apikey.IssuedToken, error) { - f.issueRequests = append(f.issueRequests, req) - if f.issueErr != nil { - return apikey.IssuedToken{}, f.issueErr - } - - return f.issued, nil -} - -func (f *fakeAPITokens) RevokeToken(_ context.Context, tokenID string) error { - f.revokedIDs = append(f.revokedIDs, tokenID) - if f.revokeErr != nil { - return f.revokeErr - } - - return nil -} - -func (f *fakeAPITokens) ListPrincipalTokenMetadata( - _ context.Context, - principalID string, -) ([]apikey.TokenMetadata, error) { - f.metadataPrincipalIDs = append(f.metadataPrincipalIDs, principalID) - if f.metadataErr != nil { - return nil, f.metadataErr - } - - return f.metadata, nil -} From a842e9a7e5f3b06159a58a0241ab07044faaf86e Mon Sep 17 00:00:00 2001 From: Joshua Gilman Date: Wed, 27 May 2026 16:30:44 -0700 Subject: [PATCH 4/5] test(management): migrate root-port fakes to mockery MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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). --- .mockery.yaml | 91 ++++++ management/apitoken_test.go | 42 ++- management/helpers_test.go | 305 +----------------- management/identity_test.go | 15 +- management/principal_test.go | 30 +- management/provisioning_test.go | 140 ++++---- management/role_test.go | 115 ++++--- management/service_test.go | 17 +- mocks/authkit/identity_linker.go | 105 ++++++ mocks/authkit/principal_creator.go | 105 ++++++ mocks/authkit/principal_lister.go | 101 ++++++ mocks/authkit/principal_role_assigner.go | 96 ++++++ .../principal_role_assignment_lister.go | 107 ++++++ mocks/authkit/principal_role_unassigner.go | 96 ++++++ mocks/authkit/provisioning_rule_creator.go | 105 ++++++ mocks/authkit/provisioning_rule_deleter.go | 95 ++++++ mocks/authkit/provisioning_rule_finder.go | 105 ++++++ mocks/authkit/provisioning_rule_lister.go | 101 ++++++ mocks/authkit/provisioning_rule_updater.go | 105 ++++++ mocks/authkit/role_action_granter.go | 96 ++++++ mocks/authkit/role_creator.go | 105 ++++++ 21 files changed, 1623 insertions(+), 454 deletions(-) create mode 100644 mocks/authkit/identity_linker.go create mode 100644 mocks/authkit/principal_creator.go create mode 100644 mocks/authkit/principal_lister.go create mode 100644 mocks/authkit/principal_role_assigner.go create mode 100644 mocks/authkit/principal_role_assignment_lister.go create mode 100644 mocks/authkit/principal_role_unassigner.go create mode 100644 mocks/authkit/provisioning_rule_creator.go create mode 100644 mocks/authkit/provisioning_rule_deleter.go create mode 100644 mocks/authkit/provisioning_rule_finder.go create mode 100644 mocks/authkit/provisioning_rule_lister.go create mode 100644 mocks/authkit/provisioning_rule_updater.go create mode 100644 mocks/authkit/role_action_granter.go create mode 100644 mocks/authkit/role_creator.go diff --git a/.mockery.yaml b/.mockery.yaml index 1202bf2..5d589f2 100644 --- a/.mockery.yaml +++ b/.mockery.yaml @@ -36,3 +36,94 @@ packages: pkgname: authkitmocks dir: mocks/authkit filename: authorizer.go + PrincipalCreator: + config: + template: testify + structname: PrincipalCreator + pkgname: authkitmocks + dir: mocks/authkit + filename: principal_creator.go + PrincipalLister: + config: + template: testify + structname: PrincipalLister + pkgname: authkitmocks + dir: mocks/authkit + filename: principal_lister.go + RoleCreator: + config: + template: testify + structname: RoleCreator + pkgname: authkitmocks + dir: mocks/authkit + filename: role_creator.go + RoleActionGranter: + config: + template: testify + structname: RoleActionGranter + pkgname: authkitmocks + dir: mocks/authkit + filename: role_action_granter.go + PrincipalRoleAssigner: + config: + template: testify + structname: PrincipalRoleAssigner + pkgname: authkitmocks + dir: mocks/authkit + filename: principal_role_assigner.go + PrincipalRoleUnassigner: + config: + template: testify + structname: PrincipalRoleUnassigner + pkgname: authkitmocks + dir: mocks/authkit + filename: principal_role_unassigner.go + PrincipalRoleAssignmentLister: + config: + template: testify + structname: PrincipalRoleAssignmentLister + pkgname: authkitmocks + dir: mocks/authkit + filename: principal_role_assignment_lister.go + ProvisioningRuleCreator: + config: + template: testify + structname: ProvisioningRuleCreator + pkgname: authkitmocks + dir: mocks/authkit + filename: provisioning_rule_creator.go + ProvisioningRuleUpdater: + config: + template: testify + structname: ProvisioningRuleUpdater + pkgname: authkitmocks + dir: mocks/authkit + filename: provisioning_rule_updater.go + ProvisioningRuleDeleter: + config: + template: testify + structname: ProvisioningRuleDeleter + pkgname: authkitmocks + dir: mocks/authkit + filename: provisioning_rule_deleter.go + ProvisioningRuleFinder: + config: + template: testify + structname: ProvisioningRuleFinder + pkgname: authkitmocks + dir: mocks/authkit + filename: provisioning_rule_finder.go + ProvisioningRuleLister: + config: + template: testify + structname: ProvisioningRuleLister + pkgname: authkitmocks + dir: mocks/authkit + filename: provisioning_rule_lister.go + IdentityLinker: + config: + template: testify + structname: IdentityLinker + pkgname: authkitmocks + dir: mocks/authkit + filename: identity_linker.go diff --git a/management/apitoken_test.go b/management/apitoken_test.go index 07a4458..b6c1e0b 100644 --- a/management/apitoken_test.go +++ b/management/apitoken_test.go @@ -7,10 +7,12 @@ import ( "time" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/mock" "github.com/stretchr/testify/require" "github.com/meigma/authkit" "github.com/meigma/authkit/management" + authkitmocks "github.com/meigma/authkit/mocks/authkit" "github.com/meigma/authkit/proof/apikey" ) @@ -23,13 +25,17 @@ func TestServiceIssueAPITokenIssuesForExistingPrincipal(t *testing.T) { Plaintext: testTokenSecret, ExpiresAt: expiresAt, } - principals := newFakePrincipalCreator() - principals.principal = authkit.Principal{ + principal := authkit.Principal{ ID: testPrincipalID, Kind: authkit.PrincipalKindService, DisplayName: testPrincipalName, } - service := newService(t, principals, newFakeIdentityLinker(), apiTokens) + finder := authkitmocks.NewPrincipalFinder(t) + finder.EXPECT().FindPrincipal(mock.Anything, testPrincipalID).Return(principal, nil) + service := management.NewService(management.Options{ + PrincipalFinder: finder, + APITokens: apiTokens, + }) req := management.IssueAPITokenRequest{ PrincipalID: testPrincipalID, Name: testTokenName, @@ -39,7 +45,6 @@ func TestServiceIssueAPITokenIssuesForExistingPrincipal(t *testing.T) { issued, err := service.IssueAPIToken(context.Background(), req) require.NoError(t, err) - assert.Equal(t, []string{testPrincipalID}, principals.findIDs) assert.Equal(t, []apikey.IssueRequest{{ PrincipalID: testPrincipalID, Name: testTokenName, @@ -54,10 +59,15 @@ func TestServiceIssueAPITokenIssuesForExistingPrincipal(t *testing.T) { } func TestServiceIssueAPITokenRejectsMissingPrincipal(t *testing.T) { - principals := newFakePrincipalCreator() - principals.err = authkit.ErrPrincipalNotFound apiTokens := newFakeAPITokens() - service := newService(t, principals, newFakeIdentityLinker(), apiTokens) + finder := authkitmocks.NewPrincipalFinder(t) + finder.EXPECT(). + FindPrincipal(mock.Anything, testPrincipalID). + Return(authkit.Principal{}, authkit.ErrPrincipalNotFound) + service := management.NewService(management.Options{ + PrincipalFinder: finder, + APITokens: apiTokens, + }) issued, err := service.IssueAPIToken(context.Background(), management.IssueAPITokenRequest{ PrincipalID: testPrincipalID, @@ -66,7 +76,6 @@ func TestServiceIssueAPITokenRejectsMissingPrincipal(t *testing.T) { require.ErrorIs(t, err, authkit.ErrPrincipalNotFound) assert.Empty(t, issued) - assert.Equal(t, []string{testPrincipalID}, principals.findIDs) assert.Empty(t, apiTokens.issueRequests) } @@ -74,10 +83,14 @@ func TestServiceIssueAPITokenReturnsIssueErrorWithoutLinkingIdentity(t *testing. issueErr := errors.New("issue failed") apiTokens := newFakeAPITokens() apiTokens.issueErr = issueErr - linker := newFakeIdentityLinker() - principals := newFakePrincipalCreator() - principals.principal = authkit.Principal{ID: testPrincipalID} - service := newService(t, principals, linker, apiTokens) + finder := authkitmocks.NewPrincipalFinder(t) + finder.EXPECT(). + FindPrincipal(mock.Anything, testPrincipalID). + Return(authkit.Principal{ID: testPrincipalID}, nil) + service := management.NewService(management.Options{ + PrincipalFinder: finder, + APITokens: apiTokens, + }) issued, err := service.IssueAPIToken(context.Background(), management.IssueAPITokenRequest{ PrincipalID: testPrincipalID, @@ -86,13 +99,12 @@ func TestServiceIssueAPITokenReturnsIssueErrorWithoutLinkingIdentity(t *testing. require.ErrorIs(t, err, issueErr) assert.Empty(t, issued) - assert.Empty(t, linker.requests) assert.Empty(t, apiTokens.revokedIDs) } func TestServiceRevokeAPIToken(t *testing.T) { apiTokens := newFakeAPITokens() - service := newService(t, newFakePrincipalCreator(), newFakeIdentityLinker(), apiTokens) + service := management.NewService(management.Options{APITokens: apiTokens}) err := service.RevokeAPIToken(context.Background(), testTokenID) @@ -104,7 +116,7 @@ func TestServiceRevokeAPITokenReturnsError(t *testing.T) { revokeErr := errors.New("revoke failed") apiTokens := newFakeAPITokens() apiTokens.revokeErr = revokeErr - service := newService(t, newFakePrincipalCreator(), newFakeIdentityLinker(), apiTokens) + service := management.NewService(management.Options{APITokens: apiTokens}) err := service.RevokeAPIToken(context.Background(), testTokenID) diff --git a/management/helpers_test.go b/management/helpers_test.go index c52603d..6a4d35e 100644 --- a/management/helpers_test.go +++ b/management/helpers_test.go @@ -21,71 +21,12 @@ const ( testPrincipalName = "deploy service" ) -func newService( - t *testing.T, - principals principalStore, - linker authkit.IdentityLinker, - apiTokens management.APITokens, -) *management.Service { - t.Helper() - - return newServiceWithRoles(t, principals, newFakeRoleStore(), linker, apiTokens) -} - -func newServiceWithRoles( - t *testing.T, - principals principalStore, - roles roleStore, - linker authkit.IdentityLinker, - apiTokens management.APITokens, -) *management.Service { - t.Helper() - - service := management.NewService(management.Options{ - PrincipalCreator: principals, - PrincipalFinder: principals, - RoleCreator: roles, - RoleActionGranter: roles, - PrincipalRoleAssigner: roles, - PrincipalRoleUnassigner: roles, - PrincipalRoleAssignmentLister: roles, - IdentityLinker: linker, - APITokens: apiTokens, - }) - - return service -} - -func newServiceWithProvisioningRules( - t *testing.T, - principals principalStore, - roles roleStore, - rules provisioningRuleStore, - linker authkit.IdentityLinker, - apiTokens management.APITokens, -) *management.Service { - t.Helper() - - service := management.NewService(management.Options{ - PrincipalCreator: principals, - PrincipalFinder: principals, - RoleCreator: roles, - RoleActionGranter: roles, - PrincipalRoleAssigner: roles, - PrincipalRoleUnassigner: roles, - PrincipalRoleAssignmentLister: roles, - ProvisioningRuleCreator: rules, - ProvisioningRuleUpdater: rules, - ProvisioningRuleDeleter: rules, - ProvisioningRuleFinder: rules, - ProvisioningRuleLister: rules, - IdentityLinker: linker, - APITokens: apiTokens, - }) - - return service +func newFakeAPITokens() *fakeAPITokens { + return &fakeAPITokens{} } +// newManagementService composes a Service wired entirely against a real memory.Store and +// apikey.Service. Used only by the two integration anchors in service_test.go. func newManagementService( t *testing.T, store *memory.Store, @@ -113,47 +54,6 @@ func newManagementService( }) } -type roleStore interface { - authkit.RoleCreator - authkit.RoleActionGranter - authkit.PrincipalRoleAssigner - authkit.PrincipalRoleUnassigner - authkit.PrincipalRoleAssignmentLister -} - -type principalStore interface { - authkit.PrincipalCreator - authkit.PrincipalFinder -} - -type provisioningRuleStore interface { - authkit.ProvisioningRuleCreator - authkit.ProvisioningRuleUpdater - authkit.ProvisioningRuleDeleter - authkit.ProvisioningRuleFinder - authkit.ProvisioningRuleLister -} - -func newFakePrincipalCreator() *fakePrincipalCreator { - return &fakePrincipalCreator{} -} - -func newFakeIdentityLinker() *fakeIdentityLinker { - return &fakeIdentityLinker{} -} - -func newFakeRoleStore() *fakeRoleStore { - return &fakeRoleStore{} -} - -func newFakeProvisioningRuleStore() *fakeProvisioningRuleStore { - return &fakeProvisioningRuleStore{} -} - -func newFakeAPITokens() *fakeAPITokens { - return &fakeAPITokens{} -} - func fixedTime() time.Time { return time.Date(2026, time.May, 8, 12, 0, 0, 0, time.UTC) } @@ -169,200 +69,9 @@ func provisioningRule() authkit.ProvisioningRule { } } -type fakePrincipalCreator struct { - requests []authkit.CreatePrincipalRequest - findIDs []string - listCalls int - principal authkit.Principal - principals []authkit.Principal - err error -} - -func (f *fakePrincipalCreator) CreatePrincipal( - _ context.Context, - req authkit.CreatePrincipalRequest, -) (authkit.Principal, error) { - f.requests = append(f.requests, req) - if f.err != nil { - return authkit.Principal{}, f.err - } - - return f.principal, nil -} - -func (f *fakePrincipalCreator) FindPrincipal(_ context.Context, id string) (authkit.Principal, error) { - f.findIDs = append(f.findIDs, id) - if f.err != nil { - return authkit.Principal{}, f.err - } - - return f.principal, nil -} - -func (f *fakePrincipalCreator) ListPrincipals(_ context.Context) ([]authkit.Principal, error) { - f.listCalls++ - if f.err != nil { - return nil, f.err - } - - return f.principals, nil -} - -type fakeIdentityLinker struct { - requests []authkit.LinkIdentityRequest - identity authkit.ExternalIdentity - err error -} - -func (f *fakeIdentityLinker) LinkIdentity( - _ context.Context, - req authkit.LinkIdentityRequest, -) (authkit.ExternalIdentity, error) { - f.requests = append(f.requests, req) - if f.err != nil { - return authkit.ExternalIdentity{}, f.err - } - - return f.identity, nil -} - -type fakeRoleStore struct { - createRequests []authkit.CreateRoleRequest - grantRequests []authkit.GrantRoleActionRequest - assignRequests []authkit.AssignPrincipalRoleRequest - unassignRequests []authkit.UnassignPrincipalRoleRequest - listPrincipalIDs []string - role authkit.Role - assignments []authkit.PrincipalRoleAssignment - err error -} - -func (f *fakeRoleStore) CreateRole( - _ context.Context, - req authkit.CreateRoleRequest, -) (authkit.Role, error) { - f.createRequests = append(f.createRequests, req) - if f.err != nil { - return authkit.Role{}, f.err - } - - return f.role, nil -} - -func (f *fakeRoleStore) GrantRoleAction( - _ context.Context, - req authkit.GrantRoleActionRequest, -) error { - f.grantRequests = append(f.grantRequests, req) - if f.err != nil { - return f.err - } - - return nil -} - -func (f *fakeRoleStore) AssignPrincipalRole( - _ context.Context, - req authkit.AssignPrincipalRoleRequest, -) error { - f.assignRequests = append(f.assignRequests, req) - if f.err != nil { - return f.err - } - - return nil -} - -func (f *fakeRoleStore) UnassignPrincipalRole( - _ context.Context, - req authkit.UnassignPrincipalRoleRequest, -) error { - f.unassignRequests = append(f.unassignRequests, req) - if f.err != nil { - return f.err - } - - return nil -} - -func (f *fakeRoleStore) ListPrincipalRoleAssignments( - _ context.Context, - principalID string, -) ([]authkit.PrincipalRoleAssignment, error) { - f.listPrincipalIDs = append(f.listPrincipalIDs, principalID) - if f.err != nil { - return nil, f.err - } - - return f.assignments, nil -} - -type fakeProvisioningRuleStore struct { - createRequests []authkit.CreateProvisioningRuleRequest - updateRequests []authkit.UpdateProvisioningRuleRequest - deleteIDs []string - findIDs []string - listCalls int - rule authkit.ProvisioningRule - err error -} - -func (f *fakeProvisioningRuleStore) CreateProvisioningRule( - _ context.Context, - req authkit.CreateProvisioningRuleRequest, -) (authkit.ProvisioningRule, error) { - f.createRequests = append(f.createRequests, req) - if f.err != nil { - return authkit.ProvisioningRule{}, f.err - } - - return f.rule, nil -} - -func (f *fakeProvisioningRuleStore) UpdateProvisioningRule( - _ context.Context, - req authkit.UpdateProvisioningRuleRequest, -) (authkit.ProvisioningRule, error) { - f.updateRequests = append(f.updateRequests, req) - if f.err != nil { - return authkit.ProvisioningRule{}, f.err - } - - return f.rule, nil -} - -func (f *fakeProvisioningRuleStore) DeleteProvisioningRule(_ context.Context, id string) error { - f.deleteIDs = append(f.deleteIDs, id) - if f.err != nil { - return f.err - } - - return nil -} - -func (f *fakeProvisioningRuleStore) FindProvisioningRule( - _ context.Context, - id string, -) (authkit.ProvisioningRule, error) { - f.findIDs = append(f.findIDs, id) - if f.err != nil { - return authkit.ProvisioningRule{}, f.err - } - - return f.rule, nil -} - -func (f *fakeProvisioningRuleStore) ListProvisioningRules( - context.Context, -) ([]authkit.ProvisioningRule, error) { - f.listCalls++ - if f.err != nil { - return nil, f.err - } - - return []authkit.ProvisioningRule{f.rule}, nil -} - +// fakeAPITokens implements the package-local management.APITokens port plus the +// apikey.TokenMetadataLister sub-package port. Both stay hand-rolled per the project's +// mockery pattern (root-package ports only). type fakeAPITokens struct { issueRequests []apikey.IssueRequest issued apikey.IssuedToken diff --git a/management/identity_test.go b/management/identity_test.go index 1e804aa..92a529c 100644 --- a/management/identity_test.go +++ b/management/identity_test.go @@ -5,24 +5,27 @@ import ( "testing" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/mock" "github.com/stretchr/testify/require" "github.com/meigma/authkit" + "github.com/meigma/authkit/management" + authkitmocks "github.com/meigma/authkit/mocks/authkit" ) func TestServiceLinkIdentity(t *testing.T) { - linker := newFakeIdentityLinker() - linker.identity = authkit.ExternalIdentity{ + want := authkit.ExternalIdentity{ Provider: testProvider, Subject: testSubject, PrincipalID: testPrincipalID, } - service := newService(t, newFakePrincipalCreator(), linker, newFakeAPITokens()) - req := authkit.LinkIdentityRequest(linker.identity) + req := authkit.LinkIdentityRequest(want) + linker := authkitmocks.NewIdentityLinker(t) + linker.EXPECT().LinkIdentity(mock.Anything, req).Return(want, nil) + service := management.NewService(management.Options{IdentityLinker: linker}) identity, err := service.LinkIdentity(context.Background(), req) require.NoError(t, err) - assert.Equal(t, linker.identity, identity) - assert.Equal(t, []authkit.LinkIdentityRequest{req}, linker.requests) + assert.Equal(t, want, identity) } diff --git a/management/principal_test.go b/management/principal_test.go index f61543f..7081457 100644 --- a/management/principal_test.go +++ b/management/principal_test.go @@ -5,52 +5,54 @@ import ( "testing" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/mock" "github.com/stretchr/testify/require" "github.com/meigma/authkit" "github.com/meigma/authkit/management" + authkitmocks "github.com/meigma/authkit/mocks/authkit" ) func TestServiceCreatePrincipal(t *testing.T) { - creator := newFakePrincipalCreator() - creator.principal = authkit.Principal{ + want := authkit.Principal{ ID: testPrincipalID, Kind: authkit.PrincipalKindService, DisplayName: testPrincipalName, } - service := newService(t, creator, newFakeIdentityLinker(), newFakeAPITokens()) req := authkit.CreatePrincipalRequest{ Kind: authkit.PrincipalKindService, DisplayName: testPrincipalName, } + creator := authkitmocks.NewPrincipalCreator(t) + creator.EXPECT().CreatePrincipal(mock.Anything, req).Return(want, nil) + service := management.NewService(management.Options{PrincipalCreator: creator}) principal, err := service.CreatePrincipal(context.Background(), req) require.NoError(t, err) - assert.Equal(t, creator.principal, principal) - assert.Equal(t, []authkit.CreatePrincipalRequest{req}, creator.requests) + assert.Equal(t, want, principal) } func TestServiceFindAndListPrincipals(t *testing.T) { - principals := newFakePrincipalCreator() - principals.principal = authkit.Principal{ + want := authkit.Principal{ ID: testPrincipalID, Kind: authkit.PrincipalKindService, DisplayName: testPrincipalName, } - principals.principals = []authkit.Principal{principals.principal} + finder := authkitmocks.NewPrincipalFinder(t) + finder.EXPECT().FindPrincipal(mock.Anything, testPrincipalID).Return(want, nil) + lister := authkitmocks.NewPrincipalLister(t) + lister.EXPECT().ListPrincipals(mock.Anything).Return([]authkit.Principal{want}, nil) service := management.NewService(management.Options{ - PrincipalFinder: principals, - PrincipalLister: principals, + PrincipalFinder: finder, + PrincipalLister: lister, }) found, err := service.FindPrincipal(context.Background(), testPrincipalID) require.NoError(t, err) - assert.Equal(t, principals.principal, found) - assert.Equal(t, []string{testPrincipalID}, principals.findIDs) + assert.Equal(t, want, found) listed, err := service.ListPrincipals(context.Background()) require.NoError(t, err) - assert.Equal(t, principals.principals, listed) - assert.Equal(t, 1, principals.listCalls) + assert.Equal(t, []authkit.Principal{want}, listed) } diff --git a/management/provisioning_test.go b/management/provisioning_test.go index dec890b..c70962f 100644 --- a/management/provisioning_test.go +++ b/management/provisioning_test.go @@ -6,62 +6,68 @@ import ( "testing" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/mock" "github.com/stretchr/testify/require" "github.com/meigma/authkit" "github.com/meigma/authkit/management" + authkitmocks "github.com/meigma/authkit/mocks/authkit" ) func TestServiceProvisioningRuleMethods(t *testing.T) { - rules := newFakeProvisioningRuleStore() - rules.rule = provisioningRule() - service := newServiceWithProvisioningRules( - t, - newFakePrincipalCreator(), - newFakeRoleStore(), - rules, - newFakeIdentityLinker(), - newFakeAPITokens(), - ) + rule := provisioningRule() createReq := authkit.CreateProvisioningRuleRequest{ - ID: rules.rule.ID, - DisplayName: rules.rule.DisplayName, - Provider: rules.rule.Provider, - Condition: rules.rule.Condition, - AssignRoleIDs: rules.rule.AssignRoleIDs, - Enabled: rules.rule.Enabled, + ID: rule.ID, + DisplayName: rule.DisplayName, + Provider: rule.Provider, + Condition: rule.Condition, + AssignRoleIDs: rule.AssignRoleIDs, + Enabled: rule.Enabled, } updateReq := authkit.UpdateProvisioningRuleRequest{ - ID: rules.rule.ID, + ID: rule.ID, DisplayName: "Updated", - Provider: rules.rule.Provider, - Condition: rules.rule.Condition, - AssignRoleIDs: rules.rule.AssignRoleIDs, + Provider: rule.Provider, + Condition: rule.Condition, + AssignRoleIDs: rule.AssignRoleIDs, Enabled: false, } + creator := authkitmocks.NewProvisioningRuleCreator(t) + creator.EXPECT().CreateProvisioningRule(mock.Anything, createReq).Return(rule, nil) + updater := authkitmocks.NewProvisioningRuleUpdater(t) + updater.EXPECT().UpdateProvisioningRule(mock.Anything, updateReq).Return(rule, nil) + deleter := authkitmocks.NewProvisioningRuleDeleter(t) + deleter.EXPECT().DeleteProvisioningRule(mock.Anything, rule.ID).Return(nil) + finder := authkitmocks.NewProvisioningRuleFinder(t) + finder.EXPECT().FindProvisioningRule(mock.Anything, rule.ID).Return(rule, nil) + lister := authkitmocks.NewProvisioningRuleLister(t) + lister.EXPECT().ListProvisioningRules(mock.Anything).Return([]authkit.ProvisioningRule{rule}, nil) + service := management.NewService(management.Options{ + ProvisioningRuleCreator: creator, + ProvisioningRuleUpdater: updater, + ProvisioningRuleDeleter: deleter, + ProvisioningRuleFinder: finder, + ProvisioningRuleLister: lister, + }) + created, err := service.CreateProvisioningRule(context.Background(), createReq) require.NoError(t, err) - assert.Equal(t, rules.rule, created) - assert.Equal(t, []authkit.CreateProvisioningRuleRequest{createReq}, rules.createRequests) + assert.Equal(t, rule, created) updated, err := service.UpdateProvisioningRule(context.Background(), updateReq) require.NoError(t, err) - assert.Equal(t, rules.rule, updated) - assert.Equal(t, []authkit.UpdateProvisioningRuleRequest{updateReq}, rules.updateRequests) + assert.Equal(t, rule, updated) - found, err := service.FindProvisioningRule(context.Background(), rules.rule.ID) + found, err := service.FindProvisioningRule(context.Background(), rule.ID) require.NoError(t, err) - assert.Equal(t, rules.rule, found) - assert.Equal(t, []string{rules.rule.ID}, rules.findIDs) + assert.Equal(t, rule, found) listed, err := service.ListProvisioningRules(context.Background()) require.NoError(t, err) - assert.Equal(t, []authkit.ProvisioningRule{rules.rule}, listed) - assert.Equal(t, 1, rules.listCalls) + assert.Equal(t, []authkit.ProvisioningRule{rule}, listed) - require.NoError(t, service.DeleteProvisioningRule(context.Background(), rules.rule.ID)) - assert.Equal(t, []string{rules.rule.ID}, rules.deleteIDs) + require.NoError(t, service.DeleteProvisioningRule(context.Background(), rule.ID)) } func TestServiceWrapsProvisioningRuleErrors(t *testing.T) { @@ -69,37 +75,62 @@ func TestServiceWrapsProvisioningRuleErrors(t *testing.T) { tests := []struct { name string - run func(*management.Service) error + run func(t *testing.T) error }{ { name: "create", - run: func(service *management.Service) error { - _, err := service.CreateProvisioningRule(context.Background(), authkit.CreateProvisioningRuleRequest{ - ID: "engineering-readers", - }) + run: func(t *testing.T) error { + t.Helper() + creator := authkitmocks.NewProvisioningRuleCreator(t) + creator.EXPECT(). + CreateProvisioningRule(mock.Anything, mock.Anything). + Return(authkit.ProvisioningRule{}, ruleErr) + service := management.NewService(management.Options{ProvisioningRuleCreator: creator}) + _, err := service.CreateProvisioningRule( + context.Background(), + authkit.CreateProvisioningRuleRequest{ID: "engineering-readers"}, + ) return err }, }, { name: "update", - run: func(service *management.Service) error { - _, err := service.UpdateProvisioningRule(context.Background(), authkit.UpdateProvisioningRuleRequest{ - ID: "engineering-readers", - }) + run: func(t *testing.T) error { + t.Helper() + updater := authkitmocks.NewProvisioningRuleUpdater(t) + updater.EXPECT(). + UpdateProvisioningRule(mock.Anything, mock.Anything). + Return(authkit.ProvisioningRule{}, ruleErr) + service := management.NewService(management.Options{ProvisioningRuleUpdater: updater}) + _, err := service.UpdateProvisioningRule( + context.Background(), + authkit.UpdateProvisioningRuleRequest{ID: "engineering-readers"}, + ) return err }, }, { name: "delete", - run: func(service *management.Service) error { + run: func(t *testing.T) error { + t.Helper() + deleter := authkitmocks.NewProvisioningRuleDeleter(t) + deleter.EXPECT().DeleteProvisioningRule(mock.Anything, mock.Anything).Return(ruleErr) + service := management.NewService(management.Options{ProvisioningRuleDeleter: deleter}) + return service.DeleteProvisioningRule(context.Background(), "engineering-readers") }, }, { name: "find", - run: func(service *management.Service) error { + run: func(t *testing.T) error { + t.Helper() + finder := authkitmocks.NewProvisioningRuleFinder(t) + finder.EXPECT(). + FindProvisioningRule(mock.Anything, mock.Anything). + Return(authkit.ProvisioningRule{}, ruleErr) + service := management.NewService(management.Options{ProvisioningRuleFinder: finder}) _, err := service.FindProvisioningRule(context.Background(), "engineering-readers") return err @@ -107,7 +138,11 @@ func TestServiceWrapsProvisioningRuleErrors(t *testing.T) { }, { name: "list", - run: func(service *management.Service) error { + run: func(t *testing.T) error { + t.Helper() + lister := authkitmocks.NewProvisioningRuleLister(t) + lister.EXPECT().ListProvisioningRules(mock.Anything).Return(nil, ruleErr) + service := management.NewService(management.Options{ProvisioningRuleLister: lister}) _, err := service.ListProvisioningRules(context.Background()) return err @@ -117,28 +152,13 @@ func TestServiceWrapsProvisioningRuleErrors(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - rules := newFakeProvisioningRuleStore() - rules.err = ruleErr - service := newServiceWithProvisioningRules( - t, - newFakePrincipalCreator(), - newFakeRoleStore(), - rules, - newFakeIdentityLinker(), - newFakeAPITokens(), - ) - - require.ErrorIs(t, tt.run(service), ruleErr) + require.ErrorIs(t, tt.run(t), ruleErr) }) } } func TestServiceProvisioningRuleMethodsRequirePorts(t *testing.T) { - service := management.NewService(management.Options{ - PrincipalCreator: newFakePrincipalCreator(), - IdentityLinker: newFakeIdentityLinker(), - APITokens: newFakeAPITokens(), - }) + service := management.NewService(management.Options{}) tests := []struct { name string diff --git a/management/role_test.go b/management/role_test.go index 52643e5..f0a2770 100644 --- a/management/role_test.go +++ b/management/role_test.go @@ -6,87 +6,83 @@ import ( "testing" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/mock" "github.com/stretchr/testify/require" "github.com/meigma/authkit" "github.com/meigma/authkit/management" + authkitmocks "github.com/meigma/authkit/mocks/authkit" ) func TestServiceCreateRole(t *testing.T) { - roles := newFakeRoleStore() - roles.role = authkit.Role{ + want := authkit.Role{ ID: "notes-reader", DisplayName: "Notes reader", Description: "Can read notes.", } - service := newServiceWithRoles(t, newFakePrincipalCreator(), roles, newFakeIdentityLinker(), newFakeAPITokens()) req := authkit.CreateRoleRequest{ - ID: roles.role.ID, - DisplayName: roles.role.DisplayName, - Description: roles.role.Description, + ID: want.ID, + DisplayName: want.DisplayName, + Description: want.Description, } + creator := authkitmocks.NewRoleCreator(t) + creator.EXPECT().CreateRole(mock.Anything, req).Return(want, nil) + service := management.NewService(management.Options{RoleCreator: creator}) role, err := service.CreateRole(context.Background(), req) require.NoError(t, err) - assert.Equal(t, roles.role, role) - assert.Equal(t, []authkit.CreateRoleRequest{req}, roles.createRequests) + assert.Equal(t, want, role) } func TestServiceGrantRoleAction(t *testing.T) { - roles := newFakeRoleStore() - service := newServiceWithRoles(t, newFakePrincipalCreator(), roles, newFakeIdentityLinker(), newFakeAPITokens()) req := authkit.GrantRoleActionRequest{ RoleID: "notes-reader", Action: "notes:read", } + granter := authkitmocks.NewRoleActionGranter(t) + granter.EXPECT().GrantRoleAction(mock.Anything, req).Return(nil) + service := management.NewService(management.Options{RoleActionGranter: granter}) - err := service.GrantRoleAction(context.Background(), req) - - require.NoError(t, err) - assert.Equal(t, []authkit.GrantRoleActionRequest{req}, roles.grantRequests) + require.NoError(t, service.GrantRoleAction(context.Background(), req)) } func TestServiceAssignPrincipalRole(t *testing.T) { - roles := newFakeRoleStore() - service := newServiceWithRoles(t, newFakePrincipalCreator(), roles, newFakeIdentityLinker(), newFakeAPITokens()) req := authkit.AssignPrincipalRoleRequest{ PrincipalID: testPrincipalID, RoleID: "notes-reader", } + assigner := authkitmocks.NewPrincipalRoleAssigner(t) + assigner.EXPECT().AssignPrincipalRole(mock.Anything, req).Return(nil) + service := management.NewService(management.Options{PrincipalRoleAssigner: assigner}) - err := service.AssignPrincipalRole(context.Background(), req) - - require.NoError(t, err) - assert.Equal(t, []authkit.AssignPrincipalRoleRequest{req}, roles.assignRequests) + require.NoError(t, service.AssignPrincipalRole(context.Background(), req)) } func TestServiceUnassignPrincipalRole(t *testing.T) { - roles := newFakeRoleStore() - service := newServiceWithRoles(t, newFakePrincipalCreator(), roles, newFakeIdentityLinker(), newFakeAPITokens()) req := authkit.UnassignPrincipalRoleRequest{ PrincipalID: testPrincipalID, RoleID: "notes-reader", } + unassigner := authkitmocks.NewPrincipalRoleUnassigner(t) + unassigner.EXPECT().UnassignPrincipalRole(mock.Anything, req).Return(nil) + service := management.NewService(management.Options{PrincipalRoleUnassigner: unassigner}) - err := service.UnassignPrincipalRole(context.Background(), req) - - require.NoError(t, err) - assert.Equal(t, []authkit.UnassignPrincipalRoleRequest{req}, roles.unassignRequests) + require.NoError(t, service.UnassignPrincipalRole(context.Background(), req)) } func TestServiceListPrincipalRoleAssignments(t *testing.T) { - roles := newFakeRoleStore() - roles.assignments = []authkit.PrincipalRoleAssignment{ + want := []authkit.PrincipalRoleAssignment{ {PrincipalID: testPrincipalID, RoleID: "notes-reader"}, } - service := newServiceWithRoles(t, newFakePrincipalCreator(), roles, newFakeIdentityLinker(), newFakeAPITokens()) + lister := authkitmocks.NewPrincipalRoleAssignmentLister(t) + lister.EXPECT().ListPrincipalRoleAssignments(mock.Anything, testPrincipalID).Return(want, nil) + service := management.NewService(management.Options{PrincipalRoleAssignmentLister: lister}) assignments, err := service.ListPrincipalRoleAssignments(context.Background(), testPrincipalID) require.NoError(t, err) - assert.Equal(t, roles.assignments, assignments) - assert.Equal(t, []string{testPrincipalID}, roles.listPrincipalIDs) + assert.Equal(t, want, assignments) } func TestServiceWrapsRoleErrors(t *testing.T) { @@ -94,11 +90,17 @@ func TestServiceWrapsRoleErrors(t *testing.T) { tests := []struct { name string - run func(*management.Service) error + run func(t *testing.T) error }{ { name: "create role", - run: func(service *management.Service) error { + run: func(t *testing.T) error { + t.Helper() + creator := authkitmocks.NewRoleCreator(t) + creator.EXPECT(). + CreateRole(mock.Anything, mock.Anything). + Return(authkit.Role{}, roleErr) + service := management.NewService(management.Options{RoleCreator: creator}) _, err := service.CreateRole(context.Background(), authkit.CreateRoleRequest{ID: "notes-reader"}) return err @@ -106,7 +108,12 @@ func TestServiceWrapsRoleErrors(t *testing.T) { }, { name: "grant role action", - run: func(service *management.Service) error { + run: func(t *testing.T) error { + t.Helper() + granter := authkitmocks.NewRoleActionGranter(t) + granter.EXPECT().GrantRoleAction(mock.Anything, mock.Anything).Return(roleErr) + service := management.NewService(management.Options{RoleActionGranter: granter}) + return service.GrantRoleAction(context.Background(), authkit.GrantRoleActionRequest{ RoleID: "notes-reader", Action: "notes:read", @@ -115,7 +122,12 @@ func TestServiceWrapsRoleErrors(t *testing.T) { }, { name: "assign principal role", - run: func(service *management.Service) error { + run: func(t *testing.T) error { + t.Helper() + assigner := authkitmocks.NewPrincipalRoleAssigner(t) + assigner.EXPECT().AssignPrincipalRole(mock.Anything, mock.Anything).Return(roleErr) + service := management.NewService(management.Options{PrincipalRoleAssigner: assigner}) + return service.AssignPrincipalRole(context.Background(), authkit.AssignPrincipalRoleRequest{ PrincipalID: testPrincipalID, RoleID: "notes-reader", @@ -124,7 +136,12 @@ func TestServiceWrapsRoleErrors(t *testing.T) { }, { name: "unassign principal role", - run: func(service *management.Service) error { + run: func(t *testing.T) error { + t.Helper() + unassigner := authkitmocks.NewPrincipalRoleUnassigner(t) + unassigner.EXPECT().UnassignPrincipalRole(mock.Anything, mock.Anything).Return(roleErr) + service := management.NewService(management.Options{PrincipalRoleUnassigner: unassigner}) + return service.UnassignPrincipalRole(context.Background(), authkit.UnassignPrincipalRoleRequest{ PrincipalID: testPrincipalID, RoleID: "notes-reader", @@ -133,7 +150,13 @@ func TestServiceWrapsRoleErrors(t *testing.T) { }, { name: "list principal role assignments", - run: func(service *management.Service) error { + run: func(t *testing.T) error { + t.Helper() + lister := authkitmocks.NewPrincipalRoleAssignmentLister(t) + lister.EXPECT(). + ListPrincipalRoleAssignments(mock.Anything, mock.Anything). + Return(nil, roleErr) + service := management.NewService(management.Options{PrincipalRoleAssignmentLister: lister}) _, err := service.ListPrincipalRoleAssignments(context.Background(), testPrincipalID) return err @@ -143,27 +166,13 @@ func TestServiceWrapsRoleErrors(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - roles := newFakeRoleStore() - roles.err = roleErr - service := newServiceWithRoles( - t, - newFakePrincipalCreator(), - roles, - newFakeIdentityLinker(), - newFakeAPITokens(), - ) - - require.ErrorIs(t, tt.run(service), roleErr) + require.ErrorIs(t, tt.run(t), roleErr) }) } } func TestServiceRoleMethodsRequireRolePorts(t *testing.T) { - service := management.NewService(management.Options{ - PrincipalCreator: newFakePrincipalCreator(), - IdentityLinker: newFakeIdentityLinker(), - APITokens: newFakeAPITokens(), - }) + service := management.NewService(management.Options{}) tests := []struct { name string diff --git a/management/service_test.go b/management/service_test.go index f9f1ced..4690743 100644 --- a/management/service_test.go +++ b/management/service_test.go @@ -10,6 +10,7 @@ import ( "github.com/meigma/authkit" "github.com/meigma/authkit/management" + authkitmocks "github.com/meigma/authkit/mocks/authkit" "github.com/meigma/authkit/proof/apikey" "github.com/meigma/authkit/store/memory" ) @@ -25,22 +26,22 @@ func TestNewServiceAllowsSparseOptions(t *testing.T) { { name: "missing principal creator", opts: management.Options{ - IdentityLinker: newFakeIdentityLinker(), + IdentityLinker: authkitmocks.NewIdentityLinker(t), APITokens: newFakeAPITokens(), }, }, { name: "missing identity linker", opts: management.Options{ - PrincipalCreator: newFakePrincipalCreator(), + PrincipalCreator: authkitmocks.NewPrincipalCreator(t), APITokens: newFakeAPITokens(), }, }, { name: "missing API tokens", opts: management.Options{ - PrincipalCreator: newFakePrincipalCreator(), - IdentityLinker: newFakeIdentityLinker(), + PrincipalCreator: authkitmocks.NewPrincipalCreator(t), + IdentityLinker: authkitmocks.NewIdentityLinker(t), }, }, } @@ -162,8 +163,8 @@ func TestServiceIssueAPITokenRequiresPrincipalFinderBeforeIssuing(t *testing.T) func TestNewServiceDoesNotRequireRolePorts(t *testing.T) { service := management.NewService(management.Options{ - PrincipalCreator: newFakePrincipalCreator(), - IdentityLinker: newFakeIdentityLinker(), + PrincipalCreator: authkitmocks.NewPrincipalCreator(t), + IdentityLinker: authkitmocks.NewIdentityLinker(t), APITokens: newFakeAPITokens(), }) assert.NotNil(t, service) @@ -171,8 +172,8 @@ func TestNewServiceDoesNotRequireRolePorts(t *testing.T) { func TestNewServiceDoesNotRequireProvisioningRulePorts(t *testing.T) { service := management.NewService(management.Options{ - PrincipalCreator: newFakePrincipalCreator(), - IdentityLinker: newFakeIdentityLinker(), + PrincipalCreator: authkitmocks.NewPrincipalCreator(t), + IdentityLinker: authkitmocks.NewIdentityLinker(t), APITokens: newFakeAPITokens(), }) assert.NotNil(t, service) diff --git a/mocks/authkit/identity_linker.go b/mocks/authkit/identity_linker.go new file mode 100644 index 0000000..a007a10 --- /dev/null +++ b/mocks/authkit/identity_linker.go @@ -0,0 +1,105 @@ +// Code generated by mockery; DO NOT EDIT. +// github.com/vektra/mockery +// template: testify + +package authkitmocks + +import ( + "context" + + "github.com/meigma/authkit" + mock "github.com/stretchr/testify/mock" +) + +// NewIdentityLinker creates a new instance of IdentityLinker. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +// The first argument is typically a *testing.T value. +func NewIdentityLinker(t interface { + mock.TestingT + Cleanup(func()) +}) *IdentityLinker { + mock := &IdentityLinker{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +} + +// IdentityLinker is an autogenerated mock type for the IdentityLinker type +type IdentityLinker struct { + mock.Mock +} + +type IdentityLinker_Expecter struct { + mock *mock.Mock +} + +func (_m *IdentityLinker) EXPECT() *IdentityLinker_Expecter { + return &IdentityLinker_Expecter{mock: &_m.Mock} +} + +// LinkIdentity provides a mock function for the type IdentityLinker +func (_mock *IdentityLinker) LinkIdentity(ctx context.Context, req authkit.LinkIdentityRequest) (authkit.ExternalIdentity, error) { + ret := _mock.Called(ctx, req) + + if len(ret) == 0 { + panic("no return value specified for LinkIdentity") + } + + var r0 authkit.ExternalIdentity + var r1 error + if returnFunc, ok := ret.Get(0).(func(context.Context, authkit.LinkIdentityRequest) (authkit.ExternalIdentity, error)); ok { + return returnFunc(ctx, req) + } + if returnFunc, ok := ret.Get(0).(func(context.Context, authkit.LinkIdentityRequest) authkit.ExternalIdentity); ok { + r0 = returnFunc(ctx, req) + } else { + r0 = ret.Get(0).(authkit.ExternalIdentity) + } + if returnFunc, ok := ret.Get(1).(func(context.Context, authkit.LinkIdentityRequest) error); ok { + r1 = returnFunc(ctx, req) + } else { + r1 = ret.Error(1) + } + return r0, r1 +} + +// IdentityLinker_LinkIdentity_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'LinkIdentity' +type IdentityLinker_LinkIdentity_Call struct { + *mock.Call +} + +// LinkIdentity is a helper method to define mock.On call +// - ctx context.Context +// - req authkit.LinkIdentityRequest +func (_e *IdentityLinker_Expecter) LinkIdentity(ctx interface{}, req interface{}) *IdentityLinker_LinkIdentity_Call { + return &IdentityLinker_LinkIdentity_Call{Call: _e.mock.On("LinkIdentity", ctx, req)} +} + +func (_c *IdentityLinker_LinkIdentity_Call) Run(run func(ctx context.Context, req authkit.LinkIdentityRequest)) *IdentityLinker_LinkIdentity_Call { + _c.Call.Run(func(args mock.Arguments) { + var arg0 context.Context + if args[0] != nil { + arg0 = args[0].(context.Context) + } + var arg1 authkit.LinkIdentityRequest + if args[1] != nil { + arg1 = args[1].(authkit.LinkIdentityRequest) + } + run( + arg0, + arg1, + ) + }) + return _c +} + +func (_c *IdentityLinker_LinkIdentity_Call) Return(externalIdentity authkit.ExternalIdentity, err error) *IdentityLinker_LinkIdentity_Call { + _c.Call.Return(externalIdentity, err) + return _c +} + +func (_c *IdentityLinker_LinkIdentity_Call) RunAndReturn(run func(ctx context.Context, req authkit.LinkIdentityRequest) (authkit.ExternalIdentity, error)) *IdentityLinker_LinkIdentity_Call { + _c.Call.Return(run) + return _c +} diff --git a/mocks/authkit/principal_creator.go b/mocks/authkit/principal_creator.go new file mode 100644 index 0000000..5762da4 --- /dev/null +++ b/mocks/authkit/principal_creator.go @@ -0,0 +1,105 @@ +// Code generated by mockery; DO NOT EDIT. +// github.com/vektra/mockery +// template: testify + +package authkitmocks + +import ( + "context" + + "github.com/meigma/authkit" + mock "github.com/stretchr/testify/mock" +) + +// NewPrincipalCreator creates a new instance of PrincipalCreator. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +// The first argument is typically a *testing.T value. +func NewPrincipalCreator(t interface { + mock.TestingT + Cleanup(func()) +}) *PrincipalCreator { + mock := &PrincipalCreator{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +} + +// PrincipalCreator is an autogenerated mock type for the PrincipalCreator type +type PrincipalCreator struct { + mock.Mock +} + +type PrincipalCreator_Expecter struct { + mock *mock.Mock +} + +func (_m *PrincipalCreator) EXPECT() *PrincipalCreator_Expecter { + return &PrincipalCreator_Expecter{mock: &_m.Mock} +} + +// CreatePrincipal provides a mock function for the type PrincipalCreator +func (_mock *PrincipalCreator) CreatePrincipal(ctx context.Context, req authkit.CreatePrincipalRequest) (authkit.Principal, error) { + ret := _mock.Called(ctx, req) + + if len(ret) == 0 { + panic("no return value specified for CreatePrincipal") + } + + var r0 authkit.Principal + var r1 error + if returnFunc, ok := ret.Get(0).(func(context.Context, authkit.CreatePrincipalRequest) (authkit.Principal, error)); ok { + return returnFunc(ctx, req) + } + if returnFunc, ok := ret.Get(0).(func(context.Context, authkit.CreatePrincipalRequest) authkit.Principal); ok { + r0 = returnFunc(ctx, req) + } else { + r0 = ret.Get(0).(authkit.Principal) + } + if returnFunc, ok := ret.Get(1).(func(context.Context, authkit.CreatePrincipalRequest) error); ok { + r1 = returnFunc(ctx, req) + } else { + r1 = ret.Error(1) + } + return r0, r1 +} + +// PrincipalCreator_CreatePrincipal_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'CreatePrincipal' +type PrincipalCreator_CreatePrincipal_Call struct { + *mock.Call +} + +// CreatePrincipal is a helper method to define mock.On call +// - ctx context.Context +// - req authkit.CreatePrincipalRequest +func (_e *PrincipalCreator_Expecter) CreatePrincipal(ctx interface{}, req interface{}) *PrincipalCreator_CreatePrincipal_Call { + return &PrincipalCreator_CreatePrincipal_Call{Call: _e.mock.On("CreatePrincipal", ctx, req)} +} + +func (_c *PrincipalCreator_CreatePrincipal_Call) Run(run func(ctx context.Context, req authkit.CreatePrincipalRequest)) *PrincipalCreator_CreatePrincipal_Call { + _c.Call.Run(func(args mock.Arguments) { + var arg0 context.Context + if args[0] != nil { + arg0 = args[0].(context.Context) + } + var arg1 authkit.CreatePrincipalRequest + if args[1] != nil { + arg1 = args[1].(authkit.CreatePrincipalRequest) + } + run( + arg0, + arg1, + ) + }) + return _c +} + +func (_c *PrincipalCreator_CreatePrincipal_Call) Return(principal authkit.Principal, err error) *PrincipalCreator_CreatePrincipal_Call { + _c.Call.Return(principal, err) + return _c +} + +func (_c *PrincipalCreator_CreatePrincipal_Call) RunAndReturn(run func(ctx context.Context, req authkit.CreatePrincipalRequest) (authkit.Principal, error)) *PrincipalCreator_CreatePrincipal_Call { + _c.Call.Return(run) + return _c +} diff --git a/mocks/authkit/principal_lister.go b/mocks/authkit/principal_lister.go new file mode 100644 index 0000000..06164ad --- /dev/null +++ b/mocks/authkit/principal_lister.go @@ -0,0 +1,101 @@ +// Code generated by mockery; DO NOT EDIT. +// github.com/vektra/mockery +// template: testify + +package authkitmocks + +import ( + "context" + + "github.com/meigma/authkit" + mock "github.com/stretchr/testify/mock" +) + +// NewPrincipalLister creates a new instance of PrincipalLister. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +// The first argument is typically a *testing.T value. +func NewPrincipalLister(t interface { + mock.TestingT + Cleanup(func()) +}) *PrincipalLister { + mock := &PrincipalLister{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +} + +// PrincipalLister is an autogenerated mock type for the PrincipalLister type +type PrincipalLister struct { + mock.Mock +} + +type PrincipalLister_Expecter struct { + mock *mock.Mock +} + +func (_m *PrincipalLister) EXPECT() *PrincipalLister_Expecter { + return &PrincipalLister_Expecter{mock: &_m.Mock} +} + +// ListPrincipals provides a mock function for the type PrincipalLister +func (_mock *PrincipalLister) ListPrincipals(ctx context.Context) ([]authkit.Principal, error) { + ret := _mock.Called(ctx) + + if len(ret) == 0 { + panic("no return value specified for ListPrincipals") + } + + var r0 []authkit.Principal + var r1 error + if returnFunc, ok := ret.Get(0).(func(context.Context) ([]authkit.Principal, error)); ok { + return returnFunc(ctx) + } + if returnFunc, ok := ret.Get(0).(func(context.Context) []authkit.Principal); ok { + r0 = returnFunc(ctx) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).([]authkit.Principal) + } + } + if returnFunc, ok := ret.Get(1).(func(context.Context) error); ok { + r1 = returnFunc(ctx) + } else { + r1 = ret.Error(1) + } + return r0, r1 +} + +// PrincipalLister_ListPrincipals_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'ListPrincipals' +type PrincipalLister_ListPrincipals_Call struct { + *mock.Call +} + +// ListPrincipals is a helper method to define mock.On call +// - ctx context.Context +func (_e *PrincipalLister_Expecter) ListPrincipals(ctx interface{}) *PrincipalLister_ListPrincipals_Call { + return &PrincipalLister_ListPrincipals_Call{Call: _e.mock.On("ListPrincipals", ctx)} +} + +func (_c *PrincipalLister_ListPrincipals_Call) Run(run func(ctx context.Context)) *PrincipalLister_ListPrincipals_Call { + _c.Call.Run(func(args mock.Arguments) { + var arg0 context.Context + if args[0] != nil { + arg0 = args[0].(context.Context) + } + run( + arg0, + ) + }) + return _c +} + +func (_c *PrincipalLister_ListPrincipals_Call) Return(principals []authkit.Principal, err error) *PrincipalLister_ListPrincipals_Call { + _c.Call.Return(principals, err) + return _c +} + +func (_c *PrincipalLister_ListPrincipals_Call) RunAndReturn(run func(ctx context.Context) ([]authkit.Principal, error)) *PrincipalLister_ListPrincipals_Call { + _c.Call.Return(run) + return _c +} diff --git a/mocks/authkit/principal_role_assigner.go b/mocks/authkit/principal_role_assigner.go new file mode 100644 index 0000000..fbf35f4 --- /dev/null +++ b/mocks/authkit/principal_role_assigner.go @@ -0,0 +1,96 @@ +// Code generated by mockery; DO NOT EDIT. +// github.com/vektra/mockery +// template: testify + +package authkitmocks + +import ( + "context" + + "github.com/meigma/authkit" + mock "github.com/stretchr/testify/mock" +) + +// NewPrincipalRoleAssigner creates a new instance of PrincipalRoleAssigner. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +// The first argument is typically a *testing.T value. +func NewPrincipalRoleAssigner(t interface { + mock.TestingT + Cleanup(func()) +}) *PrincipalRoleAssigner { + mock := &PrincipalRoleAssigner{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +} + +// PrincipalRoleAssigner is an autogenerated mock type for the PrincipalRoleAssigner type +type PrincipalRoleAssigner struct { + mock.Mock +} + +type PrincipalRoleAssigner_Expecter struct { + mock *mock.Mock +} + +func (_m *PrincipalRoleAssigner) EXPECT() *PrincipalRoleAssigner_Expecter { + return &PrincipalRoleAssigner_Expecter{mock: &_m.Mock} +} + +// AssignPrincipalRole provides a mock function for the type PrincipalRoleAssigner +func (_mock *PrincipalRoleAssigner) AssignPrincipalRole(ctx context.Context, req authkit.AssignPrincipalRoleRequest) error { + ret := _mock.Called(ctx, req) + + if len(ret) == 0 { + panic("no return value specified for AssignPrincipalRole") + } + + var r0 error + if returnFunc, ok := ret.Get(0).(func(context.Context, authkit.AssignPrincipalRoleRequest) error); ok { + r0 = returnFunc(ctx, req) + } else { + r0 = ret.Error(0) + } + return r0 +} + +// PrincipalRoleAssigner_AssignPrincipalRole_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'AssignPrincipalRole' +type PrincipalRoleAssigner_AssignPrincipalRole_Call struct { + *mock.Call +} + +// AssignPrincipalRole is a helper method to define mock.On call +// - ctx context.Context +// - req authkit.AssignPrincipalRoleRequest +func (_e *PrincipalRoleAssigner_Expecter) AssignPrincipalRole(ctx interface{}, req interface{}) *PrincipalRoleAssigner_AssignPrincipalRole_Call { + return &PrincipalRoleAssigner_AssignPrincipalRole_Call{Call: _e.mock.On("AssignPrincipalRole", ctx, req)} +} + +func (_c *PrincipalRoleAssigner_AssignPrincipalRole_Call) Run(run func(ctx context.Context, req authkit.AssignPrincipalRoleRequest)) *PrincipalRoleAssigner_AssignPrincipalRole_Call { + _c.Call.Run(func(args mock.Arguments) { + var arg0 context.Context + if args[0] != nil { + arg0 = args[0].(context.Context) + } + var arg1 authkit.AssignPrincipalRoleRequest + if args[1] != nil { + arg1 = args[1].(authkit.AssignPrincipalRoleRequest) + } + run( + arg0, + arg1, + ) + }) + return _c +} + +func (_c *PrincipalRoleAssigner_AssignPrincipalRole_Call) Return(err error) *PrincipalRoleAssigner_AssignPrincipalRole_Call { + _c.Call.Return(err) + return _c +} + +func (_c *PrincipalRoleAssigner_AssignPrincipalRole_Call) RunAndReturn(run func(ctx context.Context, req authkit.AssignPrincipalRoleRequest) error) *PrincipalRoleAssigner_AssignPrincipalRole_Call { + _c.Call.Return(run) + return _c +} diff --git a/mocks/authkit/principal_role_assignment_lister.go b/mocks/authkit/principal_role_assignment_lister.go new file mode 100644 index 0000000..0f663b4 --- /dev/null +++ b/mocks/authkit/principal_role_assignment_lister.go @@ -0,0 +1,107 @@ +// Code generated by mockery; DO NOT EDIT. +// github.com/vektra/mockery +// template: testify + +package authkitmocks + +import ( + "context" + + "github.com/meigma/authkit" + mock "github.com/stretchr/testify/mock" +) + +// NewPrincipalRoleAssignmentLister creates a new instance of PrincipalRoleAssignmentLister. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +// The first argument is typically a *testing.T value. +func NewPrincipalRoleAssignmentLister(t interface { + mock.TestingT + Cleanup(func()) +}) *PrincipalRoleAssignmentLister { + mock := &PrincipalRoleAssignmentLister{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +} + +// PrincipalRoleAssignmentLister is an autogenerated mock type for the PrincipalRoleAssignmentLister type +type PrincipalRoleAssignmentLister struct { + mock.Mock +} + +type PrincipalRoleAssignmentLister_Expecter struct { + mock *mock.Mock +} + +func (_m *PrincipalRoleAssignmentLister) EXPECT() *PrincipalRoleAssignmentLister_Expecter { + return &PrincipalRoleAssignmentLister_Expecter{mock: &_m.Mock} +} + +// ListPrincipalRoleAssignments provides a mock function for the type PrincipalRoleAssignmentLister +func (_mock *PrincipalRoleAssignmentLister) ListPrincipalRoleAssignments(ctx context.Context, principalID string) ([]authkit.PrincipalRoleAssignment, error) { + ret := _mock.Called(ctx, principalID) + + if len(ret) == 0 { + panic("no return value specified for ListPrincipalRoleAssignments") + } + + var r0 []authkit.PrincipalRoleAssignment + var r1 error + if returnFunc, ok := ret.Get(0).(func(context.Context, string) ([]authkit.PrincipalRoleAssignment, error)); ok { + return returnFunc(ctx, principalID) + } + if returnFunc, ok := ret.Get(0).(func(context.Context, string) []authkit.PrincipalRoleAssignment); ok { + r0 = returnFunc(ctx, principalID) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).([]authkit.PrincipalRoleAssignment) + } + } + if returnFunc, ok := ret.Get(1).(func(context.Context, string) error); ok { + r1 = returnFunc(ctx, principalID) + } else { + r1 = ret.Error(1) + } + return r0, r1 +} + +// PrincipalRoleAssignmentLister_ListPrincipalRoleAssignments_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'ListPrincipalRoleAssignments' +type PrincipalRoleAssignmentLister_ListPrincipalRoleAssignments_Call struct { + *mock.Call +} + +// ListPrincipalRoleAssignments is a helper method to define mock.On call +// - ctx context.Context +// - principalID string +func (_e *PrincipalRoleAssignmentLister_Expecter) ListPrincipalRoleAssignments(ctx interface{}, principalID interface{}) *PrincipalRoleAssignmentLister_ListPrincipalRoleAssignments_Call { + return &PrincipalRoleAssignmentLister_ListPrincipalRoleAssignments_Call{Call: _e.mock.On("ListPrincipalRoleAssignments", ctx, principalID)} +} + +func (_c *PrincipalRoleAssignmentLister_ListPrincipalRoleAssignments_Call) Run(run func(ctx context.Context, principalID string)) *PrincipalRoleAssignmentLister_ListPrincipalRoleAssignments_Call { + _c.Call.Run(func(args mock.Arguments) { + var arg0 context.Context + if args[0] != nil { + arg0 = args[0].(context.Context) + } + var arg1 string + if args[1] != nil { + arg1 = args[1].(string) + } + run( + arg0, + arg1, + ) + }) + return _c +} + +func (_c *PrincipalRoleAssignmentLister_ListPrincipalRoleAssignments_Call) Return(principalRoleAssignments []authkit.PrincipalRoleAssignment, err error) *PrincipalRoleAssignmentLister_ListPrincipalRoleAssignments_Call { + _c.Call.Return(principalRoleAssignments, err) + return _c +} + +func (_c *PrincipalRoleAssignmentLister_ListPrincipalRoleAssignments_Call) RunAndReturn(run func(ctx context.Context, principalID string) ([]authkit.PrincipalRoleAssignment, error)) *PrincipalRoleAssignmentLister_ListPrincipalRoleAssignments_Call { + _c.Call.Return(run) + return _c +} diff --git a/mocks/authkit/principal_role_unassigner.go b/mocks/authkit/principal_role_unassigner.go new file mode 100644 index 0000000..7887141 --- /dev/null +++ b/mocks/authkit/principal_role_unassigner.go @@ -0,0 +1,96 @@ +// Code generated by mockery; DO NOT EDIT. +// github.com/vektra/mockery +// template: testify + +package authkitmocks + +import ( + "context" + + "github.com/meigma/authkit" + mock "github.com/stretchr/testify/mock" +) + +// NewPrincipalRoleUnassigner creates a new instance of PrincipalRoleUnassigner. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +// The first argument is typically a *testing.T value. +func NewPrincipalRoleUnassigner(t interface { + mock.TestingT + Cleanup(func()) +}) *PrincipalRoleUnassigner { + mock := &PrincipalRoleUnassigner{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +} + +// PrincipalRoleUnassigner is an autogenerated mock type for the PrincipalRoleUnassigner type +type PrincipalRoleUnassigner struct { + mock.Mock +} + +type PrincipalRoleUnassigner_Expecter struct { + mock *mock.Mock +} + +func (_m *PrincipalRoleUnassigner) EXPECT() *PrincipalRoleUnassigner_Expecter { + return &PrincipalRoleUnassigner_Expecter{mock: &_m.Mock} +} + +// UnassignPrincipalRole provides a mock function for the type PrincipalRoleUnassigner +func (_mock *PrincipalRoleUnassigner) UnassignPrincipalRole(ctx context.Context, req authkit.UnassignPrincipalRoleRequest) error { + ret := _mock.Called(ctx, req) + + if len(ret) == 0 { + panic("no return value specified for UnassignPrincipalRole") + } + + var r0 error + if returnFunc, ok := ret.Get(0).(func(context.Context, authkit.UnassignPrincipalRoleRequest) error); ok { + r0 = returnFunc(ctx, req) + } else { + r0 = ret.Error(0) + } + return r0 +} + +// PrincipalRoleUnassigner_UnassignPrincipalRole_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'UnassignPrincipalRole' +type PrincipalRoleUnassigner_UnassignPrincipalRole_Call struct { + *mock.Call +} + +// UnassignPrincipalRole is a helper method to define mock.On call +// - ctx context.Context +// - req authkit.UnassignPrincipalRoleRequest +func (_e *PrincipalRoleUnassigner_Expecter) UnassignPrincipalRole(ctx interface{}, req interface{}) *PrincipalRoleUnassigner_UnassignPrincipalRole_Call { + return &PrincipalRoleUnassigner_UnassignPrincipalRole_Call{Call: _e.mock.On("UnassignPrincipalRole", ctx, req)} +} + +func (_c *PrincipalRoleUnassigner_UnassignPrincipalRole_Call) Run(run func(ctx context.Context, req authkit.UnassignPrincipalRoleRequest)) *PrincipalRoleUnassigner_UnassignPrincipalRole_Call { + _c.Call.Run(func(args mock.Arguments) { + var arg0 context.Context + if args[0] != nil { + arg0 = args[0].(context.Context) + } + var arg1 authkit.UnassignPrincipalRoleRequest + if args[1] != nil { + arg1 = args[1].(authkit.UnassignPrincipalRoleRequest) + } + run( + arg0, + arg1, + ) + }) + return _c +} + +func (_c *PrincipalRoleUnassigner_UnassignPrincipalRole_Call) Return(err error) *PrincipalRoleUnassigner_UnassignPrincipalRole_Call { + _c.Call.Return(err) + return _c +} + +func (_c *PrincipalRoleUnassigner_UnassignPrincipalRole_Call) RunAndReturn(run func(ctx context.Context, req authkit.UnassignPrincipalRoleRequest) error) *PrincipalRoleUnassigner_UnassignPrincipalRole_Call { + _c.Call.Return(run) + return _c +} diff --git a/mocks/authkit/provisioning_rule_creator.go b/mocks/authkit/provisioning_rule_creator.go new file mode 100644 index 0000000..dbc6da4 --- /dev/null +++ b/mocks/authkit/provisioning_rule_creator.go @@ -0,0 +1,105 @@ +// Code generated by mockery; DO NOT EDIT. +// github.com/vektra/mockery +// template: testify + +package authkitmocks + +import ( + "context" + + "github.com/meigma/authkit" + mock "github.com/stretchr/testify/mock" +) + +// NewProvisioningRuleCreator creates a new instance of ProvisioningRuleCreator. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +// The first argument is typically a *testing.T value. +func NewProvisioningRuleCreator(t interface { + mock.TestingT + Cleanup(func()) +}) *ProvisioningRuleCreator { + mock := &ProvisioningRuleCreator{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +} + +// ProvisioningRuleCreator is an autogenerated mock type for the ProvisioningRuleCreator type +type ProvisioningRuleCreator struct { + mock.Mock +} + +type ProvisioningRuleCreator_Expecter struct { + mock *mock.Mock +} + +func (_m *ProvisioningRuleCreator) EXPECT() *ProvisioningRuleCreator_Expecter { + return &ProvisioningRuleCreator_Expecter{mock: &_m.Mock} +} + +// CreateProvisioningRule provides a mock function for the type ProvisioningRuleCreator +func (_mock *ProvisioningRuleCreator) CreateProvisioningRule(ctx context.Context, req authkit.CreateProvisioningRuleRequest) (authkit.ProvisioningRule, error) { + ret := _mock.Called(ctx, req) + + if len(ret) == 0 { + panic("no return value specified for CreateProvisioningRule") + } + + var r0 authkit.ProvisioningRule + var r1 error + if returnFunc, ok := ret.Get(0).(func(context.Context, authkit.CreateProvisioningRuleRequest) (authkit.ProvisioningRule, error)); ok { + return returnFunc(ctx, req) + } + if returnFunc, ok := ret.Get(0).(func(context.Context, authkit.CreateProvisioningRuleRequest) authkit.ProvisioningRule); ok { + r0 = returnFunc(ctx, req) + } else { + r0 = ret.Get(0).(authkit.ProvisioningRule) + } + if returnFunc, ok := ret.Get(1).(func(context.Context, authkit.CreateProvisioningRuleRequest) error); ok { + r1 = returnFunc(ctx, req) + } else { + r1 = ret.Error(1) + } + return r0, r1 +} + +// ProvisioningRuleCreator_CreateProvisioningRule_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'CreateProvisioningRule' +type ProvisioningRuleCreator_CreateProvisioningRule_Call struct { + *mock.Call +} + +// CreateProvisioningRule is a helper method to define mock.On call +// - ctx context.Context +// - req authkit.CreateProvisioningRuleRequest +func (_e *ProvisioningRuleCreator_Expecter) CreateProvisioningRule(ctx interface{}, req interface{}) *ProvisioningRuleCreator_CreateProvisioningRule_Call { + return &ProvisioningRuleCreator_CreateProvisioningRule_Call{Call: _e.mock.On("CreateProvisioningRule", ctx, req)} +} + +func (_c *ProvisioningRuleCreator_CreateProvisioningRule_Call) Run(run func(ctx context.Context, req authkit.CreateProvisioningRuleRequest)) *ProvisioningRuleCreator_CreateProvisioningRule_Call { + _c.Call.Run(func(args mock.Arguments) { + var arg0 context.Context + if args[0] != nil { + arg0 = args[0].(context.Context) + } + var arg1 authkit.CreateProvisioningRuleRequest + if args[1] != nil { + arg1 = args[1].(authkit.CreateProvisioningRuleRequest) + } + run( + arg0, + arg1, + ) + }) + return _c +} + +func (_c *ProvisioningRuleCreator_CreateProvisioningRule_Call) Return(provisioningRule authkit.ProvisioningRule, err error) *ProvisioningRuleCreator_CreateProvisioningRule_Call { + _c.Call.Return(provisioningRule, err) + return _c +} + +func (_c *ProvisioningRuleCreator_CreateProvisioningRule_Call) RunAndReturn(run func(ctx context.Context, req authkit.CreateProvisioningRuleRequest) (authkit.ProvisioningRule, error)) *ProvisioningRuleCreator_CreateProvisioningRule_Call { + _c.Call.Return(run) + return _c +} diff --git a/mocks/authkit/provisioning_rule_deleter.go b/mocks/authkit/provisioning_rule_deleter.go new file mode 100644 index 0000000..cc35e6b --- /dev/null +++ b/mocks/authkit/provisioning_rule_deleter.go @@ -0,0 +1,95 @@ +// Code generated by mockery; DO NOT EDIT. +// github.com/vektra/mockery +// template: testify + +package authkitmocks + +import ( + "context" + + mock "github.com/stretchr/testify/mock" +) + +// NewProvisioningRuleDeleter creates a new instance of ProvisioningRuleDeleter. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +// The first argument is typically a *testing.T value. +func NewProvisioningRuleDeleter(t interface { + mock.TestingT + Cleanup(func()) +}) *ProvisioningRuleDeleter { + mock := &ProvisioningRuleDeleter{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +} + +// ProvisioningRuleDeleter is an autogenerated mock type for the ProvisioningRuleDeleter type +type ProvisioningRuleDeleter struct { + mock.Mock +} + +type ProvisioningRuleDeleter_Expecter struct { + mock *mock.Mock +} + +func (_m *ProvisioningRuleDeleter) EXPECT() *ProvisioningRuleDeleter_Expecter { + return &ProvisioningRuleDeleter_Expecter{mock: &_m.Mock} +} + +// DeleteProvisioningRule provides a mock function for the type ProvisioningRuleDeleter +func (_mock *ProvisioningRuleDeleter) DeleteProvisioningRule(ctx context.Context, id string) error { + ret := _mock.Called(ctx, id) + + if len(ret) == 0 { + panic("no return value specified for DeleteProvisioningRule") + } + + var r0 error + if returnFunc, ok := ret.Get(0).(func(context.Context, string) error); ok { + r0 = returnFunc(ctx, id) + } else { + r0 = ret.Error(0) + } + return r0 +} + +// ProvisioningRuleDeleter_DeleteProvisioningRule_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'DeleteProvisioningRule' +type ProvisioningRuleDeleter_DeleteProvisioningRule_Call struct { + *mock.Call +} + +// DeleteProvisioningRule is a helper method to define mock.On call +// - ctx context.Context +// - id string +func (_e *ProvisioningRuleDeleter_Expecter) DeleteProvisioningRule(ctx interface{}, id interface{}) *ProvisioningRuleDeleter_DeleteProvisioningRule_Call { + return &ProvisioningRuleDeleter_DeleteProvisioningRule_Call{Call: _e.mock.On("DeleteProvisioningRule", ctx, id)} +} + +func (_c *ProvisioningRuleDeleter_DeleteProvisioningRule_Call) Run(run func(ctx context.Context, id string)) *ProvisioningRuleDeleter_DeleteProvisioningRule_Call { + _c.Call.Run(func(args mock.Arguments) { + var arg0 context.Context + if args[0] != nil { + arg0 = args[0].(context.Context) + } + var arg1 string + if args[1] != nil { + arg1 = args[1].(string) + } + run( + arg0, + arg1, + ) + }) + return _c +} + +func (_c *ProvisioningRuleDeleter_DeleteProvisioningRule_Call) Return(err error) *ProvisioningRuleDeleter_DeleteProvisioningRule_Call { + _c.Call.Return(err) + return _c +} + +func (_c *ProvisioningRuleDeleter_DeleteProvisioningRule_Call) RunAndReturn(run func(ctx context.Context, id string) error) *ProvisioningRuleDeleter_DeleteProvisioningRule_Call { + _c.Call.Return(run) + return _c +} diff --git a/mocks/authkit/provisioning_rule_finder.go b/mocks/authkit/provisioning_rule_finder.go new file mode 100644 index 0000000..160fdf5 --- /dev/null +++ b/mocks/authkit/provisioning_rule_finder.go @@ -0,0 +1,105 @@ +// Code generated by mockery; DO NOT EDIT. +// github.com/vektra/mockery +// template: testify + +package authkitmocks + +import ( + "context" + + "github.com/meigma/authkit" + mock "github.com/stretchr/testify/mock" +) + +// NewProvisioningRuleFinder creates a new instance of ProvisioningRuleFinder. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +// The first argument is typically a *testing.T value. +func NewProvisioningRuleFinder(t interface { + mock.TestingT + Cleanup(func()) +}) *ProvisioningRuleFinder { + mock := &ProvisioningRuleFinder{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +} + +// ProvisioningRuleFinder is an autogenerated mock type for the ProvisioningRuleFinder type +type ProvisioningRuleFinder struct { + mock.Mock +} + +type ProvisioningRuleFinder_Expecter struct { + mock *mock.Mock +} + +func (_m *ProvisioningRuleFinder) EXPECT() *ProvisioningRuleFinder_Expecter { + return &ProvisioningRuleFinder_Expecter{mock: &_m.Mock} +} + +// FindProvisioningRule provides a mock function for the type ProvisioningRuleFinder +func (_mock *ProvisioningRuleFinder) FindProvisioningRule(ctx context.Context, id string) (authkit.ProvisioningRule, error) { + ret := _mock.Called(ctx, id) + + if len(ret) == 0 { + panic("no return value specified for FindProvisioningRule") + } + + var r0 authkit.ProvisioningRule + var r1 error + if returnFunc, ok := ret.Get(0).(func(context.Context, string) (authkit.ProvisioningRule, error)); ok { + return returnFunc(ctx, id) + } + if returnFunc, ok := ret.Get(0).(func(context.Context, string) authkit.ProvisioningRule); ok { + r0 = returnFunc(ctx, id) + } else { + r0 = ret.Get(0).(authkit.ProvisioningRule) + } + if returnFunc, ok := ret.Get(1).(func(context.Context, string) error); ok { + r1 = returnFunc(ctx, id) + } else { + r1 = ret.Error(1) + } + return r0, r1 +} + +// ProvisioningRuleFinder_FindProvisioningRule_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'FindProvisioningRule' +type ProvisioningRuleFinder_FindProvisioningRule_Call struct { + *mock.Call +} + +// FindProvisioningRule is a helper method to define mock.On call +// - ctx context.Context +// - id string +func (_e *ProvisioningRuleFinder_Expecter) FindProvisioningRule(ctx interface{}, id interface{}) *ProvisioningRuleFinder_FindProvisioningRule_Call { + return &ProvisioningRuleFinder_FindProvisioningRule_Call{Call: _e.mock.On("FindProvisioningRule", ctx, id)} +} + +func (_c *ProvisioningRuleFinder_FindProvisioningRule_Call) Run(run func(ctx context.Context, id string)) *ProvisioningRuleFinder_FindProvisioningRule_Call { + _c.Call.Run(func(args mock.Arguments) { + var arg0 context.Context + if args[0] != nil { + arg0 = args[0].(context.Context) + } + var arg1 string + if args[1] != nil { + arg1 = args[1].(string) + } + run( + arg0, + arg1, + ) + }) + return _c +} + +func (_c *ProvisioningRuleFinder_FindProvisioningRule_Call) Return(provisioningRule authkit.ProvisioningRule, err error) *ProvisioningRuleFinder_FindProvisioningRule_Call { + _c.Call.Return(provisioningRule, err) + return _c +} + +func (_c *ProvisioningRuleFinder_FindProvisioningRule_Call) RunAndReturn(run func(ctx context.Context, id string) (authkit.ProvisioningRule, error)) *ProvisioningRuleFinder_FindProvisioningRule_Call { + _c.Call.Return(run) + return _c +} diff --git a/mocks/authkit/provisioning_rule_lister.go b/mocks/authkit/provisioning_rule_lister.go new file mode 100644 index 0000000..d758f24 --- /dev/null +++ b/mocks/authkit/provisioning_rule_lister.go @@ -0,0 +1,101 @@ +// Code generated by mockery; DO NOT EDIT. +// github.com/vektra/mockery +// template: testify + +package authkitmocks + +import ( + "context" + + "github.com/meigma/authkit" + mock "github.com/stretchr/testify/mock" +) + +// NewProvisioningRuleLister creates a new instance of ProvisioningRuleLister. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +// The first argument is typically a *testing.T value. +func NewProvisioningRuleLister(t interface { + mock.TestingT + Cleanup(func()) +}) *ProvisioningRuleLister { + mock := &ProvisioningRuleLister{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +} + +// ProvisioningRuleLister is an autogenerated mock type for the ProvisioningRuleLister type +type ProvisioningRuleLister struct { + mock.Mock +} + +type ProvisioningRuleLister_Expecter struct { + mock *mock.Mock +} + +func (_m *ProvisioningRuleLister) EXPECT() *ProvisioningRuleLister_Expecter { + return &ProvisioningRuleLister_Expecter{mock: &_m.Mock} +} + +// ListProvisioningRules provides a mock function for the type ProvisioningRuleLister +func (_mock *ProvisioningRuleLister) ListProvisioningRules(ctx context.Context) ([]authkit.ProvisioningRule, error) { + ret := _mock.Called(ctx) + + if len(ret) == 0 { + panic("no return value specified for ListProvisioningRules") + } + + var r0 []authkit.ProvisioningRule + var r1 error + if returnFunc, ok := ret.Get(0).(func(context.Context) ([]authkit.ProvisioningRule, error)); ok { + return returnFunc(ctx) + } + if returnFunc, ok := ret.Get(0).(func(context.Context) []authkit.ProvisioningRule); ok { + r0 = returnFunc(ctx) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).([]authkit.ProvisioningRule) + } + } + if returnFunc, ok := ret.Get(1).(func(context.Context) error); ok { + r1 = returnFunc(ctx) + } else { + r1 = ret.Error(1) + } + return r0, r1 +} + +// ProvisioningRuleLister_ListProvisioningRules_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'ListProvisioningRules' +type ProvisioningRuleLister_ListProvisioningRules_Call struct { + *mock.Call +} + +// ListProvisioningRules is a helper method to define mock.On call +// - ctx context.Context +func (_e *ProvisioningRuleLister_Expecter) ListProvisioningRules(ctx interface{}) *ProvisioningRuleLister_ListProvisioningRules_Call { + return &ProvisioningRuleLister_ListProvisioningRules_Call{Call: _e.mock.On("ListProvisioningRules", ctx)} +} + +func (_c *ProvisioningRuleLister_ListProvisioningRules_Call) Run(run func(ctx context.Context)) *ProvisioningRuleLister_ListProvisioningRules_Call { + _c.Call.Run(func(args mock.Arguments) { + var arg0 context.Context + if args[0] != nil { + arg0 = args[0].(context.Context) + } + run( + arg0, + ) + }) + return _c +} + +func (_c *ProvisioningRuleLister_ListProvisioningRules_Call) Return(provisioningRules []authkit.ProvisioningRule, err error) *ProvisioningRuleLister_ListProvisioningRules_Call { + _c.Call.Return(provisioningRules, err) + return _c +} + +func (_c *ProvisioningRuleLister_ListProvisioningRules_Call) RunAndReturn(run func(ctx context.Context) ([]authkit.ProvisioningRule, error)) *ProvisioningRuleLister_ListProvisioningRules_Call { + _c.Call.Return(run) + return _c +} diff --git a/mocks/authkit/provisioning_rule_updater.go b/mocks/authkit/provisioning_rule_updater.go new file mode 100644 index 0000000..4d797e9 --- /dev/null +++ b/mocks/authkit/provisioning_rule_updater.go @@ -0,0 +1,105 @@ +// Code generated by mockery; DO NOT EDIT. +// github.com/vektra/mockery +// template: testify + +package authkitmocks + +import ( + "context" + + "github.com/meigma/authkit" + mock "github.com/stretchr/testify/mock" +) + +// NewProvisioningRuleUpdater creates a new instance of ProvisioningRuleUpdater. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +// The first argument is typically a *testing.T value. +func NewProvisioningRuleUpdater(t interface { + mock.TestingT + Cleanup(func()) +}) *ProvisioningRuleUpdater { + mock := &ProvisioningRuleUpdater{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +} + +// ProvisioningRuleUpdater is an autogenerated mock type for the ProvisioningRuleUpdater type +type ProvisioningRuleUpdater struct { + mock.Mock +} + +type ProvisioningRuleUpdater_Expecter struct { + mock *mock.Mock +} + +func (_m *ProvisioningRuleUpdater) EXPECT() *ProvisioningRuleUpdater_Expecter { + return &ProvisioningRuleUpdater_Expecter{mock: &_m.Mock} +} + +// UpdateProvisioningRule provides a mock function for the type ProvisioningRuleUpdater +func (_mock *ProvisioningRuleUpdater) UpdateProvisioningRule(ctx context.Context, req authkit.UpdateProvisioningRuleRequest) (authkit.ProvisioningRule, error) { + ret := _mock.Called(ctx, req) + + if len(ret) == 0 { + panic("no return value specified for UpdateProvisioningRule") + } + + var r0 authkit.ProvisioningRule + var r1 error + if returnFunc, ok := ret.Get(0).(func(context.Context, authkit.UpdateProvisioningRuleRequest) (authkit.ProvisioningRule, error)); ok { + return returnFunc(ctx, req) + } + if returnFunc, ok := ret.Get(0).(func(context.Context, authkit.UpdateProvisioningRuleRequest) authkit.ProvisioningRule); ok { + r0 = returnFunc(ctx, req) + } else { + r0 = ret.Get(0).(authkit.ProvisioningRule) + } + if returnFunc, ok := ret.Get(1).(func(context.Context, authkit.UpdateProvisioningRuleRequest) error); ok { + r1 = returnFunc(ctx, req) + } else { + r1 = ret.Error(1) + } + return r0, r1 +} + +// ProvisioningRuleUpdater_UpdateProvisioningRule_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'UpdateProvisioningRule' +type ProvisioningRuleUpdater_UpdateProvisioningRule_Call struct { + *mock.Call +} + +// UpdateProvisioningRule is a helper method to define mock.On call +// - ctx context.Context +// - req authkit.UpdateProvisioningRuleRequest +func (_e *ProvisioningRuleUpdater_Expecter) UpdateProvisioningRule(ctx interface{}, req interface{}) *ProvisioningRuleUpdater_UpdateProvisioningRule_Call { + return &ProvisioningRuleUpdater_UpdateProvisioningRule_Call{Call: _e.mock.On("UpdateProvisioningRule", ctx, req)} +} + +func (_c *ProvisioningRuleUpdater_UpdateProvisioningRule_Call) Run(run func(ctx context.Context, req authkit.UpdateProvisioningRuleRequest)) *ProvisioningRuleUpdater_UpdateProvisioningRule_Call { + _c.Call.Run(func(args mock.Arguments) { + var arg0 context.Context + if args[0] != nil { + arg0 = args[0].(context.Context) + } + var arg1 authkit.UpdateProvisioningRuleRequest + if args[1] != nil { + arg1 = args[1].(authkit.UpdateProvisioningRuleRequest) + } + run( + arg0, + arg1, + ) + }) + return _c +} + +func (_c *ProvisioningRuleUpdater_UpdateProvisioningRule_Call) Return(provisioningRule authkit.ProvisioningRule, err error) *ProvisioningRuleUpdater_UpdateProvisioningRule_Call { + _c.Call.Return(provisioningRule, err) + return _c +} + +func (_c *ProvisioningRuleUpdater_UpdateProvisioningRule_Call) RunAndReturn(run func(ctx context.Context, req authkit.UpdateProvisioningRuleRequest) (authkit.ProvisioningRule, error)) *ProvisioningRuleUpdater_UpdateProvisioningRule_Call { + _c.Call.Return(run) + return _c +} diff --git a/mocks/authkit/role_action_granter.go b/mocks/authkit/role_action_granter.go new file mode 100644 index 0000000..e07d492 --- /dev/null +++ b/mocks/authkit/role_action_granter.go @@ -0,0 +1,96 @@ +// Code generated by mockery; DO NOT EDIT. +// github.com/vektra/mockery +// template: testify + +package authkitmocks + +import ( + "context" + + "github.com/meigma/authkit" + mock "github.com/stretchr/testify/mock" +) + +// NewRoleActionGranter creates a new instance of RoleActionGranter. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +// The first argument is typically a *testing.T value. +func NewRoleActionGranter(t interface { + mock.TestingT + Cleanup(func()) +}) *RoleActionGranter { + mock := &RoleActionGranter{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +} + +// RoleActionGranter is an autogenerated mock type for the RoleActionGranter type +type RoleActionGranter struct { + mock.Mock +} + +type RoleActionGranter_Expecter struct { + mock *mock.Mock +} + +func (_m *RoleActionGranter) EXPECT() *RoleActionGranter_Expecter { + return &RoleActionGranter_Expecter{mock: &_m.Mock} +} + +// GrantRoleAction provides a mock function for the type RoleActionGranter +func (_mock *RoleActionGranter) GrantRoleAction(ctx context.Context, req authkit.GrantRoleActionRequest) error { + ret := _mock.Called(ctx, req) + + if len(ret) == 0 { + panic("no return value specified for GrantRoleAction") + } + + var r0 error + if returnFunc, ok := ret.Get(0).(func(context.Context, authkit.GrantRoleActionRequest) error); ok { + r0 = returnFunc(ctx, req) + } else { + r0 = ret.Error(0) + } + return r0 +} + +// RoleActionGranter_GrantRoleAction_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GrantRoleAction' +type RoleActionGranter_GrantRoleAction_Call struct { + *mock.Call +} + +// GrantRoleAction is a helper method to define mock.On call +// - ctx context.Context +// - req authkit.GrantRoleActionRequest +func (_e *RoleActionGranter_Expecter) GrantRoleAction(ctx interface{}, req interface{}) *RoleActionGranter_GrantRoleAction_Call { + return &RoleActionGranter_GrantRoleAction_Call{Call: _e.mock.On("GrantRoleAction", ctx, req)} +} + +func (_c *RoleActionGranter_GrantRoleAction_Call) Run(run func(ctx context.Context, req authkit.GrantRoleActionRequest)) *RoleActionGranter_GrantRoleAction_Call { + _c.Call.Run(func(args mock.Arguments) { + var arg0 context.Context + if args[0] != nil { + arg0 = args[0].(context.Context) + } + var arg1 authkit.GrantRoleActionRequest + if args[1] != nil { + arg1 = args[1].(authkit.GrantRoleActionRequest) + } + run( + arg0, + arg1, + ) + }) + return _c +} + +func (_c *RoleActionGranter_GrantRoleAction_Call) Return(err error) *RoleActionGranter_GrantRoleAction_Call { + _c.Call.Return(err) + return _c +} + +func (_c *RoleActionGranter_GrantRoleAction_Call) RunAndReturn(run func(ctx context.Context, req authkit.GrantRoleActionRequest) error) *RoleActionGranter_GrantRoleAction_Call { + _c.Call.Return(run) + return _c +} diff --git a/mocks/authkit/role_creator.go b/mocks/authkit/role_creator.go new file mode 100644 index 0000000..339f5b2 --- /dev/null +++ b/mocks/authkit/role_creator.go @@ -0,0 +1,105 @@ +// Code generated by mockery; DO NOT EDIT. +// github.com/vektra/mockery +// template: testify + +package authkitmocks + +import ( + "context" + + "github.com/meigma/authkit" + mock "github.com/stretchr/testify/mock" +) + +// NewRoleCreator creates a new instance of RoleCreator. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +// The first argument is typically a *testing.T value. +func NewRoleCreator(t interface { + mock.TestingT + Cleanup(func()) +}) *RoleCreator { + mock := &RoleCreator{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +} + +// RoleCreator is an autogenerated mock type for the RoleCreator type +type RoleCreator struct { + mock.Mock +} + +type RoleCreator_Expecter struct { + mock *mock.Mock +} + +func (_m *RoleCreator) EXPECT() *RoleCreator_Expecter { + return &RoleCreator_Expecter{mock: &_m.Mock} +} + +// CreateRole provides a mock function for the type RoleCreator +func (_mock *RoleCreator) CreateRole(ctx context.Context, req authkit.CreateRoleRequest) (authkit.Role, error) { + ret := _mock.Called(ctx, req) + + if len(ret) == 0 { + panic("no return value specified for CreateRole") + } + + var r0 authkit.Role + var r1 error + if returnFunc, ok := ret.Get(0).(func(context.Context, authkit.CreateRoleRequest) (authkit.Role, error)); ok { + return returnFunc(ctx, req) + } + if returnFunc, ok := ret.Get(0).(func(context.Context, authkit.CreateRoleRequest) authkit.Role); ok { + r0 = returnFunc(ctx, req) + } else { + r0 = ret.Get(0).(authkit.Role) + } + if returnFunc, ok := ret.Get(1).(func(context.Context, authkit.CreateRoleRequest) error); ok { + r1 = returnFunc(ctx, req) + } else { + r1 = ret.Error(1) + } + return r0, r1 +} + +// RoleCreator_CreateRole_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'CreateRole' +type RoleCreator_CreateRole_Call struct { + *mock.Call +} + +// CreateRole is a helper method to define mock.On call +// - ctx context.Context +// - req authkit.CreateRoleRequest +func (_e *RoleCreator_Expecter) CreateRole(ctx interface{}, req interface{}) *RoleCreator_CreateRole_Call { + return &RoleCreator_CreateRole_Call{Call: _e.mock.On("CreateRole", ctx, req)} +} + +func (_c *RoleCreator_CreateRole_Call) Run(run func(ctx context.Context, req authkit.CreateRoleRequest)) *RoleCreator_CreateRole_Call { + _c.Call.Run(func(args mock.Arguments) { + var arg0 context.Context + if args[0] != nil { + arg0 = args[0].(context.Context) + } + var arg1 authkit.CreateRoleRequest + if args[1] != nil { + arg1 = args[1].(authkit.CreateRoleRequest) + } + run( + arg0, + arg1, + ) + }) + return _c +} + +func (_c *RoleCreator_CreateRole_Call) Return(role authkit.Role, err error) *RoleCreator_CreateRole_Call { + _c.Call.Return(role, err) + return _c +} + +func (_c *RoleCreator_CreateRole_Call) RunAndReturn(run func(ctx context.Context, req authkit.CreateRoleRequest) (authkit.Role, error)) *RoleCreator_CreateRole_Call { + _c.Call.Return(run) + return _c +} From 4e91b2e895e1cbc8d33ddd54c7d2c6e4eae685ac Mon Sep 17 00:00:00 2001 From: Joshua Gilman Date: Wed, 27 May 2026 16:32:43 -0700 Subject: [PATCH 5/5] chore(management): collapse struct field copies into type conversions 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. --- management/provisioning_test.go | 9 +-------- management/role_test.go | 6 +----- 2 files changed, 2 insertions(+), 13 deletions(-) diff --git a/management/provisioning_test.go b/management/provisioning_test.go index c70962f..f842444 100644 --- a/management/provisioning_test.go +++ b/management/provisioning_test.go @@ -16,14 +16,7 @@ import ( func TestServiceProvisioningRuleMethods(t *testing.T) { rule := provisioningRule() - createReq := authkit.CreateProvisioningRuleRequest{ - ID: rule.ID, - DisplayName: rule.DisplayName, - Provider: rule.Provider, - Condition: rule.Condition, - AssignRoleIDs: rule.AssignRoleIDs, - Enabled: rule.Enabled, - } + createReq := authkit.CreateProvisioningRuleRequest(rule) updateReq := authkit.UpdateProvisioningRuleRequest{ ID: rule.ID, DisplayName: "Updated", diff --git a/management/role_test.go b/management/role_test.go index f0a2770..df73bcf 100644 --- a/management/role_test.go +++ b/management/role_test.go @@ -20,11 +20,7 @@ func TestServiceCreateRole(t *testing.T) { DisplayName: "Notes reader", Description: "Can read notes.", } - req := authkit.CreateRoleRequest{ - ID: want.ID, - DisplayName: want.DisplayName, - Description: want.Description, - } + req := authkit.CreateRoleRequest(want) creator := authkitmocks.NewRoleCreator(t) creator.EXPECT().CreateRole(mock.Anything, req).Return(want, nil) service := management.NewService(management.Options{RoleCreator: creator})