feat: ReadingEvent serde tests + NoteAnchor with from_position constructor and tests

This commit is contained in:
wangdl 2026-05-30 21:18:30 +08:00
parent b3a7fe0414
commit b5f8e273a9
2 changed files with 158 additions and 1 deletions

View File

@ -29,3 +29,82 @@ pub enum NoteAnchor {
knowledge_item_id: String,
},
}
impl NoteAnchor {
/// Create a NoteAnchor from a material_id and optional ReadingPosition.
pub fn from_position(material_id: &str, position: Option<&crate::progress::ReadingPosition>) -> Self {
match position {
Some(crate::progress::ReadingPosition::Markdown { block_id, .. }) => {
NoteAnchor::MarkdownBlock {
material_id: material_id.to_string(),
block_id: block_id.clone(),
}
}
Some(crate::progress::ReadingPosition::Text { line_number, .. }) => {
NoteAnchor::TextLine {
material_id: material_id.to_string(),
line_number: *line_number,
}
}
Some(crate::progress::ReadingPosition::Pdf { page_number, .. }) => {
NoteAnchor::PdfPage {
material_id: material_id.to_string(),
page_number: *page_number,
}
}
Some(crate::progress::ReadingPosition::Image { .. }) => NoteAnchor::Image {
material_id: material_id.to_string(),
},
Some(crate::progress::ReadingPosition::Epub { chapter_id, .. }) => {
NoteAnchor::EpubChapter {
material_id: material_id.to_string(),
chapter_id: chapter_id.clone(),
}
}
_ => NoteAnchor::Material {
material_id: material_id.to_string(),
},
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::progress::ReadingPosition;
#[test]
fn test_material_anchor() {
let a = NoteAnchor::from_position("abc", None);
assert_eq!(a, NoteAnchor::Material { material_id: "abc".into() });
}
#[test]
fn test_markdown_anchor() {
let pos = ReadingPosition::Markdown { block_id: "h1".into(), scroll_progress: 0.5 };
let a = NoteAnchor::from_position("abc", Some(&pos));
assert_eq!(a, NoteAnchor::MarkdownBlock { material_id: "abc".into(), block_id: "h1".into() });
}
#[test]
fn test_pdf_anchor() {
let pos = ReadingPosition::Pdf { page_number: 3, page_progress: 0.5, overall_progress: 0.1 };
let a = NoteAnchor::from_position("abc", Some(&pos));
assert_eq!(a, NoteAnchor::PdfPage { material_id: "abc".into(), page_number: 3 });
}
#[test]
fn test_anchor_serde() {
let a = NoteAnchor::MarkdownBlock { material_id: "abc".into(), block_id: "h1".into() };
let json = serde_json::to_string(&a).unwrap();
let back: NoteAnchor = serde_json::from_str(&json).unwrap();
assert_eq!(back, a);
}
#[test]
fn test_unknown_position_falls_back_to_material() {
let pos = ReadingPosition::Unknown;
let a = NoteAnchor::from_position("abc", Some(&pos));
assert_eq!(a, NoteAnchor::Material { material_id: "abc".into() });
}
}

View File

@ -2,7 +2,7 @@ use serde::{Deserialize, Serialize};
use crate::progress::ReadingPosition;
#[derive(Debug, Clone, Serialize, Deserialize)]
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(tag = "type")]
pub enum ReadingEvent {
MaterialOpened {
@ -30,3 +30,81 @@ pub enum ReadingEvent {
timestamp_ms: i64,
},
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_material_opened_serde() {
let e = ReadingEvent::MaterialOpened {
material_id: "abc".into(),
timestamp_ms: 1000,
};
let json = serde_json::to_string(&e).unwrap();
assert!(json.contains("\"type\":\"MaterialOpened\""));
let back: ReadingEvent = serde_json::from_str(&json).unwrap();
assert_eq!(back, e);
}
#[test]
fn test_material_closed_serde() {
let e = ReadingEvent::MaterialClosed {
material_id: "abc".into(),
timestamp_ms: 2000,
active_seconds: 120,
};
let json = serde_json::to_string(&e).unwrap();
let back: ReadingEvent = serde_json::from_str(&json).unwrap();
assert_eq!(back, e);
}
#[test]
fn test_position_changed_serde() {
let e = ReadingEvent::PositionChanged {
material_id: "abc".into(),
position: ReadingPosition::Markdown { block_id: "h1".into(), scroll_progress: 0.5 },
timestamp_ms: 3000,
};
let json = serde_json::to_string(&e).unwrap();
let back: ReadingEvent = serde_json::from_str(&json).unwrap();
assert_eq!(back, e);
}
#[test]
fn test_heartbeat_serde() {
let e = ReadingEvent::Heartbeat {
material_id: "abc".into(),
active_seconds: 15,
position: None,
timestamp_ms: 4000,
};
let json = serde_json::to_string(&e).unwrap();
let back: ReadingEvent = serde_json::from_str(&json).unwrap();
assert_eq!(back, e);
}
#[test]
fn test_heartbeat_with_position_serde() {
let e = ReadingEvent::Heartbeat {
material_id: "abc".into(),
active_seconds: 15,
position: Some(ReadingPosition::Pdf { page_number: 3, page_progress: 0.5, overall_progress: 0.1 }),
timestamp_ms: 5000,
};
let json = serde_json::to_string(&e).unwrap();
let back: ReadingEvent = serde_json::from_str(&json).unwrap();
assert_eq!(back, e);
}
#[test]
fn test_marked_as_read_serde() {
let e = ReadingEvent::MarkedAsRead {
material_id: "abc".into(),
timestamp_ms: 6000,
};
let json = serde_json::to_string(&e).unwrap();
let back: ReadingEvent = serde_json::from_str(&json).unwrap();
assert_eq!(back, e);
}
}