#![allow(clippy::expect_used, clippy::unwrap_used, clippy::panic)]
use camino::{Utf8Path, Utf8PathBuf};
use doiget_cli::commands::fetch::{build_dry_run_envelope, build_fetch_plan, run_with_options};
use doiget_cli::commands::output::OutputMode;
use doiget_core::Ref;
use serial_test::serial;
use tempfile::TempDir;
mod common;
use common::env_guard::EnvGuard;
#[tokio::test]
#[serial]
async fn dry_run_fetch_doi_returns_ok_without_side_effects() {
let td = TempDir::new().expect("tempdir");
let temp_root: Utf8PathBuf = Utf8Path::from_path(td.path())
.expect("temp dir is utf-8")
.to_path_buf();
let store_root = temp_root.join("papers");
let log_path = temp_root.join("log.jsonl");
let env = EnvGuard::new(&[
"DOIGET_STORE_ROOT",
"DOIGET_LOG_PATH",
"DOIGET_ARXIV_BASE",
"DOIGET_CROSSREF_BASE",
"DOIGET_UNPAYWALL_BASE",
"DOIGET_OA_PUBLISHER_BASE",
"DOIGET_CONTACT_EMAIL",
"DOIGET_UNPAYWALL_EMAIL",
]);
env.set("DOIGET_STORE_ROOT", store_root.as_str());
env.set("DOIGET_LOG_PATH", log_path.as_str());
let result = run_with_options("10.1234/foo".to_string(), true, OutputMode::Human).await;
result.expect("dry-run fetch::run_with_options succeeds");
assert!(
!log_path.exists(),
"dry-run path must not create the provenance log file: {log_path}",
);
let pdf_path = store_root.join("doi_10.1234_foo.pdf");
let toml_path = store_root.join(".metadata").join("doi_10.1234_foo.toml");
assert!(
!pdf_path.exists(),
"dry-run path must not write the PDF: {pdf_path}",
);
assert!(
!toml_path.exists(),
"dry-run path must not write the metadata TOML: {toml_path}",
);
drop(env);
drop(td);
}
#[tokio::test]
#[serial]
async fn dry_run_fetch_arxiv_returns_ok_without_side_effects() {
let td = TempDir::new().expect("tempdir");
let temp_root: Utf8PathBuf = Utf8Path::from_path(td.path())
.expect("temp dir is utf-8")
.to_path_buf();
let store_root = temp_root.join("papers");
let log_path = temp_root.join("log.jsonl");
let env = EnvGuard::new(&[
"DOIGET_STORE_ROOT",
"DOIGET_LOG_PATH",
"DOIGET_ARXIV_BASE",
"DOIGET_CROSSREF_BASE",
"DOIGET_UNPAYWALL_BASE",
"DOIGET_OA_PUBLISHER_BASE",
]);
env.set("DOIGET_STORE_ROOT", store_root.as_str());
env.set("DOIGET_LOG_PATH", log_path.as_str());
let result = run_with_options("arxiv:2401.12345".to_string(), true, OutputMode::Human).await;
result.expect("dry-run arxiv fetch::run_with_options succeeds");
assert!(
!log_path.exists(),
"dry-run arxiv path must not create the provenance log file: {log_path}",
);
let pdf_path = store_root.join("arxiv_2401.12345.pdf");
let toml_path = store_root.join(".metadata").join("arxiv_2401.12345.toml");
assert!(
!pdf_path.exists(),
"dry-run arxiv path must not write the PDF: {pdf_path}",
);
assert!(
!toml_path.exists(),
"dry-run arxiv path must not write the metadata TOML: {toml_path}",
);
drop(env);
drop(td);
}
#[test]
fn build_fetch_plan_doi_envelope_matches_adr_0022_shape() {
let r = Ref::parse("10.1234/foo").expect("DOI parses");
let plan = build_fetch_plan(&r, &Utf8PathBuf::from("/tmp/store"));
let env = build_dry_run_envelope(&r, &plan);
assert_eq!(env["ok"], serde_json::json!(true));
assert_eq!(env["dry_run"], serde_json::json!(true));
assert_eq!(env["ref"], serde_json::json!({ "doi": "10.1234/foo" }));
let plan_json = &env["plan"];
assert_eq!(
plan_json["metadata_sources"],
serde_json::json!(["crossref", "unpaywall"])
);
assert!(plan_json["pdf_sources"].is_array());
assert_eq!(plan_json["pdf_sources"][0]["key"], "oa-publisher");
assert!(plan_json["pdf_sources"][0]["candidate_hosts"].is_array());
assert_eq!(
plan_json["redirect_allowlists_loaded"],
serde_json::json!(["crossref", "unpaywall", "arxiv", "oa-publisher"])
);
let expect_pdf = Utf8PathBuf::from("/tmp/store")
.join("doi_10.1234_foo.pdf")
.to_string();
let expect_toml = Utf8PathBuf::from("/tmp/store")
.join(".metadata")
.join("doi_10.1234_foo.toml")
.to_string();
assert_eq!(plan_json["target_pdf_path"], serde_json::json!(expect_pdf));
assert_eq!(
plan_json["target_metadata_path"],
serde_json::json!(expect_toml)
);
assert_eq!(
plan_json["would_append_provenance"],
serde_json::json!(true)
);
assert_eq!(
env["rate_limit_budget"]["global_per_sec"],
serde_json::json!(5.0)
);
assert_eq!(
env["rate_limit_budget"]["per_source_min_gap_ms"],
serde_json::json!(200)
);
}
#[test]
fn build_fetch_plan_arxiv_envelope_uses_arxiv_pdf_source() {
let r = Ref::parse("arxiv:2401.12345").expect("arxiv parses");
let plan = build_fetch_plan(&r, &Utf8PathBuf::from("/tmp/store"));
let env = build_dry_run_envelope(&r, &plan);
assert_eq!(env["ref"], serde_json::json!({ "arxiv": "2401.12345" }));
assert_eq!(env["plan"]["metadata_sources"], serde_json::json!([]));
assert_eq!(env["plan"]["pdf_sources"][0]["key"], "arxiv");
let expect_pdf = Utf8PathBuf::from("/tmp/store")
.join("arxiv_2401.12345.pdf")
.to_string();
assert_eq!(
env["plan"]["target_pdf_path"],
serde_json::json!(expect_pdf)
);
}