Skip to content

Testing

Starkite includes a built-in test runner that executes Starlark-based script tests. Functions prefixed with test_ are automatically discovered, run, and reported by the harness. Tests execute under the same runtime engine, permission profiles, and sandbox settings as production scripts.

Writing tests

Any top-level function starting with test_ is treated as a test case. The runner executes these functions sequentially. To verify expectations, use global assertion functions:

# math_test.star
def test_addition():
    assert_equal(1 + 1, 2)
    assert_true(10 > 5)

def test_strings():
    assert_contains("hello world", "world")
    assert_not_equal("a", "b")

Assertions

The following assertions are available globally (or via the explicit test. namespace):

Function Checks
assert(cond, msg?, *args) cond is truthy
assert_equal(actual, expected, msg?, *args) actual == expected
assert_not_equal(actual, unexpected, msg?, *args) actual != unexpected
assert_contains(haystack, needle, msg?, *args) haystack contains needle
assert_true(value, msg?, *args) value is True
assert_false(value, msg?, *args) value is False
skip(reason?) Skips the current test
fail(msg) Fails the current test immediately

Assertions accept an optional, printf-style format string and arguments for custom failure messages:

def test_deployment():
    result = try_exec("kubectl get pods -l app=web -o json")
    assert(result.ok, "kubectl failed: %s", result.stderr)

When this assertion fails, the test report prints the actual kubectl error output. For complete signatures, see the test API reference.

Setup and teardown

To avoid repeating preparation and cleanup logic, define setup() and teardown() functions. The runner executes setup() before each test and teardown() after each test, even if a test case fails:

def setup():
    exec("kubectl create namespace test-ns")

def teardown():
    exec("kubectl delete namespace test-ns")

def test_deploy():
    result = try_exec("kubectl apply -f manifest.yaml -n test-ns")
    assert(result.ok, "apply failed: %s", result.stderr)

Running tests

The kite test command accepts a single file or a directory. When given a directory, it executes all files ending in _test.star:

kite test math_test.star      # Execute a single file
kite test ./tests/            # Execute all *_test.star files in a directory

Use optional flags to configure the test execution:

kite test ./tests/ --verbose       # Display assertion details
kite test ./tests/ --run sql       # Run only tests matching a name pattern
kite test ./tests/ --parallel 4    # Run up to four files concurrently

[!NOTE] Unlike standard executions, the test runner does not invoke main(). It executes only the discovered test_* functions.

Permissions and tests

Tests run under the same permission rules as standard executions. Without explicit flags, tests run under the deny-all profile. If your tests perform I/O, network requests, or shell commands, pass an appropriate permission profile:

kite test ./tests/ --permissions=allow-local

To ensure test reliability, run your test suite under the same permission profile the script uses in production.

Sandbox isolation

On Linux systems, you can isolate test execution. The kite test --sandbox command runs each test file in its own separate sandbox process, preventing tests from mutating host files or leaking state to other tests.

For maximum security, combine sandbox isolation with explicit API permissions:

kite test ./tests/ --sandbox=opaque --permissions=allow-fs

For more details on sandbox isolation, see the Sandbox Guide.