synta 0.1.11

ASN.1 parser, decoder, and encoder library with DER/BER support and C FFI
Documentation
# Testing Guide

<!-- toc -->
<!-- /toc -->

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

| Suite | Location | What it covers |
|-------|----------|---------------|
| 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:

| CI Job | Command | Notes |
|--------|---------|-------|
| `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