This document explains how to write, maintain, and enforce documentation tests (doctests) for StarForge's public utility APIs.
Doctests are Rust code examples embedded in /// doc comments that are compiled and optionally run as part of cargo test --doc. They serve as both documentation and regression tests, ensuring examples in the codebase stay accurate and compilable.
StarForge enforces doctests in CI for selected public utility modules. Broken examples will fail the build.
The following modules have doctests enforced in CI (via the Documentation Tests job in .github/workflows/ci.yml):
| Module | Path | Doctest Status |
|---|---|---|
logging |
src/utils/logging.rs |
no_run — compiles but does not execute (initializes global state) |
print |
src/utils/print.rs |
text — displays output format, not compiled |
doc_extractor |
src/utils/doc_extractor.rs |
Compiled examples in tests |
doc_generator |
src/utils/doc_generator.rs |
Compiled examples in tests |
ai_docs |
src/utils/ai_docs.rs |
Compiled examples in tests |
contract_test_framework |
src/utils/contract_test_framework.rs |
ignore — requires external setup |
starforge_plugin_sdk |
crates/starforge-plugin-sdk/src/lib.rs |
ignore — requires Default impl and macro context |
To enforce doctests for additional modules, add them to the Documentation Tests CI job in .github/workflows/ci.yml.
Doc comments use /// (item-level) or //! (module-level). Code blocks are fenced with triple backticks:
/// Add two numbers together.
///
/// # Examples
///
/// ```
/// let result = starforge::utils::add(2, 3);
/// assert_eq!(result, 5);
/// ```
pub fn add(a: i32, b: i32) -> i32 {
a + b
}Rustdoc supports several attributes on code fences:
| Attribute | When to use |
|---|---|
``` |
Default. Code must compile and pass. Use for pure-logic examples. |
```no_run |
Code compiles but does not execute. Use when the example has side effects (file I/O, network, global state). |
```ignore |
Code is skipped entirely by cargo test --doc. Use when the example cannot compile in isolation (needs external setup, mock context, or framework-specific initialization). |
```compile_fail |
Code is expected to fail compilation. Use to demonstrate error cases. |
```text |
Not treated as Rust code. Use for output format examples. |
```rust |
Explicitly marks the block as Rust (same as default). Useful when combined with ignore or no_run. |
- All public items should have doc comments with at least a one-line description.
- Public utility functions (in
src/utils/) should have# Examplessections with compilable doctests when practical. - Do not use
ignoreunnecessarily. If the example can be made to compile, preferno_runor plain```. - Use
no_runfor side-effectful code — logging init, file writes, network calls, etc. - Use
ignoreonly when compilation is impossible — e.g., the example requires types or setup not available in the doctest harness. - Keep examples concise — doctests are documentation first, tests second.
- Use
#hidden lines for setup code that would clutter the example:/// ``` /// # use starforge::utils::logging::{LogConfig, init}; /// # fn example() -> anyhow::Result<()> { /// init(LogConfig::default())?; /// # Ok(()) /// # } /// ```
Doctests run as separate binaries. To use your crate's items:
/// ```
/// use starforge::utils::print;
/// print::success("Done!");
/// ```
Or use the crate name directly (Rust 2018+ edition):
/// ```
/// starforge::utils::print::success("Done!");
/// ```
The Documentation Tests job in .github/workflows/ci.yml runs:
cargo test --doc --lockedThis compiles and runs all non-ignore doctests in the library crate. If any doctest fails to compile or panics during execution, the CI job fails.
- Plain
```blocks: Compiled and executed. ```no_runblocks: Compiled but not executed.```ignoreblocks: Skipped entirely.```compile_failblocks: Expected to fail compilation.```textblocks: Not compiled.
- Add a
# Examplesdoctest section to the public item. - Ensure the example compiles with
cargo test --doclocally. - The new doctest is automatically included in CI (since
cargo test --docruns all library doctests).
/// Multiply two numbers.
///
/// # Examples
///
/// ```
/// assert_eq!(starforge::utils::multiply(3, 4), 12);
/// ```
pub fn multiply(a: i32, b: i32) -> i32 {
a * b
}/// Initialize the global logging subscriber.
///
/// # Examples
///
/// ```no_run
/// use starforge::utils::logging::{LogConfig, LogFormat, init};
/// init(LogConfig { format: LogFormat::Json, ..Default::default() }).unwrap();
/// ```
pub fn init(config: LogConfig) -> Result<()> {
// ...
}/// Run the contract test framework.
///
/// ```rust,ignore
/// let result = ContractTestFramework::new(config)
/// .add_suite(my_suite)
/// .run()?;
/// ```
pub struct ContractTestFramework { /* ... */ }/// Print a structured CLI error to stderr.
///
/// Output format:
///
/// ```text
/// ✗ Error: <message>
/// Context: <context>
///
/// What to try:
/// → hint one
/// ```
pub fn cli_error(err: &anyhow::Error, hints: &[&str]) { /* ... */ }The doctest cannot find your crate's items. Use the full path:
/// ```
/// use starforge::utils::print;
/// ```
The example uses an API that has changed. Update the example to match the current API.
Ensure you're running with --locked to match CI's dependency versions:
cargo test --doc --lockedMark it no_run if it has side effects, or ignore if it requires external resources.
- The Rust Reference — Documentation tests
- RFC 1574 — More API documentation conventions
- CI Enforcement
- Contributing Guide
Last updated: 2026-08-30 — Issue #793: Enforce documentation tests for public utility APIs