diff --git a/app/lib/features/mood/views/mood_page.dart b/app/lib/features/mood/views/mood_page.dart index 03389e2..ae8329e 100644 --- a/app/lib/features/mood/views/mood_page.dart +++ b/app/lib/features/mood/views/mood_page.dart @@ -213,10 +213,10 @@ class _MoodDistributionChart extends StatelessWidget { color: color, radius: 50, title: '${mc.percentage.toStringAsFixed(0)}%', - titleStyle: const TextStyle( + titleStyle: TextStyle( fontSize: 12, fontWeight: FontWeight.bold, - color: Colors.white, + color: colorScheme.onPrimary, ), ); }).toList(), diff --git a/crates/erp-diary/src/dto.rs b/crates/erp-diary/src/dto.rs index eafa3b1..2c1f871 100644 --- a/crates/erp-diary/src/dto.rs +++ b/crates/erp-diary/src/dto.rs @@ -101,7 +101,7 @@ pub struct SyncReq { } /// 同步变更条目 -#[derive(Debug, Deserialize, ToSchema)] +#[derive(Debug, Serialize, Deserialize, ToSchema)] pub enum SyncChange { CreateJournal { data: serde_json::Value }, UpdateJournal { id: uuid::Uuid, version: i32, data: serde_json::Value }, @@ -284,3 +284,150 @@ pub struct AchievementResp { pub is_unlocked: bool, pub unlocked_at: Option>, } + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn mood_serializes_to_lowercase() { + let json = serde_json::to_string(&Mood::Happy).unwrap(); + assert_eq!(json, "\"happy\""); + let json = serde_json::to_string(&Mood::Thinking).unwrap(); + assert_eq!(json, "\"thinking\""); + } + + #[test] + fn mood_deserializes() { + let m: Mood = serde_json::from_str("\"calm\"").unwrap(); + assert!(matches!(m, Mood::Calm)); + let m: Mood = serde_json::from_str("\"angry\"").unwrap(); + assert!(matches!(m, Mood::Angry)); + } + + #[test] + fn weather_roundtrip() { + let w = Weather::Snowy; + let json = serde_json::to_string(&w).unwrap(); + let back: Weather = serde_json::from_str(&json).unwrap(); + assert!(matches!(back, Weather::Snowy)); + } + + #[test] + fn create_journal_req_deserializes() { + let json = r#"{ + "title": "测试日记", + "date": "2026-06-01", + "mood": "happy", + "weather": "sunny", + "tags": ["日常"], + "is_private": true + }"#; + let req: CreateJournalReq = serde_json::from_str(json).unwrap(); + assert_eq!(req.title, "测试日记"); + assert!(req.is_private); + assert!(req.class_id.is_none()); + } + + #[test] + fn update_journal_req_minimal() { + let req: UpdateJournalReq = serde_json::from_str(r#"{"version": 2}"#).unwrap(); + assert_eq!(req.version, 2); + assert!(req.title.is_none()); + assert!(req.mood.is_none()); + } + + #[test] + fn notification_type_serializes_snake_case() { + assert_eq!( + serde_json::to_string(&NotificationType::CommentReceived).unwrap(), + "\"comment_received\"" + ); + assert_eq!( + serde_json::to_string(&NotificationType::AchievementUnlocked).unwrap(), + "\"achievement_unlocked\"" + ); + } + + #[test] + fn sync_change_variants_serialize() { + let create = SyncChange::CreateJournal { + data: serde_json::json!({"title": "test"}), + }; + let json = serde_json::to_string(&create).unwrap(); + assert!(json.contains("CreateJournal")); + + let delete = SyncChange::DeleteJournal { + id: uuid::Uuid::nil(), + version: 3, + }; + let json = serde_json::to_string(&delete).unwrap(); + assert!(json.contains("DeleteJournal")); + } + + #[test] + fn conflict_info_serializes() { + let info = ConflictInfo { + journal_id: uuid::Uuid::nil(), + local_version: 1, + server_version: 3, + }; + let json = serde_json::to_string(&info).unwrap(); + assert!(json.contains("\"local_version\":1")); + assert!(json.contains("\"server_version\":3")); + } + + #[test] + fn sticker_pack_resp_serializes() { + let resp = StickerPackResp { + id: uuid::Uuid::nil(), + name: "测试贴纸包".into(), + description: None, + cover_image_url: None, + sticker_count: 10, + is_free: true, + category: Some("动物".into()), + }; + let json = serde_json::to_string(&resp).unwrap(); + assert!(json.contains("\"sticker_count\":10")); + assert!(json.contains("\"is_free\":true")); + } + + #[test] + fn mood_count_percentage() { + let mc = MoodCount { + mood: Mood::Happy, + count: 3, + percentage: 42.5, + }; + let json = serde_json::to_string(&mc).unwrap(); + assert!(json.contains("\"percentage\":42.5")); + } + + #[test] + fn class_member_resp_fields() { + let resp = ClassMemberResp { + user_id: uuid::Uuid::nil(), + role: "student".into(), + nickname: Some("小明".into()), + joined_at: chrono::Utc::now(), + }; + let json = serde_json::to_string(&resp).unwrap(); + assert!(json.contains("\"role\":\"student\"")); + } + + #[test] + fn topic_resp_serializes() { + let resp = TopicResp { + id: uuid::Uuid::nil(), + class_id: uuid::Uuid::nil(), + teacher_id: uuid::Uuid::nil(), + title: "我的周末".into(), + description: Some("描述".into()), + due_date: None, + is_active: true, + }; + let json = serde_json::to_string(&resp).unwrap(); + assert!(json.contains("\"is_active\":true")); + } +}