parlov 0.4.0

HTTP oracle detection tool — systematic probing for RFC-compliant information leakage.
Documentation
//! Extended sanity tests for RFC-defined PATCH response patterns.
//!
//! Covers 200/404, 204/404, 409/404, 415/404, and 412/404 differentials
//! per RFC 5789 and RFC 9110.

#![deny(clippy::all)]

mod fixtures {
    pub mod patch_server;
}

use fixtures::patch_server::spawn;
use serde_json::json;

// --- /success/{id} (200 vs 404) ---

#[tokio::test]
async fn patch_success_known_id_returns_200() {
    let addr = spawn().await;
    let resp = reqwest::Client::new()
        .patch(format!("http://{addr}/success/42"))
        .json(&json!({"role": "test"}))
        .send()
        .await
        .expect("request failed");
    assert_eq!(resp.status(), reqwest::StatusCode::OK);
}

#[tokio::test]
async fn patch_success_unknown_id_returns_404() {
    let addr = spawn().await;
    let resp = reqwest::Client::new()
        .patch(format!("http://{addr}/success/999"))
        .json(&json!({"role": "test"}))
        .send()
        .await
        .expect("request failed");
    assert_eq!(resp.status(), reqwest::StatusCode::NOT_FOUND);
}

// --- /no-content/{id} (204 vs 404) ---

#[tokio::test]
async fn patch_no_content_known_id_returns_204() {
    let addr = spawn().await;
    let resp = reqwest::Client::new()
        .patch(format!("http://{addr}/no-content/42"))
        .json(&json!({"role": "test"}))
        .send()
        .await
        .expect("request failed");
    assert_eq!(resp.status(), reqwest::StatusCode::NO_CONTENT);
}

#[tokio::test]
async fn patch_no_content_unknown_id_returns_404() {
    let addr = spawn().await;
    let resp = reqwest::Client::new()
        .patch(format!("http://{addr}/no-content/999"))
        .json(&json!({"role": "test"}))
        .send()
        .await
        .expect("request failed");
    assert_eq!(resp.status(), reqwest::StatusCode::NOT_FOUND);
}

// --- /conflict/{id} (409 vs 404) ---

#[tokio::test]
async fn patch_conflict_known_id_returns_409() {
    let addr = spawn().await;
    let resp = reqwest::Client::new()
        .patch(format!("http://{addr}/conflict/42"))
        .json(&json!({"role": "test"}))
        .send()
        .await
        .expect("request failed");
    assert_eq!(resp.status(), reqwest::StatusCode::CONFLICT);
}

#[tokio::test]
async fn patch_conflict_unknown_id_returns_404() {
    let addr = spawn().await;
    let resp = reqwest::Client::new()
        .patch(format!("http://{addr}/conflict/999"))
        .json(&json!({"role": "test"}))
        .send()
        .await
        .expect("request failed");
    assert_eq!(resp.status(), reqwest::StatusCode::NOT_FOUND);
}

// --- /media/{id} (415 vs 404) ---

#[tokio::test]
async fn patch_media_known_id_returns_415() {
    let addr = spawn().await;
    let resp = reqwest::Client::new()
        .patch(format!("http://{addr}/media/42"))
        .json(&json!({"role": "test"}))
        .send()
        .await
        .expect("request failed");
    assert_eq!(resp.status(), reqwest::StatusCode::UNSUPPORTED_MEDIA_TYPE);
}

#[tokio::test]
async fn patch_media_unknown_id_returns_404() {
    let addr = spawn().await;
    let resp = reqwest::Client::new()
        .patch(format!("http://{addr}/media/999"))
        .json(&json!({"role": "test"}))
        .send()
        .await
        .expect("request failed");
    assert_eq!(resp.status(), reqwest::StatusCode::NOT_FOUND);
}

// --- /precondition/{id} (412 vs 404) ---

#[tokio::test]
async fn patch_precondition_known_id_returns_412() {
    let addr = spawn().await;
    let resp = reqwest::Client::new()
        .patch(format!("http://{addr}/precondition/42"))
        .json(&json!({"role": "test"}))
        .send()
        .await
        .expect("request failed");
    assert_eq!(resp.status(), reqwest::StatusCode::PRECONDITION_FAILED);
}

#[tokio::test]
async fn patch_precondition_unknown_id_returns_404() {
    let addr = spawn().await;
    let resp = reqwest::Client::new()
        .patch(format!("http://{addr}/precondition/999"))
        .json(&json!({"role": "test"}))
        .send()
        .await
        .expect("request failed");
    assert_eq!(resp.status(), reqwest::StatusCode::NOT_FOUND);
}