- Rust stable (1.75+)
- Git
git clone https://github.com/raskell-io/ushio.git
cd ushio
cargo buildRelease build:
cargo build --release# All tests
cargo test
# Unit tests only (fast, no network)
cargo test --lib
# Integration tests only (uses wiremock)
cargo test --test integration
# A specific test
cargo test test_body_diff_differentCI runs all three — make sure they pass locally before pushing:
# Formatting
cargo fmt -- --check
# Linting (warnings = errors in CI)
cargo clippy -- -D warnings
# Tests
cargo test.
├── src/
│ ├── lib.rs # Library root
│ ├── main.rs # Binary entry point
│ ├── har.rs # HAR parsing
│ ├── capture.rs # Capture format
│ ├── replay.rs # Replay engine
│ ├── diff.rs # Diff engine
│ ├── output.rs # Output formatters
│ └── proxy.rs # Capture proxy + remote fetch
├── tests/
│ ├── integration.rs # Integration tests (wiremock-based)
│ └── fixtures/ # Sample HAR and capture files
├── docs/
│ ├── *.md # User-facing documentation
│ └── dev/ # Developer documentation
└── .claude/
├── CLAUDE.md # AI assistant context
└── ROADMAP.md # Feature status
- Implement in the appropriate module (
replay.rs,diff.rs, etc.) - Expose through
lib.rsif it's public API - Wire CLI in
main.rsif it has a flag - Add unit tests in the module's
#[cfg(test)]section - Add integration tests in
tests/integration.rsif it's end-to-end - Update docs in
docs/for user-facing changes
WAF body patterns are in diff.rs in the WAF_BODY_PATTERNS constant. Add the pattern as a lowercase string — matching is case-insensitive.
const WAF_BODY_PATTERNS: &[&str] = &[
// ... existing patterns ...
"your new pattern here",
];Add a test in the tests module:
#[test]
fn test_waf_block_body_new_vendor() {
let result = make_result_with_body(0, 200, vec![], Some("Your New Pattern Here"));
assert!(is_waf_block(&result));
}- Add a variant to
OutputFormatinmain.rs - Implement
print_replay_<format>()andprint_diff_<format>()inoutput.rs - Wire it into the
match args.formatblocks inmain.rs
Test fixtures live in tests/fixtures/:
simple.har— 3-request HAR with GET, POST, and a 403capture.json— 2-request ushio capture
To add a new fixture, create the file and reference it in tests via:
fn fixture_path(name: &str) -> PathBuf {
PathBuf::from(env!("CARGO_MANIFEST_DIR"))
.join("tests")
.join("fixtures")
.join(name)
}Note: .gitignore has *.har but an exception for tests/fixtures/*.har.