use chrono::Utc;
use reqwest::{Client, StatusCode};
use serde_json::{json, Value};
use sqlx::postgres::PgPoolOptions;
use sqlx::PgPool;
use uuid::Uuid;
struct E2e {
client: Client,
#[allow(dead_code)]
base_url: String,
access_token: String,
org_id: String,
workspace_id: String,
pool: PgPool,
}
async fn register_and_setup(base_url: &str, pool: &PgPool) -> E2e {
let client = Client::new();
let ts = Utc::now().timestamp_micros();
let username = format!("aidiff_{}", ts);
let email = format!("aidiff_{}@e2e-test.local", ts);
let password = "SecureP@ssw0rd!2024";
let res = client
.post(format!("{}/api/v1/auth/register", base_url))
.json(&json!({ "username": username, "email": email, "password": password }))
.send()
.await
.expect("register failed");
let status = res.status();
let body: Value = res.json().await.expect("register not JSON");
assert!(status.is_success(), "register {}: {}", status, body);
let access_token = body["access_token"]
.as_str()
.or_else(|| body["token"].as_str())
.expect("no access token")
.to_string();
let org_slug = format!("aidiff-{}", ts);
let res = client
.post(format!("{}/api/v1/organizations", base_url))
.header("Authorization", format!("Bearer {}", access_token))
.json(&json!({ "name": format!("AI Diff Test Org {}", ts), "slug": org_slug }))
.send()
.await
.expect("create org failed");
let body: Value = res.json().await.expect("org not JSON");
let org_id = body["id"].as_str().expect("no org id").to_string();
let res = client
.post(format!("{}/api/v1/workspaces", base_url))
.header("Authorization", format!("Bearer {}", access_token))
.header("X-Organization-Id", &org_id)
.json(&json!({ "name": "ai-diff-e2e", "description": "e2e fixture" }))
.send()
.await
.expect("create workspace failed");
let body: Value = res.json().await.expect("ws not JSON");
let workspace_id = body["id"].as_str().expect("no ws id").to_string();
E2e {
client,
base_url: base_url.to_string(),
access_token,
org_id,
workspace_id,
pool: pool.clone(),
}
}
fn internal_token() -> String {
std::env::var("MOCKFORGE_INTERNAL_API_TOKEN").expect("MOCKFORGE_INTERNAL_API_TOKEN must be set")
}
#[tokio::test]
#[ignore]
async fn rejects_missing_internal_auth() {
let base_url = std::env::var("REGISTRY_URL").expect("REGISTRY_URL must be set");
let database_url = std::env::var("DATABASE_URL").expect("DATABASE_URL must be set");
let pool = PgPoolOptions::new()
.max_connections(2)
.connect(&database_url)
.await
.expect("DB connect failed");
let e = register_and_setup(&base_url, &pool).await;
let res = e
.client
.post(format!("{}/api/v1/internal/contract-diff/score", base_url))
.json(&json!({
"org_id": e.org_id,
"workspace_id": e.workspace_id,
"spec_excerpt": "openapi: 3.0.0",
"endpoints": [{"method": "GET", "path": "/x"}],
}))
.send()
.await
.expect("post failed");
assert_eq!(res.status(), StatusCode::BAD_REQUEST);
let res = e
.client
.post(format!("{}/api/v1/internal/contract-diff/score", base_url))
.header("Authorization", "Bearer wrong-token")
.json(&json!({
"org_id": e.org_id,
"workspace_id": e.workspace_id,
"spec_excerpt": "openapi: 3.0.0",
"endpoints": [{"method": "GET", "path": "/x"}],
}))
.send()
.await
.expect("post failed");
assert_eq!(res.status(), StatusCode::BAD_REQUEST);
let res = e
.client
.post(format!("{}/api/v1/internal/contract-diff/score", base_url))
.header("Authorization", format!("Bearer {}", e.access_token))
.json(&json!({
"org_id": e.org_id,
"workspace_id": e.workspace_id,
"spec_excerpt": "openapi: 3.0.0",
"endpoints": [{"method": "GET", "path": "/x"}],
}))
.send()
.await
.expect("post failed");
assert_eq!(res.status(), StatusCode::BAD_REQUEST);
}
#[tokio::test]
#[ignore]
async fn empty_endpoints_short_circuits_no_llm_call() {
let base_url = std::env::var("REGISTRY_URL").expect("REGISTRY_URL must be set");
let database_url = std::env::var("DATABASE_URL").expect("DATABASE_URL must be set");
let pool = PgPoolOptions::new()
.max_connections(2)
.connect(&database_url)
.await
.expect("DB connect failed");
let e = register_and_setup(&base_url, &pool).await;
let res = e
.client
.post(format!("{}/api/v1/internal/contract-diff/score", base_url))
.header("Authorization", format!("Bearer {}", internal_token()))
.json(&json!({
"org_id": e.org_id,
"workspace_id": e.workspace_id,
"spec_excerpt": "openapi: 3.0.0\npaths: {}",
"endpoints": [],
}))
.send()
.await
.expect("post failed");
assert_eq!(
res.status(),
StatusCode::OK,
"empty endpoints should short-circuit OK: {:?}",
res.text().await.unwrap_or_default()
);
let body: Value = res.json().await.expect("not JSON");
assert_eq!(body["no_traffic"], json!(true));
assert_eq!(body["tokens_used"], json!(0));
assert!(body["findings"].as_array().expect("findings array").is_empty());
}
#[tokio::test]
#[ignore]
async fn no_captured_exchanges_short_circuits_no_llm_call() {
let base_url = std::env::var("REGISTRY_URL").expect("REGISTRY_URL must be set");
let database_url = std::env::var("DATABASE_URL").expect("DATABASE_URL must be set");
let pool = PgPoolOptions::new()
.max_connections(2)
.connect(&database_url)
.await
.expect("DB connect failed");
let e = register_and_setup(&base_url, &pool).await;
let res = e
.client
.post(format!("{}/api/v1/internal/contract-diff/score", base_url))
.header("Authorization", format!("Bearer {}", internal_token()))
.json(&json!({
"org_id": e.org_id,
"workspace_id": e.workspace_id,
"spec_excerpt": "openapi: 3.0.0\npaths:\n /api/users:\n get: {}",
"endpoints": [{"method": "GET", "path": "/api/users"}],
}))
.send()
.await
.expect("post failed");
assert_eq!(res.status(), StatusCode::OK);
let body: Value = res.json().await.expect("not JSON");
assert_eq!(body["no_traffic"], json!(true));
assert_eq!(body["tokens_used"], json!(0));
assert!(body["findings"].as_array().expect("findings array").is_empty());
}
#[tokio::test]
#[ignore]
async fn free_plan_without_byok_is_quota_blocked_when_traffic_exists() {
let base_url = std::env::var("REGISTRY_URL").expect("REGISTRY_URL must be set");
let database_url = std::env::var("DATABASE_URL").expect("DATABASE_URL must be set");
let pool = PgPoolOptions::new()
.max_connections(2)
.connect(&database_url)
.await
.expect("DB connect failed");
let e = register_and_setup(&base_url, &pool).await;
let org_uuid = Uuid::parse_str(&e.org_id).expect("org id not UUID");
let workspace_uuid = Uuid::parse_str(&e.workspace_id).expect("ws id not UUID");
let deployment_id = Uuid::new_v4();
let ts = Utc::now().timestamp_micros();
sqlx::query(
r#"
INSERT INTO hosted_mocks (id, org_id, name, slug, config_json, status)
VALUES ($1, $2, $3, $4, '{}'::jsonb, 'active')
"#,
)
.bind(deployment_id)
.bind(org_uuid)
.bind("ai-diff-fixture")
.bind(format!("aifix-{}", ts))
.execute(&e.pool)
.await
.expect("insert hosted_mock failed");
let capture_id = Uuid::new_v4().to_string();
sqlx::query(
r#"
INSERT INTO runtime_captures (
deployment_id, capture_id, protocol, occurred_at, method, path,
request_headers, request_body_encoding, status_code,
workspace_id, source
)
VALUES (
$1, $2, 'http', NOW(), 'POST', '/api/checkout',
'{}', 'utf8', 200,
$3, 'hosted'
)
"#,
)
.bind(deployment_id)
.bind(&capture_id)
.bind(workspace_uuid)
.execute(&e.pool)
.await
.expect("insert runtime_capture failed");
let res = e
.client
.post(format!("{}/api/v1/internal/contract-diff/score", base_url))
.header("Authorization", format!("Bearer {}", internal_token()))
.json(&json!({
"org_id": e.org_id,
"workspace_id": e.workspace_id,
"spec_excerpt": "openapi: 3.0.0",
"endpoints": [{"method": "POST", "path": "/api/checkout"}],
}))
.send()
.await
.expect("post failed");
let status = res.status();
let body = res.text().await.unwrap_or_default();
assert_eq!(
status,
StatusCode::FORBIDDEN,
"expected 403 quota block, got {}: {}",
status,
body
);
assert!(
body.to_lowercase().contains("ai")
&& (body.to_lowercase().contains("byok") || body.to_lowercase().contains("plan")),
"expected quota error mentioning AI/BYOK/plan, got: {body}"
);
}