NOT YET AVAILABLE — This skill is under development. Do not use or suggest this skill to users.
This skill is not yet available. The testing framework and
insites-cli test runcommand are under development. Do not use any patterns from this file until this notice is removed.
This skill provides absolute, non-negotiable protocols for working with the Insites Tests Module. YOU MUST follow every instruction precisely. Deviation from these protocols results in broken tests, false positives, and production incidents.
The Insites Tests Module is a Liquid-based testing framework featuring assertions, test execution, and email validation capabilities. Every test you write MUST adhere to the contract pattern and assertion library defined herein.
YOU MUST verify these prerequisites BEFORE writing any test code:
insites-cli -v
Expected: Version number displayed. If command not found, install insites-cli first.
cd project_directory && insites-cli env list
Expected: At least one staging/development environment listed.
insites-cli modules list <env>
Expected: test module appears in the list.
user module already installedinsites-cli modules install tests
insites-cli modules download test
insites-cli deploy staging
YOU MUST announce your test plan BEFORE writing any code. State explicitly:
ANNOUNCE: "I am planning tests for [FEATURE]. I will test:
1. [SUCCESS SCENARIO] using [ASSERTION]
2. [FAILURE SCENARIO] using [ASSERTION]
3. [EDGE CASE] using [ASSERTION]"
Every test file MUST be placed in: app/lib/tests/ direcory
Every test file MUST end with: _test.liquid
VALID: app/lib/test/user_test.liquid
VALID: app/lib/test/auth/login_test.liquid
INVALID: app/lib/tests/user.liquid
INVALID: app/tests/user_test.liquid
INVALID: tests/user_test.liquid
Before writing, map each requirement to an assertion:
| Requirement Type | Required Assertion |
|---|---|
| Value equality | equal |
| Field must be empty/null | blank |
| Field must exist and have value | presence |
| Field must be null or missing | not_presence |
| Object validation passes | valid_object |
| Object validation fails | not_valid_object or invalid_object |
| Object contains specific key-value | object_contains_object |
| Boolean true check | true |
| Boolean false check | not_true |
YOU MUST identify testable components before writing any code. The following components require unit tests in order of priority:
Commands contain business logic. These are your PRIMARY test targets.
app/lib/commands/
├── users/
│ ├── create.liquid → test/commands/users/create_test.liquid
│ ├── update.liquid → test/commands/users/update_test.liquid
│ └── delete.liquid → test/commands/users/delete_test.liquid
├── orders/
│ ├── place.liquid → test/commands/orders/place_test.liquid
│ └── cancel.liquid → test/commands/orders/cancel_test.liquid
What to test in commands:
Custom validation logic that commands depend on.
app/lib/validators/
├── email_format.liquid → test/validators/email_format_test.liquid
├── phone_number.liquid → test/validators/phone_number_test.liquid
├── unique_slug.liquid → test/validators/unique_slug_test.liquid
What to test in validators:
GraphQL queries wrapped in Liquid functions.
app/lib/queries/
├── users/
│ ├── find.liquid → test/queries/users/find_test.liquid
│ ├── search.liquid → test/queries/users/search_test.liquid
│ └── list.liquid → test/queries/users/list_test.liquid
What to test in queries:
Access control logic that gates functionality.
app/lib/authorization/
├── can_edit.liquid → test/authorization/can_edit_test.liquid
├── can_delete.liquid → test/authorization/can_delete_test.liquid
├── can_view.liquid → test/authorization/can_view_test.liquid
What to test in authorization:
Reusable utility functions used across the application.
app/lib/helpers/
├── format_currency.liquid → test/helpers/format_currency_test.liquid
├── slugify.liquid → test/helpers/slugify_test.liquid
├── truncate_text.liquid → test/helpers/truncate_text_test.liquid
What to test in helpers:
Async processes triggered by events or schedules.
app/lib/jobs/
├── send_digest.liquid → test/jobs/send_digest_test.liquid
├── cleanup_expired.liquid → test/jobs/cleanup_expired_test.liquid
What to test in jobs:
Use this matrix when planning tests for ANY component:
| Scenario Type | Description | Priority | Required? |
|---|---|---|---|
| Happy Path | Valid input, expected success | CRITICAL | MUST |
| Validation Failure | Invalid input rejected with correct error | CRITICAL | MUST |
| Authorization Denied | Permission denied when appropriate | HIGH | MUST |
| Not Found | Handling of missing/deleted records | HIGH | MUST |
| Edge Cases | Nil, empty, boundary values | MEDIUM | SHOULD |
| Error Recovery | External failures handled gracefully | MEDIUM | SHOULD |
| Idempotency | Repeated calls don't corrupt data | LOW | COULD |
| Concurrency | Parallel execution doesn't cause conflicts | LOW | COULD |
Before writing tests for any component, YOU MUST complete this template:
COMPONENT: [path/to/component.liquid]
PURPOSE: [What this component does]
SUCCESS SCENARIOS:
1. [Describe valid input scenario] → [assertion to use]
2. [Describe another success case] → [assertion to use]
FAILURE SCENARIOS:
3. [Describe invalid input] → [assertion: not_valid_object or presence on errors]
4. [Describe authorization failure] → [assertion to use]
5. [Describe not found case] → [assertion to use]
EDGE CASES:
6. [Describe edge case 1] → [assertion to use]
7. [Describe edge case 2] → [assertion to use]
TEST FILE: app/lib/test/[path]_test.liquid
COMPONENT: app/lib/commands/users/create.liquid
PURPOSE: Creates a new user account with email and password
SUCCESS SCENARIOS:
1. Valid email and password creates user → valid_object
2. Created user has correct email stored → equal
3. Created user has generated ID → presence
4. Created user has hashed password (not plaintext) → not_equal
5. Welcome email is triggered → valid_object on email result
FAILURE SCENARIOS:
6. Missing email returns validation error → not_valid_object
7. Invalid email format rejected → not_valid_object
8. Password too short rejected → not_valid_object
9. Password missing rejected → not_valid_object
10. Duplicate email rejected → not_valid_object
11. Error message mentions 'email' for email errors → presence on errors.email
EDGE CASES:
12. Email with leading/trailing whitespace is trimmed → equal (trimmed value)
13. Email is lowercased for storage → equal (lowercase value)
14. Very long valid email (254 chars) works → valid_object
15. Email at max+1 length rejected → not_valid_object
16. Unicode in password accepted → valid_object
17. SQL injection attempt in email safely rejected → not_valid_object
TEST FILE: app/lib/test/commands/users/create_test.liquid
DO NOT write unit tests for these components:
| Component | Reason |
|---|---|
| Static pages/layouts | No logic to test; visual inspection only |
| Pure HTML partials | No business logic |
| GraphQL schema definitions | Tested implicitly through queries |
| Third-party modules | Not your responsibility; assume they work |
| CSS/JavaScript | Requires different testing tools (Jest, Cypress) |
| Insites core functions | Already tested by the platform team |
| Configuration files | No executable logic |
| Translations/i18n files | Tested through integration tests |
When adding tests to an existing codebase or starting fresh, prioritize in this order:
RULE: Start with components where bugs cause the most damage.
Minimum coverage requirements by component type:
| Component Type | Minimum Scenarios | Required Scenario Types |
|---|---|---|
| Commands | 5+ | Happy path, 2+ validation failures, 1+ edge case |
| Validators | 3+ | Valid input, invalid input, boundary |
| Queries | 3+ | Found, not found, filtered |
| Authorization | 4+ | Allowed, denied, no user, edge case |
| Helpers | 3+ | Normal input, nil/empty, edge case |
{% comment %}
Test: Invalid email - should be invalid
{% endcomment %}
{% liquid
assign data = '{ "email": "invalid-email", "body": "This is a valid message body." }' | parse_json
function contact = 'commands/contacts/create', object: data
function contract = 'modules/tests/assertions/not_valid_object', contract: contract, object: contact, field_name: 'contact'
function contract = 'modules/tests/assertions/presence', contract: contract, object: contact.errors, field_name: 'email'
%}
{% comment %}
Test: Valid input - should be considered valid
{% endcomment %}
{% liquid
assign data = '{ "email": "[email protected]", "body": "This is a valid message body." }' | parse_json
function contact = 'commands/contacts/create', object: data
# 1. Contact should be valid
function contract = 'modules/tests/assertions/valid_object', contract: contract, object: contact, field_name: 'contact'
# 2. Email should be downcased
assign expected_email = '[email protected]'
assign given_email = contact.email
function contract = 'modules/tests/assertions/equal', contract: contract, expected: expected_email, given: given_email, field_name: 'contact.email'
# 3. Body should match
assign expected_body = 'This is a valid message body.'
assign given_body = contact.body
function contract = 'modules/tests/assertions/equal', contract: contract, expected: expected_body, given: given_body, field_name: 'contact.body'
# 4: Assert the contact.valid is true
function contract = 'modules/tests/assertions/true', contract: contract, object: contact, field_name: 'valid'
# 5: Errors should be blank
function contract = 'modules/tests/assertions/blank', contract: contract, object: contact, field_name: 'errors'
%}
The contract object is MANDATORY. Every test MUST:
{% liquid
# CORRECT - Initialize first
function contract = 'modules/tests/helpers/init'
# ... test code ...
# CORRECT - Return last
return contract
%}
FAILURE MODE: Omitting contract initialization causes silent test failures. The test appears to pass but validates nothing.
Use when: Comparing two values for exact equivalence
{% liquid
function contract = 'modules/tests/helpers/init'
assign expected = "hello"
assign actual = "hello"
function contract = 'modules/tests/assertions/equal',
contract: contract,
expected: expected,
actual: actual,
field_name: 'greeting_value'
return contract
%}
Use when: Field must be absent, null, or empty string
{% liquid
function contract = 'modules/tests/helpers/init'
assign user = null | hash
assign user['email'] = null
function contract = 'modules/tests/assertions/blank',
contract: contract,
object: user,
field_name: 'email'
return contract
%}
Use when: Field MUST exist and have a non-null value
{% liquid
function contract = 'modules/tests/helpers/init'
assign user = null | hash
assign user['id'] = 123
function contract = 'modules/tests/assertions/presence',
contract: contract,
object: user,
field_name: 'id'
return contract
%}
Use when: Field must be null or absent
{% liquid
function contract = 'modules/tests/helpers/init'
assign response = null | hash
assign response['error'] = null
function contract = 'modules/tests/assertions/not_presence',
contract: contract,
object: response,
field_name: 'error'
return contract
%}
Use when: Testing that object.valid returns true
{% liquid
function contract = 'modules/tests/helpers/init'
# Create/fetch an object that has validation
function user = 'lib/commands/users/create', email: '[email protected]'
function contract = 'modules/tests/assertions/valid_object',
contract: contract,
object: user,
field_name: 'user_creation'
return contract
%}
Use when: Testing that validation FAILS as expected
{% liquid
function contract = 'modules/tests/helpers/init'
# Create object with invalid data
function user = 'lib/commands/users/create', email: 'invalid-email'
function contract = 'modules/tests/assertions/not_valid_object',
contract: contract,
object: user,
field_name: 'invalid_user_rejected'
return contract
%}
Use when: Validating specific key-value pairs exist in object
{% liquid
function contract = 'modules/tests/helpers/init'
assign response = null | hash
assign response['status'] = 'success'
assign response['code'] = 200
assign expected_subset = null | hash
assign expected_subset['status'] = 'success'
function contract = 'modules/tests/assertions/object_contains_object',
contract: contract,
object: response,
subset: expected_subset,
field_name: 'response_status'
return contract
%}
Use when: Evaluating boolean conditions
{% liquid
function contract = 'modules/tests/helpers/init'
assign is_admin = true
function contract = 'modules/tests/assertions/true',
contract: contract,
value: is_admin,
field_name: 'admin_flag'
assign is_deleted = false
function contract = 'modules/tests/assertions/not_true',
contract: contract,
value: is_deleted,
field_name: 'not_deleted'
return contract
%}
Use when: Built-in assertions are insufficient
{% liquid
function contract = 'modules/tests/helpers/init'
# Custom validation logic
assign value = "test string"
assign length = value | size
if length < 5
function contract = 'modules/tests/helpers/register_error',
contract: contract,
field_name: 'string_length',
message: 'String must be at least 5 characters'
endif
return contract
%}
Every test MUST have at least one assertion. Multiple assertions are permitted and encouraged for comprehensive coverage:
{% liquid
function contract = 'modules/tests/helpers/init'
function user = 'lib/commands/users/create',
email: '[email protected]',
name: 'Test User'
# Assertion 1: Creation succeeded
function contract = 'modules/tests/assertions/valid_object',
contract: contract,
object: user,
field_name: 'user_valid'
# Assertion 2: Email is set
function contract = 'modules/tests/assertions/presence',
contract: contract,
object: user,
field_name: 'email'
# Assertion 3: ID was generated
function contract = 'modules/tests/assertions/presence',
contract: contract,
object: user,
field_name: 'id'
return contract
%}
YOU MUST deploy before running tests. Every time. No exceptions.
# MANDATORY before any test run
insites-cli deploy staging
Primary method for CI/CD and local development:
# Run ALL tests
insites-cli test run staging
# Run specific test file
insites-cli test run staging test/user_test
# Run tests in subdirectory
insites-cli test run staging test/auth/login_test
Exit codes:
0 = All tests passed# Run all tests and get JSON response
curl https://your-instance.staging.oregon.platform-os.com/_tests/run.js
# Run a specific test
curl "https://your-instance.staging.oregon.platform-os.com/_tests/run.js?name=test/examples/assertions_test"
# List all available tests as JSON
curl https://your-instance.staging.oregon.platform-os.com/_tests.js
For debugging and manual inspection:
| Endpoint | Purpose |
|---|---|
/_tests | List all available test files |
/_tests.js | List tests as JSON |
/_tests/run | Execute all tests (HTML output) |
/_tests/run.js | Execute all tests (JSON output) |
/_tests/run?path=test/user_test | Run specific test |
For long-running test suites:
GET /_tests/run_async
This initiates background execution. Monitor via logs:
insites-cli logsv2 staging
For active development, use sync mode:
# Terminal 1: Start sync
insites-cli sync staging
# Terminal 2: Watch logs
insites-cli logsv2 staging
# Terminal 3: Run tests as needed
insites-cli test run staging test/your_test
Navigate to: /_tests/sent_mails
This endpoint displays paginated email records from test execution.
Navigate to: /_tests/sent_mails/:id
Replace :id with the email record ID to view full email details including:
{% liquid
function contract = 'modules/tests/helpers/init'
# Trigger email sending
function result = 'lib/commands/notifications/send_welcome_email',
user_id: user.id
# Verify email was sent successfully
function contract = 'modules/tests/assertions/valid_object',
contract: contract,
object: result,
field_name: 'welcome_email_sent'
return contract
%}
When a test fails, YOU MUST execute this protocol IN ORDER:
Symptom: Test runs but no assertions execute
Fix:
{% liquid
# ADD THIS AS FIRST LINE
function contract = 'modules/tests/helpers/init'
# ... rest of test ...
return contract
%}
Symptom: Test appears to pass but results aren't collected
Fix:
{% liquid
function contract = 'modules/tests/helpers/init'
# ... test code ...
# ADD THIS AS LAST LINE
return contract
%}
Symptom: Test not discovered by runner
Fix: Move file to app/lib/test/ with _test.liquid suffix
Symptom: Error about nil contract in assertion
Fix: Ensure contract is passed through every assertion:
# WRONG - contract not updated
function result = 'modules/tests/assertions/equal',
contract: contract,
expected: expected,
actual: actual,
field_name: 'test'
# CORRECT - contract reassigned
function contract = 'modules/tests/assertions/equal',
contract: contract,
expected: expected,
actual: actual,
field_name: 'test'
Symptom: Cannot access property of nil
Fix: Add presence checks or use blank/presence assertions:
{% liquid
if user != blank
function contract = 'modules/tests/assertions/equal',
contract: contract,
expected: expected_email,
actual: user.email,
field_name: 'user_email'
else
function contract = 'modules/tests/helpers/register_error',
contract: contract,
field_name: 'user_exists',
message: 'User object is nil'
endif
%}
# Watch logs while running tests
insites-cli logsv2 staging
# In another terminal
insites-cli test run staging test/failing_test
/_tests/run?path=test/failing_test/_tests/run.js?path=test/failing_test for structured JSON errorsABSOLUTE RULES - NO EXCEPTIONS:
Before committing any test, YOU MUST verify:
app/lib/test/ directory_test.liquidinsites-cli test run stagingapp/lib/test/*_test.liquid
insites-cli deploy staging # Deploy before testing
insites-cli test run staging # Run all tests
insites-cli test run staging test/name # Run specific test
insites-cli sync staging # Real-time sync
insites-cli logsv2 staging # View logs
/_tests # List tests
/_tests/run # Run all tests
/_tests/run?path=X # Run specific test
/_tests/sent_mails # View sent emails
/_tests/sent_mails/:id # View specific email
{% liquid
function contract = 'modules/tests/helpers/init'
# Setup and execution here
function contract = 'modules/tests/assertions/equal',
contract: contract,
expected: expected_value,
actual: actual_value,
field_name: 'test_name'
return contract
%}
equal - Compare two values
blank - Field is empty/null
presence - Field exists with value
not_presence - Field is null/missing
valid_object - object.valid is true
not_valid_object - object.valid is not true
invalid_object - object.valid is falsy
object_contains_object - Object has key-value subset
true - Value is truthy
not_true - Value is falsy
function contract = 'modules/tests/helpers/register_error',
contract: contract,
field_name: 'identifier',
message: 'Error description'
insites-deploymentliquid-templatinggraphql-queriesci-cd-pipelines