@progalaxyelabs/stonescriptphp-api-tester
v0.1.1
Published
API test infrastructure for StoneScriptPHP projects — coverage validation and test execution
Downloads
95
Maintainers
Readme
@progalaxyelabs/stonescriptphp-api-tester
API test infrastructure for StoneScriptPHP projects — coverage validation and test execution.
Zero npm dependencies. Uses only Node.js built-ins.
Installation
npm install @progalaxyelabs/stonescriptphp-api-testerOr use directly via npx:
npx @progalaxyelabs/stonescriptphp-api-tester validate
npx @progalaxyelabs/stonescriptphp-api-tester run --flow=smokeCommands
api-tester validate
Static analysis — cross-references your testplan.json against routes.php and the generated TypeScript api-client to find coverage gaps.
api-tester validate [options]Options:
| Option | Default | Description |
|--------|---------|-------------|
| --testplan=<path> | ./api/tests/testplan.json | Path to testplan.json |
| --client=<path> | ./api/client/src/index.ts | Path to api-client index.ts |
| --routes=<path> | ./api/src/config/routes.php | Path to routes.php |
| --verbose | | Show all matches, not just failures |
| --json | | Output as JSON (for CI pipelines) |
Coverage checks performed:
- API Client -> Testplan (MUST be 100%): Every endpoint in the TypeScript client must appear in testplan.json
- Routes.php -> Testplan (MUST be 100%): Every route in routes.php must appear in testplan.json
- Testplan -> API Client (informational): Shows testplan endpoints not in api-client (expected for untyped routes)
- Graph Coverage (MUST be 100%): Every endpoint ID must appear in at least one graph edge
- Flow Coverage (SHOULD be 100%): Every endpoint ID should appear in at least one flow
Exit codes: 0 = all mandatory checks pass, 1 = coverage gaps found.
api-tester run
Runtime test execution — sends real HTTP requests following flows defined in testplan.json.
api-tester run [options]Options:
| Option | Default | Description |
|--------|---------|-------------|
| --testplan=<path> | ./api/tests/testplan.json | Path to testplan.json |
| --base-url=<url> | http://localhost:3000 | Base URL for API |
| --flow=<name> | | Flow name to run, or all for all flows |
| --endpoint=<id> | | Run a single endpoint by ID (for debugging) |
| --dry-run | | Print what would happen without making HTTP calls |
| --verbose | | Show request/response bodies |
| --token=<token> | | Pre-obtained auth token (skip login) |
| --email=<email> | | Email for login step |
| --password=<pass> | | Password for login step |
| --platform=<name> | | Platform name for auth login |
Examples:
# Run the smoke flow
api-tester run --flow=smoke --base-url=http://localhost:3011
# Run all flows with auth credentials
api-tester run --flow=all [email protected] --password=TestPass1 --platform=myapp
# Debug a single endpoint
api-tester run --endpoint=15 --verbose --base-url=http://localhost:3011
# Dry run — see what would execute without making requests
api-tester run --flow=mvp --dry-run
# Use a pre-obtained token
api-tester run --flow=smoke --token=eyJhbG...testplan.json Format
The testplan is a JSON file that describes your API surface, how endpoints relate to each other, and how to test them.
{
"meta": {
"version": "1.0",
"total_endpoints": 128,
"api_base": "http://localhost:3011"
},
"endpoints": [
{
"id": 1,
"method": "GET",
"path": "/health",
"name": "system.health",
"group": "system",
"auth_required": false,
"in_api_client": false,
"implementation_status": "implemented-untyped",
"sample_request": null,
"expected_status": 200,
"response_shape": ["status", "service", "timestamp"]
},
{
"id": 89,
"method": "POST",
"path": "/invoices",
"name": "invoices.create",
"group": "invoices",
"auth_required": true,
"in_api_client": true,
"implementation_status": "implemented-typed",
"sample_request": {
"invoice_number": "INV-{{timestamp}}",
"invoice_date": "2026-01-15",
"distributor_id": "{{distributor_id}}",
"items": []
},
"expected_status": 200,
"response_shape": ["invoice_id"]
}
],
"graph": [
{
"from": 89,
"to": 29,
"carry": {
"invoice_id": "response.data.invoice_id"
},
"note": "Create invoice, then fetch it by ID"
}
],
"flows": {
"smoke": [1, 2, 3],
"mvp": [14, 107, 105, 89, 91]
}
}Endpoint fields
| Field | Type | Description |
|-------|------|-------------|
| id | number | Unique endpoint identifier (referenced in graph/flows) |
| method | string | HTTP method: GET, POST, PUT, DELETE |
| path | string | URL path with :param placeholders |
| name | string | Dot-notation name (e.g., invoices.create) |
| group | string | Logical group for organization |
| auth_required | boolean | Whether the endpoint requires a JWT |
| in_api_client | boolean | Whether this endpoint is in the TypeScript api-client |
| implementation_status | string | implemented-typed, implemented-untyped, or missing |
| sample_request | object/null | Request body template with {{placeholder}} support |
| expected_status | number | Expected HTTP status code |
| response_shape | string[] | Array of field names expected in response data |
Graph edges
Graph edges define data dependencies between endpoints. When endpoint from succeeds, values are extracted from its response and carried to subsequent steps.
The carry object maps variable names to dot-notation paths into the response:
{
"from": 89,
"to": 29,
"carry": {
"invoice_id": "response.data.invoice_id"
}
}After step 89 (POST /invoices), the invoice_id is extracted from the response and used to resolve :id in step 29 (GET /invoices/:id).
Flows
Flows are named sequences of endpoint IDs executed in order. Each flow represents a business workflow (e.g., create distributor -> create item -> create invoice -> create bill).
Placeholders
sample_request fields support {{placeholder}} syntax:
| Placeholder | Resolves to |
|-------------|-------------|
| {{timestamp}} | Current Unix timestamp |
| {{email}} | Generated test email |
| {{any_carried_key}} | Value carried from a previous step |
Path parameters (:id, :slug) are resolved from carried data automatically.
StoneScriptPHP Awareness
The runner understands the StoneScriptPHP response format:
{"status": "ok", "message": "", "data": { ... }}When validating response_shape, the runner checks body.data (not the wrapper) if the response has status: "ok". This matches how StoneScriptPHP wraps all responses via res_ok() and res_error().
Requirements
- Node.js >= 18.0.0 (uses native
fetch) - Zero npm dependencies
License
MIT
