#![allow(clippy::expect_used, clippy::unwrap_used, clippy::panic)]
use std::collections::BTreeMap;
use assert_cmd::Command;
use camino::Utf8PathBuf;
use chrono::TimeZone;
use predicates::prelude::*;
use tempfile::TempDir;
use doiget_core::store::{DoigetExtension, FsStore, Metadata, Store};
use doiget_core::{Doi, Ref, SCHEMA_VERSION};
fn utf8_path(dir: &TempDir) -> Utf8PathBuf {
Utf8PathBuf::from_path_buf(dir.path().to_path_buf()).expect("temp dir path must be UTF-8")
}
fn fixture(type_: Option<&str>) -> Metadata {
Metadata {
schema_version: SCHEMA_VERSION.to_string(),
title: "Quantum Stuff".to_string(),
authors: vec!["Alice Researcher".to_string(), "Bob Coauthor".to_string()],
year: Some(2026),
doi: Some(Doi::parse("10.1234/example").expect("valid DOI")),
arxiv_id: None,
abstract_: None,
venue: Some("Phys Rev X".to_string()),
publisher: Some("APS".to_string()),
issn: Some("2160-3308".to_string()),
isbn: None,
type_: type_.map(str::to_string),
keywords: vec![],
url: None,
pdf_path: None,
doiget: Some(DoigetExtension {
fetched_at: chrono::Utc
.with_ymd_and_hms(2026, 5, 6, 12, 0, 0)
.single()
.expect("valid timestamp"),
source: "unpaywall".to_string(),
license: "CC-BY-4.0".to_string(),
size_bytes: 1234,
mcp_call_id: None,
}),
other: BTreeMap::new(),
}
}
fn seeded_store(type_: Option<&str>) -> (TempDir, Utf8PathBuf) {
let dir = TempDir::new().expect("tempdir");
let root = utf8_path(&dir).join("papers");
let store = FsStore::new(root.clone()).expect("FsStore::new");
let ref_ = Ref::Doi(Doi::parse("10.1234/example").expect("valid DOI"));
let safekey = ref_.safekey();
let metadata = fixture(type_);
store
.write(&safekey, &metadata, None)
.expect("seed bib entry");
(dir, root)
}
fn doiget(root: &Utf8PathBuf) -> Command {
let mut cmd = Command::cargo_bin("doiget").expect("locate doiget binary");
cmd.env("DOIGET_STORE_ROOT", root.as_str())
.env("HOME", root.as_str())
.env("USERPROFILE", root.as_str());
cmd
}
#[test]
fn bib_emits_article_for_journal_article_type() {
let (_dir_guard, root) = seeded_store(Some("journal-article"));
doiget(&root)
.args(["bib", "doi:10.1234/example"])
.assert()
.success()
.stdout(predicate::str::contains("@article{doi_10.1234_example,"))
.stdout(predicate::str::contains("title = {Quantum Stuff},"))
.stdout(predicate::str::contains(
"author = {Alice Researcher and Bob Coauthor},",
))
.stdout(predicate::str::contains("year = {2026},"))
.stdout(predicate::str::contains("doi = {10.1234/example},"))
.stdout(predicate::str::contains("journal = {Phys Rev X},"))
.stdout(predicate::str::contains("publisher = {APS},"))
.stdout(predicate::str::contains("issn = {2160-3308},"))
.stdout(predicate::str::contains("\n}\n"));
}
#[test]
fn bib_fails_for_missing_entry() {
let dir = TempDir::new().expect("tempdir");
let root = utf8_path(&dir).join("papers");
FsStore::new(root.clone()).expect("FsStore::new");
doiget(&root)
.args(["bib", "doi:10.9999/missing"])
.assert()
.failure()
.stderr(predicate::str::contains("no entry for"));
}
#[test]
fn bib_emits_misc_for_missing_type() {
let (_dir_guard, root) = seeded_store(None);
doiget(&root)
.args(["bib", "doi:10.1234/example"])
.assert()
.success()
.stdout(predicate::str::contains("@misc{doi_10.1234_example,"));
}