# Acting as a user

An action receives its caller, the person or service signed in, as a
typed parameter ([The principal](/docs/the-principal.md)). So a test
passes one in:

```jadpo
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](/docs/concealing-records.md)),
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](/docs/checking-the-person-sign-in.md#providers)). A route that asks the
signed-in person for a passkey is confirmed with
`test_passkeys.confirm(passkey)`
([Passkeys in tests](/docs/in-development-and-tests.md#passkeys-in-tests)).

A role can count only for someone who signed in a particular way
([Roles that need a way in](/docs/a-membership-role.md#roles-that-need-a-way-in)), so
a user's request says how they did, beside `as:`:

```jadpo
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 a `SignInMethod`, and `strength:` an
  `AuthenticationStrength`. 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 is `semantic.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 401
  `invalid_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:`
  or `strength:`, 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.

```jadpo
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](/docs/policy.md).
