Documentation
¶
Overview ¶
Package tasktest is the conformance suite for contracts.Service, and a fake that passes it.
It exists because an interface is justified by a passing fake and not by a second production implementation (AGENTS.md rule 8). RunService is the specification of the lifecycle written as executable cases; the real service and the fake both run it, so "the fake behaves like the real thing" is a test result rather than a hope, and a consumer that tests against the fake is testing against the rules the database enforces.
Index ¶
- func RunService(t *testing.T, h Harness)
- type Fake
- func (f *Fake) Assign(_ context.Context, _ db.Tx[db.Tenant], id, assignee uuid.UUID) (*contracts.Task, error)
- func (f *Fake) CheckSLA(_ context.Context, _ db.Tx[db.Tenant], id uuid.UUID) (*contracts.Task, error)
- func (f *Fake) Published() []string
- func (f *Fake) Put(task *contracts.Task) uuid.UUID
- func (f *Fake) Resolve(_ context.Context, _ db.Tx[db.Tenant], id uuid.UUID, resolution string) (*contracts.Task, error)
- func (f *Fake) Tasks() map[uuid.UUID]contracts.Task
- type Fixture
- type Harness
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func RunService ¶
RunService is the conformance suite. Every implementation of contracts.Service passes it, or it is not one.
Types ¶
type Fake ¶
type Fake struct {
// contains filtered or unexported fields
}
Fake is contracts.Service over a map: the same rules, no database, no transaction. A consumer that wants to test what it does when a task is assigned takes one of these instead of a Postgres.
It ignores the transaction it is handed, and that is the honest limit of it: it cannot tell a caller that a write did not commit, because nothing here commits. Everything it can be wrong about is what RunService checks, and fake_test.go runs the whole suite against it.
func (*Fake) Assign ¶
func (f *Fake) Assign(_ context.Context, _ db.Tx[db.Tenant], id, assignee uuid.UUID) (*contracts.Task, error)
Assign mirrors internal.Service.Assign.
func (*Fake) CheckSLA ¶
func (f *Fake) CheckSLA(_ context.Context, _ db.Tx[db.Tenant], id uuid.UUID) (*contracts.Task, error)
CheckSLA mirrors internal.Service.CheckSLA.
func (*Fake) Put ¶
Put stores a task, giving it an id if it has none, and returns the id. It is the fake's stand-in for the create route.
type Fixture ¶
type Fixture struct {
Ctx context.Context
Tx db.Tx[db.Tenant]
Service contracts.Service
// Seed stores a task and returns the id it was given. It is the one thing
// the suite cannot do through the interface, because the interface is the
// lifecycle and creating a task is kit/rest's five routes.
Seed func(*contracts.Task) uuid.UUID
// Published is the events the implementation has published so far, in
// order. The fake returns what it recorded; the real service's harness
// reads the outbox rows its transaction has written.
//
// It is part of the fixture because half of what the lifecycle promises is
// silence: every command is idempotent, and an idempotent command that
// publishes is a subscriber told twice about one thing. A suite that could
// only see return values could not check that, and it is the half a retry
// exercises every day.
Published func() []string
}
Fixture is one case's world: a Service, the transaction its commands take, and a store to put tasks in. The transaction is the real thing for the real service and the zero value for the fake, which never looks at it.
type Harness ¶
Harness builds one Fixture and calls run with it. It is written this way round — the harness calling the case rather than returning to it — because the real service's fixture is a transaction, and a transaction is a scope somebody has to close: the harness wraps the case in db.Run and rolls back on the way out, which a Harness that only returned a Fixture could not do.
RunService calls it once per case, so no case sees another's rows.