# Testing Guide
This guide explains how to run, extend, and debug the test suites across all
Synta crates and language bindings. For CI configuration see
[Contributing](contribution.md#running-ci-locally); for architecture background
see [System Architecture](system-architecture.md).
---
## Test Suite Overview
| Core DER/BER unit tests | `tests/*.rs` | Decoder, encoder, all ASN.1 types, tag/length, roundtrip, serde, BER indefinite-length |
| Certificate tests | `synta-certificate/tests/` | X.509/CRL/CSR/OCSP parsing, PKCS#7/12, algorithm IDs, owned types |
| Codegen tests | `synta-codegen/tests/` | ASN.1 schema parsing and Rust code generation |
| Kerberos tests | `synta-krb5/tests/` | Kerberos V5 ASN.1 structures |
| MTC tests | `synta-mtc/tests/` | Merkle Tree Certificate validation and property tests |
| x509-limbo | `synta-x509-verification/tests/limbo/` | RFC 5280 path validation compliance (~39 MB harness) |
| Python binding tests | `tests/python/` | Full Python API surface via pytest |
| C FFI tests | `tests/c/` | C API bindings via `make` |
---
## Running Tests
### All tests at once
```bash
./contrib/ci/local-ci.sh test
```
This runs `cargo test --workspace --all-features` on the stable, beta, and
nightly toolchains in sequence.
### Individual crate tests
```bash
# Core ASN.1 parser
cargo test -p synta
# X.509 / PKI
cargo test -p synta-certificate
# Code generator
cargo test -p synta-codegen --all-features
# Kerberos types
cargo test -p synta-krb5
# Merkle Tree Certificates
cargo test -p synta-mtc
# RFC 5280 path validation (x509-limbo)
cargo test -p synta-x509-verification
```
### Python binding tests
The Python binding must be compiled first. Use `maturin develop` (recommended
for development) or the CI build step:
```bash
# Quick development cycle
cd synta-python
uv venv
uv pip install maturin pytest
uv run maturin develop
uv run pytest ../tests/python/ -v
# Or use the CI helper (also builds the release .so)
./contrib/ci/local-ci.sh python-test
```
Alternatively, if you have a compiled `.so` in `python/`:
```bash
PYTHONPATH=python python3 -m pytest tests/python/ -v
```
### C FFI tests
Build the shared library first, then run the C test suite:
```bash
cargo build --release -p synta-ffi
make -C tests/c test
```
For memory-error checking:
```bash
make -C tests/c valgrind
```
### RFC 5280 compliance (x509-limbo)
The x509-limbo test vectors are large (~39 MB). They are checked out
automatically by the test runner if the `tests/limbo/` directory contains the
harness JSON:
```bash
./contrib/ci/local-ci.sh test-limbo
```
---
### PKCS#11 / HSM Integration Test (kryoptic)
The `pkcs11_kryoptic` integration test signs and verifies a CA certificate
using a real PKCS#11 token. It exercises `BackendPrivateKey::from_pkcs11_uri`
end-to-end and confirms that the resulting signature is valid against the
public key extracted from the token.
#### Prerequisites
- **kryoptic** — a software PKCS#11 token implementation
(`libkryoptic_pkcs11.so` must be discoverable at build time)
- **pkcs11-tool** at `/usr/bin/pkcs11-tool` (from the `opensc` package)
- **modutil** — optional, required for the NSS backend path
(from the `nss-tools` package on Fedora/RHEL)
#### Build-time discovery
`synta-certificate/build.rs` probes for kryoptic at build time and sets two
constants:
- `SYNTA_TEST_KRYOPTIC_LIB` — absolute path to `libkryoptic_pkcs11.so`
- `SYNTA_TEST_PKCS11_PROVIDER` — absolute path to the OpenSSL pkcs11-provider shared object
When `SYNTA_TEST_KRYOPTIC_LIB` is empty (kryoptic not found), every test
function prints a skip message and returns without failure.
`SYNTA_TEST_PKCS11_PROVIDER` is checked only in the OpenSSL backend path; it
does not affect the NSS backend test. The library search path can be
overridden at build time via the `LT_SYS_LIBRARY_PATH` environment variable.
#### Running the test
```bash
# OpenSSL backend
cargo test -p synta-certificate --test pkcs11_kryoptic --features openssl -- --test-threads=1 --nocapture
# NSS backend
cargo test -p synta-certificate --test pkcs11_kryoptic --features nss -- --test-threads=1 --nocapture
```
`--test-threads=1` is **required**. Both `OPENSSL_CONF` (OpenSSL backend) and
NSS module state (NSS backend) are process-global. Running test functions in
parallel on separate threads would cause them to race on that global state,
producing spurious failures.
#### Graceful skip behaviour
When kryoptic or `pkcs11-tool` are absent, each `#[test]` function prints a
human-readable skip message (visible with `--nocapture`) and returns
`Ok(())` rather than failing. This means the test binary exits with status 0,
and CI jobs that run on machines without an HSM do not fail.
---
## Test Vectors
Binary DER/BER test vectors live under `tests/vectors/`. They are organised
by standard:
```
tests/vectors/
├── pkcs/ # PKCS#7, #8, #10, #12 DER binaries
├── cryptography/ # Vectors from the cryptography Python library
├── certs/ # X.509 certificate DER files
└── README.md # Origin and licence notes for each vector set
```
When adding a new vector:
1. Place the raw binary under the appropriate subdirectory.
2. Write a test that decodes it and asserts expected field values.
3. Document the origin and licence in `tests/vectors/README.md`.
---
## Serde Tests
Serde support is feature-gated (`--features serde`). Run serde tests in
isolation to avoid interference from non-serde builds:
```bash
./contrib/ci/local-ci.sh test-serde
# Equivalent to:
cargo test -p synta --features serde
```
---
## Writing New Tests
### Rust unit / integration tests
Place unit tests in a `#[cfg(test)]` module at the bottom of the source file
they test. Integration tests go in `tests/` at the crate root.
```rust
#[cfg(test)]
mod tests {
use super::*;
use synta::{Decoder, Encoding};
#[test]
fn decode_my_type() {
let der = hex::decode("3003020101").unwrap();
let mut dec = Decoder::new(&der, Encoding::Der);
let val = dec.decode::<MyType>().unwrap();
assert_eq!(val.field, 1);
}
}
```
### Python tests
Python tests use `pytest` and live in `tests/python/`. Always set
`PYTHONPATH=python` (or use the `maturin develop` environment):
```python
import synta
def test_decode_certificate(der_bytes):
cert = synta.Certificate.from_der(der_bytes)
assert cert.serial_number is not None
```
### C FFI tests
C tests are small programs in `tests/c/`. Each must link against `libcsynta`
(the Makefile handles this) and follow the OpenSSL-style ownership convention:
- `synta_*_parse_der()` returns an owned handle.
- `synta_*_free()` frees it; call it exactly once.
- `get0_*()` getters return borrowed pointers valid while the parent is alive.
---
## Code Coverage
Coverage reports can be generated with `cargo-llvm-cov`:
```bash
cargo llvm-cov --workspace --html --open
# Or for a lcov report:
cargo llvm-cov --workspace --lcov --output-path coverage/lcov.info
```
Existing `.profraw` files in the repository root and `synta-certificate/` are
from previous coverage runs and can be safely deleted.
---
## Fuzzing
The `synta-fuzz` crate provides a structured ASN.1 fuzzer:
```bash
cargo +nightly fuzz run fuzz_target -- -max_len=65536
```
Seeds for the fuzzer live in `tests/fuzz/corpus/`. Add any DER files that
exercise new code paths to expand the corpus.
---
## Continuous Integration
The full test pipeline is described in [Contributing](contribution.md#running-ci-locally).
Key jobs for testing:
| `test` | `cargo test --workspace` | stable + beta + nightly |
| `test-certificate` | `cargo test -p synta-certificate` | PKI types |
| `test-codegen` | `cargo test -p synta-codegen --all-features` | ASN.1 codegen |
| `test-krb5` | `cargo test -p synta-krb5` | Kerberos types |
| `test-mtc` | `cargo test -p synta-mtc` | MTC validation |
| `test-limbo` | `cargo test -p synta-x509-verification` | x509-limbo RFC 5280 compliance |
| `test-serde` | `cargo test -p synta --features serde` | Serde roundtrip |
| `python-test` | `pytest tests/python/` | Python bindings |
| `c-test` | `make -C tests/c test` | C FFI |
---
## See Also
- [Contributing](contribution.md) — commit conventions, CI setup, code style
- [System Architecture](system-architecture.md) — crate dependency graph
- [Codebase Summary](codebase-summary.md) — file inventory and test suite table
- [Limitations](limitations.md) — known unsupported ASN.1 constructs