Beta

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

Core Design Principles

  1. Deterministic parsing — no NLP, no guessing.
  2. Single entrypoint and single exit per spec.
  3. Clear separation between:
    • Trigger (RECEIVE)
    • External dependencies (EXPECT)
    • System response (RESPOND)
  4. All payload shapes defined externally in YAML or JSON files.

DSL Grammar Overview

A LineSpec file MUST follow this structure:

  1. Exactly one RECEIVE statement
  2. Zero or more EXPECT statements
  3. Zero or more EXPECT_NOT statements
  4. Exactly one RESPOND statement

Statements MUST appear in this order:

LineSpec
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:

LineSpec
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

LineSpec
RECEIVE HTTP: 
[WITH {{}}]
[HEADERS
  : 
  ...]

Example

LineSpec
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

LineSpec
EXPECT  
[USING_SQL """

"""]
[WITH {{}}]
[RETURNS {{}}]
[RETURNS EMPTY]
[VERIFY query CONTAINS '']
[VERIFY query NOT_CONTAINS '']
[VERIFY query MATCHES //]

EXPECT HTTP

LineSpec
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

LineSpec
EXPECT READ:MYSQL 
[USING_SQL """

"""]
RETURNS {{}}

# Or for empty results:
EXPECT READ:MYSQL 
[USING_SQL """

"""]
RETURNS EMPTY

EXPECT WRITE:MYSQL

LineSpec
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

LineSpec
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

LineSpec
EXPECT_NOT  
[USING_SQL """

"""]

Example — Testing Efficient Queries

LineSpec
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

LineSpec
RESPOND HTTP:
[WITH {{}}]
[NOISE
  body.
  body.]

Example

LineSpec
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:

  • NOISE must appear after RESPOND (and after WITH if 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)
  • NOISE is optional; omit it when no fields need filtering

Complete Examples

Example 1: Create Todo Success

LineSpec
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

LineSpec
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:

Terminal
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.