un-inception/TESTING-STRATEGY.md
russell@unturf.com 8695578d89 feat: add smart multi-language testing strategy for client SDK growth
Add comprehensive testing infrastructure for UN clients:

1. Smart Change Detection (detect-changes.sh)
   - Detects changes in BOTH root-level (un.py, un.go) AND clients/ directory
   - Maps file extensions and directory names to languages
   - Triggers test_all when infrastructure changes

2. Language-Specific CI Matrix (generate-matrix.sh compatible)
   - Only runs tests for languages with changes
   - Example: modify clients/python/ → pytest runs, Go/Ruby skipped

3. Testing Strategy Document (TESTING-STRATEGY.md)
   - Complete testing matrix by language tier (compiled, interpreted, inception)
   - Unit, integration, embedding, and parity tests
   - Inception pattern for languages without local interpreters
   - Common failures and fixes
   - Rollout schedule for client/ migration

4. Makefile Targets
   - 'make test-python', 'make test-go', etc. for local development
   - 'make test-all' for comprehensive testing
   - 'make test-integration' for API contract validation
   - 'make test-ci-locally' to simulate CI pipeline

5. Updated CLAUDE.md
   - Documents SDK architecture (in growth state)
   - Explains three purposes: CLI, library, embeddable
   - References TESTING-STRATEGY.md for details

This enables:
✓ Per-language testing (only run what changed)
✓ Local developer workflow (make test-LANG)
✓ 42+ language feature parity validation
✓ Cross-language integration testing
2026-01-15 15:52:40 -05:00

10 KiB

Testing Strategy for Multi-Language UN Clients

Overview

UN Inception implements the UN CLI in 42+ languages. Each implementation serves as:

  1. Standalone CLI - Full featured command-line tool
  2. Client Library - Importable SDK for other projects
  3. Embeddable - Can be bundled/copied into other projects

Goal: Ensure all clients work correctly as the codebase evolves while only running necessary tests.


Smart Change Detection

The CI pipeline uses smart change detection to run only relevant tests:

How it Works

  1. Pre-stage: detect-changes.sh

    • Compares HEAD with origin/main
    • Identifies which languages/clients changed
    • Generates changes.json with affected languages
  2. Pre-stage: generate-matrix.sh

    • Reads changes.json
    • Generates test-matrix.yml with only needed test jobs
    • Includes cross-language validation for all clients
  3. Test Stage

    • Dynamic job inclusion from test-matrix.yml
    • Only language-specific tests run when that language changed
    • Always includes integration tests (API contract validation)

Example: Changes in clients/python/

{
  "changed_clients": ["python"],
  "root_implementations": [],
  "test_matrix": {
    "python": {
      "test_script": "pytest tests/test_un_py.py -v",
      "environment": ["python3"],
      "should_run": true
    },
    "integration": {
      "test_script": "bash tests/integration-all-clients.sh",
      "should_run": true
    }
  }
}

Supported Change Patterns

Changed File Triggered Tests
clients/python/* Python tests + integration
clients/go/* Go tests + integration
clients/javascript/* Node.js/TypeScript tests + integration
clients/*/tests/* Language tests + integration
test/ (shared tests) All language tests + integration
un.c (root) C tests + integration + all clients (fundamental change)
un.py (root) Python tests + integration
CLAUDE.md Documentation validation only

Testing Matrix by Language

Tier 1: Compiled Languages (Fast)

These languages have fast compilation and test cycles.

Go

  • File: clients/go/un.go
  • Tests: go test ./...
  • Integration: bash tests/integration-go-client.sh
  • Time: ~5 seconds
cd clients/go
go test -v
go run un.go test/fib.py

Rust

  • File: clients/rust/un.rs
  • Tests: cargo test
  • Integration: bash tests/integration-rust-client.sh
  • Time: ~10 seconds (cached)
cd clients/rust
cargo test --release
cargo run -- test/fib.py

C/C++

  • File: clients/c/un.c OR clients/cpp/un.cpp
  • Build: gcc un.c -o un -lssl -lcrypto OR g++ un.cpp -o un
  • Tests: bash tests/test_un_c.sh
  • Integration: ./un test/fib.py
  • Time: ~2 seconds

Java

  • File: clients/java/Un.java
  • Compile: javac Un.java
  • Tests: java -cp . Un test/fib.py
  • Integration: Full feature test suite
  • Time: ~8 seconds (warm JVM)

Tier 2: Interpreted Languages (Medium)

These have runtime interpretation but fast test cycles.

Python

  • File: clients/python/un.py
  • Tests:
    pytest tests/test_un_py.py -v
    python3 -m py_compile clients/python/un.py  # Syntax check
    python3 clients/python/un.py test/fib.py   # Integration
    
  • Async Support: Tests both sync and async client modes
  • Time: ~3 seconds

JavaScript/TypeScript

  • File: clients/javascript/un.js (Node.js)
  • Tests:
    node tests/test_un_js.js
    npx eslint clients/javascript/un.js  # Linting
    node clients/javascript/un.js test/fib.py
    
  • Time: ~2 seconds

Ruby

  • File: clients/ruby/un.rb
  • Tests:
    ruby -w clients/ruby/un.rb test/fib.py  # Syntax + execution
    ruby tests/test_un_rb.rb
    
  • Time: ~2 seconds

PHP

  • File: clients/php/un.php
  • Tests:
    php -l clients/php/un.php  # Syntax check
    php clients/php/un.php test/fib.py  # Integration
    php -d display_errors=1 tests/test_un_php.php
    
  • Time: ~2 seconds

Bash/Shell

  • File: clients/bash/un.sh
  • Tests:
    bash -n clients/bash/un.sh  # Syntax check
    shellcheck clients/bash/un.sh  # Linting
    bash clients/bash/un.sh test/fib.py
    
  • Time: ~1 second

Tier 3: Languages Requiring Inception

These languages may not be installed locally. Use the Inception Pattern to test via unsandbox itself.

Haskell, Julia, Clojure, Erlang, etc.

  • Pattern: Use un (C implementation) to execute the language implementation through unsandbox
  • Command:
    un -n semitrusted \
       -e UNSANDBOX_PUBLIC_KEY=$UNSANDBOX_PUBLIC_KEY \
       -e UNSANDBOX_SECRET_KEY=$UNSANDBOX_SECRET_KEY \
       clients/haskell/un.hs test/fib.py
    
  • Time: ~6 seconds (network + unsandbox execution)

Test Categories

1. Unit Tests

Test: Language-specific syntax and core logic

# Each language tests its specific features
pytest tests/test_un_py.py
go test ./clients/go/...
cargo test -p un_client
ruby tests/test_un_rb.rb

What fails here: Syntax errors, missing dependencies, core logic bugs

2. Integration Tests (API Contract)

Test: All clients communicate correctly with the API

# Test EVERY client can:
# ✓ Authenticate (HMAC signature generation)
# ✓ Execute code (run test/fib.py via API)
# ✓ Handle responses
# ✓ Respect error codes

bash tests/integration-all-clients.sh

What fails here: Authentication issues, API schema changes, network problems

3. Embedding Tests

Test: Client can be imported/embedded in other code

# Python: Can import the client
from clients.python.un import UnsandboxClient
client = UnsandboxClient()
// JavaScript: Can require the module
const { UnsandboxClient } = require('./clients/javascript/un.js');

What fails here: Export/import structure, module API incompatibility

4. Parity Tests (CI/Feature Checklist)

Test: All clients have feature parity

Verify all 42+ clients support:

  • ✓ Execute: un file.py
  • ✓ Session: un session
  • ✓ Service: un service
  • ✓ All flags: -e, -f, -n, -a, --tmux, --screen, etc.
bash tests/feature-parity-matrix.sh

What fails here: Missing features, argument parsing bugs, logic differences


CI Pipeline Structure

Stage: Pre (Change Detection)

detect-changes.sh     → changes.json
generate-matrix.sh    → test-matrix.yml (dynamic jobs)

Stage: Build

build-clients.sh      → Compile/prepare all changed clients

Stage: Test (Dynamic)

include: test-matrix.yml  ← Dynamically generated per-language jobs
  test-python.sh        (only if clients/python/* changed)
  test-go.sh           (only if clients/go/* changed)
  test-javascript.sh   (only if clients/javascript/* changed)
  ...
  integration-all.sh   (always runs - validates all clients)

Stage: Science (Pool Burning)

lint-all-sdks.sh      → Code quality for all clients
benchmark-clients.sh  → Performance comparison
validate-examples.sh  → Real-world examples work

Stage: Validate & Report

Summary of test results, coverage, and quality metrics

Local Development

Running All Tests Locally

# Set environment
export UNSANDBOX_PUBLIC_KEY="unsb-pk-..."
export UNSANDBOX_SECRET_KEY="unsb-sk-..."

# Run everything
./tests/run_all_tests.sh

# Or run specific language
make test-python
make test-go
make test-javascript
make test-ruby
make test-php

Testing a Specific Client After Changes

# After editing clients/python/un.py
cd clients/python
pytest -v
python3 -m py_compile un.py
python3 un.py test/fib.py
python3 un.py session  # Interactive test
python3 un.py service --help

Testing Without Local Interpreter (Inception)

# Don't have Haskell installed? Test it via unsandbox
un -n semitrusted \
   -e UNSANDBOX_PUBLIC_KEY=$UNSANDBOX_PUBLIC_KEY \
   -e UNSANDBOX_SECRET_KEY=$UNSANDBOX_SECRET_KEY \
   clients/haskell/un.hs test/fib.py

# Test ALL languages via inception (if needed)
bash tests/inception-test-all.sh

Common Failures & Fixes

Python Tests Fail

❌ ModuleNotFoundError: No module named 'X'
✓ Fix: pip install -r clients/python/requirements.txt

❌ HMAC signature mismatch
✓ Fix: Verify authentication code in un.py matches other clients

❌ Import error when embedded
✓ Fix: Ensure no absolute imports, use relative imports for bundling

Go Tests Fail

❌ go.mod not found
✓ Fix: cd clients/go && go mod init clients/go

❌ Module not found after changes
✓ Fix: go mod tidy

❌ Compilation error with crypto
✓ Fix: Ensure Go 1.16+ (crypto/hmac in stdlib)

JavaScript Tests Fail

❌ Cannot find module
✓ Fix: npm install in clients/javascript

❌ Async/await not working
✓ Fix: Ensure Node.js version supports async/await (>7.6.0)

Integration Fails (All Languages)

❌ 401 Unauthorized
✓ Fix: HMAC signature generation - compare with C reference (un.c)

❌ 503 API unavailable
✓ Fix: Check api.unsandbox.com is up and accessible

❌ Client hangs/timeout
✓ Fix: Check network access, proxy settings, firewall rules

Rollout Schedule

Phase Goal Timeline
Phase 1 (Now) Setup clients/ structure, smart CI This week
Phase 2 Migrate top 10 languages to clients/ 2 weeks
Phase 3 Migrate remaining 32 languages 4 weeks
Phase 4 Deprecate root un.* files 8 weeks
Phase 5 Archive/document legacy implementations Optional

Metrics & Coverage

Success Criteria:

  • ✓ All test jobs pass
  • ✓ Each language has unit tests
  • ✓ Each language has integration tests
  • ✓ Feature parity matrix 100% (all 42 features in all 42 languages)
  • ✓ CI matrix correctly detects changes and skips irrelevant tests
  • ✓ No false positives (unrelated changes don't trigger irrelevant tests)

Tracking:

  • Build time by language (identify slow tests)
  • Test coverage by client
  • Feature parity matrix (visual dashboard)
  • Inception test success rate (languages tested via unsandbox)