testing
tursodatabase/turso
Write and run SQL, TCL, and Rust tests for Turso database development.
What is testing?
This skill teaches how to write different types of tests (SQL `.sqltest`, legacy TCL, and Rust integration tests) for the Turso database project, when to use each format, and how to run them. Use it when adding features or fixing bugs that require test coverage.
- .sqltest format for SQL compatibility testing (preferred for new tests)
- TCL `.test` format for legacy SQL compatibility (being phased out)
- Rust integration and unit tests for regression and complex scenarios
- Fuzz testing for edge case discovery
- Test conversion utilities from TCL to `.sqltest` format
- Assertion helpers using the `asserting` crate for readable test code
How to install testing
npx skills add https://github.com/tursodatabase/turso --skill testing- Rust toolchain installed (for running `cargo test`)
- Make installed (for running `make test` commands)
- Access to the Turso repository
How to use testing
- 1.Choose your test type: `.sqltest` for SQL compatibility (preferred), TCL for legacy tests, or Rust for integration tests
- 2.For `.sqltest`: write tests in `sqlite/conformance/sqlite-sqltests/` with `test` blocks and `expect` sections
- 3.For TCL: write tests in `testing/*.test` using `do_execsql_test_on_specific_db` syntax
- 4.For Rust: create test files in `tests/integration/` using `#[test]` functions and the `asserting` crate
- 5.Run tests with `make test` (full suite), `make test-single TEST=filename.test` (single TCL test), `cargo test` (Rust tests), or `cargo run -p sqltest -- run <path>` (SQL tests)
- 6.For converting TCL to `.sqltest`: use `cargo run -- convert <TCL_path> -o <out_dir>`, then verify and adjust expected results
Use cases
- Writing SQL compatibility tests for new database features
- Converting legacy TCL tests to the modern `.sqltest` format
- Creating Rust integration tests for regression prevention
- Testing complex database scenarios with multiple rows and conditions
- Verifying query plans and index usage in tests
- Database developers working on Turso
- Contributors adding new SQL features
- Engineers maintaining test suites
- QA engineers writing regression tests
testing FAQ
.sqltest is preferred for new tests. It allows the same test cases to run against multiple backends (CLI, Rust bindings, etc.) and is replacing the legacy TCL format.
Use the conversion command `cargo run -- convert <TCL_test_path> -o <out_dir>`. The tool is not always accurate, so you may need to manually fix warnings, unroll loops, and adjust expected results to match the new database output. Avoid changing SQL statements; only update expected results.
Use the `asserting` crate for anything beyond trivial assertions. It provides extensive, readable assertions. For database-specific assertions, use `crate::assertions` which adds `row!`, `column`, and query-plan helpers.
Use `make test-single TEST=filename.test` for TCL tests, `cargo test` for Rust tests, `make -C sqlite/conformance run-cli` for SQL tests, or `cargo run -p sqltest -- run <path>` for specific SQL test files.
Every test must fail without the change and pass with it. If a test fails before your change, either fix the test or investigate why the existing code doesn't pass it before proceeding.
Full instructions (SKILL.md)
Source of truth, from tursodatabase/turso.
name: testing
description: How to write tests, when to use each type of test, and how to run them. Contains information about conversion of .test to .sqltest, and how to write .sqltest and rust tests
Testing Guide
Test Types & When to Use
| Type | Location | Use Case |
|---|---|---|
.sqltest | sqlite/conformance/sqlite-sqltests/ | SQL compatibility. Preferred for new tests |
TCL .test | testing/ | Legacy SQL compat (being phased out) |
| Rust integration | tests/integration/ | Regression tests, complex scenarios |
| Fuzz | tests/fuzz/ | Complex features, edge case discovery |
Note: TCL tests are being phased out in favor of the .sqltest suites in sqlite/conformance/. The .sqltest format allows the same test cases to run against multiple backends (CLI, Rust bindings, etc.).
Running Tests
# Main test suite (TCL compat, sqlite3 compat, Python wrappers)
make test
# Single TCL test
make test-single TEST=select.test
# SQL test runner
make -C sqlite/conformance run-cli
# OR
cargo run -p sqltest -- run <test-file or directory>
# Rust unit/integration tests (full workspace)
cargo test
Writing Tests
.sqltest (Preferred)
@database :default:
test example-addition {
SELECT 1 + 1;
}
expect {
2
}
test example-multiple-rows {
SELECT id, name FROM users WHERE id < 3;
}
expect {
1|alice
2|bob
}
Location: sqlite/conformance/sqlite-sqltests/*.sqltest
You must start converting TCL tests with the convert command from the test runner (e.g cargo run -- convert <TCL_test_path> -o <out_dir>). It is not always accurate, but it will convert most of the tests. If some conversion emits a warning you will have to write by hand whatever is missing from it (e.g unroll a for each loop by hand). Then you need to verify the tests work by running them with make -C sqlite/conformance run-rust, and adjust their output if something was wrong with the conversion. Also, we use harcoded databases in TCL, but with .sqltest we generate the database with a different seed, so you will probably need to change the expected test result to match the new database query output. Avoid changing the SQL statements from the test, just change the expected result
TCL
do_execsql_test_on_specific_db {:memory:} test-name {
SELECT 1 + 1;
} {2}
Location: testing/*.test
Rust Integration
// tests/integration/test_foo.rs
#[test]
fn test_something() {
let conn = Connection::open_in_memory().unwrap();
// ...
}
Assertions
Prefer the asserting crate for anything more than trivial assertions. The assertions it offers are extensive
and make tests more readable. Some simple examples:
use asserting::prelude::*;
assert_that!(conn.execute("SELECT * FROM t1;")).is_err();
assert_that_code!(|| parse(bad_input)).panics_with_message("unexpected token");
assert_that!(&rows)
.has_length(3)
.any_satisfies(|r| r
.name == "bob")
.first_element_ref()
.is_equal_to(&Row { id: 1, name: "alice".into() });
assert_that!(&header)
.named("page 2 header")
.satisfies_with_message("be a leaf table page", |h| h[0] == 0x0d);
// soft assertions: mark the test as failed but don't stop
verify_that!(&plan)
.starts_with("SEARCH")
.contains("USING INDEX")
.soft_panic();
crate::assertions adds row! for result rows, plus column and query-plan
assertions:
use crate::assertions::{AssertColumn, AssertQueryPlan, Cell, NULL};
assert_that!(limbo_exec_rows(&conn, "SELECT id, name FROM t ORDER BY id"))
.is_equal_to(vec![row![1, "alice"], row![2, NULL]]);
assert_that!(limbo_exec_rows(&conn, "SELECT id FROM t WHERE id = 1"))
.single_element()
.is_equal_to(row![1]);
assert_that!(limbo_exec_rows(&conn, "SELECT id, name FROM t"))
.column(1)
.contains(Cell::from("alice"));
assert_that!(limbo_exec_rows(&conn, "EXPLAIN QUERY PLAN SELECT id FROM t WHERE name = 'a'"))
.uses_index("idx_name")
.searches_table("t")
.has_table_access_order(["t"]);
Key Rules
- Every functional change needs a test
- Test must fail without change, pass with it
- Prefer in-memory DBs:
:memory:(sqltest) or{:memory:}(TCL) - Don't invent new test formats. Follow existing patterns
- Write tests first when possible
Test Database Schema
testing/system/testing.db has users and products tables. See docs/testing.md for schema.
Logging During Tests
RUST_LOG=none,turso_core=trace make test
Output: testing/system/test.log. Warning: very verbose.
Related skills
More from tursodatabase/turso and the wider catalog.

transaction-correctness
How WAL mechanics, checkpointing, concurrency rules, recovery work in tursodb

async-io-model
Cooperative async patterns for Turso: IOResult state machines, re-entrancy safety, and CompletionGroup aggregation.

code-quality
Production-grade correctness rules for Rust: crash over corruption, assert invariants, avoid hacks.

debugging
Debug Turso database issues using bytecode comparison, logging, ThreadSanitizer, and deterministic simulation.
kami
Typeset professional documents and landing pages with warm parchment design and serif hierarchy.
check
Reviews code diffs, PRs, issue queues, release readiness, and project audits before shipping.