2fa05cd605
要求用户消息包含非空文本,附件仅作为消息上下文。 同步后端领域校验、接口错误和请求测试。 禁用前端纯附件发送并更新交互回归测试。 保留发送失败时的草稿与附件恢复行为。
130 lines
4.4 KiB
Rust
130 lines
4.4 KiB
Rust
use shared_kernel::normalize_required_string;
|
|
|
|
use crate::{
|
|
EDITOR_AGENT_MAX_ATTACHMENTS, EditorAgentConversationCreateInput,
|
|
EditorAgentConversationDeleteInput, EditorAgentConversationTouchInput, EditorAgentError,
|
|
};
|
|
|
|
pub fn validate_conversation_key(
|
|
conversation_id: &str,
|
|
owner_user_id: &str,
|
|
) -> Result<(String, String), EditorAgentError> {
|
|
let conversation_id = normalize_required_string(conversation_id)
|
|
.ok_or(EditorAgentError::MissingConversationId)?;
|
|
let owner_user_id =
|
|
normalize_required_string(owner_user_id).ok_or(EditorAgentError::MissingOwnerUserId)?;
|
|
Ok((conversation_id, owner_user_id))
|
|
}
|
|
|
|
pub fn validate_create_conversation(
|
|
input: &EditorAgentConversationCreateInput,
|
|
) -> Result<(), EditorAgentError> {
|
|
validate_conversation_key(&input.conversation_id, &input.owner_user_id)?;
|
|
if normalize_required_string(&input.project_id).is_none() {
|
|
return Err(EditorAgentError::MissingProjectId);
|
|
}
|
|
Ok(())
|
|
}
|
|
|
|
pub fn validate_touch_conversation(
|
|
input: &EditorAgentConversationTouchInput,
|
|
) -> Result<(), EditorAgentError> {
|
|
validate_conversation_key(&input.conversation_id, &input.owner_user_id)?;
|
|
Ok(())
|
|
}
|
|
|
|
pub fn validate_delete_conversation(
|
|
input: &EditorAgentConversationDeleteInput,
|
|
) -> Result<(), EditorAgentError> {
|
|
validate_conversation_key(&input.conversation_id, &input.owner_user_id)?;
|
|
Ok(())
|
|
}
|
|
|
|
/// 校验会话访问权:拥有者匹配且未软删。
|
|
pub fn ensure_conversation_accessible(
|
|
snapshot_owner_user_id: &str,
|
|
snapshot_deleted: bool,
|
|
requester_user_id: &str,
|
|
) -> Result<(), EditorAgentError> {
|
|
if snapshot_owner_user_id != requester_user_id {
|
|
return Err(EditorAgentError::NotConversationOwner);
|
|
}
|
|
if snapshot_deleted {
|
|
return Err(EditorAgentError::ConversationDeleted);
|
|
}
|
|
Ok(())
|
|
}
|
|
|
|
/// 校验用户消息:文本不能为空,附件数量不超过上限,附件引用需带资源标识。
|
|
pub fn validate_user_message(
|
|
text: &str,
|
|
attachment_reference_ids: &[String],
|
|
) -> Result<(), EditorAgentError> {
|
|
if normalize_required_string(text).is_none() {
|
|
return Err(EditorAgentError::EmptyMessage);
|
|
}
|
|
if attachment_reference_ids.len() > EDITOR_AGENT_MAX_ATTACHMENTS {
|
|
return Err(EditorAgentError::TooManyAttachments);
|
|
}
|
|
if attachment_reference_ids
|
|
.iter()
|
|
.any(|reference_id| normalize_required_string(reference_id).is_none())
|
|
{
|
|
return Err(EditorAgentError::InvalidAttachmentReference);
|
|
}
|
|
Ok(())
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use super::*;
|
|
use crate::derive_conversation_title;
|
|
|
|
#[test]
|
|
fn derive_title_truncates_and_normalizes_whitespace() {
|
|
assert_eq!(
|
|
derive_conversation_title(" 帮我生成\n一张森林背景 "),
|
|
"帮我生成 一张森林背景"
|
|
);
|
|
assert_eq!(derive_conversation_title(""), "新对话");
|
|
assert_eq!(derive_conversation_title(" \n "), "新对话");
|
|
let long_text = "一二三四五六七八九十一二三四五六七八九十超出部分";
|
|
assert_eq!(derive_conversation_title(long_text).chars().count(), 20);
|
|
}
|
|
|
|
#[test]
|
|
fn validate_user_message_rules() {
|
|
assert_eq!(
|
|
validate_user_message("", &[]),
|
|
Err(EditorAgentError::EmptyMessage)
|
|
);
|
|
assert_eq!(
|
|
validate_user_message("", &["resource-1".to_string()]),
|
|
Err(EditorAgentError::EmptyMessage)
|
|
);
|
|
assert!(validate_user_message("画一棵树", &[]).is_ok());
|
|
let too_many: Vec<String> = (0..10).map(|i| format!("resource-{i}")).collect();
|
|
assert_eq!(
|
|
validate_user_message("画一棵树", &too_many),
|
|
Err(EditorAgentError::TooManyAttachments)
|
|
);
|
|
assert_eq!(
|
|
validate_user_message("画一棵树", &[" ".to_string()]),
|
|
Err(EditorAgentError::InvalidAttachmentReference)
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn ensure_conversation_accessible_rules() {
|
|
assert!(ensure_conversation_accessible("user-1", false, "user-1").is_ok());
|
|
assert_eq!(
|
|
ensure_conversation_accessible("user-1", false, "user-2"),
|
|
Err(EditorAgentError::NotConversationOwner)
|
|
);
|
|
assert_eq!(
|
|
ensure_conversation_accessible("user-1", true, "user-1"),
|
|
Err(EditorAgentError::ConversationDeleted)
|
|
);
|
|
}
|
|
}
|