use pubmed_client::{cache::CacheConfig, ClientConfig, PmcClient};
use std::time::Duration;
use wiremock::{
matchers::{method, path_regex},
Mock, MockServer, ResponseTemplate,
};
const PMC_XML_RESPONSE: &str = r#"<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE pmc-articleset PUBLIC "-//NLM//DTD ARTICLE SET 2.0//EN" "https://dtd.nlm.nih.gov/ncbi/pmc/articleset/nlm-articleset-2.0.dtd">
<pmc-articleset>
<article xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:mml="http://www.w3.org/1998/Math/MathML" article-type="research-article">
<front>
<journal-meta>
<journal-title-group>
<journal-title>Test Journal</journal-title>
</journal-title-group>
</journal-meta>
<article-meta>
<article-id pub-id-type="pmc">1234567</article-id>
<title-group>
<article-title>Test Article Title</article-title>
</title-group>
<contrib-group>
<contrib contrib-type="author">
<name>
<surname>Doe</surname>
<given-names>John</given-names>
</name>
</contrib>
</contrib-group>
<abstract>
<p>This is a test abstract.</p>
</abstract>
</article-meta>
</front>
<body>
<sec>
<title>Introduction</title>
<p>This is the introduction section.</p>
</sec>
</body>
</article>
</pmc-articleset>"#;
#[tokio::test]
async fn test_pmc_cache_hit() {
let mock_server = MockServer::start().await;
let mock = Mock::given(method("GET"))
.and(path_regex(r"/efetch\.fcgi.*"))
.respond_with(ResponseTemplate::new(200).set_body_string(PMC_XML_RESPONSE))
.expect(1) .mount_as_scoped(&mock_server)
.await;
let cache_config = CacheConfig {
max_capacity: 100,
time_to_live: Duration::from_secs(60),
};
let config = ClientConfig::new()
.with_base_url(mock_server.uri())
.with_cache_config(cache_config);
let client = PmcClient::with_config(config);
let article1 = client.fetch_full_text("PMC1234567").await.unwrap();
assert_eq!(article1.title, "Test Article Title");
let article2 = client.fetch_full_text("PMC1234567").await.unwrap();
assert_eq!(article2.title, "Test Article Title");
assert_eq!(article1.pmcid, article2.pmcid);
assert_eq!(article1.sections.len(), article2.sections.len());
drop(mock);
}
#[tokio::test]
async fn test_pmc_cache_miss_different_ids() {
let mock_server = MockServer::start().await;
Mock::given(method("GET"))
.and(path_regex(r"/efetch\.fcgi.*"))
.respond_with(ResponseTemplate::new(200).set_body_string(PMC_XML_RESPONSE))
.expect(2) .mount(&mock_server)
.await;
let config = ClientConfig::new()
.with_base_url(mock_server.uri())
.with_cache_config(CacheConfig::default());
let client = PmcClient::with_config(config);
let _article1 = client.fetch_full_text("PMC1234567").await.unwrap();
let _article2 = client.fetch_full_text("PMC7654321").await.unwrap();
}
#[tokio::test]
async fn test_pmc_cache_clear() {
let mock_server = MockServer::start().await;
Mock::given(method("GET"))
.and(path_regex(r"/efetch\.fcgi.*"))
.respond_with(ResponseTemplate::new(200).set_body_string(PMC_XML_RESPONSE))
.expect(2) .mount(&mock_server)
.await;
let config = ClientConfig::new()
.with_base_url(mock_server.uri())
.with_cache_config(CacheConfig::default());
let client = PmcClient::with_config(config);
let _article1 = client.fetch_full_text("PMC1234567").await.unwrap();
client.clear_cache().await;
let _article2 = client.fetch_full_text("PMC1234567").await.unwrap();
}
#[tokio::test]
async fn test_pmc_cache_stats() {
let mock_server = MockServer::start().await;
Mock::given(method("GET"))
.and(path_regex(r"/efetch\.fcgi.*"))
.respond_with(ResponseTemplate::new(200).set_body_string(PMC_XML_RESPONSE))
.mount(&mock_server)
.await;
let cache_config = CacheConfig {
max_capacity: 100,
time_to_live: Duration::from_secs(60),
};
let config = ClientConfig::new()
.with_base_url(mock_server.uri())
.with_cache_config(cache_config);
let client = PmcClient::with_config(config);
let count = client.cache_entry_count();
assert_eq!(count, 0);
let _article = client.fetch_full_text("PMC1234567").await.unwrap();
client.sync_cache().await;
let count = client.cache_entry_count();
assert_eq!(count, 1);
let _article2 = client.fetch_full_text("PMC7654321").await.unwrap();
client.sync_cache().await;
let count = client.cache_entry_count();
assert_eq!(count, 2);
}
#[tokio::test]
async fn test_pmc_no_cache() {
let mock_server = MockServer::start().await;
Mock::given(method("GET"))
.and(path_regex(r"/efetch\.fcgi.*"))
.respond_with(ResponseTemplate::new(200).set_body_string(PMC_XML_RESPONSE))
.expect(2) .mount(&mock_server)
.await;
let config = ClientConfig::new().with_base_url(mock_server.uri());
let client = PmcClient::with_config(config);
let _article1 = client.fetch_full_text("PMC1234567").await.unwrap();
let _article2 = client.fetch_full_text("PMC1234567").await.unwrap();
assert_eq!(client.cache_entry_count(), 0);
}
#[tokio::test]
async fn test_pmc_cache_normalization() {
let mock_server = MockServer::start().await;
Mock::given(method("GET"))
.and(path_regex(r"/efetch\.fcgi.*"))
.respond_with(ResponseTemplate::new(200).set_body_string(PMC_XML_RESPONSE))
.expect(1) .mount(&mock_server)
.await;
let config = ClientConfig::new()
.with_base_url(mock_server.uri())
.with_cache_config(CacheConfig::default());
let client = PmcClient::with_config(config);
let article1 = client.fetch_full_text("1234567").await.unwrap();
let article2 = client.fetch_full_text("PMC1234567").await.unwrap();
assert_eq!(article1.pmcid, article2.pmcid);
client.sync_cache().await;
let count = client.cache_entry_count();
assert_eq!(count, 1);
}