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,175 @@
|
||||
//! Core message model — maps to PostgreSQL `message` table.
|
||||
//!
|
||||
//! Discord-style: `body` holds plain text / markdown, rich content lives in
|
||||
//! companion tables (attachment, embed, poll, reaction, mention).
|
||||
//! IDs are UUID v7 (time-ordered) so `ORDER BY id` = chronological order.
|
||||
|
||||
use chrono::{DateTime, Utc};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use uuid::Uuid;
|
||||
|
||||
/// Discriminator for system / event messages vs. regular user messages.
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Default)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub enum MessageType {
|
||||
/// Regular user message (text / markdown).
|
||||
#[default]
|
||||
Text,
|
||||
/// System-generated notice (e.g. "user joined the channel").
|
||||
System,
|
||||
/// Channel event (pinned a message, changed topic, etc.).
|
||||
Event,
|
||||
/// Forum article / long-form post displayed as waterfall cards.
|
||||
Article,
|
||||
}
|
||||
|
||||
impl MessageType {
|
||||
pub fn as_str(&self) -> &'static str {
|
||||
match self {
|
||||
Self::Text => "text",
|
||||
Self::System => "system",
|
||||
Self::Event => "event",
|
||||
Self::Article => "article",
|
||||
}
|
||||
}
|
||||
|
||||
pub fn from_str_lossy(s: &str) -> Self {
|
||||
match s {
|
||||
"system" => Self::System,
|
||||
"event" => Self::Event,
|
||||
"article" => Self::Article,
|
||||
_ => Self::Text,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl std::fmt::Display for MessageType {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
f.write_str(self.as_str())
|
||||
}
|
||||
}
|
||||
|
||||
/// Direct mapping of the `message` table row.
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, sqlx::FromRow)]
|
||||
pub struct Message {
|
||||
/// UUID v7 — time-ordered primary key.
|
||||
pub id: Uuid,
|
||||
pub channel_id: Uuid,
|
||||
pub author_id: Uuid,
|
||||
/// Thread this message belongs to (NULL = not threaded).
|
||||
pub thread_id: Option<Uuid>,
|
||||
/// Direct reply reference (NULL = top-level message).
|
||||
pub reply_to_message_id: Option<Uuid>,
|
||||
/// "text" | "system" | "event"
|
||||
pub message_type: String,
|
||||
/// Plain text or markdown body.
|
||||
pub body: String,
|
||||
/// Extensible metadata (flags, locale, interaction ref, etc.).
|
||||
pub metadata: Option<serde_json::Value>,
|
||||
pub pinned: bool,
|
||||
/// True for bot / system generated messages.
|
||||
pub system: bool,
|
||||
pub edited_at: Option<DateTime<Utc>>,
|
||||
pub deleted_at: Option<DateTime<Utc>>,
|
||||
pub created_at: DateTime<Utc>,
|
||||
pub updated_at: DateTime<Utc>,
|
||||
}
|
||||
|
||||
/// Lightweight author info embedded in [`MessageDetail`].
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct AuthorInfo {
|
||||
pub id: Uuid,
|
||||
pub username: String,
|
||||
pub display_name: Option<String>,
|
||||
pub avatar_url: Option<String>,
|
||||
pub is_bot: bool,
|
||||
}
|
||||
|
||||
/// Message with resolved author and reaction/attachment aggregates.
|
||||
/// Returned by read APIs; never stored directly.
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct MessageDetail {
|
||||
#[serde(flatten)]
|
||||
pub message: Message,
|
||||
pub author: AuthorInfo,
|
||||
/// Aggregated reaction counts: `{ "👍": 3, "🎉": 1 }`.
|
||||
pub reactions: std::collections::HashMap<String, i64>,
|
||||
pub attachment_count: i64,
|
||||
pub embed_count: i64,
|
||||
/// Whether the current user has bookmarked this message.
|
||||
pub bookmarked: bool,
|
||||
/// Reply chain depth (0 = top-level).
|
||||
pub reply_depth: i32,
|
||||
}
|
||||
|
||||
/// Generate a new UUID v7 (time-ordered) for message IDs.
|
||||
pub fn new_message_id() -> Uuid {
|
||||
Uuid::now_v7()
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_message_type_roundtrip() {
|
||||
let t = MessageType::Text;
|
||||
assert_eq!(t.as_str(), "text");
|
||||
assert_eq!(MessageType::from_str_lossy("text"), MessageType::Text);
|
||||
assert_eq!(MessageType::from_str_lossy("system"), MessageType::System);
|
||||
assert_eq!(MessageType::from_str_lossy("unknown"), MessageType::Text);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_message_id_ordering() {
|
||||
// UUID v7 IDs generated later should sort after earlier ones.
|
||||
let a = new_message_id();
|
||||
std::thread::sleep(std::time::Duration::from_millis(2));
|
||||
let b = new_message_id();
|
||||
assert!(b > a, "UUID v7 should be time-ordered");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_message_detail_serialize() {
|
||||
let msg = Message {
|
||||
id: Uuid::now_v7(),
|
||||
channel_id: Uuid::now_v7(),
|
||||
author_id: Uuid::now_v7(),
|
||||
thread_id: None,
|
||||
reply_to_message_id: None,
|
||||
message_type: "text".to_string(),
|
||||
body: "hello world".to_string(),
|
||||
metadata: None,
|
||||
pinned: false,
|
||||
system: false,
|
||||
edited_at: None,
|
||||
deleted_at: None,
|
||||
created_at: Utc::now(),
|
||||
updated_at: Utc::now(),
|
||||
};
|
||||
|
||||
let detail = MessageDetail {
|
||||
message: msg,
|
||||
author: AuthorInfo {
|
||||
id: Uuid::now_v7(),
|
||||
username: "alice".to_string(),
|
||||
display_name: Some("Alice".to_string()),
|
||||
avatar_url: None,
|
||||
is_bot: false,
|
||||
},
|
||||
reactions: std::collections::HashMap::from([
|
||||
("👍".to_string(), 3),
|
||||
("🎉".to_string(), 1),
|
||||
]),
|
||||
attachment_count: 0,
|
||||
embed_count: 0,
|
||||
bookmarked: false,
|
||||
reply_depth: 0,
|
||||
};
|
||||
|
||||
let json = serde_json::to_value(&detail).unwrap();
|
||||
assert_eq!(json["body"], "hello world");
|
||||
assert_eq!(json["author"]["username"], "alice");
|
||||
assert_eq!(json["reactions"]["👍"], 3);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user