#![deny(missing_docs)]
use serde_json::Value;
use crate::Revision;
use crate::page_query::{PageContext, PageQueryResult, PageQueryResultList};
#[derive(Debug, Clone)]
pub struct PageRevisionEntry {
pub page: PageContext,
pub revision: Revision,
}
impl PageQueryResult for PageRevisionEntry {
fn from_page_value(page: &Value) -> Vec<Self> {
let ctx = PageContext::from_value(page);
page["revisions"]
.as_array()
.map(|arr| {
arr.iter()
.filter_map(|v| {
Some(Self {
page: ctx.clone(),
revision: Revision::from_json(v).ok()?,
})
})
.collect()
})
.unwrap_or_default()
}
}
pub type PageRevisionList = PageQueryResultList<PageRevisionEntry>;
#[cfg(test)]
mod tests {
use super::*;
use serde_json::json;
fn sample_result() -> Value {
json!({
"query": {
"pages": {
"736": {
"pageid": 736,
"ns": 0,
"title": "Albert Einstein",
"revisions": [
{
"revid": 100,
"parentid": 99,
"user": "Alice",
"timestamp": "2024-01-15T10:30:00Z",
"size": 50000,
"comment": "Fixed typo"
},
{
"revid": 99,
"user": "Bob",
"timestamp": "2024-01-14T08:00:00Z",
"size": 49990,
"comment": "Added reference"
}
]
}
}
}
})
}
fn sample_result_v2_multi() -> Value {
json!({
"query": {
"pages": [
{
"pageid": 1,
"ns": 0,
"title": "Page A",
"revisions": [{"revid": 10}]
},
{
"pageid": 2,
"ns": 0,
"title": "Page B",
"revisions": [{"revid": 20}, {"revid": 21}]
}
]
}
})
}
#[test]
fn from_result_v1() {
let list = PageRevisionList::from_result(&sample_result());
assert_eq!(list.len(), 2);
assert_eq!(list.items()[0].page.title, "Albert Einstein");
assert_eq!(list.items()[0].revision.id(), 100);
assert_eq!(list.items()[0].revision.user(), Some("Alice"));
assert_eq!(list.items()[1].revision.id(), 99);
assert_eq!(list.items()[1].revision.user(), Some("Bob"));
}
#[test]
fn from_result_v2_multiple_pages() {
let list = PageRevisionList::from_result(&sample_result_v2_multi());
assert_eq!(list.len(), 3);
assert_eq!(list.items()[0].page.title, "Page A");
assert_eq!(list.items()[0].revision.id(), 10);
assert_eq!(list.items()[1].page.title, "Page B");
assert_eq!(list.items()[1].revision.id(), 20);
assert_eq!(list.items()[2].revision.id(), 21);
}
#[test]
fn page_with_no_revisions() {
let result = json!({
"query": {
"pages": {
"1": {"pageid": 1, "ns": 0, "title": "Empty"}
}
}
});
let list = PageRevisionList::from_result(&result);
assert!(list.is_empty());
}
#[test]
fn invalid_revision_skipped() {
let result = json!({
"query": {
"pages": {
"1": {
"pageid": 1, "ns": 0, "title": "Test",
"revisions": [
{"revid": 10},
{"no_revid": true},
{"revid": 12}
]
}
}
}
});
let list = PageRevisionList::from_result(&result);
assert_eq!(list.len(), 2);
assert_eq!(list.items()[0].revision.id(), 10);
assert_eq!(list.items()[1].revision.id(), 12);
}
#[test]
fn page_context_preserved() {
let list = PageRevisionList::from_result(&sample_result());
for entry in list.items() {
assert_eq!(entry.page.pageid, Some(736));
assert_eq!(entry.page.ns, 0);
assert_eq!(entry.page.title, "Albert Einstein");
}
}
#[tokio::test]
async fn integration_fetch_revisions() {
use crate::Api;
use crate::action_api::{ActionApiQuery, ActionApiQueryCommonBuilder, ActionApiRunnable};
use wiremock::matchers::query_param;
use wiremock::{Mock, ResponseTemplate};
let server = crate::test_helpers::test_helpers_mod::start_enwiki_mock().await;
Mock::given(query_param("prop", "revisions"))
.respond_with(ResponseTemplate::new(200).set_body_json(json!({
"batchcomplete": "",
"query": {
"pages": {
"736": {
"pageid": 736, "ns": 0, "title": "Albert Einstein",
"revisions": [
{"revid": 1001, "parentid": 1000, "user": "Editor1", "timestamp": "2024-01-01T00:00:00Z", "size": 12345},
{"revid": 1000, "parentid": 999, "user": "Editor2", "timestamp": "2023-12-01T00:00:00Z", "size": 12300},
{"revid": 999, "parentid": 998, "user": "Editor3", "timestamp": "2023-11-01T00:00:00Z", "size": 12200}
]
}
}
}
})))
.mount(&server)
.await;
let api = Api::new(&server.uri()).await.unwrap();
let result = ActionApiQuery::revisions()
.rvprop(&["ids", "timestamp", "user", "size"])
.rvlimit(3)
.titles(&["Albert Einstein"])
.run(&api)
.await
.unwrap();
let list = PageRevisionList::from_result(&result);
assert!(!list.is_empty());
assert!(list.len() <= 3);
for entry in list.items() {
assert_eq!(entry.page.title, "Albert Einstein");
assert!(entry.revision.id() > 0);
}
}
}