MishkaCmsCore.DataCase (Mishka CMS Core v0.0.3)

Copy Markdown View Source

Test case for anything that touches the database but not the web layer — the resources, their actions and policies, the version trail, the dashboard queries.

Stands in for the CMS's MishkaCms.DataCase so this suite, and an extension's, runs without the CMS around it: it ships in lib for that, and sandboxes the repo config :mishka_cms_core, repo: … names. Each test gets its own sandboxed connection and everything it writes is rolled back at the end; async: false (the default here) shares that connection with any process the test spawns, which the async data loaders behind the dashboard widgets rely on.

See MishkaCmsCore.ConnCase for tests that go through the JSON API instead.

Summary

Functions

Turns changeset errors into a map of field to messages.

Drops settings keys from the RAM cache when the test ends.

One sandboxed connection for a whole module, opened in its setup_all and shared with every test and every process they spawn; what the module wrote is rolled back after its last test.

Makes this node one that serves the site (MishkaCmsCore.Runtime.Handlers.BootHandler.serving?/0) until the test ends, by setting the host's endpoint to serve — what mix phx.server and a release's PHX_SERVER do. The suite's endpoint does not serve, so a test without it runs as a node that does not. Only for a test that is not async: the setting is the node's.

Checks out a sandboxed connection for the test, shared with its spawned processes unless async, and — for a test that is not async — puts MishkaCmsCore.Runtime.States.SiteState back as it found it when the test ends, and unloads the runtime modules the test built.

Unloads, when the test (or the setup_all) that calls it ends, every runtime module — MishkaCmsCore.Runtime.<hash>.Component, .Page, .Layout, … — that was built while it ran, and forgets the compile failures recorded while it ran.

Sends the test {:signal, payload} for every Oban queue signal — a pause, resume, scale, start or stop — sent from then on until it ends, by any Oban instance on the node.

Functions

errors_on(changeset)

Turns changeset errors into a map of field to messages.

assert %{password: ["password is too short"]} = errors_on(changeset)

forget_settings(keys, scopes \\ [nil])

@spec forget_settings([String.t()], [String.t() | nil]) :: :ok

Drops settings keys from the RAM cache when the test ends.

setup do
  forget_settings(~w(cms.offline), [nil, site.id])
  :ok
end

The cache is process-global rather than sandboxed, so a value a test writes is still in it when the next suite runs — and cms.offline is not an inert one: left on, it takes the front end down and closes registration for everything that follows. A write reaches the cache through the "setting:updated" broadcast, so this is needed whether the test went through MishkaCmsCore.Settings or straight at the Setting resource.

The keys are dropped rather than re-read: on_exit runs in its own process, which does not hold the sandbox's connection, so there is no database to re-read them from.

module_sandbox()

@spec module_sandbox() :: %{module_sandbox: pid()}

One sandboxed connection for a whole module, opened in its setup_all and shared with every test and every process they spawn; what the module wrote is rolled back after its last test.

For a module whose tests only READ what one slow setup writes — a UI kit installed into the database, seconds each time — so it is written once instead of before every test:

setup_all do
  context = module_sandbox()
  UiKitHandler.install_chelekom!(site_id: nil)
  context
end

A test in such a module sees what the tests before it wrote, so keep them to reading. Only for a module that is not async.

serve_the_site(context)

@spec serve_the_site(map()) :: :ok

Makes this node one that serves the site (MishkaCmsCore.Runtime.Handlers.BootHandler.serving?/0) until the test ends, by setting the host's endpoint to serve — what mix phx.server and a release's PHX_SERVER do. The suite's endpoint does not serve, so a test without it runs as a node that does not. Only for a test that is not async: the setting is the node's.

setup :serve_the_site

setup_sandbox(tags)

Checks out a sandboxed connection for the test, shared with its spawned processes unless async, and — for a test that is not async — puts MishkaCmsCore.Runtime.States.SiteState back as it found it when the test ends, and unloads the runtime modules the test built.

A site a test creates is saved to SiteState, which is the node's, not the test's: the rollback takes the row back and leaves the entry, and a later test that saves a site of the same name or host is refused — its pages and its email then read as a site the node does not know. ExUnit runs the async modules first and the others one at a time after them, so a test that is not async is the only one touching the tables while it runs.

A runtime module is the node's too — see unload_built_on_exit/0.

unload_built_on_exit()

@spec unload_built_on_exit() :: :ok

Unloads, when the test (or the setup_all) that calls it ends, every runtime module — MishkaCmsCore.Runtime.<hash>.Component, .Page, .Layout, … — that was built while it ran, and forgets the compile failures recorded while it ran.

setup_sandbox/1 calls it for every test that is not async, and module_sandbox/0 for the module. A module built from a row is node-wide and outlives the rollback that takes the row away, and the runtime reaches a loaded module by its name without reading the row again; so a later test that writes its own row of the same name, from another bundle or with other attributes, would be drawn with the earlier test's build. What was loaded before the call is left as it was, unless it was rebuilt while the test ran.

watch_queue_signals()

@spec watch_queue_signals() :: :ok

Sends the test {:signal, payload} for every Oban queue signal — a pause, resume, scale, start or stop — sent from then on until it ends, by any Oban instance on the node.