175 lines
8.4 KiB
Markdown
175 lines
8.4 KiB
Markdown
---
|
||
phase: 03-round-trip-verification
|
||
plan: 01
|
||
type: execute
|
||
wave: 1
|
||
depends_on: []
|
||
files_modified:
|
||
- src/lib.rs
|
||
- src/main.rs
|
||
- Cargo.toml
|
||
- src/crypto.rs
|
||
- src/compression.rs
|
||
- src/format.rs
|
||
autonomous: true
|
||
requirements: [TST-03]
|
||
|
||
must_haves:
|
||
truths:
|
||
- "Unit tests pass for crypto module: encrypt/decrypt round-trip, HMAC compute/verify, SHA-256 known value"
|
||
- "Unit tests pass for compression module: compress/decompress round-trip, empty data, should_compress heuristic"
|
||
- "Unit tests pass for format module: header write/read round-trip, TOC entry write/read round-trip (ASCII + Cyrillic names)"
|
||
- "Project compiles as both library crate and binary crate (cargo build succeeds)"
|
||
artifacts:
|
||
- path: "src/lib.rs"
|
||
provides: "Library crate re-exporting all modules for integration tests"
|
||
contains: "pub mod crypto"
|
||
- path: "Cargo.toml"
|
||
provides: "Dev-dependencies for test infrastructure"
|
||
contains: "tempfile"
|
||
key_links:
|
||
- from: "src/main.rs"
|
||
to: "src/lib.rs"
|
||
via: "use encrypted_archive:: imports"
|
||
pattern: "use encrypted_archive::"
|
||
- from: "src/lib.rs"
|
||
to: "src/crypto.rs"
|
||
via: "pub mod crypto"
|
||
pattern: "pub mod crypto"
|
||
---
|
||
|
||
<objective>
|
||
Create the library crate structure, add test dependencies, and implement unit tests for every pipeline module (crypto, compression, format serialization).
|
||
|
||
Purpose: TST-03 requires unit tests for each pipeline stage. The lib.rs creation also unblocks Plan 02 which needs to import library functions from integration tests in `tests/`.
|
||
|
||
Output: `src/lib.rs` with module re-exports, dev-dependencies in `Cargo.toml`, unit tests in `#[cfg(test)]` blocks inside `crypto.rs`, `compression.rs`, `format.rs`.
|
||
</objective>
|
||
|
||
<execution_context>
|
||
@/home/nick/.claude/get-shit-done/workflows/execute-plan.md
|
||
@/home/nick/.claude/get-shit-done/templates/summary.md
|
||
</execution_context>
|
||
|
||
<context>
|
||
@.planning/PROJECT.md
|
||
@.planning/ROADMAP.md
|
||
@.planning/STATE.md
|
||
@.planning/phases/03-round-trip-verification/03-RESEARCH.md
|
||
@docs/FORMAT.md
|
||
@src/main.rs
|
||
@src/crypto.rs
|
||
@src/compression.rs
|
||
@src/format.rs
|
||
@src/key.rs
|
||
@Cargo.toml
|
||
</context>
|
||
|
||
<tasks>
|
||
|
||
<task type="auto">
|
||
<name>Task 1: Create lib.rs, update main.rs imports, add dev-dependencies</name>
|
||
<files>src/lib.rs, src/main.rs, Cargo.toml</files>
|
||
<action>
|
||
1. Create `src/lib.rs` that re-exports all modules as public:
|
||
```rust
|
||
pub mod archive;
|
||
pub mod cli;
|
||
pub mod compression;
|
||
pub mod crypto;
|
||
pub mod format;
|
||
pub mod key;
|
||
```
|
||
|
||
2. Update `src/main.rs` to use the library crate instead of local `mod` declarations:
|
||
- Remove all `mod` declarations (archive, cli, compression, crypto, format, key)
|
||
- Replace with `use encrypted_archive::cli::{Cli, Commands};` and `use encrypted_archive::archive;`
|
||
- Keep the `use clap::Parser;` import
|
||
- The `main()` function body stays the same
|
||
|
||
3. Add dev-dependencies to `Cargo.toml`:
|
||
```toml
|
||
[dev-dependencies]
|
||
tempfile = "3.26"
|
||
assert_cmd = "2.1"
|
||
hex-literal = "1.1"
|
||
predicates = "3.1"
|
||
```
|
||
|
||
IMPORTANT: Use Context7 to verify `tempfile` and `assert_cmd` API before writing code if needed. The research doc already verified versions (tempfile 3.26.0, assert_cmd 2.1.2, hex-literal 1.1.0, predicates 3.1.4).
|
||
|
||
4. Verify: `cargo build` succeeds (both lib and bin targets compile).
|
||
</action>
|
||
<verify>
|
||
<automated>cd /home/nick/Projects/Rust/encrypted_archive && cargo build 2>&1</automated>
|
||
<manual>No lib.rs compilation errors, main.rs uses encrypted_archive:: imports</manual>
|
||
</verify>
|
||
<done>Project compiles as both library and binary crate. All six modules re-exported via lib.rs. Dev-dependencies declared in Cargo.toml.</done>
|
||
</task>
|
||
|
||
<task type="auto">
|
||
<name>Task 2: Add unit tests to crypto.rs, compression.rs, and format.rs</name>
|
||
<files>src/crypto.rs, src/compression.rs, src/format.rs</files>
|
||
<action>
|
||
Add `#[cfg(test)] mod tests { ... }` blocks at the bottom of each module file.
|
||
|
||
**src/crypto.rs** -- add these unit tests:
|
||
- `test_encrypt_decrypt_roundtrip`: Encrypt "Hello, World!" with KEY and a zero IV, decrypt, assert equality.
|
||
- `test_encrypt_decrypt_empty`: Encrypt empty slice, decrypt, assert empty result. Verifies PKCS7 full-block padding on 0-byte input.
|
||
- `test_encrypted_size_formula`: Assert `encrypt_data(b"Hello", &KEY, &iv).len() == 16` (5 bytes -> ((5/16)+1)*16 = 16). Also test 16 bytes -> 32 (full padding block), and 0 bytes -> 16.
|
||
- `test_hmac_compute_verify`: Compute HMAC for a fixed IV + ciphertext, then verify_hmac returns true. Also test with wrong expected value returns false.
|
||
- `test_sha256_known_value`: SHA-256("Hello") must equal `185f8db32271fe25f561a6fc938b2e264306ec304eda518007d1764826381969` (from FORMAT.md Section 12.3). Use `hex_literal::hex!()` macro for the expected value.
|
||
- `test_sha256_empty`: SHA-256("") must equal `e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855`.
|
||
|
||
Import `use super::*;` and `use crate::key::KEY;` and `use hex_literal::hex;` inside the test module.
|
||
|
||
**src/compression.rs** -- add these unit tests:
|
||
- `test_compress_decompress_roundtrip`: Compress "Hello, World! This is test data for compression." -> decompress -> assert equals original.
|
||
- `test_compress_decompress_empty`: Compress empty slice -> decompress -> assert empty.
|
||
- `test_compress_decompress_large`: Compress 10000 bytes of pattern data -> decompress -> assert equals original.
|
||
- `test_should_compress_text`: Assert `should_compress("readme.txt", &[])` is true. Also test "data.json".
|
||
- `test_should_not_compress_known_extensions`: Assert `should_compress("app.apk", &[])` is false. Also test "photo.jpg", "archive.zip".
|
||
- `test_should_not_compress_excluded`: Assert `should_compress("special.dat", &["special.dat".into()])` is false.
|
||
|
||
Import `use super::*;` inside the test module.
|
||
|
||
**src/format.rs** -- add these unit tests:
|
||
- `test_header_write_read_roundtrip`: Create a Header with typical values (version=1, flags=0x01, file_count=3, toc_offset=HEADER_SIZE, toc_size=330, toc_iv=zeroed, reserved=zeroed). Write to Vec<u8>, assert len == HEADER_SIZE. Read back from Cursor, assert all fields match.
|
||
- `test_toc_entry_roundtrip_ascii`: Create a TocEntry for "hello.txt" with known field values. Write to Vec<u8>, assert len == 101 + 9. Read back, assert all fields match including iv/hmac/sha256 byte arrays.
|
||
- `test_toc_entry_roundtrip_cyrillic`: Create a TocEntry for "тестовый_файл.txt". Write, read back, assert name and all fields match. Verify buf length == 101 + "тестовый_файл.txt".len() (which is 29 bytes for UTF-8 Cyrillic).
|
||
- `test_toc_entry_roundtrip_empty_name`: Create a TocEntry with name="" (edge case). Write, read back, assert name is empty string.
|
||
- `test_header_rejects_bad_magic`: Create a buffer with wrong magic bytes, attempt read_header, assert error.
|
||
- `test_header_rejects_bad_version`: Create a valid-magic buffer but version=2, attempt read_header, assert error.
|
||
- `test_entry_size_calculation`: Assert `entry_size(&entry_for_hello_txt) == 110` and `entry_size(&entry_for_data_bin) == 109`. Test `compute_toc_size` on both entries equals 219 (matching FORMAT.md worked example).
|
||
|
||
Import `use super::*;` and `use std::io::Cursor;` inside the test module.
|
||
|
||
All assertions should use `assert_eq!` for value comparison (gives diff on failure) or `assert!` for boolean checks.
|
||
</action>
|
||
<verify>
|
||
<automated>cd /home/nick/Projects/Rust/encrypted_archive && cargo test 2>&1</automated>
|
||
<manual>All unit tests pass, no compilation errors</manual>
|
||
</verify>
|
||
<done>Unit tests exist and pass for all three pipeline modules: crypto (6 tests), compression (6 tests), format (7 tests). Total: 19 unit tests covering encrypt/decrypt, HMAC, SHA-256, compress/decompress, should_compress, header/TOC serialization round-trips, and error cases.</done>
|
||
</task>
|
||
|
||
</tasks>
|
||
|
||
<verification>
|
||
- `cargo build` succeeds (lib + bin)
|
||
- `cargo test` passes all unit tests
|
||
- `src/lib.rs` exists and re-exports all modules
|
||
- `src/main.rs` uses `use encrypted_archive::` imports (no local `mod` declarations)
|
||
- Each of crypto.rs, compression.rs, format.rs has a `#[cfg(test)]` module with tests
|
||
</verification>
|
||
|
||
<success_criteria>
|
||
1. `cargo test` runs 19+ unit tests, all pass
|
||
2. Project structure supports both `cargo run` (binary) and `use encrypted_archive::module` (library imports)
|
||
3. TST-03 is satisfied: unit tests cover compression, encryption, HMAC, SHA-256, format serialization/deserialization
|
||
</success_criteria>
|
||
|
||
<output>
|
||
After completion, create `.planning/phases/03-round-trip-verification/03-01-SUMMARY.md`
|
||
</output>
|