* Add v3 proposals (ALP-017 through ALP-028) and update language reference Twelve new proposals covering transition graphs, state-dependent field presence via when clauses, ordered collection semantics, backtick-quoted enum literals and black box collection operations. Language reference updated with all adopted features, produces/consumes removed. * Add propagate skill for plan/model test generation New propagate skill generates property-based tests from Allium specs using allium plan and allium model output. * Broaden agent and skill trigger language, add propagate to root skill Tend, weed, distill and elicit frontmatter updated with wider trigger vocabulary. Root SKILL.md updated with v3 syntax and propagate workflow. README updated with CLI installation.
10 KiB
Test generation
This document describes categories of tests that a person, agent or skill should derive from an Allium specification. It is not a tool to invoke. The taxonomy maps spec constructs to test obligations; how those tests are expressed depends on the target language and test framework.
From an Allium specification, generate:
Entity and value type tests (per entity and value type):
- Verify all declared fields are present with correct types
- Verify optional fields accept null and non-null values
- Verify optional navigation (
?.) short-circuits to null when the left side is absent - Verify null coalescing (
??) produces the default when the left side is null and the original value otherwise - Verify relationships navigate to the correct related entities
- Verify join lookups (
Entity{field1, field2}) resolve to the correct instance, or null when no match exists - For value types, verify equality is structural (by field values, not reference)
Enumeration tests (per named enum):
- Verify fields typed with the same named enum are comparable
- Verify
inandnot inmembership tests work against set literals of enum values
Sum type tests (per sum type):
- Verify each variant has its variant-specific fields accessible within a type guard
- Verify variant-specific fields are inaccessible outside type guards
- Verify all variants listed in the discriminator are handled in conditional logic
- Verify an entity cannot be multiple variants simultaneously
- Verify creation uses the variant name, not the base entity name
- Verify a
.createdtrigger on the base entity fires for any variant and can be narrowed with a type guard
Derived value and projection tests (per derived value or projection):
- Verify derived values compute correctly for representative entity states
- Verify projections filter correctly against their
wherepredicate - Verify projections with
-> fieldmapping extract the correct field and exclude nulls - Verify parameterised derived values return correct results for representative arguments
- Verify derived values involving
noware volatile (re-evaluate on each read) - Verify built-in collection operations (
.any(),.all(),.count, set+/-,.first,.last) produce correct results - Verify black box collection functions (free-standing calls like
filter(collection, predicate)) are treated as opaque with implementation-defined semantics
Default instance tests (per default declaration):
- Verify the named instance exists unconditionally
- Verify all specified field values match the declaration
- Verify cross-references between defaults resolve correctly (e.g.
inherits_from: viewer)
Config tests (per config block):
- Verify each parameter has its declared default value when not overridden
- Verify overriding a parameter replaces the default
- Verify mandatory parameters (no default) cause an error when omitted
- Verify expression-form defaults evaluate correctly, including type compatibility of arithmetic operands
- Verify qualified references to imported config resolve through the override chain
- Verify config parameters that depend on other parameters resolve in the correct order
Invariant tests (per expression-bearing invariant Name { expr }):
- Verify the invariant holds after every state-changing rule that touches the constrained entities
- Verify the invariant holds for edge-case entity states (boundary values, empty collections)
- For entity-level invariants, verify they hold after any field mutation on the entity
- For invariants using
implies, verify the consequent holds when the antecedent is true and that the invariant is trivially satisfied when the antecedent is false
Rule tests (per rule):
- Success case: all preconditions met, verify all postconditions hold
- Failure cases: verify the rule is rejected when each
requiresclause independently fails - Edge cases: boundary values for numeric conditions
- For conditional ensures, verify each branch fires under the correct condition and that
ifguards read resulting state, not pre-rule state - For entity-creating ensures, verify the created entity has the specified field values
- For
letbindings, verify the bound value is correct and available to subsequent clauses - For
not existsin ensures, verify the entity is removed from the system - For bulk updates (
forin ensures), verify the postcondition applies to every element in the collection - For rule-level
foriteration, verify the rule body executes for each matching element - For chained triggers (trigger emission in ensures), verify the downstream rule fires with the correct parameters
State transition tests (per entity with status enum):
- Valid transitions succeed via their rules
- Invalid transitions are rejected (no rule allows them)
- Terminal states have no outbound transitions
- For
transitions_totriggers, verify the rule does not fire on entity creation - For
becomestriggers, verify the rule fires both on creation and on transition
State-dependent field tests (per field with a when clause):
- Verify the field is present (has a meaningful value) when the entity is in a qualifying state
- Verify the field is absent (has no meaningful value) when the entity is outside the qualifying states
- When a rule transitions into the
whenset, verify it sets the field (entering obligation) - When a rule transitions out of the
whenset, verify it clears the field (leaving obligation) - When a rule moves within the
whenset, verify no obligation fires (field is already present) - When two rules converge on the same qualifying state, verify both set the field
- Verify accessing a
when-qualified field without a state guard is rejected - For derived values computed from
when-qualified fields, verify the inferredwhenset matches the intersection of the inputs'whensets
How "present" and "absent" are tested depends on how the implementation represents the entity. When the entity is modelled as a sealed hierarchy or variant type (one class per lifecycle state), presence and absence are structural: the field exists on one variant and not another. The compiler enforces the when clause. When the entity is modelled as a single mutable class with nullable fields, test that the field is meaningfully populated in qualifying states and null or empty outside them. Both representations are valid for the same spec; the choice is an implementation concern. The spec-level concept is lifecycle-dependent presence; the test adapts to the representation.
Temporal tests (per time-based trigger):
- Before deadline: rule does not fire, state unchanged
- At deadline: rule fires, postconditions hold
- After deadline: rule has already fired, does not re-fire
- Verify
requiresguard prevents re-firing when entity remains in the qualifying state - Verify temporal triggers on optional fields do not fire when the field is null
Communication tests (per Notification/Email/etc):
- Verify communication is triggered by the correct rule
- Verify recipient is correct
- Verify template and data are passed
Surface tests (per surface):
- Exposure tests: verify each item in
exposesis accessible to the specified party - For
foriteration inexposes, verify each element in the collection is exposed - Provides availability: verify provided operations appear when their
whenconditions are true - Provides unavailability: verify provided operations are hidden when
whenconditions are false, including when the corresponding rule'srequiresclauses are not met - Actor identification: verify only entities matching the actor's
identified_bypredicate can interact - For actors with
within, verify interaction is scoped to the declared context (e.g. actions in one workspace do not affect another) - Party restriction: verify the surface is not accessible to other party types
- Context scoping: verify the surface instance is absent when no entity matches the
contextpredicate - Related surface navigation: verify navigation to related surfaces resolves to the correct context entity
- Guarantee tests: verify stated
@guaranteeannotations hold across the boundary - Timeout tests: verify the referenced temporal rule fires within the surface's context
Contract declaration tests (per contract declaration):
- Verify the implementation satisfies each typed signature in the contract
- Verify
@invariantannotations are honoured across the boundary - For surfaces that
demanda contract, verify the counterpart provides all signatures - For surfaces that
fulfila contract, verify this surface supplies all signatures
Cross-module tests (per use declaration):
- Verify qualified entity references resolve to the imported module's entities
- Verify rules that respond to external triggers (state transitions, trigger emissions) from imported modules fire correctly
- Verify external entities referenced as type placeholders accept the consuming spec's concrete type
Cross-rule interaction tests (per rule with entity-creating ensures):
- Verify guards prevent duplicate entity creation when sibling rules re-trigger on the same parent
- Verify
providesentries are unavailable when anyrequiresconjunct of the corresponding rule is false
Scenario tests (per specification):
- Happy path through the main rule chain (follow chained triggers from entry point to terminal state)
- Edge cases and error paths at each decision point
- When two rules could fire on the same entity, verify the resulting state is consistent regardless of order
Concurrency note: The language reference does not formally define rule atomicity or evaluation order. Treat rules as atomic (completing entirely or not at all) as a reasonable default.
Constructs without dedicated test categories: given block bindings are exercised through rule tests that reference them. deferred specifications are tested in their own modules. open question declarations are checker warnings, not test obligations. exists as an if-condition is covered by rule conditional ensures tests. Discard bindings (_) have no observable effect to test.