Documentation
¶
Overview ¶
Package testhelper ships the fakes and assertions consumers of github.com/AtomiCloud/diene.go-auth-engine would otherwise rebuild in every test suite.
The auth engine is seam-heavy by design — a verification-key source, an identity provider, a token store, a deferred-login store, per-backend onboarding surfaces — and every one of them has to be faked before a consumer can test a single guarded handler. Rebuilding that scaffolding per repository is how subtly different fakes (one that forgets token expiry, one whose store is not atomic) end up proving different contracts in different services.
FakeIDP is the centrepiece: it signs REAL RS256 tokens with a generated key pair and publishes them through a real [VerificationKeys] implementation, so a consumer's validation path is exercised end to end without a network, a container, or a live tenant. Its scriptable failure hooks then let a consumer prove the unhappy paths — expired, wrong issuer, unknown key — that are otherwise unreachable in a test.
The assertion helpers depend only on the minimal TestingT interface, never on the concrete testing type, so they stay framework-free and are themselves black-box testable with a recording double.
Index ¶
- Constants
- Variables
- func AssertAuthProblem(t TestingT, err error, id string) problem.Problem
- func AssertNoAuthProblem(t TestingT, err error)
- func AssertOwnershipDenied(t TestingT, err error) problem.Problem
- func AssertPhase(t TestingT, states map[string]onboard.State, backend string, ...)
- func CheckAuthProblem(err error, id string) (problem.Problem, error)
- func CheckPhase(states map[string]onboard.State, backend string, want onboard.Phase) error
- func FixedNow() time.Time
- func ProblemID(envelope problem.Problem) string
- func SampleErrorPortal() problem.ErrorPortal
- type ClaimCall
- type FakeBackend
- func (b *FakeBackend) Configured(_ context.Context, token authengine.AccessToken) (bool, error)
- func (b *FakeBackend) Create(_ context.Context, token authengine.AccessToken, ...) error
- func (b *FakeBackend) EnqueueConfiguredError(err error)
- func (b *FakeBackend) EnqueueCreateError(err error)
- func (b *FakeBackend) EnqueueExistsError(err error)
- func (b *FakeBackend) Exists(_ context.Context, token authengine.AccessToken) (bool, error)
- func (b *FakeBackend) Name() string
- func (b *FakeBackend) Registrations() []onboard.Registration
- func (b *FakeBackend) Tokens() []authengine.AccessToken
- type FakeBackendOptions
- type FakeIDP
- func (f *FakeIDP) Advance(by time.Duration)
- func (f *FakeIDP) Audience() string
- func (f *FakeIDP) Clock() *mocks.InMemorySystem
- func (f *FakeIDP) Guard() (authengine.Guard, error)
- func (f *FakeIDP) Issuer() string
- func (f *FakeIDP) Keys() authengine.VerificationKeys
- func (f *FakeIDP) MintAccessToken(request TokenRequest) (string, error)
- func (f *FakeIDP) MintIDToken(request TokenRequest) (string, error)
- func (f *FakeIDP) Problems() *authengine.Problems
- func (f *FakeIDP) Validator() (authengine.Validator, error)
- type FakeIDPOptions
- type FakePinger
- type FakeProvider
- func (p *FakeProvider) ClaimCalls() []ClaimCall
- func (p *FakeProvider) ClientCredentials(_ context.Context, request authengine.ClientCredentialsRequest) (authengine.AccessToken, error)
- func (p *FakeProvider) ClientCredentialsCalls() []authengine.ClientCredentialsRequest
- func (p *FakeProvider) EnqueueClaimError(err error)
- func (p *FakeProvider) EnqueueClientCredentialsError(err error)
- func (p *FakeProvider) EnqueueOneTimeTokenError(err error)
- func (p *FakeProvider) EnqueueRefreshError(err error)
- func (p *FakeProvider) EnqueueResourceTokenError(err error)
- func (p *FakeProvider) MintOneTimeToken(_ context.Context, request authengine.OneTimeTokenRequest) (authengine.OneTimeToken, error)
- func (p *FakeProvider) Minted() int
- func (p *FakeProvider) OneTimeTokenCalls() []authengine.OneTimeTokenRequest
- func (p *FakeProvider) Refresh(_ context.Context, refreshToken string) (authengine.Session, error)
- func (p *FakeProvider) RefreshCalls() []string
- func (p *FakeProvider) ResourceToken(_ context.Context, request authengine.ResourceTokenRequest) (authengine.AccessToken, error)
- func (p *FakeProvider) ResourceTokenCalls() []authengine.ResourceTokenRequest
- func (p *FakeProvider) SetClaim(_ context.Context, subject string, name string, value any) error
- type FakeProviderOptions
- type FakeRefresher
- type MemoryDeferredStore
- func (s *MemoryDeferredStore) Consume(_ context.Context, digest string) (deferred.Record, bool, error)
- func (s *MemoryDeferredStore) Digests() []string
- func (s *MemoryDeferredStore) EnqueueError(err error)
- func (s *MemoryDeferredStore) Put(_ context.Context, record deferred.Record, _ time.Duration) error
- type MemoryRefreshStore
- func (s *MemoryRefreshStore) EnqueueError(err error)
- func (s *MemoryRefreshStore) Read(_ context.Context, fingerprint string) (authengine.RefreshRecord, bool, error)
- func (s *MemoryRefreshStore) Records() map[string]authengine.RefreshRecord
- func (s *MemoryRefreshStore) Write(_ context.Context, record authengine.RefreshRecord, _ time.Duration) error
- type MemoryTokenStore
- func (s *MemoryTokenStore) Delete(_ context.Context, key string) error
- func (s *MemoryTokenStore) EnqueueError(err error)
- func (s *MemoryTokenStore) Get(_ context.Context, key string) (authengine.AccessToken, bool, error)
- func (s *MemoryTokenStore) Keys() []string
- func (s *MemoryTokenStore) Set(_ context.Context, key string, token authengine.AccessToken, _ time.Duration) error
- type TestingT
- type TokenRequest
Examples ¶
Constants ¶
const ( // DefaultIssuer is the fake IdP's issuer. DefaultIssuer = "https://api.lithium.alcohol.mew.cluster.atomi.cloud" // DefaultAudience is the fake IdP's access-token audience. DefaultAudience = "alcohol-zinc" // DefaultKeyID is the fake IdP's signing key id. DefaultKeyID = "fake-idp-key-1" // DefaultSubject is the fake IdP's default token subject. DefaultSubject = "user-1" // DefaultKeyBits is the generated signing-key size, matching what a real // tenant publishes. DefaultKeyBits = 2048 )
Fake IdP defaults. They are realistic rather than minimal so a consumer's fixtures look like the real thing: the issuer follows the per-platform identity host convention, and the audience is a resource-tree style backend name.
const HeaderKeyID = "kid"
HeaderKeyID is the JOSE header naming the signing key.
Variables ¶
var ErrFakeProvider = errors.New("fake identity provider failure")
ErrFakeProvider is the error the fake raises when a test enqueues a failure without supplying one of its own.
var ErrLandscapeUnreachable = errors.New("fake pinger cannot reach landscape")
ErrLandscapeUnreachable is the error a fake pinger reports for a landscape a test marked unreachable without supplying an error of its own.
Functions ¶
func AssertAuthProblem ¶
AssertAuthProblem fails t unless err carries an auth-engine problem with the expected id.
Matching on the ID rather than the full type URI is deliberate: the URI embeds the consumer's own landscape, platform, service, and module, so a test that spelled it out would break the moment it ran in a different landscape.
Example ¶
package main
import (
"fmt"
"github.com/AtomiCloud/diene.go-auth-engine/lib/authengine"
"github.com/AtomiCloud/diene.go-auth-engine/testhelper"
)
func main() {
idp, _ := testhelper.NewFakeIDP(testhelper.FakeIDPOptions{})
guard, _ := idp.Guard()
owner := authengine.NewClaimMapper().Map(authengine.Claims{authengine.ClaimSubject: "user-1"})
other := "user-2"
// Matching on the id keeps a test portable: the full type URI embeds the
// consumer's own landscape, platform, service, and module.
envelope, failure := testhelper.CheckAuthProblem(
guard.Sub(owner, &other), authengine.ProblemOwnershipDenied,
)
fmt.Println(envelope.Status, failure == nil)
}
Output: 403 true
func AssertNoAuthProblem ¶
AssertNoAuthProblem fails t when err is non-nil, rendering the carried problem so the failure message names the auth decision that went wrong rather than just "unexpected error".
func AssertOwnershipDenied ¶
AssertOwnershipDenied fails t unless err is the ownership-denied problem, which is the single most repeated assertion in a consumer's guard tests.
func AssertPhase ¶
AssertPhase fails t unless the backend's onboarding state reached want.
func CheckAuthProblem ¶
CheckAuthProblem recovers an auth-engine problem from a (T, error) result and verifies its id, returning a descriptive error rather than failing a test.
It is the half a consumer composes into its own assertions, and the half the meta tier drives directly.
func CheckPhase ¶
CheckPhase verifies one backend's onboarding phase, returning a descriptive error rather than failing a test. A backend absent from the round is reported as such rather than compared against the zero phase, because "never ran" and "ran and failed" are different bugs.
func FixedNow ¶
FixedNow returns the deterministic instant the fakes issue tokens at.
It is a real, plausible date rather than the Unix epoch so a token's claims read sensibly in a failure message, and it is fixed so a suite that crosses an expiry boundary does so by advancing the clock rather than by sleeping.
func ProblemID ¶
ProblemID returns the trailing id segment of a problem type URI, so a caller can match a problem without spelling out the consumer-specific URI.
func SampleErrorPortal ¶
func SampleErrorPortal() problem.ErrorPortal
SampleErrorPortal returns a realistic, valid error portal for tests, so fixtures never hand-format a problem type URI.
Types ¶
type ClaimCall ¶
type ClaimCall struct {
// Subject is the identity-provider user the claim was written on.
Subject string
// Name is the claim name.
Name string
// Value is the claim value.
Value any
}
ClaimCall records one claim write-back.
type FakeBackend ¶
type FakeBackend struct {
// contains filtered or unexported fields
}
FakeBackend is a scriptable onboard.Backend.
Onboarding is a five-step sequence whose interesting cases are all failures at step three, four, or five, so this fake exists to make each of them reachable: a row that already exists (the first-sign-in race), a create that fails, a probe that fails. It records the registrations it received so a test can prove the raw tokens really travelled as data.
func NewFakeBackend ¶
func NewFakeBackend(options FakeBackendOptions) *FakeBackend
NewFakeBackend creates a fake onboarding backend.
func (*FakeBackend) Configured ¶
func (b *FakeBackend) Configured(_ context.Context, token authengine.AccessToken) (bool, error)
Configured implements onboard.Configurable. The fake always advertises the capability so a consumer can drive both settled outcomes from one type; a backend with no second step simply does not implement the interface.
func (*FakeBackend) Create ¶
func (b *FakeBackend) Create( _ context.Context, token authengine.AccessToken, registration onboard.Registration, ) error
Create implements onboard.Backend with create-or-ok semantics: registering an already-registered caller succeeds rather than conflicting.
func (*FakeBackend) EnqueueConfiguredError ¶
func (b *FakeBackend) EnqueueConfiguredError(err error)
EnqueueConfiguredError makes the next Configured call fail with err.
func (*FakeBackend) EnqueueCreateError ¶
func (b *FakeBackend) EnqueueCreateError(err error)
EnqueueCreateError makes the next Create call fail with err.
func (*FakeBackend) EnqueueExistsError ¶
func (b *FakeBackend) EnqueueExistsError(err error)
EnqueueExistsError makes the next Exists call fail with err.
func (*FakeBackend) Exists ¶
func (b *FakeBackend) Exists(_ context.Context, token authengine.AccessToken) (bool, error)
Exists implements onboard.Backend.
func (*FakeBackend) Registrations ¶
func (b *FakeBackend) Registrations() []onboard.Registration
Registrations returns the registrations the backend received.
func (*FakeBackend) Tokens ¶
func (b *FakeBackend) Tokens() []authengine.AccessToken
Tokens returns the access tokens the backend was called with, so a test can prove each backend was called with its OWN per-resource token.
type FakeBackendOptions ¶
type FakeBackendOptions struct {
// Name is the backend's resource-tree name.
Name string
// Exists starts the backend with a user row already present, which is the
// concurrent-first-sign-in race.
Exists bool
// NeedsConfiguration makes the backend report its app-specific onboarding step
// as outstanding, so a round settles in onboard.PhaseNeedsOnboarding.
NeedsConfiguration bool
}
FakeBackendOptions configures a FakeBackend.
type FakeIDP ¶
type FakeIDP struct {
// contains filtered or unexported fields
}
FakeIDP is an in-memory identity provider that signs real RS256 tokens.
It is not a mock of the JWT library: the tokens it mints are genuinely signed and genuinely verified, so a consumer's validator, key resolution, algorithm restriction, and claim mapping all run for real. What is faked is only the network and the tenant.
func NewFakeIDP ¶
func NewFakeIDP(options FakeIDPOptions) (*FakeIDP, error)
NewFakeIDP creates a fake identity provider with a freshly generated signing key.
The key is 2048-bit because that is what a real tenant publishes; generating it per instance keeps suites independent, and it costs a few milliseconds once.
Example ¶
package main
import (
"context"
"fmt"
"github.com/AtomiCloud/diene.go-auth-engine/lib/authengine"
"github.com/AtomiCloud/diene.go-auth-engine/testhelper"
)
func main() {
idp, _ := testhelper.NewFakeIDP(testhelper.FakeIDPOptions{})
validator, _ := idp.Validator()
// The tokens are really signed and really verified: only the network and the
// tenant are faked.
token, _ := idp.MintAccessToken(testhelper.TokenRequest{Subject: "user-1", Roles: []string{"admin"}})
principal, err := validator.Validate(context.Background(), token)
fmt.Println(principal.Subject, principal.HasRole("admin"), err == nil)
// Advancing the clock reaches the expiry branch without a sleep.
idp.Advance(authengine.AccessTokenLifetime + authengine.DefaultClockSkew + 1)
_, err = validator.Validate(context.Background(), token)
fmt.Println(err != nil)
}
Output: user-1 true true true
func (*FakeIDP) Advance ¶
Advance moves the fake clock forward, which is how a test crosses a token's expiry boundary without sleeping.
func (*FakeIDP) Clock ¶
func (f *FakeIDP) Clock() *mocks.InMemorySystem
Clock returns the fake's injectable clock seam.
func (*FakeIDP) Guard ¶
func (f *FakeIDP) Guard() (authengine.Guard, error)
Guard returns an ownership guard sharing this fake's problem factory.
func (*FakeIDP) Keys ¶
func (f *FakeIDP) Keys() authengine.VerificationKeys
Keys returns a authengine.VerificationKeys publishing this fake's public key.
func (*FakeIDP) MintAccessToken ¶
func (f *FakeIDP) MintAccessToken(request TokenRequest) (string, error)
MintAccessToken signs an access token from request.
func (*FakeIDP) MintIDToken ¶
func (f *FakeIDP) MintIDToken(request TokenRequest) (string, error)
MintIDToken signs an ID token from request.
An ID token is minted for the CLIENT rather than for a resource server, so its audience is deliberately different from the access token's — which is exactly the asymmetry a consumer's ID-token validation has to tolerate.
func (*FakeIDP) Problems ¶
func (f *FakeIDP) Problems() *authengine.Problems
Problems returns the problem factory bound to this fake's error portal, so a consumer's own engine components share one portal with the fake.
type FakeIDPOptions ¶
type FakeIDPOptions struct {
// Issuer overrides [DefaultIssuer].
Issuer string
// Audience overrides [DefaultAudience].
Audience string
// KeyID overrides [DefaultKeyID].
KeyID string
// Now fixes the fake clock. Zero uses a fixed, deterministic instant.
Now time.Time
// Portal overrides the error portal problems are attributed to.
Portal *problem.ErrorPortal
// ExtraProblems registers a consumer's own problem types on the fake's factory,
// so a suite asserts its domain problems and the engine.s through one registry.
ExtraProblems []problem.Type
// KeyBits overrides the generated signing-key size. Zero uses 2048, which is
// what a real tenant publishes; a large suite may trade strength for speed.
KeyBits int
}
FakeIDPOptions configures a FakeIDP. Every field is optional.
type FakePinger ¶
type FakePinger struct {
// contains filtered or unexported fields
}
FakePinger is a scriptable onboard.Pinger returning fixed latencies per landscape name.
func NewFakePinger ¶
func NewFakePinger() *FakePinger
NewFakePinger creates a pinger with no known landscapes.
func (*FakePinger) Calls ¶
func (p *FakePinger) Calls() []string
Calls returns the landscape names that were pinged.
func (*FakePinger) Ping ¶
Ping implements onboard.Pinger. A landscape with no configured latency and no configured error is treated as unreachable, so a test never accidentally picks a region it forgot to script.
func (*FakePinger) SetError ¶
func (p *FakePinger) SetError(landscape string, err error)
SetError makes landscape fail to answer, which is how a test proves an unreachable region is skipped rather than chosen.
func (*FakePinger) SetLatency ¶
func (p *FakePinger) SetLatency(landscape string, milliseconds int)
SetLatency makes landscape answer with the given latency in milliseconds.
type FakeProvider ¶
type FakeProvider struct {
// contains filtered or unexported fields
}
FakeProvider is a scriptable in-memory authengine.Provider.
It records every call and lets a test enqueue the next outcome per operation, which is what makes the awkward cases reachable: a refresh that fails once and then succeeds, a claim write-back that errors after the user row was created, a provider that returns a token with no rotation. Recording the calls is what proves the single-flight cache made exactly ONE mint under concurrency.
func NewFakeProvider ¶
func NewFakeProvider(options FakeProviderOptions) *FakeProvider
NewFakeProvider creates a fake provider.
func (*FakeProvider) ClaimCalls ¶
func (p *FakeProvider) ClaimCalls() []ClaimCall
ClaimCalls returns the recorded claim write-backs.
func (*FakeProvider) ClientCredentials ¶
func (p *FakeProvider) ClientCredentials( _ context.Context, request authengine.ClientCredentialsRequest, ) (authengine.AccessToken, error)
ClientCredentials implements authengine.Provider.
func (*FakeProvider) ClientCredentialsCalls ¶
func (p *FakeProvider) ClientCredentialsCalls() []authengine.ClientCredentialsRequest
ClientCredentialsCalls returns the recorded machine-to-machine requests.
func (*FakeProvider) EnqueueClaimError ¶
func (p *FakeProvider) EnqueueClaimError(err error)
EnqueueClaimError makes the next SetClaim call fail with err.
func (*FakeProvider) EnqueueClientCredentialsError ¶
func (p *FakeProvider) EnqueueClientCredentialsError(err error)
EnqueueClientCredentialsError makes the next ClientCredentials call fail with err.
func (*FakeProvider) EnqueueOneTimeTokenError ¶
func (p *FakeProvider) EnqueueOneTimeTokenError(err error)
EnqueueOneTimeTokenError makes the next MintOneTimeToken call fail with err.
func (*FakeProvider) EnqueueRefreshError ¶
func (p *FakeProvider) EnqueueRefreshError(err error)
EnqueueRefreshError makes the next Refresh call fail with err.
func (*FakeProvider) EnqueueResourceTokenError ¶
func (p *FakeProvider) EnqueueResourceTokenError(err error)
EnqueueResourceTokenError makes the next ResourceToken call fail with err.
func (*FakeProvider) MintOneTimeToken ¶
func (p *FakeProvider) MintOneTimeToken( _ context.Context, request authengine.OneTimeTokenRequest, ) (authengine.OneTimeToken, error)
MintOneTimeToken implements authengine.Provider.
func (*FakeProvider) Minted ¶
func (p *FakeProvider) Minted() int
Minted returns how many tokens the provider has minted, which is the assertion a single-flight test makes.
func (*FakeProvider) OneTimeTokenCalls ¶
func (p *FakeProvider) OneTimeTokenCalls() []authengine.OneTimeTokenRequest
OneTimeTokenCalls returns the recorded one-time-token requests.
func (*FakeProvider) Refresh ¶
func (p *FakeProvider) Refresh(_ context.Context, refreshToken string) (authengine.Session, error)
Refresh implements authengine.Provider, rotating the refresh token unless the fake was built without rotation.
func (*FakeProvider) RefreshCalls ¶
func (p *FakeProvider) RefreshCalls() []string
RefreshCalls returns the refresh tokens presented to Refresh.
func (*FakeProvider) ResourceToken ¶
func (p *FakeProvider) ResourceToken( _ context.Context, request authengine.ResourceTokenRequest, ) (authengine.AccessToken, error)
ResourceToken implements authengine.Provider.
func (*FakeProvider) ResourceTokenCalls ¶
func (p *FakeProvider) ResourceTokenCalls() []authengine.ResourceTokenRequest
ResourceTokenCalls returns the recorded per-resource token requests.
func (*FakeProvider) SetClaim ¶
SetClaim implements authengine.Provider.
type FakeProviderOptions ¶
type FakeProviderOptions struct {
// Now fixes the instant minted tokens are issued at. Zero uses [FixedNow].
Now time.Time
// NoRotation makes Refresh return no replacement refresh token, so a consumer
// can prove it copes with a provider that does not rotate.
NoRotation bool
}
FakeProviderOptions configures a FakeProvider.
type FakeRefresher ¶
type FakeRefresher struct {
// contains filtered or unexported fields
}
FakeRefresher is a scriptable onboard.TokenRefresher.
The queue models claim propagation: a test enqueues the principal each refresh should observe, so it can prove that a claim which never appears is reported as a stalled onboarding rather than as success.
func NewFakeRefresher ¶
func NewFakeRefresher(principal authengine.Principal) *FakeRefresher
NewFakeRefresher creates a refresher that returns principal by default.
func (*FakeRefresher) Calls ¶
func (r *FakeRefresher) Calls() []string
Calls returns the subjects Refresh was called for.
func (*FakeRefresher) Enqueue ¶
func (r *FakeRefresher) Enqueue(principal authengine.Principal)
Enqueue makes the next Refresh call return principal.
func (*FakeRefresher) EnqueueError ¶
func (r *FakeRefresher) EnqueueError(err error)
EnqueueError makes the next Refresh call fail with err.
func (*FakeRefresher) Refresh ¶
func (r *FakeRefresher) Refresh(_ context.Context, subject string) (authengine.Principal, error)
Refresh implements onboard.TokenRefresher.
type MemoryDeferredStore ¶
type MemoryDeferredStore struct {
// contains filtered or unexported fields
}
MemoryDeferredStore is an in-memory deferred.Store with a genuinely atomic consume.
The atomicity is the whole point of the fake. A test that consumes concurrently must see exactly one first-redemption, and a store that read-then-wrote would let two callers both believe they won — which is the bug a replay-rejection test exists to catch.
func NewMemoryDeferredStore ¶
func NewMemoryDeferredStore() *MemoryDeferredStore
NewMemoryDeferredStore creates an empty deferred-login store.
func (*MemoryDeferredStore) Consume ¶
func (s *MemoryDeferredStore) Consume(_ context.Context, digest string) (deferred.Record, bool, error)
Consume implements deferred.Store, returning the record as it was before the call and marking it consumed under one lock.
func (*MemoryDeferredStore) Digests ¶
func (s *MemoryDeferredStore) Digests() []string
Digests returns the stored nonce digests in sorted order.
func (*MemoryDeferredStore) EnqueueError ¶
func (s *MemoryDeferredStore) EnqueueError(err error)
EnqueueError makes the next store operation fail with err.
type MemoryRefreshStore ¶
type MemoryRefreshStore struct {
// contains filtered or unexported fields
}
MemoryRefreshStore is an in-memory authengine.RefreshStore.
func NewMemoryRefreshStore ¶
func NewMemoryRefreshStore() *MemoryRefreshStore
NewMemoryRefreshStore creates an empty refresh store.
func (*MemoryRefreshStore) EnqueueError ¶
func (s *MemoryRefreshStore) EnqueueError(err error)
EnqueueError makes the next store operation fail with err.
func (*MemoryRefreshStore) Read ¶
func (s *MemoryRefreshStore) Read( _ context.Context, fingerprint string, ) (authengine.RefreshRecord, bool, error)
Read implements authengine.RefreshStore.
func (*MemoryRefreshStore) Records ¶
func (s *MemoryRefreshStore) Records() map[string]authengine.RefreshRecord
Records returns the stored records keyed by fingerprint.
func (*MemoryRefreshStore) Write ¶
func (s *MemoryRefreshStore) Write( _ context.Context, record authengine.RefreshRecord, _ time.Duration, ) error
Write implements authengine.RefreshStore.
type MemoryTokenStore ¶
type MemoryTokenStore struct {
// contains filtered or unexported fields
}
MemoryTokenStore is an in-memory authengine.TokenStore.
It is the fake a single-process consumer can also ship to production behind the same seam, so the contract it satisfies is the real one — including that a miss is (zero, false, nil) rather than an error, which is the distinction a cache built on an error-on-miss store gets subtly wrong.
func NewMemoryTokenStore ¶
func NewMemoryTokenStore() *MemoryTokenStore
NewMemoryTokenStore creates an empty token store.
func (*MemoryTokenStore) Delete ¶
func (s *MemoryTokenStore) Delete(_ context.Context, key string) error
Delete implements authengine.TokenStore.
func (*MemoryTokenStore) EnqueueError ¶
func (s *MemoryTokenStore) EnqueueError(err error)
EnqueueError makes the next store operation fail with err.
func (*MemoryTokenStore) Get ¶
func (s *MemoryTokenStore) Get(_ context.Context, key string) (authengine.AccessToken, bool, error)
Get implements authengine.TokenStore.
func (*MemoryTokenStore) Keys ¶
func (s *MemoryTokenStore) Keys() []string
Keys returns the cached keys in sorted order.
func (*MemoryTokenStore) Set ¶
func (s *MemoryTokenStore) Set( _ context.Context, key string, token authengine.AccessToken, _ time.Duration, ) error
Set implements authengine.TokenStore. The TTL is recorded rather than enforced: expiry is the cache's own decision, made off the injected clock, and a fake that expired entries on a real timer would make that untestable.
type TestingT ¶
type TestingT interface {
// Helper marks the caller as a test helper.
Helper()
// Fatalf reports a fatal failure.
Fatalf(format string, args ...any)
}
TestingT is the minimal subset of *testing.T the assertion helpers use.
Depending on the interface rather than the concrete type keeps the helpers framework-free and — more importantly — lets them be black-box tested with a recording double, which is how the meta tier proves an assertion fails on known-bad input instead of merely passing on known-good.
type TokenRequest ¶
type TokenRequest struct {
// Subject overrides [DefaultSubject].
Subject string
// Username sets the username claim; blank omits it.
Username string
// Email sets the email claim; blank omits it.
Email string
// EmailUnverified emits email_verified=false instead of true.
EmailUnverified bool
// Roles sets the roles claim.
Roles []string
// Scopes sets the space-delimited scope claim.
Scopes []string
// HomeLandscape sets the home-landscape claim; blank omits it.
HomeLandscape string
// Registered names the backends whose registration claim is emitted as true.
Registered []string
// Claims are extra claims merged in last, so a test can emit anything.
Claims map[string]any
// Issuer overrides the fake IdP's issuer, to mint an untrusted token.
Issuer string
// Audience overrides the fake IdP's audience, to mint a mis-audienced token.
Audience string
// KeyID overrides the signing key id, to mint a token with an unknown key.
KeyID string
// ExpiresAt overrides the expiry, to mint an expired token.
ExpiresAt time.Time
// OmitExpiry omits the exp claim entirely.
OmitExpiry bool
// OmitKeyID omits the kid header entirely, which is how a consumer proves its
// key resolver refuses to guess a key rather than picking the only one it has.
OmitKeyID bool
}
TokenRequest describes a token the fake IdP should mint.
The deliberately wrong-looking fields — Issuer, Audience, KeyID, ExpiresAt — are how a consumer reaches the failure branches of its own validation code. A test that can only mint valid tokens cannot prove that an expired one is rejected.