refactor(tests): reformat code and update dependency management
- Reorganized import statements in adapter tests for better readability - Replaced or_insert_with(Vec::new) with or_default() in test closures - Updated Cargo.lock with new dependency versions and checksums - Added TLS features to tonic dependency configuration - Included sqlx, chrono, and uuid dependencies with specific features - Added jsonwebtoken and arc-swap as project dependencies - Reformatted assertion statements to comply with line length limits - Adjusted base64 import order in engine codec module - Updated protobuf include statement formatting
This commit is contained in:
@@ -0,0 +1,92 @@
|
||||
//! Per-user read state — maps to `message_read_state` table.
|
||||
//!
|
||||
//! Tracks the last message each user has read in each channel.
|
||||
//! Used for unread badges, "mark as read", and notification suppression.
|
||||
//! One row per (channel_id, user_id), upserted on each read.
|
||||
|
||||
use chrono::{DateTime, Utc};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use uuid::Uuid;
|
||||
|
||||
/// A user's read progress in one channel.
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, sqlx::FromRow)]
|
||||
pub struct MessageReadState {
|
||||
pub id: Uuid,
|
||||
pub channel_id: Uuid,
|
||||
pub user_id: Uuid,
|
||||
/// The last message id the user has read (cursor).
|
||||
pub last_read_message_id: Option<Uuid>,
|
||||
/// When the user last opened / scrolled through this channel.
|
||||
pub last_read_at: Option<DateTime<Utc>>,
|
||||
/// Total unread message count (denormalized for fast badge display).
|
||||
pub unread_count: i64,
|
||||
/// Total unread @mentions for this channel (denormalized).
|
||||
pub unread_mentions: i64,
|
||||
pub created_at: DateTime<Utc>,
|
||||
pub updated_at: DateTime<Utc>,
|
||||
}
|
||||
|
||||
/// Summary of a user's read state for the client-side channel list.
|
||||
///
|
||||
/// Includes unread badge count and mention count for a single channel.
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct ReadStateSummary {
|
||||
pub channel_id: Uuid,
|
||||
pub unread_count: i64,
|
||||
pub unread_mentions: i64,
|
||||
pub has_unread: bool,
|
||||
}
|
||||
|
||||
impl From<MessageReadState> for ReadStateSummary {
|
||||
fn from(s: MessageReadState) -> Self {
|
||||
Self {
|
||||
channel_id: s.channel_id,
|
||||
unread_count: s.unread_count,
|
||||
unread_mentions: s.unread_mentions,
|
||||
has_unread: s.unread_count > 0,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_read_state_summary_conversion() {
|
||||
let state = MessageReadState {
|
||||
id: Uuid::now_v7(),
|
||||
channel_id: Uuid::now_v7(),
|
||||
user_id: Uuid::now_v7(),
|
||||
last_read_message_id: None,
|
||||
last_read_at: None,
|
||||
unread_count: 5,
|
||||
unread_mentions: 2,
|
||||
created_at: Utc::now(),
|
||||
updated_at: Utc::now(),
|
||||
};
|
||||
|
||||
let summary: ReadStateSummary = state.into();
|
||||
assert!(summary.has_unread);
|
||||
assert_eq!(summary.unread_count, 5);
|
||||
assert_eq!(summary.unread_mentions, 2);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_read_state_summary_no_unread() {
|
||||
let state = MessageReadState {
|
||||
id: Uuid::now_v7(),
|
||||
channel_id: Uuid::now_v7(),
|
||||
user_id: Uuid::now_v7(),
|
||||
last_read_message_id: None,
|
||||
last_read_at: None,
|
||||
unread_count: 0,
|
||||
unread_mentions: 0,
|
||||
created_at: Utc::now(),
|
||||
updated_at: Utc::now(),
|
||||
};
|
||||
|
||||
let summary: ReadStateSummary = state.into();
|
||||
assert!(!summary.has_unread);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user