Beamtalk REPL Protocol Test Suite

April 25, 2026 · View on GitHub

REPL TCP-protocol tests that validate the complete compilation and execution pipeline by evaluating Beamtalk expressions through the REPL JSON protocol.

History: Previously called the "E2E test suite" and located at tests/e2e/. The directory was renamed to tests/repl-protocol/ in BT-2085 because these tests exercise one specific surface (the REPL TCP protocol), not "end-to-end across surfaces". Cross-surface parity tests live under tests/parity/.

Feature Coverage Matrix

FeatureStatusTest FileNotes
Literals
Integer literalsliterals.bt42, 0, 1000000
String literalsliterals.bt'hello', ''
Boolean literalsbooleans.bttrue, false
Binary Operations
Additionarithmetic.bt3 + 4
Subtractionarithmetic.bt10 - 3
Multiplicationarithmetic.bt5 * 6
Divisionarithmetic.bt20 / 4 → float
Math precedencearithmetic.bt2 + 3 * 4 → 14
Unary Messages
Block valueunary_messages.bt[42] value
Integer negatedunary_messages.bt5 negated-5
Integer absunary_messages.bt-5 abs5
Integer isZerounary_messages.bt0 isZerotrue
Integer isEvenunary_messages.bt4 isEventrue
Integer isOddunary_messages.bt5 isOddtrue
String lengthunary_messages.bt'hello' length5
String isEmptyunary_messages.bt'' isEmptytrue
Keyword Messages
value:keyword_messages.bt`[:x
value:value:keyword_messages.bt`[:x :y
value:value:value:keyword_messages.btThree-arg blocks
Blocks
Zero-arg blocksblocks.bt[42] value
One-arg blocksblocks.bt`[:x
Two-arg blocksblocks.bt`[:x :y
Nested blocksblocks.bt`[[:x
Return statementsblocks.bt`[:x
Variable Persistence
Simple assignmentvariable_persistence.btx := 42 persists
Variable referencevariable_persistence.btx reads back
Multiple variablesvariable_persistence.btx + y works
Variable reassignmentvariable_persistence.btx := 100 updates
Control Flow
Block evaluationcontrol_flow.bt[5 + 3] value
Block with variablescontrol_flow.btUses REPL bindings
whileTrue:🔧blocks.btNon-mutating loop ([false] whileTrue: [42]) works; assignments inside blocks don't persist (BT-90)
whileFalse:🔧Implemented but assignments inside blocks don't persist (BT-90)
timesRepeat:actor_local_mutations.btTested in stdlib
to:do:nested_to_do.btTested in stdlib
Boolean Operations
ifTrue:ifFalse:booleans.bttrue ifTrue: [1] ifFalse: [2]1
ifTrue:booleans.bttrue ifTrue: [42]42
ifFalse:booleans.btfalse ifFalse: [42]42
and:booleans.bttrue and: [false]false
or:booleans.btfalse or: [true]true
notbooleans.bttrue notfalse
Cascades
Cascade syntax🔧cascades.btUse // @load + stateful tests¹
Actors
spawn🔧actors.btUse // @load to load class definitions²
Async messages🔧actors.btUse // @load + stateful tests
await🔧actors.btUse // @load + stateful tests
Error Handling
Division by zeroerrors.bt1 / 0 → badarith
Semantic Analysis
Stored closure field errorsemantic_diagnostics.bt@load-error on stored closure
Sealed class errorsemantic_diagnostics.bt@load-error on sealed subclass
Reflection validatorsemantic_diagnostics.btrespondsTo: with non-symbol
Undefined variablesemantic_scope.btCompile-time error
Variable scopingsemantic_scope.btBlock params, closures
Self in methodssemantic_scope.btActor methods use self

Legend:

  • ✅ = Fully tested and working
  • 🔄 = Implemented but needs refinement (returns future in REPL)
  • 🔧 = Implemented, REPL-protocol test infrastructure ready (needs real tests)
  • 📋 = Documented, implementation in progress
  • — = No separate test file (documented elsewhere)

Footnotes:

  1. Cascades send async actor messages. Use // @load tests/repl-protocol/fixtures/counter.bt to load an actor class, then test cascades with stateful expressions.
  2. Actor classes must be defined in files and loaded with // @load. See tests/repl-protocol/fixtures/ for example actors.

Directory Structure

tests/repl-protocol/
├── README.md              # This file
├── cases/                 # Test case files
│   ├── actors.btscript          # Actor documentation (syntax examples)
│   ├── arithmetic.btscript      # Arithmetic operations (+, -, *, /)
│   ├── blocks.btscript          # Block/closure tests
│   ├── booleans.btscript        # Boolean literals (true, false)
│   ├── cascades.btscript        # Cascade documentation (syntax examples)
│   ├── control_flow.btscript    # Control flow (block evaluation, variables)
│   ├── errors.btscript          # Error handling tests
│   ├── semantic_diagnostics.btscript # Semantic analysis error tests
│   ├── semantic_scope.btscript  # Variable scope and resolution tests
│   ├── keyword_messages.btscript # Keyword message sends
│   ├── literals.btscript        # Integer and string literals
│   ├── unary_messages.btscript  # Unary message sends
│   └── variable_persistence.btscript # Variable assignment and persistence
└── fixtures/              # Actor/class definitions for stateful tests
    └── counter.bt         # Simple counter actor class

Running Tests

# Run REPL protocol tests only
just test-repl-protocol

# Or directly via cargo (these tests are #[ignore]d, require --ignored flag)
cargo test --test repl_protocol -- --ignored

# Run with verbose output
cargo test --test repl_protocol -- --ignored --nocapture

Prerequisites:

  • Erlang/OTP must be installed (for the REPL runtime)
  • The project must be built (just build)

Test File Format

Test files use the .bt extension and contain Beamtalk expressions with expected results.

Basic Format

Each test case consists of an expression followed by a // => comment with the expected result:

3 + 4
// => 7

[:x | x + 1] value: 5
// => 6

Comments

Regular comments (not starting with // =>) are ignored and can be used for documentation:

// Test basic arithmetic
3 + 4
// => 7

// Test with larger numbers
1000 + 2000
// => 3000

Error Testing

To test that an expression produces an error, use ERROR: prefix in the expected result:

undefined_variable
// => ERROR: Undefined variable

The test passes if the error message contains the specified text.

See tests/repl-protocol/cases/errors.btscript for examples of error test cases.

Compilation Error Testing (@load-error)

To test that a file fails to compile with a specific error, use the @load-error directive:

// @load-error tests/repl-protocol/fixtures/bad_class.bt => cannot assign to field

The directive attempts to load the file and expects compilation to fail. The test passes if the error message contains the specified substring. If the load succeeds, the test fails.

See tests/repl-protocol/cases/semantic_diagnostics.bt for examples.

Multi-line Expressions

Currently, each expression must be on a single line within the test file format. This is a test parser limitation, not a language limitation - Beamtalk itself supports multi-line expressions. The test format reads one line at a time looking for // => markers. Multi-line test format support may be added in the future.

Stateful Tests (Actors and Cascades)

Test files support stateful multi-expression tests where variables persist between expressions within the same file. This is essential for testing actors and cascades.

Variable persistence within a file:

// Assign a variable
x := 42
// => 42

// Use it in the next expression - state persists!
x + 10
// => 52

Loading actor classes:

To test actors and cascades, first load a file containing class definitions using the // @load directive:

// @load tests/repl-protocol/fixtures/counter.bt

// Now Counter class is available
counter := Counter spawn
// => <pid>

// Send messages (state persists)
counter increment
// => 1

// Cascades - send multiple messages to same receiver
counter increment; increment; getValue
// => 3

Fixture files:

  • tests/repl-protocol/fixtures/ - Actor class definitions for REPL-protocol tests
  • tests/repl-protocol/fixtures/counter.bt - Simple counter actor example

Note: Bindings are cleared at the start of each test file, but persist across all expressions within that file. Loaded modules also persist for the file's duration.

Writing New Tests

  1. Create a new .btscript file in tests/repl-protocol/cases/
  2. Add expressions with // => expected results
  3. Run just test-repl-protocol to verify

Guidelines

  • One concept per file: Group related tests (e.g., all arithmetic in arithmetic.bt)
  • Start simple: Begin with basic cases before edge cases
  • Test both success and failure: Include error cases where appropriate
  • Add comments: Document what each test is checking
  • Keep expressions simple: The test format doesn't support multi-line expressions

Test Harness

The test harness (crates/beamtalk-cli/tests/repl_protocol.rs) handles:

  1. REPL startup: Starts a REPL workspace automatically (ephemeral port)
  2. REPL connection: Uses TCP socket to communicate with REPL
  3. Test execution: Parses .bt files and evaluates each expression
  4. Result comparison: Compares actual results with expected values

Protocol

The harness uses the same JSON protocol as the REPL CLI:

// Request
{"op": "eval", "id": "1", "code": "3 + 4"}

// Success response
{"id": "1", "value": "7"}

Troubleshooting

Debugging Output

To see BEAM output during tests:

cargo test --test repl_protocol -- --ignored --nocapture

Tests timeout

The default timeout is 30 seconds per expression. If tests are timing out:

  1. Check if the expression is causing an infinite loop
  2. Verify the Erlang runtime is functioning correctly
  3. Check for resource exhaustion