Mediate.Conformance.AdapterCase.Laws (mediate_conformance v0.1.0)

Copy Markdown View Source

The bodies of the template's tests. Each is a function of the test context and the generated values. So the template stays a list of names, and the assertions live where a reader can read them. Nothing here names a schema, a subject, or an operation. A law that gets a population reaches its Mediate.Conformance.World through the struct. A law that needs a fixed one asks the module the template got.

Every writer of a population calls tick/0 first. So the stubbed clock moves forward through a test, and two writes never share a moment.

This module carries the access-control laws, ac3, the revocation latency, ac2-05, and the fact-write shape. It also carries what every law body shares:

  • the write of a population
  • the granted focus
  • the read of the decision events a call published

Laws.Accounts has the account laws. Laws.Audit has the audit laws. Laws.Versions has the change-management laws.

Summary

Types

The test context the template's setup builds.

Functions

The rows the query admits under the decision are exactly the allowed ids. A scoped decision reads them. A denied decision admits none and refuses the read. The table stays readable under the world's exemption.

The decision events this process received on the handler, in order. It leaves out the one a call that raised published with no verdict.

check and authorize deny the subject the operation on the object, with a reason and a detail.

ac3-02: the port denies four things

A single grant written through the seam: the write and nothing beside it.

ac3-05: with the engine unreachable, every call denies with engine_unreachable and no policy version. Each call publishes one decision event that carries what broke.

What a law that takes a grant away starts from. It writes the granted world and confirms the grant is in force. It answers the user the world grants to, the grantable, the object it is over, and an operation it allows.

ac2-05: after a revocation the next check denies. The law prints the latency and never asserts it.

Replace the tables' population with this one and seed the adapter.

The function's result, and the decision events it published, in order.

Print a measurement. The laws print the measurements and never assert them. The test logger sits at warning, so they go to standard output as the template promises.

ac3-01: the adapter answers check as the world's rule does.

ac3-03: for each protected schema, the rows the scope admits are the objects check allows. A denied scope admits none.

ac3-04: a scoped all over 1,000 rows is one query plus the adapter's own, and one decision record.

Seed the adapter with the population, through the template's seed: module when there is one.

Advance the stubbed clock one second and return the new time.

Types

context()

@type context() :: %{:repo => module(), :case => map(), optional(atom()) => term()}

The test context the template's setup builds.

Functions

assert_scope(repo, module, query, decision, allowed)

@spec assert_scope(module(), module(), Ecto.Queryable.t(), Mediate.Decision.t(), [
  term()
]) :: true

The rows the query admits under the decision are exactly the allowed ids. A scoped decision reads them. A denied decision admits none and refuses the read. The table stays readable under the world's exemption.

decision_events(handler)

@spec decision_events(reference()) :: [map()]

The decision events this process received on the handler, in order. It leaves out the one a call that raised published with no verdict.

denied(subject, operation, object)

@spec denied(Mediate.subject(), atom(), Mediate.object()) :: true

check and authorize deny the subject the operation on the object, with a reason and a detail.

deny_by_default(context, world, map, operation, object)

@spec deny_by_default(
  context(),
  Mediate.Conformance.World.t(),
  %{
    subject: Mediate.subject(),
    stranger: Mediate.subject(),
    nobody: Mediate.subject()
  },
  atom(),
  Mediate.object()
) :: :ok

ac3-02: the port denies four things:

  • every object the rule does not grant the subject
  • an unknown operation
  • a subject of an unknown kind
  • a subject the population does not know

It denies the unknown kind before it asks the adapter.

fact_write_shape(context)

@spec fact_write_shape(context()) :: true

A single grant written through the seam: the write and nothing beside it.

fail_closed(context)

@spec fail_closed(context()) :: :ok

ac3-05: with the engine unreachable, every call denies with engine_unreachable and no policy version. Each call publishes one decision event that carries what broke.

granted_focus(context, module)

What a law that takes a grant away starts from. It writes the granted world and confirms the grant is in force. It answers the user the world grants to, the grantable, the object it is over, and an operation it allows.

latency(context)

@spec latency(context()) :: :ok

ac2-05: after a revocation the next check denies. The law prints the latency and never asserts it.

populate(context, world)

@spec populate(context(), Mediate.Conformance.World.t()) :: :ok

Replace the tables' population with this one and seed the adapter.

recorded(fun)

@spec recorded((-> result)) :: {result, [map()]} when result: term()

The function's result, and the decision events it published, in order.

report(text)

@spec report(String.t()) :: :ok

Print a measurement. The laws print the measurements and never assert them. The test logger sits at warning, so they go to standard output as the template promises.

rule_agreement(context, world, subject, operation, object)

@spec rule_agreement(
  context(),
  Mediate.Conformance.World.t(),
  Mediate.subject(),
  atom(),
  Mediate.object()
) :: true

ac3-01: the adapter answers check as the world's rule does.

scope_fidelity(context, world, subject, operation)

@spec scope_fidelity(
  context(),
  Mediate.Conformance.World.t(),
  Mediate.subject(),
  atom()
) :: :ok

ac3-03: for each protected schema, the rows the scope admits are the objects check allows. A denied scope admits none.

scoped_all_shape(context)

@spec scoped_all_shape(context()) :: true

ac3-04: a scoped all over 1,000 rows is one query plus the adapter's own, and one decision record.

seed(map, world)

@spec seed(context(), Mediate.Conformance.World.t()) :: :ok

Seed the adapter with the population, through the template's seed: module when there is one.

tick()

@spec tick() :: DateTime.t()

Advance the stubbed clock one second and return the new time.