Testing schema — QA Protocol reference
Testing schemas of the ObjectStack QA Protocol: TestAction, TestActionType and 6 more — each property with its type, default and a TypeScript example.
Source: packages/spec/src/qa/testing.zod.ts
TypeScript Usage
import { TestActionSchema, TestActionTypeSchema, TestAssertionSchema, TestAssertionTypeSchema, TestContextSchema, TestScenarioSchema, TestStepSchema, TestSuiteSchema } from '@objectstack/spec/qa';
import type { TestAction, TestActionType, TestAssertion, TestAssertionType, TestContext, TestScenario, TestStep, TestSuite } from '@objectstack/spec/qa';
// Validate data
const result = TestActionSchema.parse(data);TestAction
A single test action to execute against the system
Properties
| Property | Type | Required | Description |
|---|---|---|---|
| type | Enum<'create_record' | 'update_record' | 'delete_record' | 'read_record' | 'query_records' | 'api_call' | 'run_script' | 'wait'> | ✅ | The action type to execute |
| target | string | ✅ | Target Object, API Endpoint, or Function Name |
| payload | Record<string, any> | optional | Data to send or use |
| user | string | optional | Run as specific user/role for impersonation testing |
TestActionType
Type of test action to perform
Allowed Values
create_recordupdate_recorddelete_recordread_recordquery_recordsapi_callrun_scriptwait
TestAssertion
A test assertion that validates the result of a test action
Properties
| Property | Type | Required | Description |
|---|---|---|---|
| field | string | ✅ | Field path in the result to check, resolved against the parsed response body root — no "body." prefix (e.g. "data.0.status") |
| operator | Enum<'equals' | 'not_equals' | 'contains' | 'not_contains' | 'is_null' | 'not_null' | 'gt' | 'gte' | 'lt' | 'lte' | 'error'> | ✅ | Comparison operator to use |
| expectedValue | any | ✅ | Expected value to compare against |
TestAssertionType
Comparison operator for test assertions
Allowed Values
equalsnot_equalscontainsnot_containsis_nullnot_nullgtgteltlteerror
TestContext
Initial context or variables for the test
Type: Record<string, any>
TestScenario
A complete test scenario with setup, execution steps, and teardown
Properties
| Property | Type | Required | Description |
|---|---|---|---|
| id | string | ✅ | Unique scenario identifier |
| name | string | ✅ | Scenario name for test reports |
| description | string | optional | Detailed description of the test scenario |
| tags | string[] | optional | Tags for filtering and categorization (e.g. "critical", "regression", "crm") |
| setup | { name: string; description?: string; action: object; assertions?: object[]; … }[] | optional | Steps to run before main test (preconditions) |
| steps | { name: string; description?: string; action: object; assertions?: object[]; … }[] | ✅ | Main test sequence to execute |
| teardown | { name: string; description?: string; action: object; assertions?: object[]; … }[] | optional | Steps to cleanup after test execution |
| requires | { params?: string[]; services?: Enum<'metadata' | 'data' | 'auth' | 'storage' | 'file-storage' | 'search' | 'cache' | …>[] } | optional | Preconditions judged before the scenario's first step (setup included). Every entry must hold, or the scenario is SKIPPED with a reason naming each unmet entry — counted separately by os test and never counted as passed |
Nested Shape: TestScenario.setup[number]
A single step in a test scenario, consisting of an action and optional assertions
| Property | Type | Required | Description |
|---|---|---|---|
| name | string | ✅ | Step name for identification in test reports |
| description | string | optional | Human-readable description of what this step tests |
| action | { type: Enum<'create_record' | 'update_record' | 'delete_record' | 'read_record' | …>; target: string; payload?: Record<string, any>; user?: string } | ✅ | The action to execute in this step |
| assertions | { field: string; operator: Enum<'equals' | 'not_equals' | 'contains' | 'not_contains' | 'is_null' | 'not_null' | …>; expectedValue: any }[] | optional | Assertions to validate after the action completes |
| capture | Record<string, string> | optional | Map result fields to context variables, paths resolved against the response body root (e.g. { "newId": "data.id" }) |
Nested Shape: TestScenario.steps[number]
A single step in a test scenario, consisting of an action and optional assertions
| Property | Type | Required | Description |
|---|---|---|---|
| name | string | ✅ | Step name for identification in test reports |
| description | string | optional | Human-readable description of what this step tests |
| action | { type: Enum<'create_record' | 'update_record' | 'delete_record' | 'read_record' | …>; target: string; payload?: Record<string, any>; user?: string } | ✅ | The action to execute in this step |
| assertions | { field: string; operator: Enum<'equals' | 'not_equals' | 'contains' | 'not_contains' | 'is_null' | 'not_null' | …>; expectedValue: any }[] | optional | Assertions to validate after the action completes |
| capture | Record<string, string> | optional | Map result fields to context variables, paths resolved against the response body root (e.g. { "newId": "data.id" }) |
Nested Shape: TestScenario.teardown[number]
A single step in a test scenario, consisting of an action and optional assertions
| Property | Type | Required | Description |
|---|---|---|---|
| name | string | ✅ | Step name for identification in test reports |
| description | string | optional | Human-readable description of what this step tests |
| action | { type: Enum<'create_record' | 'update_record' | 'delete_record' | 'read_record' | …>; target: string; payload?: Record<string, any>; user?: string } | ✅ | The action to execute in this step |
| assertions | { field: string; operator: Enum<'equals' | 'not_equals' | 'contains' | 'not_contains' | 'is_null' | 'not_null' | …>; expectedValue: any }[] | optional | Assertions to validate after the action completes |
| capture | Record<string, string> | optional | Map result fields to context variables, paths resolved against the response body root (e.g. { "newId": "data.id" }) |
Nested Shape: TestScenario.requires
| Property | Type | Required | Description |
|---|---|---|---|
| params | string[] | optional | Environment variables that must be set to a non-empty value in the process running os test — the runner's own environment, not the target server's. An unset or empty variable skips the scenario (SKIPPED, never passed) with a reason naming the variable |
| services | Enum<'metadata' | 'data' | 'auth' | 'storage' | 'file-storage' | 'search' | 'cache' | …>[] | optional | Services the target must declare in its discovery document as enabled with status available (ADR-0076 D12), read from the discovery document os test already fetches once per run. An entry the target does not declare that way skips the scenario (SKIPPED, never passed) with a reason naming the service and the services the target does declare available |
| plugins | never | optional | [REMOVED] scenarios[].requires.plugins was removed in @objectstack/spec 17.5.0 (ADR-0049 enforce-or-remove) — nothing ever checked it: os test reaches its target over HTTP and no served surface lists the loaded plugins, so a scenario naming a missing plugin ran anyway. Delete the key and name the service the scenario needs in requires.services, which is judged against the services the target's discovery document declares enabled and available — an unmet entry skips the scenario and says why. Plugin → service: @objectstack/service-analytics → analytics, @objectstack/plugin-auth → auth, @objectstack/service-automation → automation, @objectstack/service-cache → cache, @objectstack/service-queue → queue, @objectstack/service-job → job, @objectstack/service-realtime → realtime, @objectstack/service-storage → storage, @objectstack/service-i18n → i18n, @objectstack/service-messaging → notification, @objectstack/metadata-protocol → ui. A plugin not listed fills no discovery service slot, so there is no service to require for it. |
TestStep
A single step in a test scenario, consisting of an action and optional assertions
Properties
| Property | Type | Required | Description |
|---|---|---|---|
| name | string | ✅ | Step name for identification in test reports |
| description | string | optional | Human-readable description of what this step tests |
| action | { type: Enum<'create_record' | 'update_record' | 'delete_record' | 'read_record' | …>; target: string; payload?: Record<string, any>; user?: string } | ✅ | The action to execute in this step |
| assertions | { field: string; operator: Enum<'equals' | 'not_equals' | 'contains' | 'not_contains' | 'is_null' | 'not_null' | …>; expectedValue: any }[] | optional | Assertions to validate after the action completes |
| capture | Record<string, string> | optional | Map result fields to context variables, paths resolved against the response body root (e.g. { "newId": "data.id" }) |
Nested Shape: TestStep.action
| Property | Type | Required | Description |
|---|---|---|---|
| type | Enum<'create_record' | 'update_record' | 'delete_record' | 'read_record' | …> | ✅ | The action type to execute |
| target | string | ✅ | Target Object, API Endpoint, or Function Name |
| payload | Record<string, any> | optional | Data to send or use |
| user | string | optional | Run as specific user/role for impersonation testing |
Nested Shape: TestStep.assertions[number]
A test assertion that validates the result of a test action
| Property | Type | Required | Description |
|---|---|---|---|
| field | string | ✅ | Field path in the result to check, resolved against the parsed response body root — no "body." prefix (e.g. "data.0.status") |
| operator | Enum<'equals' | 'not_equals' | 'contains' | 'not_contains' | 'is_null' | 'not_null' | …> | ✅ | Comparison operator to use |
| expectedValue | any | ✅ | Expected value to compare against |
TestSuite
A collection of test scenarios grouped into a test suite
Properties
| Property | Type | Required | Description |
|---|---|---|---|
| name | string | ✅ | Test suite name |
| scenarios | { id: string; name: string; description?: string; tags?: string[]; … }[] | ✅ | List of test scenarios in this suite |
Nested Shape: TestSuite.scenarios[number]
A complete test scenario with setup, execution steps, and teardown
| Property | Type | Required | Description |
|---|---|---|---|
| id | string | ✅ | Unique scenario identifier |
| name | string | ✅ | Scenario name for test reports |
| description | string | optional | Detailed description of the test scenario |
| tags | string[] | optional | Tags for filtering and categorization (e.g. "critical", "regression", "crm") |
| setup | { name: string; description?: string; action: object; assertions?: object[]; … }[] | optional | Steps to run before main test (preconditions) |
| steps | { name: string; description?: string; action: object; assertions?: object[]; … }[] | ✅ | Main test sequence to execute |
| teardown | { name: string; description?: string; action: object; assertions?: object[]; … }[] | optional | Steps to cleanup after test execution |
| requires | { params?: string[]; services?: Enum<'metadata' | 'data' | 'auth' | 'storage' | 'file-storage' | 'search' | 'cache' | …>[] } | optional | Preconditions judged before the scenario's first step (setup included). Every entry must hold, or the scenario is SKIPPED with a reason naming each unmet entry — counted separately by os test and never counted as passed |
QA Protocol
The ObjectStack QA Protocol in 1 reference page: every schema in @objectstack/spec with its properties, types, defaults and a TypeScript example.
Security Protocol
The ObjectStack Security Protocol in 5 reference pages: every schema in @objectstack/spec with its properties, types, defaults and a TypeScript example.