Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

220 changes: 220 additions & 0 deletions crates/minibf/src/routes/scripts.rs
Original file line number Diff line number Diff line change
Expand Up @@ -159,3 +159,223 @@ where
cbor: hex::encode(minicbor::to_vec(&datum).map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?),
}))
}

#[cfg(test)]
mod tests {
use super::*;
use crate::test_support::{TestApp, TestFault};

fn fixture_app() -> TestApp {
TestApp::new()
}

fn invalid_script_hash() -> &'static str {
"not-a-script-hash"
}

fn missing_script_hash() -> &'static str {
"ffffffffffffffffffffffffffffffffffffffffffffffffffffffff"
}

fn invalid_datum_hash() -> &'static str {
"not-a-datum-hash"
}

fn missing_datum_hash() -> &'static str {
"ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"
}

async fn assert_status(app: &TestApp, path: &str, expected: StatusCode) {
let (status, bytes) = app.get_bytes(path).await;
assert_eq!(
status,
expected,
"unexpected status {status} with body: {}",
String::from_utf8_lossy(&bytes)
);
}

#[tokio::test]
async fn scripts_by_hash_happy_path() {
let app = fixture_app();
let script_hash = app.vectors().script_hash.as_str();
let path = format!("/scripts/{script_hash}");
let (status, bytes) = app.get_bytes(&path).await;

assert_eq!(
status,
StatusCode::OK,
"unexpected status {status} with body: {}",
String::from_utf8_lossy(&bytes)
);

let item: Script = serde_json::from_slice(&bytes).expect("failed to parse script");
assert_eq!(item.script_hash, script_hash);
assert_eq!(item.r#type, ScriptType::Timelock);
assert_eq!(item.serialised_size, None);
}

#[tokio::test]
async fn scripts_by_hash_not_found_for_invalid_hash() {
let app = fixture_app();
let path = format!("/scripts/{}", invalid_script_hash());
assert_status(&app, &path, StatusCode::NOT_FOUND).await;
}

#[tokio::test]
async fn scripts_by_hash_not_found_for_missing_hash() {
let app = fixture_app();
let path = format!("/scripts/{}", missing_script_hash());
assert_status(&app, &path, StatusCode::NOT_FOUND).await;
}

#[tokio::test]
async fn scripts_by_hash_internal_error() {
let app = TestApp::new_with_fault(Some(TestFault::ArchiveStoreError));
let script_hash = app.vectors().script_hash.as_str();
let path = format!("/scripts/{script_hash}");
assert_status(&app, &path, StatusCode::INTERNAL_SERVER_ERROR).await;
}

#[tokio::test]
async fn scripts_by_hash_json_happy_path() {
let app = fixture_app();
let script_hash = app.vectors().script_hash.as_str();
let path = format!("/scripts/{script_hash}/json");
let (status, bytes) = app.get_bytes(&path).await;

assert_eq!(status, StatusCode::OK);

let item: ScriptJson = serde_json::from_slice(&bytes).expect("failed to parse script json");
assert!(item.json.is_some());
}

#[tokio::test]
async fn scripts_by_hash_json_not_found_for_invalid_hash() {
let app = fixture_app();
let path = format!("/scripts/{}/json", invalid_script_hash());
assert_status(&app, &path, StatusCode::NOT_FOUND).await;
}

#[tokio::test]
async fn scripts_by_hash_json_not_found_for_missing_hash() {
let app = fixture_app();
let path = format!("/scripts/{}/json", missing_script_hash());
assert_status(&app, &path, StatusCode::NOT_FOUND).await;
}

#[tokio::test]
async fn scripts_by_hash_json_internal_error() {
let app = TestApp::new_with_fault(Some(TestFault::ArchiveStoreError));
let script_hash = app.vectors().script_hash.as_str();
let path = format!("/scripts/{script_hash}/json");
assert_status(&app, &path, StatusCode::INTERNAL_SERVER_ERROR).await;
}

#[tokio::test]
async fn scripts_by_hash_cbor_happy_path() {
let app = fixture_app();
let script_hash = app.vectors().script_hash.as_str();
let path = format!("/scripts/{script_hash}/cbor");
let (status, bytes) = app.get_bytes(&path).await;

assert_eq!(status, StatusCode::OK);

let item: ScriptCbor = serde_json::from_slice(&bytes).expect("failed to parse script cbor");
assert_eq!(item.cbor, None);
}

#[tokio::test]
async fn scripts_by_hash_cbor_not_found_for_invalid_hash() {
let app = fixture_app();
let path = format!("/scripts/{}/cbor", invalid_script_hash());
assert_status(&app, &path, StatusCode::NOT_FOUND).await;
}

#[tokio::test]
async fn scripts_by_hash_cbor_not_found_for_missing_hash() {
let app = fixture_app();
let path = format!("/scripts/{}/cbor", missing_script_hash());
assert_status(&app, &path, StatusCode::NOT_FOUND).await;
}

#[tokio::test]
async fn scripts_by_hash_cbor_internal_error() {
let app = TestApp::new_with_fault(Some(TestFault::ArchiveStoreError));
let script_hash = app.vectors().script_hash.as_str();
let path = format!("/scripts/{script_hash}/cbor");
assert_status(&app, &path, StatusCode::INTERNAL_SERVER_ERROR).await;
}

#[tokio::test]
async fn scripts_by_datum_hash_happy_path() {
let app = fixture_app();
let datum_hash = app.vectors().datum_hash.as_str();
let path = format!("/scripts/datum/{datum_hash}");
let (status, bytes) = app.get_bytes(&path).await;

assert_eq!(status, StatusCode::OK);

let item: ScriptDatum =
serde_json::from_slice(&bytes).expect("failed to parse script datum");
assert_eq!(item.json_value.get("int"), Some(&serde_json::json!(42)));
}

#[tokio::test]
async fn scripts_by_datum_hash_not_found_for_invalid_hash() {
let app = fixture_app();
let path = format!("/scripts/datum/{}", invalid_datum_hash());
assert_status(&app, &path, StatusCode::NOT_FOUND).await;
}

#[tokio::test]
async fn scripts_by_datum_hash_not_found_for_missing_hash() {
let app = fixture_app();
let path = format!("/scripts/datum/{}", missing_datum_hash());
assert_status(&app, &path, StatusCode::NOT_FOUND).await;
}

#[tokio::test]
async fn scripts_by_datum_hash_internal_error() {
let app = TestApp::new_with_fault(Some(TestFault::ArchiveStoreError));
let datum_hash = app.vectors().datum_hash.as_str();
let path = format!("/scripts/datum/{datum_hash}");
assert_status(&app, &path, StatusCode::INTERNAL_SERVER_ERROR).await;
}

#[tokio::test]
async fn scripts_by_datum_hash_cbor_happy_path() {
let app = fixture_app();
let datum_hash = app.vectors().datum_hash.as_str();
let path = format!("/scripts/datum/{datum_hash}/cbor");
let (status, bytes) = app.get_bytes(&path).await;

assert_eq!(status, StatusCode::OK);

let item: ScriptDatumCbor =
serde_json::from_slice(&bytes).expect("failed to parse script datum cbor");
assert_eq!(item.cbor, app.vectors().datum_cbor_hex);
}

#[tokio::test]
async fn scripts_by_datum_hash_cbor_not_found_for_invalid_hash() {
let app = fixture_app();
let path = format!("/scripts/datum/{}/cbor", invalid_datum_hash());
assert_status(&app, &path, StatusCode::NOT_FOUND).await;
}

#[tokio::test]
async fn scripts_by_datum_hash_cbor_not_found_for_missing_hash() {
let app = fixture_app();
let path = format!("/scripts/datum/{}/cbor", missing_datum_hash());
assert_status(&app, &path, StatusCode::NOT_FOUND).await;
}

#[tokio::test]
async fn scripts_by_datum_hash_cbor_internal_error() {
let app = TestApp::new_with_fault(Some(TestFault::ArchiveStoreError));
let datum_hash = app.vectors().datum_hash.as_str();
let path = format!("/scripts/datum/{datum_hash}/cbor");
assert_status(&app, &path, StatusCode::INTERNAL_SERVER_ERROR).await;
}
}
5 changes: 5 additions & 0 deletions crates/minikupo/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -24,3 +24,8 @@ axum = { version = "0.8.4" }

dolos-core = { path = "../core" }
dolos-cardano = { path = "../cardano" }

[dev-dependencies]
dolos-testing = { path = "../testing" }
tower = { workspace = true, features = ["util"] }
http-body-util = "0.1.2"
2 changes: 2 additions & 0 deletions crates/minikupo/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ use crate::types::BadRequest;

pub mod patterns;
mod routes;
#[cfg(test)]
mod test_support;
mod types;

#[derive(Clone)]
Expand Down
66 changes: 66 additions & 0 deletions crates/minikupo/src/routes/datums.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,3 +37,69 @@ fn parse_datum_hash(value: &str) -> Result<Hash<32>, StatusCode> {
fn datum_hash_hint() -> String {
"Invalid datum hash. Hash must be 64 lowercase hex characters.".to_string()
}

#[cfg(test)]
mod tests {
use axum::http::StatusCode;

use crate::{
test_support::{TestApp, TestFault},
types::Datum,
};

fn missing_hash() -> &'static str {
"ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"
}

async fn assert_status(app: &TestApp, path: &str, expected: StatusCode) {
let (status, bytes) = app.get_bytes(path).await;
assert_eq!(
status,
expected,
"unexpected status {status} with body: {}",
String::from_utf8_lossy(&bytes)
);
}

#[tokio::test]
async fn datums_happy_path() {
let app = TestApp::new();
let path = format!("/datums/{}", app.vectors().datum_hash);
let (status, bytes) = app.get_bytes(&path).await;

assert_eq!(
status,
StatusCode::OK,
"unexpected status {status} with body: {}",
String::from_utf8_lossy(&bytes)
);

let datum: Datum = serde_json::from_slice(&bytes).expect("failed to parse datum response");
assert_eq!(datum.datum, app.vectors().datum_cbor_hex);
}

#[tokio::test]
async fn datums_missing_returns_null() {
let app = TestApp::new();
let path = format!("/datums/{}", missing_hash());
let (status, bytes) = app.get_bytes(&path).await;

assert_eq!(status, StatusCode::OK);
let datum: Option<Datum> =
serde_json::from_slice(&bytes).expect("failed to parse null datum response");
assert_eq!(datum, None);
}

#[tokio::test]
async fn datums_bad_request() {
let app = TestApp::new();
assert_status(&app, "/datums/not-a-hash", StatusCode::BAD_REQUEST).await;
}

#[tokio::test]
async fn datums_internal_error() {
let app = TestApp::new_with_fault(Some(TestFault::ArchiveStoreError));
let path = format!("/datums/{}", app.vectors().datum_hash);
assert_status(&app, &path, StatusCode::INTERNAL_SERVER_ERROR).await;
}
}
Loading
Loading