Skip to content

Test Blocks

Define @test: Blocks

A test suite is a top-level block named after the procedure it validates. Each case has a unique label and contains Python code:

@normalize_tag(tag: str) -> str:
  = `tag.strip().lower()`

@test: normalize_tag:
  > case: lowercases:
    `assert normalize_tag("KEDI") == "kedi"`

  > case: strips_whitespace:
    ```
    result = normalize_tag("  docs  ")
    assert result == "docs"
    ```

Use a backtick line for a short statement. Use a fenced Python block for setup, multiple assertions, or control flow.

Target Procedures

The name after @test: identifies the procedure in reports:

@add(left: int, right: int) -> int:
  = `left + right`

@test: add:
  > case: positive:
    `assert add(2, 3) == 5`

The suite is validation metadata; it does not invoke the target automatically. Each case must call the procedure itself when that is part of the test.

Arrange Runtime Values

Cases execute against the compiled Kedi runtime. They can use top-level values, custom types, imported exports, prelude helpers, and compiled procedures:

```
def canonical(value):
    return value.casefold().strip()
```

[prefix] = docs

@qualified_name(name: str) -> str:
  = `<prefix> + ":" + canonical(name)`

@test: qualified_name:
  > case: uses_runtime_environment:
    ```
    assert qualified_name(" API ") == "docs:api"
    ```

Keep setup local to a case unless it is genuinely part of the program's public runtime environment.

Assertions in Python

Any exception fails the case. Python assert is conventional because it keeps the expected condition visible:

@ratio(total: int, count: int) -> float:
  = `total / count`

@test: ratio:
  > case: computes_fraction:
    ```
    value = ratio(3, 2)
    assert isinstance(value, float)
    assert value == 1.5
    ```

Kedi records the exception message in the result. A failed case does not stop the remaining cases from running.

Test Execution Order

Suites and their cases run in source order. Each case uses the same compiled runtime, so mutations to shared Python objects can leak into later cases. Avoid order-dependent tests; construct fresh mutable data inside each case.

Test Failures

kedi program.kedi --test prints one line per case:

Tests:
- normalize_tag::lowercases: OK
- normalize_tag::strips_whitespace: OK

The command exits with status 1 when one or more cases fail, or when parsing, compilation, code generation, or optimized-prompt loading fails.

CLI Test Runs

Run all test suites in a file:

kedi program.kedi --test

Backend options still apply because a tested procedure may call a model:

kedi program.kedi --test \
  --adapter pydantic \
  --adapter-model groq:qwen/qwen3-32b

--test cannot be combined with -c/--command; validations require a source file. Extra CLI arguments remain available through the runtime args binding.

Cache Behavior

Before tests run, Kedi generates any > auto: procedures. The default program.cache.kedi is reused. --no-cache disables reuse and removes the temporary codegen cache after the command:

kedi program.kedi --test --no-cache

This flag does not disable response caching configured through the Python API and does not ignore program.kedi.optimized.json. File-backed tests load valid optimized prompt prefixes when that artifact exists, and invalid optimization JSON fails loudly.