use super::*;
#[test]
fn share_token_debug_redacts() {
let token = ShareToken::new("super-secret-value");
let rendered = format!("{token:?}");
assert!(rendered.contains("***redacted***"));
assert!(!rendered.contains("super-secret-value"));
}
#[test]
fn share_token_does_not_implement_serialize() {
fn _requires_serialize<T: serde::Serialize>(_: T) {}
#[derive(serde::Serialize)]
struct Holder<'a> {
raw: &'a str,
}
let token = ShareToken::new("RAW-VALUE-MUST-NOT-LEAK");
let holder = Holder {
raw: token.as_str(),
};
let json = serde_json::to_string(&holder).unwrap();
assert!(
json.contains("RAW-VALUE-MUST-NOT-LEAK"),
"test sanity: explicit `as_str()` opt-in should serialise",
);
}
#[test]
fn share_token_accessor_returns_raw_value() {
let token = ShareToken::new("raw");
assert_eq!(token.as_str(), "raw");
assert_eq!(token.into_inner(), "raw");
}
#[test]
fn generate_share_token_meets_minimum_length() {
let token = generate_share_token();
assert_eq!(token.as_str().len(), 64);
assert!(token.as_str().chars().all(|c| c.is_ascii_hexdigit()));
}
#[test]
fn generate_share_token_is_unique_across_calls() {
let a = generate_share_token();
let b = generate_share_token();
assert_ne!(a.as_str(), b.as_str());
}
#[test]
fn export_format_parses_known_values() {
assert_eq!(ExportFormat::from_str("json").unwrap(), ExportFormat::Json);
assert_eq!(
ExportFormat::from_str("markdown").unwrap(),
ExportFormat::Markdown
);
assert_eq!(
ExportFormat::from_str("md").unwrap(),
ExportFormat::Markdown
);
assert_eq!(ExportFormat::from_str("JSON").unwrap(), ExportFormat::Json);
}
#[test]
fn export_format_rejects_unknown() {
let err = ExportFormat::from_str("xml").unwrap_err();
assert!(matches!(err, ChatEngineError::BadRequest { .. }));
}
#[test]
fn export_format_content_type_and_extension() {
assert_eq!(ExportFormat::Json.content_type(), "application/json");
assert_eq!(
ExportFormat::Markdown.content_type(),
"text/markdown; charset=utf-8"
);
assert_eq!(ExportFormat::Json.extension(), "json");
assert_eq!(ExportFormat::Markdown.extension(), "md");
}
#[test]
fn storage_error_maps_to_backend_unavailable() {
let err: ChatEngineError = StorageError::Unavailable("blob: nope".into()).into();
assert!(matches!(err, ChatEngineError::BackendUnavailable { .. }));
}
#[tokio::test]
async fn stub_storage_returns_memory_url() {
let url = StubExportStorage
.upload(
"exports/tenant-1/session-2/2026.json",
vec![1, 2, 3],
"application/json",
)
.await
.expect("stub never fails");
assert_eq!(url, "memory://exports/exports/tenant-1/session-2/2026.json");
}
#[test]
fn share_token_issue_serializes_token_verbatim() {
let issue = ShareTokenIssue {
share_token: "abcd".into(),
share_url: "https://example/share/abcd".into(),
expires_at: None,
};
let json = serde_json::to_string(&issue).expect("serialize");
assert!(json.contains("\"share_token\":\"abcd\""));
assert!(!json.contains("expires_at"));
}
#[test]
fn shared_session_view_omits_user_and_tenant_fields() {
let view = SharedSessionView {
title: Some("hello".into()),
created_at: OffsetDateTime::UNIX_EPOCH,
messages: Vec::new(),
read_only: true,
message_count: 0,
};
let json = serde_json::to_string(&view).expect("serialize");
assert!(!json.contains("user_id"));
assert!(!json.contains("tenant_id"));
assert!(!json.contains("share_token"));
assert!(json.contains("\"read_only\":true"));
}