Acting as a user
An action receives its caller, the person or service signed in, as a typed parameter (The principal). So a test passes one in:
test "bob cannot complete alice's todo" { var alice: Principal.user = alice_with_todos() var bob: Principal.user = bob_with_no_todos() var todo: Todo = first_todo(alice) assert complete_todo(todo.id, bob) fails TodoNotFound}
Here Bob tries to complete one of Alice's todos. Policy reports a record the caller may not see as the same not-found failure as a record that doesn't exist (Concealing records), so the test checks exactly what Bob would see.
Making a caller
Tests, fixtures and seeds make a user caller with Principal.user(user),
and a service caller with Principal.service(service). Application code
can't, because there a caller only comes from signing in.
Signing in for a route test
A route test signs in with as:, which goes through the application's
real authentication. A service signs in with a key from
Service.issue_key. So a disabled user is refused exactly as in
production, and the sign-in checks are tested too. To test signing in
itself, test_passkeys signs in with a passkey and test_providers
through a sign-in provider, with the test run's local issuer as the
provider (Providers). A route that asks the
signed-in person for a passkey is confirmed with
test_passkeys.confirm(passkey)
(Passkeys in tests).
A role can count only for someone who signed in a particular way
(Roles that need a way in), so
a user's request says how they did, beside as::
var stock: Response = request { method: HttpMethod.get path: "/staff/stock" as: Principal.user(ann) sign_in: SignInMethod.google strength: AuthenticationStrength.multi_factor}
sign_in:is aSignInMethod, andstrength:anAuthenticationStrength. The session is kept as one started that way is, a provider's lasting a day where a role's requirement names it, without the round trip to the provider.- Without them,
as:is someone who signed in by magic link with one factor. So a test of an admin's route fails until it says how the admin signed in, as the admin's own request would. - Either without
as:naming a user issemantic.request_invalid.
A call with a caller, outside a request, runs as someone signed in by magic link with one factor too, so a role that needs a way in is tested through a route.
A caller signs in once in a test, as a browser keeps its cookie. The first request as: a person signs them in, and each
later request by the same caller with the same sign_in: and
strength: sends that session again. So the session behaves as a real
one does across the test:
- After
clock.advance(24h), a session through a provider that a role's requirement names has ended, and the next request is 401invalid_credentials. A magic-link session ends after its 30 days. - A role taken away, or a person made inactive, is refused on the next request, as the session's next read finds it.
- A different caller, or the same caller with a different
sign_in:orstrength:, starts a session of its own, as a second browser would.
A service keeps its key the same way. A user on an API that takes only external tokens gets a fresh token from the local issuer for each request, as a client refreshes its provider's token, since no session keeps it. A request keeps the credential it signed in with, not a signed part a response renews, so each request reads the caller's record.
test "a Google session ends after a day" { var ann: Patron = staff_at("ann@lending.example", StaffRole.front_desk) var signed_in: Response = request { method: HttpMethod.get path: "/staff/stock" as: Principal.user(ann) sign_in: SignInMethod.google } assert signed_in.status == 200 clock.advance(24h) var a_day_later: Response = request { method: HttpMethod.get path: "/staff/stock" as: Principal.user(ann) sign_in: SignInMethod.google } assert a_day_later.status == 401}
Calls with no caller
A call a test, fixture or seed makes runs as the caller it passes, as
complete_todo runs as Bob here. A call that passes none, such as one
setting up data, runs without policy. So a call with no caller proves
behaviour and data, and a route test or a call with a caller proves
policy.
Markdown for agents: /docs/acting-as-a-user.md.Built from Jadpo main at aee2e8f.