← Week 3: TLS Integration and CLI

Day 20: Integration Test — Full Workflow

Phase 2 · July 6, 2026

← Week 3: TLS Integration and CLI

Agenda (2–3 hours)

  • Triage (30 min): Identify any incomplete or broken modules from Days 1–19
  • Test (120 min): Write and run the full end-to-end integration test suite
  • Fix (30 min): Address any failures
← Week 3: TLS Integration and CLI

What the Full Integration Test Covers

[init]         → root CA + intermediate CA created
[issue server] → server cert for localhost
[issue client] → client cert for device-001
[issue client] → client cert for device-002
[mTLS]         → device-001 connects to server → succeeds, identity extracted
[mTLS]         → device-002 connects to server → succeeds
[revoke]       → device-002 revoked (keyCompromise)
[CRL]          → new CRL generated, device-002 serial present
[mTLS + CRL]   → device-001 reconnects with CRL check → still succeeds
[mTLS + CRL]   → device-002 reconnects with CRL check → rejected
[OCSP]         → device-001 OCSP query → good
[OCSP]         → device-002 OCSP query → revoked
[inspect]      → parse all certs, verify fields
[list]         → correct status for all 3 certs
[round-trip]   → reload store from disk, all records intact
← Week 3: TLS Integration and CLI

Integration Test Structure

// tests/integration.rs

#[tokio::test]
async fn test_full_pki_workflow() {
    let dir = tempfile::tempdir().unwrap();
    let pki = PkiContext::init(dir.path()).await.unwrap();

    let server_serial = pki.issue_server("localhost").await.unwrap();
    let client1_serial = pki.issue_client("device-001").await.unwrap();
    let client2_serial = pki.issue_client("device-002").await.unwrap();

    // Start echo server
    let server_handle = pki.start_echo_server(4433).await.unwrap();

    // Both clients connect
    assert!(pki.echo_connect("device-001", "hello").await.is_ok());
    assert!(pki.echo_connect("device-002", "hello").await.is_ok());

    // Revoke device-002
    pki.revoke(&client2_serial, "keyCompromise").await.unwrap();

    // device-001 still works, device-002 is rejected
    assert!(pki.echo_connect_with_crl_check("device-001").await.is_ok());
    assert!(pki.echo_connect_with_crl_check("device-002").await.is_err());
}
← Week 3: TLS Integration and CLI

PkiContext Test Helper

Create a PkiContext struct in tests/integration.rs that wraps the toy-pki modules
and provides high-level methods for the integration test. This keeps test code readable.

struct PkiContext {
    dir:          TempDir,
    root_ca:      Ca,
    intermediate: Ca,
    cert_store:   CertStore,
    crl_store:    CrlStore,
}

impl PkiContext {
    async fn init(dir: &Path) -> anyhow::Result<Self> { ... }
    async fn issue_server(&mut self, hostname: &str) -> anyhow::Result<String> { ... }
    async fn issue_client(&mut self, identity: &str) -> anyhow::Result<String> { ... }
    async fn revoke(&mut self, serial: &str, reason: &str) -> anyhow::Result<()> { ... }
    async fn echo_connect(&self, identity: &str, msg: &str) -> anyhow::Result<String> { ... }
}
← Week 3: TLS Integration and CLI

Debugging Failures

When a test fails, add temporary println! or use cargo test -- --nocapture.

Common failure modes at this stage:

  • Serial mismatch between CertRecord and actual cert bytes
  • CRL not regenerated after revocation (stale CRL check)
  • TLS handshake fails with CertificateUnknown — check chain ordering (leaf first)
  • OCSP responder returns unknown — check certid_matches_ca() hash computation
← Week 3: TLS Integration and CLI

Challenge Assignment

Get all integration tests green. Every scenario from slide 2 must pass.

Then add one more test: test_expired_cert_rejected

  • Issue a cert but override not_after to a timestamp in the past
  • Verify validate_chain() rejects it immediately with a clear expiry error
  • Verify the TLS server rejects it during handshake
← Week 3: TLS Integration and CLI

Resources

  • tempfile::TempDir: isolated test directories that clean up on drop
  • tokio::test attribute for async integration tests
  • cargo test -- --nocapture to see test output
  • Your test infrastructure from Days 7 and 14