#![allow(clippy::expect_used, clippy::unwrap_used, clippy::panic)]
use assert_cmd::Command;
use predicates::str::contains;
use serde_json::json;
use tempfile::TempDir;
use wiremock::matchers::{method, path};
use wiremock::{Mock, MockServer, ResponseTemplate};
const TEST_DOI: &str = "10.1234/cite.test";
fn crossref_body() -> serde_json::Value {
json!({
"status": "ok",
"message": {
"title": ["A Synthetic Result on <i>Spin</i> Chains"],
"author": [
{ "family": "Doe", "given": "Jane" },
{ "family": "Roe", "given": "Richard" },
],
"issued": { "date-parts": [[2026, 1, 1]] },
"container-title": ["Synthetic Journal of Physics"],
"publisher": "Synthetic Society",
"ISSN": ["1234-5678"],
"volume": "42",
"issue": "7",
"page": "100-115",
"type": "journal-article",
}
})
}
const SAMPLE_ATOM_FEED: &str = r#"<?xml version="1.0" encoding="UTF-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
<entry>
<id>http://arxiv.org/abs/2401.12345v1</id>
<title>Example arXiv Paper Title</title>
<summary>This is an example abstract.</summary>
<author><name>Jane Doe</name></author>
<published>2024-01-15T00:00:00Z</published>
</entry>
</feed>"#;
fn doiget(dir: &TempDir) -> Command {
let mut cmd = Command::cargo_bin("doiget").expect("locate doiget binary");
let p = dir.path().to_str().expect("tempdir path utf-8");
let log_path = format!("{p}/log.jsonl");
cmd.env("HOME", p)
.env("USERPROFILE", p)
.env("XDG_CONFIG_HOME", p)
.env("APPDATA", p)
.env("DOIGET_STORE_ROOT", p)
.env("DOIGET_LOG_PATH", log_path)
.env("DOIGET_CACHE_ROOT", p);
cmd
}
#[test]
fn cite_typo_doi_is_rejected() {
let dir = TempDir::new().expect("tempdir");
doiget(&dir)
.args(["cite", "1O.1234/typo"])
.assert()
.failure();
}
#[tokio::test]
async fn cite_doi_emits_enriched_bibtex() {
let server = MockServer::start().await;
Mock::given(method("GET"))
.and(path(format!("/works/{TEST_DOI}")))
.respond_with(ResponseTemplate::new(200).set_body_json(crossref_body()))
.mount(&server)
.await;
let dir = TempDir::new().expect("tempdir");
doiget(&dir)
.args(["cite", TEST_DOI])
.env("DOIGET_CROSSREF_BASE", server.uri())
.assert()
.success()
.stdout(contains("@article{"))
.stdout(contains("A Synthetic Result on Spin Chains")) .stdout(contains("author = {Doe, Jane and Roe, Richard},"))
.stdout(contains("year = {2026},"))
.stdout(contains("journal = {Synthetic Journal of Physics},"))
.stdout(contains("volume = {42},"))
.stdout(contains("number = {7},"))
.stdout(contains("pages = {100--115},"))
.stdout(contains("publisher = {Synthetic Society},"))
.stdout(contains("issn = {1234-5678},"))
.stdout(contains("doi = {10.1234/cite.test},"));
}
#[tokio::test]
async fn cite_arxiv_emits_bibtex() {
let server = MockServer::start().await;
Mock::given(method("GET"))
.and(path("/api/query"))
.respond_with(ResponseTemplate::new(200).set_body_string(SAMPLE_ATOM_FEED))
.mount(&server)
.await;
let dir = TempDir::new().expect("tempdir");
doiget(&dir)
.args(["cite", "arxiv:2401.12345"])
.env("DOIGET_ARXIV_BASE", server.uri())
.assert()
.success()
.stdout(contains("@misc{"))
.stdout(contains("Example arXiv Paper Title"));
}
#[tokio::test]
async fn cite_unresolved_doi_fails() {
let server = MockServer::start().await;
Mock::given(method("GET"))
.respond_with(ResponseTemplate::new(404))
.mount(&server)
.await;
let dir = TempDir::new().expect("tempdir");
doiget(&dir)
.args(["cite", TEST_DOI])
.env("DOIGET_CROSSREF_BASE", server.uri())
.env("DOIGET_UNPAYWALL_BASE", server.uri())
.assert()
.failure();
}