Split fake TDLib client helpers
This commit is contained in:
86
tests/helpers/fake_tdclient/builders.rs
Normal file
86
tests/helpers/fake_tdclient/builders.rs
Normal file
@@ -0,0 +1,86 @@
|
||||
use super::{FakeTdClient, TdUpdate};
|
||||
use tele_tui::tdlib::types::FolderInfo;
|
||||
use tele_tui::tdlib::{AuthState, ChatInfo, MessageInfo, NetworkState, ProfileInfo};
|
||||
use tokio::sync::mpsc;
|
||||
|
||||
#[allow(dead_code)]
|
||||
impl FakeTdClient {
|
||||
/// Create an update channel for receiving simulated TDLib events.
|
||||
pub fn with_update_channel(self) -> (Self, mpsc::UnboundedReceiver<TdUpdate>) {
|
||||
let (tx, rx) = mpsc::unbounded_channel();
|
||||
*self.update_tx.lock().unwrap() = Some(tx);
|
||||
(self, rx)
|
||||
}
|
||||
|
||||
/// Enable simulated delays, closer to real TDLib behavior.
|
||||
pub fn with_delays(mut self) -> Self {
|
||||
self.simulate_delays = true;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn with_chat(self, chat: ChatInfo) -> Self {
|
||||
self.chats.lock().unwrap().push(chat);
|
||||
self
|
||||
}
|
||||
|
||||
pub fn with_chats(self, chats: Vec<ChatInfo>) -> Self {
|
||||
self.chats.lock().unwrap().extend(chats);
|
||||
self
|
||||
}
|
||||
|
||||
pub fn with_message(self, chat_id: i64, message: MessageInfo) -> Self {
|
||||
self.messages
|
||||
.lock()
|
||||
.unwrap()
|
||||
.entry(chat_id)
|
||||
.or_default()
|
||||
.push(message);
|
||||
self
|
||||
}
|
||||
|
||||
pub fn with_messages(self, chat_id: i64, messages: Vec<MessageInfo>) -> Self {
|
||||
self.messages.lock().unwrap().insert(chat_id, messages);
|
||||
self
|
||||
}
|
||||
|
||||
pub fn with_folder(self, id: i32, name: &str) -> Self {
|
||||
self.folders
|
||||
.lock()
|
||||
.unwrap()
|
||||
.push(FolderInfo { id, name: name.to_string() });
|
||||
self
|
||||
}
|
||||
|
||||
pub fn with_user(self, id: i64, name: &str) -> Self {
|
||||
self.user_names.lock().unwrap().insert(id, name.to_string());
|
||||
self
|
||||
}
|
||||
|
||||
pub fn with_profile(self, chat_id: i64, profile: ProfileInfo) -> Self {
|
||||
self.profiles.lock().unwrap().insert(chat_id, profile);
|
||||
self
|
||||
}
|
||||
|
||||
pub fn with_network_state(self, state: NetworkState) -> Self {
|
||||
*self.network_state.lock().unwrap() = state;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn with_auth_state(self, state: AuthState) -> Self {
|
||||
*self.auth_state.lock().unwrap() = state;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn with_downloaded_file(self, file_id: i32, path: &str) -> Self {
|
||||
self.downloaded_files
|
||||
.lock()
|
||||
.unwrap()
|
||||
.insert(file_id, path.to_string());
|
||||
self
|
||||
}
|
||||
|
||||
pub fn with_available_reactions(self, reactions: Vec<String>) -> Self {
|
||||
*self.available_reactions.lock().unwrap() = reactions;
|
||||
self
|
||||
}
|
||||
}
|
||||
92
tests/helpers/fake_tdclient/inspect.rs
Normal file
92
tests/helpers/fake_tdclient/inspect.rs
Normal file
@@ -0,0 +1,92 @@
|
||||
use super::{
|
||||
DeletedMessages, EditedMessage, FakeTdClient, ForwardedMessages, SearchQuery, SentMessage,
|
||||
TdUpdate,
|
||||
};
|
||||
use tele_tui::tdlib::types::FolderInfo;
|
||||
use tele_tui::tdlib::{ChatInfo, MessageInfo, NetworkState};
|
||||
use tokio::sync::mpsc;
|
||||
|
||||
#[allow(dead_code)]
|
||||
impl FakeTdClient {
|
||||
pub fn get_chats(&self) -> Vec<ChatInfo> {
|
||||
self.chats.lock().unwrap().clone()
|
||||
}
|
||||
|
||||
pub fn get_folders(&self) -> Vec<FolderInfo> {
|
||||
self.folders.lock().unwrap().clone()
|
||||
}
|
||||
|
||||
pub fn get_messages(&self, chat_id: i64) -> Vec<MessageInfo> {
|
||||
self.messages
|
||||
.lock()
|
||||
.unwrap()
|
||||
.get(&chat_id)
|
||||
.cloned()
|
||||
.unwrap_or_default()
|
||||
}
|
||||
|
||||
pub fn get_sent_messages(&self) -> Vec<SentMessage> {
|
||||
self.sent_messages.lock().unwrap().clone()
|
||||
}
|
||||
|
||||
pub fn get_edited_messages(&self) -> Vec<EditedMessage> {
|
||||
self.edited_messages.lock().unwrap().clone()
|
||||
}
|
||||
|
||||
pub fn get_deleted_messages(&self) -> Vec<DeletedMessages> {
|
||||
self.deleted_messages.lock().unwrap().clone()
|
||||
}
|
||||
|
||||
pub fn get_forwarded_messages(&self) -> Vec<ForwardedMessages> {
|
||||
self.forwarded_messages.lock().unwrap().clone()
|
||||
}
|
||||
|
||||
pub fn get_search_queries(&self) -> Vec<SearchQuery> {
|
||||
self.searched_queries.lock().unwrap().clone()
|
||||
}
|
||||
|
||||
pub fn get_viewed_messages(&self) -> Vec<(i64, Vec<i64>)> {
|
||||
self.viewed_messages.lock().unwrap().clone()
|
||||
}
|
||||
|
||||
pub fn get_chat_actions(&self) -> Vec<(i64, String)> {
|
||||
self.chat_actions.lock().unwrap().clone()
|
||||
}
|
||||
|
||||
pub fn get_network_state(&self) -> NetworkState {
|
||||
self.network_state.lock().unwrap().clone()
|
||||
}
|
||||
|
||||
pub fn get_current_chat_id(&self) -> Option<i64> {
|
||||
*self.current_chat_id.lock().unwrap()
|
||||
}
|
||||
|
||||
pub fn set_current_pinned_message(&mut self, msg: Option<MessageInfo>) {
|
||||
*self.current_pinned_message.lock().unwrap() = msg;
|
||||
}
|
||||
|
||||
pub async fn process_pending_view_messages(&mut self) {
|
||||
let mut pending = self.pending_view_messages.lock().unwrap();
|
||||
for (chat_id, message_ids) in pending.drain(..) {
|
||||
let ids: Vec<i64> = message_ids.iter().map(|id| id.as_i64()).collect();
|
||||
self.viewed_messages
|
||||
.lock()
|
||||
.unwrap()
|
||||
.push((chat_id.as_i64(), ids));
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_update_channel(&self, tx: mpsc::UnboundedSender<TdUpdate>) {
|
||||
*self.update_tx.lock().unwrap() = Some(tx);
|
||||
}
|
||||
|
||||
pub fn clear_all_history(&self) {
|
||||
self.sent_messages.lock().unwrap().clear();
|
||||
self.edited_messages.lock().unwrap().clear();
|
||||
self.deleted_messages.lock().unwrap().clear();
|
||||
self.forwarded_messages.lock().unwrap().clear();
|
||||
self.searched_queries.lock().unwrap().clear();
|
||||
self.viewed_messages.lock().unwrap().clear();
|
||||
self.chat_actions.lock().unwrap().clear();
|
||||
}
|
||||
}
|
||||
458
tests/helpers/fake_tdclient/operations.rs
Normal file
458
tests/helpers/fake_tdclient/operations.rs
Normal file
@@ -0,0 +1,458 @@
|
||||
use super::{
|
||||
DeletedMessages, EditedMessage, FakeTdClient, ForwardedMessages, SearchQuery, SentMessage,
|
||||
TdUpdate,
|
||||
};
|
||||
use tele_tui::tdlib::types::ReactionInfo;
|
||||
use tele_tui::tdlib::{ChatInfo, MessageInfo, ProfileInfo, ReplyInfo};
|
||||
use tele_tui::types::{ChatId, MessageId, UserId};
|
||||
|
||||
#[allow(dead_code)]
|
||||
impl FakeTdClient {
|
||||
pub async fn load_chats(&self, limit: usize) -> Result<Vec<ChatInfo>, String> {
|
||||
if self.should_fail() {
|
||||
return Err("Failed to load chats".to_string());
|
||||
}
|
||||
|
||||
if self.simulate_delays {
|
||||
tokio::time::sleep(tokio::time::Duration::from_millis(50)).await;
|
||||
}
|
||||
|
||||
let chats = self
|
||||
.chats
|
||||
.lock()
|
||||
.unwrap()
|
||||
.iter()
|
||||
.take(limit)
|
||||
.cloned()
|
||||
.collect();
|
||||
Ok(chats)
|
||||
}
|
||||
|
||||
pub async fn open_chat(&self, chat_id: ChatId) -> Result<(), String> {
|
||||
if self.should_fail() {
|
||||
return Err("Failed to open chat".to_string());
|
||||
}
|
||||
|
||||
*self.current_chat_id.lock().unwrap() = Some(chat_id.as_i64());
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn get_chat_history(
|
||||
&self,
|
||||
chat_id: ChatId,
|
||||
limit: i32,
|
||||
) -> Result<Vec<MessageInfo>, String> {
|
||||
if self.should_fail() {
|
||||
return Err("Failed to load history".to_string());
|
||||
}
|
||||
|
||||
if self.simulate_delays {
|
||||
tokio::time::sleep(tokio::time::Duration::from_millis(100)).await;
|
||||
}
|
||||
|
||||
let messages = self
|
||||
.messages
|
||||
.lock()
|
||||
.unwrap()
|
||||
.get(&chat_id.as_i64())
|
||||
.map(|msgs| msgs.iter().take(limit as usize).cloned().collect())
|
||||
.unwrap_or_default();
|
||||
|
||||
Ok(messages)
|
||||
}
|
||||
|
||||
pub async fn load_older_messages(
|
||||
&self,
|
||||
chat_id: ChatId,
|
||||
from_message_id: MessageId,
|
||||
) -> Result<Vec<MessageInfo>, String> {
|
||||
if self.should_fail() {
|
||||
return Err("Failed to load older messages".to_string());
|
||||
}
|
||||
|
||||
let messages = self.messages.lock().unwrap();
|
||||
let chat_messages = messages.get(&chat_id.as_i64()).ok_or("Chat not found")?;
|
||||
|
||||
if let Some(idx) = chat_messages.iter().position(|m| m.id() == from_message_id) {
|
||||
let older = chat_messages.iter().take(idx).cloned().collect();
|
||||
Ok(older)
|
||||
} else {
|
||||
Ok(vec![])
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn send_message(
|
||||
&self,
|
||||
chat_id: ChatId,
|
||||
text: String,
|
||||
reply_to: Option<MessageId>,
|
||||
reply_info: Option<ReplyInfo>,
|
||||
) -> Result<MessageInfo, String> {
|
||||
if self.should_fail() {
|
||||
return Err("Failed to send message".to_string());
|
||||
}
|
||||
|
||||
if self.simulate_delays {
|
||||
tokio::time::sleep(tokio::time::Duration::from_millis(200)).await;
|
||||
}
|
||||
|
||||
let message_id = MessageId::new((self.sent_messages.lock().unwrap().len() as i64) + 1000);
|
||||
|
||||
self.sent_messages.lock().unwrap().push(SentMessage {
|
||||
chat_id: chat_id.as_i64(),
|
||||
text: text.clone(),
|
||||
reply_to,
|
||||
reply_info: reply_info.clone(),
|
||||
});
|
||||
|
||||
let message = MessageInfo::new(
|
||||
message_id,
|
||||
"You".to_string(),
|
||||
true,
|
||||
text,
|
||||
vec![],
|
||||
chrono::Utc::now().timestamp() as i32,
|
||||
0,
|
||||
false,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
reply_info,
|
||||
None,
|
||||
vec![],
|
||||
);
|
||||
|
||||
self.messages
|
||||
.lock()
|
||||
.unwrap()
|
||||
.entry(chat_id.as_i64())
|
||||
.or_default()
|
||||
.push(message.clone());
|
||||
|
||||
self.send_update(TdUpdate::NewMessage { chat_id, message: Box::new(message.clone()) });
|
||||
|
||||
Ok(message)
|
||||
}
|
||||
|
||||
pub async fn edit_message(
|
||||
&self,
|
||||
chat_id: ChatId,
|
||||
message_id: MessageId,
|
||||
new_text: String,
|
||||
) -> Result<MessageInfo, String> {
|
||||
if self.should_fail() {
|
||||
return Err("Failed to edit message".to_string());
|
||||
}
|
||||
|
||||
if self.simulate_delays {
|
||||
tokio::time::sleep(tokio::time::Duration::from_millis(150)).await;
|
||||
}
|
||||
|
||||
self.edited_messages.lock().unwrap().push(EditedMessage {
|
||||
chat_id: chat_id.as_i64(),
|
||||
message_id,
|
||||
new_text: new_text.clone(),
|
||||
});
|
||||
|
||||
let mut messages = self.messages.lock().unwrap();
|
||||
if let Some(chat_msgs) = messages.get_mut(&chat_id.as_i64()) {
|
||||
if let Some(msg) = chat_msgs.iter_mut().find(|m| m.id() == message_id) {
|
||||
msg.content.text = new_text.clone();
|
||||
msg.metadata.edit_date = msg.metadata.date + 60;
|
||||
|
||||
let updated = msg.clone();
|
||||
drop(messages);
|
||||
|
||||
self.send_update(TdUpdate::MessageContent { chat_id, message_id, new_text });
|
||||
|
||||
return Ok(updated);
|
||||
}
|
||||
}
|
||||
|
||||
Err("Message not found".to_string())
|
||||
}
|
||||
|
||||
pub async fn delete_messages(
|
||||
&self,
|
||||
chat_id: ChatId,
|
||||
message_ids: Vec<MessageId>,
|
||||
revoke: bool,
|
||||
) -> Result<(), String> {
|
||||
if self.should_fail() {
|
||||
return Err("Failed to delete messages".to_string());
|
||||
}
|
||||
|
||||
if self.simulate_delays {
|
||||
tokio::time::sleep(tokio::time::Duration::from_millis(100)).await;
|
||||
}
|
||||
|
||||
self.deleted_messages.lock().unwrap().push(DeletedMessages {
|
||||
chat_id: chat_id.as_i64(),
|
||||
message_ids: message_ids.clone(),
|
||||
revoke,
|
||||
});
|
||||
|
||||
let mut messages = self.messages.lock().unwrap();
|
||||
if let Some(chat_msgs) = messages.get_mut(&chat_id.as_i64()) {
|
||||
chat_msgs.retain(|m| !message_ids.contains(&m.id()));
|
||||
}
|
||||
drop(messages);
|
||||
|
||||
self.send_update(TdUpdate::DeleteMessages { chat_id, message_ids });
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn forward_messages(
|
||||
&self,
|
||||
to_chat_id: ChatId,
|
||||
from_chat_id: ChatId,
|
||||
message_ids: Vec<MessageId>,
|
||||
) -> Result<(), String> {
|
||||
if self.should_fail() {
|
||||
return Err("Failed to forward messages".to_string());
|
||||
}
|
||||
|
||||
if self.simulate_delays {
|
||||
tokio::time::sleep(tokio::time::Duration::from_millis(150)).await;
|
||||
}
|
||||
|
||||
self.forwarded_messages
|
||||
.lock()
|
||||
.unwrap()
|
||||
.push(ForwardedMessages {
|
||||
from_chat_id: from_chat_id.as_i64(),
|
||||
to_chat_id: to_chat_id.as_i64(),
|
||||
message_ids,
|
||||
});
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn search_messages(
|
||||
&self,
|
||||
chat_id: ChatId,
|
||||
query: &str,
|
||||
) -> Result<Vec<MessageInfo>, String> {
|
||||
if self.should_fail() {
|
||||
return Err("Failed to search messages".to_string());
|
||||
}
|
||||
|
||||
let messages = self.messages.lock().unwrap();
|
||||
let results: Vec<_> = messages
|
||||
.get(&chat_id.as_i64())
|
||||
.map(|msgs| {
|
||||
msgs.iter()
|
||||
.filter(|m| m.text().to_lowercase().contains(&query.to_lowercase()))
|
||||
.cloned()
|
||||
.collect()
|
||||
})
|
||||
.unwrap_or_default();
|
||||
|
||||
self.searched_queries.lock().unwrap().push(SearchQuery {
|
||||
chat_id: chat_id.as_i64(),
|
||||
query: query.to_string(),
|
||||
results_count: results.len(),
|
||||
});
|
||||
|
||||
Ok(results)
|
||||
}
|
||||
|
||||
pub async fn set_draft_message(&self, chat_id: ChatId, text: String) -> Result<(), String> {
|
||||
if text.is_empty() {
|
||||
self.drafts.lock().unwrap().remove(&chat_id.as_i64());
|
||||
} else {
|
||||
self.drafts
|
||||
.lock()
|
||||
.unwrap()
|
||||
.insert(chat_id.as_i64(), text.clone());
|
||||
}
|
||||
|
||||
self.send_update(TdUpdate::ChatDraftMessage {
|
||||
chat_id,
|
||||
draft_text: if text.is_empty() { None } else { Some(text) },
|
||||
});
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn send_chat_action(&self, chat_id: ChatId, action: String) {
|
||||
self.chat_actions
|
||||
.lock()
|
||||
.unwrap()
|
||||
.push((chat_id.as_i64(), action.clone()));
|
||||
|
||||
if action == "Typing" {
|
||||
*self.typing_chat_id.lock().unwrap() = Some(chat_id.as_i64());
|
||||
} else if action == "Cancel" {
|
||||
*self.typing_chat_id.lock().unwrap() = None;
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn get_message_available_reactions(
|
||||
&self,
|
||||
_chat_id: ChatId,
|
||||
_message_id: MessageId,
|
||||
) -> Result<Vec<String>, String> {
|
||||
if self.should_fail() {
|
||||
return Err("Failed to get available reactions".to_string());
|
||||
}
|
||||
|
||||
Ok(self.available_reactions.lock().unwrap().clone())
|
||||
}
|
||||
|
||||
pub async fn toggle_reaction(
|
||||
&self,
|
||||
chat_id: ChatId,
|
||||
message_id: MessageId,
|
||||
emoji: String,
|
||||
) -> Result<(), String> {
|
||||
if self.should_fail() {
|
||||
return Err("Failed to toggle reaction".to_string());
|
||||
}
|
||||
|
||||
let mut messages = self.messages.lock().unwrap();
|
||||
if let Some(chat_msgs) = messages.get_mut(&chat_id.as_i64()) {
|
||||
if let Some(msg) = chat_msgs.iter_mut().find(|m| m.id() == message_id) {
|
||||
let reactions = &mut msg.interactions.reactions;
|
||||
|
||||
if let Some(pos) = reactions
|
||||
.iter()
|
||||
.position(|reaction| reaction.emoji == emoji && reaction.is_chosen)
|
||||
{
|
||||
reactions.remove(pos);
|
||||
} else if let Some(reaction) = reactions
|
||||
.iter_mut()
|
||||
.find(|reaction| reaction.emoji == emoji)
|
||||
{
|
||||
reaction.is_chosen = true;
|
||||
reaction.count += 1;
|
||||
} else {
|
||||
reactions.push(ReactionInfo {
|
||||
emoji: emoji.clone(),
|
||||
count: 1,
|
||||
is_chosen: true,
|
||||
});
|
||||
}
|
||||
|
||||
let updated_reactions = reactions.clone();
|
||||
drop(messages);
|
||||
|
||||
self.send_update(TdUpdate::MessageInteractionInfo {
|
||||
chat_id,
|
||||
message_id,
|
||||
reactions: updated_reactions,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn download_file(&self, file_id: i32) -> Result<String, String> {
|
||||
if self.should_fail() {
|
||||
return Err("Failed to download file".to_string());
|
||||
}
|
||||
|
||||
self.downloaded_files
|
||||
.lock()
|
||||
.unwrap()
|
||||
.get(&file_id)
|
||||
.cloned()
|
||||
.ok_or_else(|| format!("File {} not found", file_id))
|
||||
}
|
||||
|
||||
pub async fn get_profile_info(&self, chat_id: ChatId) -> Result<ProfileInfo, String> {
|
||||
if self.should_fail() {
|
||||
return Err("Failed to get profile info".to_string());
|
||||
}
|
||||
|
||||
self.profiles
|
||||
.lock()
|
||||
.unwrap()
|
||||
.get(&chat_id.as_i64())
|
||||
.cloned()
|
||||
.ok_or_else(|| "Profile not found".to_string())
|
||||
}
|
||||
|
||||
pub async fn view_messages(&self, chat_id: ChatId, message_ids: Vec<MessageId>) {
|
||||
self.viewed_messages
|
||||
.lock()
|
||||
.unwrap()
|
||||
.push((chat_id.as_i64(), message_ids.iter().map(|id| id.as_i64()).collect()));
|
||||
}
|
||||
|
||||
pub async fn load_folder_chats(&self, _folder_id: i32, _limit: usize) -> Result<(), String> {
|
||||
if self.should_fail() {
|
||||
return Err("Failed to load folder chats".to_string());
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn send_update(&self, update: TdUpdate) {
|
||||
if let Some(tx) = self.update_tx.lock().unwrap().as_ref() {
|
||||
let _ = tx.send(update);
|
||||
}
|
||||
}
|
||||
|
||||
fn should_fail(&self) -> bool {
|
||||
let mut fail = self.fail_next_operation.lock().unwrap();
|
||||
if *fail {
|
||||
*fail = false;
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
pub fn fail_next(&self) {
|
||||
*self.fail_next_operation.lock().unwrap() = true;
|
||||
}
|
||||
|
||||
pub fn simulate_incoming_message(&self, chat_id: ChatId, text: String, sender_name: &str) {
|
||||
let message_id = MessageId::new(9000 + chrono::Utc::now().timestamp());
|
||||
|
||||
let message = MessageInfo::new(
|
||||
message_id,
|
||||
sender_name.to_string(),
|
||||
false,
|
||||
text,
|
||||
vec![],
|
||||
chrono::Utc::now().timestamp() as i32,
|
||||
0,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
None,
|
||||
None,
|
||||
vec![],
|
||||
);
|
||||
|
||||
self.messages
|
||||
.lock()
|
||||
.unwrap()
|
||||
.entry(chat_id.as_i64())
|
||||
.or_default()
|
||||
.push(message.clone());
|
||||
|
||||
self.send_update(TdUpdate::NewMessage { chat_id, message: Box::new(message) });
|
||||
}
|
||||
|
||||
pub fn simulate_typing(&self, chat_id: ChatId, user_id: UserId) {
|
||||
self.send_update(TdUpdate::ChatAction { chat_id, user_id, action: "Typing".to_string() });
|
||||
}
|
||||
|
||||
pub fn simulate_network_change(&self, state: tele_tui::tdlib::NetworkState) {
|
||||
*self.network_state.lock().unwrap() = state.clone();
|
||||
self.send_update(TdUpdate::ConnectionState { state });
|
||||
}
|
||||
|
||||
pub fn simulate_read_outbox(&self, chat_id: ChatId, last_read_message_id: MessageId) {
|
||||
self.send_update(TdUpdate::ChatReadOutbox {
|
||||
chat_id,
|
||||
last_read_outbox_message_id: last_read_message_id,
|
||||
});
|
||||
}
|
||||
}
|
||||
201
tests/helpers/fake_tdclient/state.rs
Normal file
201
tests/helpers/fake_tdclient/state.rs
Normal file
@@ -0,0 +1,201 @@
|
||||
use std::collections::HashMap;
|
||||
use std::sync::{Arc, Mutex};
|
||||
use tele_tui::tdlib::types::{FolderInfo, ReactionInfo};
|
||||
use tele_tui::tdlib::{AuthState, ChatInfo, MessageInfo, NetworkState, ProfileInfo, ReplyInfo};
|
||||
use tele_tui::types::{ChatId, MessageId, UserId};
|
||||
use tokio::sync::mpsc;
|
||||
|
||||
pub type ViewedMessages = Vec<(i64, Vec<i64>)>;
|
||||
pub type PendingViewMessages = Vec<(ChatId, Vec<MessageId>)>;
|
||||
|
||||
/// Update events from TDLib, simplified for tests.
|
||||
#[derive(Debug, Clone)]
|
||||
#[allow(dead_code)]
|
||||
pub enum TdUpdate {
|
||||
NewMessage {
|
||||
chat_id: ChatId,
|
||||
message: Box<MessageInfo>,
|
||||
},
|
||||
MessageContent {
|
||||
chat_id: ChatId,
|
||||
message_id: MessageId,
|
||||
new_text: String,
|
||||
},
|
||||
DeleteMessages {
|
||||
chat_id: ChatId,
|
||||
message_ids: Vec<MessageId>,
|
||||
},
|
||||
ChatAction {
|
||||
chat_id: ChatId,
|
||||
user_id: UserId,
|
||||
action: String,
|
||||
},
|
||||
MessageInteractionInfo {
|
||||
chat_id: ChatId,
|
||||
message_id: MessageId,
|
||||
reactions: Vec<ReactionInfo>,
|
||||
},
|
||||
ConnectionState {
|
||||
state: NetworkState,
|
||||
},
|
||||
ChatReadOutbox {
|
||||
chat_id: ChatId,
|
||||
last_read_outbox_message_id: MessageId,
|
||||
},
|
||||
ChatDraftMessage {
|
||||
chat_id: ChatId,
|
||||
draft_text: Option<String>,
|
||||
},
|
||||
}
|
||||
|
||||
/// Simplified mock TDLib client for tests.
|
||||
#[allow(dead_code)]
|
||||
pub struct FakeTdClient {
|
||||
pub chats: Arc<Mutex<Vec<ChatInfo>>>,
|
||||
pub messages: Arc<Mutex<HashMap<i64, Vec<MessageInfo>>>>,
|
||||
pub folders: Arc<Mutex<Vec<FolderInfo>>>,
|
||||
pub user_names: Arc<Mutex<HashMap<i64, String>>>,
|
||||
pub profiles: Arc<Mutex<HashMap<i64, ProfileInfo>>>,
|
||||
pub drafts: Arc<Mutex<HashMap<i64, String>>>,
|
||||
pub available_reactions: Arc<Mutex<Vec<String>>>,
|
||||
|
||||
pub network_state: Arc<Mutex<NetworkState>>,
|
||||
pub typing_chat_id: Arc<Mutex<Option<i64>>>,
|
||||
pub current_chat_id: Arc<Mutex<Option<i64>>>,
|
||||
pub current_pinned_message: Arc<Mutex<Option<MessageInfo>>>,
|
||||
pub auth_state: Arc<Mutex<AuthState>>,
|
||||
|
||||
pub sent_messages: Arc<Mutex<Vec<SentMessage>>>,
|
||||
pub edited_messages: Arc<Mutex<Vec<EditedMessage>>>,
|
||||
pub deleted_messages: Arc<Mutex<Vec<DeletedMessages>>>,
|
||||
pub forwarded_messages: Arc<Mutex<Vec<ForwardedMessages>>>,
|
||||
pub searched_queries: Arc<Mutex<Vec<SearchQuery>>>,
|
||||
pub viewed_messages: Arc<Mutex<ViewedMessages>>,
|
||||
pub chat_actions: Arc<Mutex<Vec<(i64, String)>>>,
|
||||
pub pending_view_messages: Arc<Mutex<PendingViewMessages>>,
|
||||
|
||||
pub update_tx: Arc<Mutex<Option<mpsc::UnboundedSender<TdUpdate>>>>,
|
||||
pub downloaded_files: Arc<Mutex<HashMap<i32, String>>>,
|
||||
|
||||
pub simulate_delays: bool,
|
||||
pub fail_next_operation: Arc<Mutex<bool>>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
#[allow(dead_code)]
|
||||
pub struct SentMessage {
|
||||
pub chat_id: i64,
|
||||
pub text: String,
|
||||
pub reply_to: Option<MessageId>,
|
||||
pub reply_info: Option<ReplyInfo>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
#[allow(dead_code)]
|
||||
pub struct EditedMessage {
|
||||
pub chat_id: i64,
|
||||
pub message_id: MessageId,
|
||||
pub new_text: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
#[allow(dead_code)]
|
||||
pub struct DeletedMessages {
|
||||
pub chat_id: i64,
|
||||
pub message_ids: Vec<MessageId>,
|
||||
pub revoke: bool,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
#[allow(dead_code)]
|
||||
pub struct ForwardedMessages {
|
||||
pub from_chat_id: i64,
|
||||
pub to_chat_id: i64,
|
||||
pub message_ids: Vec<MessageId>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
#[allow(dead_code)]
|
||||
pub struct SearchQuery {
|
||||
pub chat_id: i64,
|
||||
pub query: String,
|
||||
pub results_count: usize,
|
||||
}
|
||||
|
||||
impl Default for FakeTdClient {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
|
||||
impl Clone for FakeTdClient {
|
||||
fn clone(&self) -> Self {
|
||||
Self {
|
||||
chats: Arc::clone(&self.chats),
|
||||
messages: Arc::clone(&self.messages),
|
||||
folders: Arc::clone(&self.folders),
|
||||
user_names: Arc::clone(&self.user_names),
|
||||
profiles: Arc::clone(&self.profiles),
|
||||
drafts: Arc::clone(&self.drafts),
|
||||
available_reactions: Arc::clone(&self.available_reactions),
|
||||
network_state: Arc::clone(&self.network_state),
|
||||
typing_chat_id: Arc::clone(&self.typing_chat_id),
|
||||
current_chat_id: Arc::clone(&self.current_chat_id),
|
||||
current_pinned_message: Arc::clone(&self.current_pinned_message),
|
||||
auth_state: Arc::clone(&self.auth_state),
|
||||
sent_messages: Arc::clone(&self.sent_messages),
|
||||
edited_messages: Arc::clone(&self.edited_messages),
|
||||
deleted_messages: Arc::clone(&self.deleted_messages),
|
||||
forwarded_messages: Arc::clone(&self.forwarded_messages),
|
||||
searched_queries: Arc::clone(&self.searched_queries),
|
||||
viewed_messages: Arc::clone(&self.viewed_messages),
|
||||
chat_actions: Arc::clone(&self.chat_actions),
|
||||
pending_view_messages: Arc::clone(&self.pending_view_messages),
|
||||
downloaded_files: Arc::clone(&self.downloaded_files),
|
||||
update_tx: Arc::clone(&self.update_tx),
|
||||
simulate_delays: self.simulate_delays,
|
||||
fail_next_operation: Arc::clone(&self.fail_next_operation),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
impl FakeTdClient {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
chats: Arc::new(Mutex::new(vec![])),
|
||||
messages: Arc::new(Mutex::new(HashMap::new())),
|
||||
folders: Arc::new(Mutex::new(vec![FolderInfo { id: 0, name: "All".to_string() }])),
|
||||
user_names: Arc::new(Mutex::new(HashMap::new())),
|
||||
profiles: Arc::new(Mutex::new(HashMap::new())),
|
||||
drafts: Arc::new(Mutex::new(HashMap::new())),
|
||||
available_reactions: Arc::new(Mutex::new(vec![
|
||||
"👍".to_string(),
|
||||
"❤️".to_string(),
|
||||
"😂".to_string(),
|
||||
"😮".to_string(),
|
||||
"😢".to_string(),
|
||||
"🙏".to_string(),
|
||||
"👏".to_string(),
|
||||
"🔥".to_string(),
|
||||
])),
|
||||
network_state: Arc::new(Mutex::new(NetworkState::Ready)),
|
||||
typing_chat_id: Arc::new(Mutex::new(None)),
|
||||
current_chat_id: Arc::new(Mutex::new(None)),
|
||||
current_pinned_message: Arc::new(Mutex::new(None)),
|
||||
auth_state: Arc::new(Mutex::new(AuthState::Ready)),
|
||||
sent_messages: Arc::new(Mutex::new(vec![])),
|
||||
edited_messages: Arc::new(Mutex::new(vec![])),
|
||||
deleted_messages: Arc::new(Mutex::new(vec![])),
|
||||
forwarded_messages: Arc::new(Mutex::new(vec![])),
|
||||
searched_queries: Arc::new(Mutex::new(vec![])),
|
||||
viewed_messages: Arc::new(Mutex::new(vec![])),
|
||||
chat_actions: Arc::new(Mutex::new(vec![])),
|
||||
pending_view_messages: Arc::new(Mutex::new(vec![])),
|
||||
downloaded_files: Arc::new(Mutex::new(HashMap::new())),
|
||||
update_tx: Arc::new(Mutex::new(None)),
|
||||
simulate_delays: false,
|
||||
fail_next_operation: Arc::new(Mutex::new(false)),
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user