LineSpec DSL Reference
Deterministic domain-specific language for integration testing. Intercepts database and HTTP traffic at the protocol level for language-agnostic, deterministic test execution.
brew install linespec-beta
go install -tags beta github.com/livecodelife/linespec/cmd/linespec@v1.3.0
Table of Contents
Core Design Principles
- Deterministic parsing — no NLP, no guessing.
- Single entrypoint and single exit per spec.
- Clear separation between:
- Trigger (RECEIVE)
- External dependencies (EXPECT)
- System response (RESPOND)
- All payload shapes defined externally in YAML or JSON files.
DSL Grammar Overview
A LineSpec file MUST follow this structure:
- Exactly one
RECEIVEstatement - Zero or more
EXPECTstatements - Zero or more
EXPECT_NOTstatements - Exactly one
RESPONDstatement
Statements MUST appear in this order:
RECEIVE
EXPECT (0..n)
EXPECT_NOT (0..n)
RESPOND
No statements may appear after RESPOND.
File Extension
Recommended extension: .linespec
Example: create_todo_success.linespec
Test Name
Optional test name declaration:
TEST
If omitted, the filename (without extension) is used as the test name.
RECEIVE Statement
Defines the trigger request into the System Under Test (SUT).
Syntax
RECEIVE HTTP:
[WITH {{}}]
[HEADERS
:
...]
Example
RECEIVE HTTP:POST /api/v1/todos
WITH {{todo.yaml}}
RECEIVE HTTP:GET /api/v1/users/42
HEADERS
Authorization: Bearer token_abc123xyz
Rules
- Exactly one RECEIVE per file
- MUST appear before any EXPECT or EXPECT_NOT
- HTTP method is required
- URL is required (full URL including protocol and host)
- WITH is optional for HTTP requests without a body
- Body must reference an external YAML or JSON file
- HEADERS is optional and supports multiple header lines with indentation
- WITH must come before HEADERS if both are present
EXPECT Statement
Defines an external dependency interaction that MUST occur during execution.
General Syntax
EXPECT
[USING_SQL """
"""]
[WITH {{}}]
[RETURNS {{}}]
[RETURNS EMPTY]
[VERIFY query CONTAINS '']
[VERIFY query NOT_CONTAINS '']
[VERIFY query MATCHES //]
EXPECT HTTP
EXPECT HTTP:
[HEADERS
:
...]
RETURNS {{}}
Rules:
- RETURNS is required for HTTP expectations
- HEADERS is optional; headers are matched against the actual request
- The proxy intercepts calls to the hostname and returns the mocked response
- Tests fail if the HTTP mock is defined but not invoked
EXPECT READ:MYSQL
EXPECT READ:MYSQL
[USING_SQL """
"""]
RETURNS {{}}
# Or for empty results:
EXPECT READ:MYSQL
[USING_SQL """
"""]
RETURNS EMPTY
EXPECT WRITE:MYSQL
EXPECT WRITE:MYSQL
[USING_SQL """
"""]
[WITH {{}}]
[NO TRANSACTION]
[VERIFY query CONTAINS '']
[VERIFY query NOT_CONTAINS '']
[VERIFY query MATCHES //]
EXPECT READ:POSTGRESQL / EXPECT WRITE:POSTGRESQL
Same syntax as MySQL variants. The proxy supports both MySQL and PostgreSQL protocols.
VERIFY Clause
The VERIFY clause validates the actual SQL query executed by the application at runtime. It can be attached to any MySQL or PostgreSQL EXPECT statement.
Use cases include:
- Security: Ensuring passwords are hashed before storage
- Compliance: Verifying sensitive data is not logged in plain text
- Correctness: Confirming proper SQL structure
- Injection prevention: Validating query patterns match expected templates
Operators
CONTAINS— Query must include the specified string (substring match)NOT_CONTAINS— Query must NOT include the specified string (substring match)MATCHES— Query must match the specified regex pattern (full Go regexp support)
Best Practices
Use MATCHES with word boundaries (\b) for precise column name matching:
# GOOD: Uses word boundaries to match exact column name
VERIFY query MATCHES /\bpassword_digest\b/
# BAD: Would also match 'password_digest' in 'old_password_digest_column'
VERIFY query CONTAINS 'password_digest'
Example — Password Hashing
TEST create-user-with-hashing
RECEIVE HTTP:POST /api/v1/users
WITH {{user_create_request.yaml}}
# Ensure password is hashed before storage
EXPECT WRITE:MYSQL users
WITH {{user_with_hashed_password.yaml}}
VERIFY query MATCHES /\bpassword_digest\b/
VERIFY query NOT_CONTAINS '`password`'
RESPOND HTTP:201
EXPECT_NOT Statement
Defines an external dependency interaction that must NOT occur during execution. Useful for testing query optimization and ensuring certain operations are avoided.
Syntax
EXPECT_NOT
[USING_SQL """
"""]
Example — Testing Efficient Queries
TEST efficient-user-lookup
RECEIVE HTTP:GET /api/v1/users/123
# Assert that we DON'T do a full table scan
EXPECT_NOT READ:MYSQL users
USING_SQL """
SELECT * FROM users
"""
# Should use indexed lookup instead
EXPECT READ:MYSQL users
USING_SQL """
SELECT * FROM users WHERE id = 123 LIMIT 1
"""
RETURNS {{user_response.yaml}}
RESPOND HTTP:200
WITH {{user_response.yaml}}
Rules
- Exactly one of READ_MYSQL or WRITE_MYSQL
- USING_SQL is optional; if provided, matches that specific query
- If no USING_SQL, matches any read/write on the table
- Test fails if the forbidden operation is detected
RESPOND Statement
Defines the final response of the System Under Test.
Syntax
RESPOND HTTP:
[WITH {{}}]
[NOISE
body.
body.]
Example
RESPOND HTTP:201
WITH {{saved_todo.yaml}}
NOISE
body.id
body.created_at
body.updated_at
Rules
- Exactly one RESPOND per file
- MUST be the final statement
- Status MUST be numeric (e.g., 200, 201, 400, 500)
- WITH is optional for responses without a body
- NOISE must appear after WITH if both are present
NOISE (optional)
Field paths to exclude from comparison:
NOISEmust appear afterRESPOND(and afterWITHif present)- Each indented line names one field path to exclude from comparison
- Field paths use dot notation matching the JSON response body (e.g.
body.created_at) NOISEis optional; omit it when no fields need filtering
Complete Examples
Example 1: Create Todo Success
TEST create_todo_success
RECEIVE HTTP:POST /api/v1/todos
WITH {{todo.yaml}}
HEADERS
Authorization: Bearer token_abc123xyz
EXPECT HTTP:GET http://user-service.local/api/v1/users/auth
HEADERS
Authorization: Bearer token_abc123xyz
RETURNS {{user_info.yaml}}
EXPECT WRITE:MYSQL todos
WITH {{todo_insert.yaml}}
EXPECT EVENT:todo-events
WITH {{todo_created_event.yaml}}
RESPOND HTTP:201
WITH {{saved_todo.yaml}}
NOISE
body.id
body.created_at
body.updated_at
Example 2: Create User with Validation
TEST create-user-secure
RECEIVE HTTP:POST http://localhost:3000/users
WITH {{payloads/user_create_req.yaml}}
HEADERS
Authorization: Bearer token
EXPECT WRITE:MYSQL users
WITH {{payloads/user_with_password_digest.yaml}}
VERIFY query MATCHES /\bpassword_digest\b/
VERIFY query NOT_CONTAINS '`password`'
RESPOND HTTP:201
WITH {{payloads/user_create_resp.yaml}}
NOISE
body.id
body.created_at
CLI Usage
Execute a spec:
linespec test create_todo_success.linespec
linespec test /path/to/linespecs/
Philosophy
LineSpec is not a natural language tool. It is a strict behavioral specification language designed to:
- Be readable by humans
- Be trivial to parse
- Execute deterministically
- Support modern microservice testing workflows
No inference. No heuristics. No ambiguity.