Savvi Studio

Core Test Module Architecture

This document describes how module authors can verify module correctness using declarative, metadata-native tests. The emphasis is purity and determinism: tests should use the same expression language and object model as runtime modules, not imperative side scripts.

Author-Centric Principles

  • Test with the same primitives used in production modules.
  • Keep assertions declarative and side-effect aware.
  • Prefer pure expression evaluation over imperative code.
  • Validate behavior at lifecycle boundaries, not only at API endpoints.
  • Treat test modules as contracts for module author intent.

What Module Authors Should Assert

  1. Installation lifecycle
  • Required resource types/predicates/templates are installed.
  • Referential links are resolvable after install.
  • Default configuration is applied as expected.
  1. Template invocation lifecycle
  • Inputs are validated and mapped correctly.
  • Exported objects are present and structurally correct.
  • Invocation remains deterministic for identical inputs.
  1. Upgrade and rollback lifecycle
  • Canonical version transitions preserve required invariants.
  • No unexpected object shape drift.
  • Deprecation/rename semantics are explicit and queryable.
  1. Runtime contract surface
  • Expected procedures/routes are present.
  • Expected operation groups (for example get/list/put/del) are available where the module declares support.
  • Missing operations are explicit and asserted as known gaps.
  1. Feature module local test templates
  • Each feature module exports a small set of author-facing test templates.
  • Suggested naming:
    • assert-install-invariants
    • assert-template-invocation
    • assert-lifecycle-transition
    • seed-minimal-fixture
  1. Optional shared core.test module
  • Introduce only when cross-module composition repeats.
  • Role: compose module-exported assertions and seed templates into reusable suites.
  • Do not encode product business logic in core.test.
  1. Module profile boot helper

Declarative Assertion Model

Assertions should be represented as module/test objects evaluated by the same expression language used by templates and reducers.

Example shape (illustrative):

type: test.assert
with:
  name: install-registers-required-types
  when:
    phase: postInstall
  given:
    modules: [feature-x]
  expect:
    all:
      - expr: { $exists: { $resourceType: feature_x.item } }
      - expr: { $exists: { $predicate: feature_x.relates_to } }

Template invocation assertion (illustrative):

type: test.assert
with:
  name: template-produces-exported-root
  when:
    phase: templateInvoke
  invoke:
    template: feature-x-seed
    with:
      key: sample-1
  expect:
    all:
      - expr: { $exists: { $ref: invoke.exports.root } }
      - expr: { $eq: [{ $ref: invoke.exports.root.data.status }, active] }

Rules for purity:

  • Assertions must not rely on wall-clock time unless explicitly fixed in test input.
  • Expression evaluation should be deterministic for equal input state.
  • Randomness must be seeded and declared.
  • Assertions should not mutate global state beyond declared setup/teardown scopes.

Lifecycle Hook Coverage Matrix

Each module should maintain a minimal matrix:

  • preInstall: guardrails and preconditions.
  • postInstall: registration and wiring invariants.
  • templateInvoke: input/output contract checks.
  • preUpgrade/postUpgrade: compatibility and migration checks.
  • preUninstall/postUninstall: dependency and cleanup checks.

Not every module needs every hook, but unsupported hooks should be explicit.

Integration with Existing Suites

  • Router/API contracts remain in integration tests.
  • Declarative module assertions should complement, not replace, API tests.
  • Prefer parameterized profile tests for broad coverage.
  • Keep component/visual tests focused on metadata-to-UI mapping and behavior outcomes.

Current related files:

Adoption Path

  1. Start local: add one install assertion template and one template-invocation assertion template per module.
  2. Add lifecycle assertions for upgrade/uninstall where module lifecycle complexity exists.
  3. Promote repeated cross-module assertions into core.test.
  4. Keep assertion templates versioned with modules to prevent drift.