chore: add tests for session serialization issues (#1985)

This commit is contained in:
Salman Mohammed
2025-04-01 16:39:01 -04:00
committed by GitHub
parent 974d109fb3
commit 35c2536975
2 changed files with 132 additions and 6 deletions
+125
View File
@@ -421,4 +421,129 @@ mod tests {
// Time part should be 6 digits
assert_eq!(parts[1].len(), 6);
}
#[tokio::test]
async fn test_special_characters_and_long_text() -> Result<()> {
let dir = tempdir()?;
let file_path = dir.path().join("special.jsonl");
// Insert some problematic JSON-like content between long text
let long_text = format!(
"Start_of_message\n{}{}SOME_MIDDLE_TEXT{}End_of_message",
"A".repeat(100_000),
"\"}]\n",
"A".repeat(100_000)
);
let special_chars = vec![
// Long text
long_text.as_str(),
// Newlines in different positions
"Line 1\nLine 2",
"Line 1\r\nLine 2",
"\nStart with newline",
"End with newline\n",
"\n\nMultiple\n\nNewlines\n\n",
// JSON special characters
"Quote\"in middle",
"\"Quote at start",
"Quote at end\"",
"Multiple\"\"Quotes",
"{\"json\": \"looking text\"}",
// Unicode and special characters
"Unicode: 🦆🤖👾",
"Special: \\n \\r \\t",
"Mixed: \n\"🦆\"\r\n\\n",
// Control characters
"Tab\there",
"Bell\u{0007}char",
"Null\u{0000}char",
// Long text with mixed content
"A very long message with multiple lines\nand \"quotes\"\nand emojis 🦆\nand \\escaped chars",
// Potentially problematic JSON content
"}{[]\",\\",
"]}}\"\\n\\\"{[",
"Edge case: } ] some text",
"{\"foo\": \"} ]\"}",
"}]",
];
let mut messages = Vec::new();
for text in special_chars {
messages.push(Message::user().with_text(text));
messages.push(Message::assistant().with_text(text));
}
// Write messages with special characters
persist_messages(&file_path, &messages, None).await?;
// Read them back
let read_messages = read_messages(&file_path)?;
// Compare all messages
assert_eq!(messages.len(), read_messages.len());
for (i, (orig, read)) in messages.iter().zip(read_messages.iter()).enumerate() {
assert_eq!(orig.role, read.role, "Role mismatch at message {}", i);
assert_eq!(
orig.content.len(),
read.content.len(),
"Content length mismatch at message {}",
i
);
if let (Some(MessageContent::Text(orig_text)), Some(MessageContent::Text(read_text))) =
(orig.content.first(), read.content.first())
{
assert_eq!(
orig_text.text, read_text.text,
"Text mismatch at message {}\nExpected: {}\nGot: {}",
i, orig_text.text, read_text.text
);
} else {
panic!("Messages don't match expected structure at index {}", i);
}
}
// Verify file format
let contents = fs::read_to_string(&file_path)?;
let lines: Vec<&str> = contents.lines().collect();
// First line should be metadata
assert!(
lines[0].contains("\"description\""),
"First line should be metadata"
);
// Each subsequent line should be valid JSON
for (i, line) in lines.iter().enumerate().skip(1) {
assert!(
serde_json::from_str::<Message>(line).is_ok(),
"Invalid JSON at line {}: {}",
i + 1,
line
);
}
Ok(())
}
#[tokio::test]
async fn test_metadata_special_chars() -> Result<()> {
let dir = tempdir()?;
let file_path = dir.path().join("metadata.jsonl");
let mut metadata = SessionMetadata::default();
metadata.description = "Description with\nnewline and \"quotes\" and 🦆".to_string();
let messages = vec![Message::user().with_text("test")];
// Write with special metadata
save_messages_with_metadata(&file_path, &metadata, &messages)?;
// Read back metadata
let read_metadata = read_metadata(&file_path)?;
assert_eq!(metadata.description, read_metadata.description);
Ok(())
}
}
@@ -1,14 +1,9 @@
import React, { useState } from 'react';
import { ViewConfig } from '../../App';
import { fetchSessionDetails, type SessionDetails } from '../../sessions';
import { fetchSharedSessionDetails } from '../../sharedSessions';
import SessionListView from './SessionListView';
import SessionHistoryView from './SessionHistoryView';
import { Card } from '../ui/card';
import { Input } from '../ui/input';
import { Button } from '../ui/button';
import BackButton from '../ui/BackButton';
import { ScrollArea } from '../ui/scroll-area';
import { toastError } from '../../toasts';
interface SessionsViewProps {
setView: (view: ViewConfig['view'], viewOptions?: Record<any, any>) => void;
@@ -34,6 +29,12 @@ const SessionsView: React.FC<SessionsViewProps> = ({ setView }) => {
setError('Failed to load session details. Please try again later.');
// Keep the selected session null if there's an error
setSelectedSession(null);
toastError({
title: 'Failed to load session. The file may be corrupted.',
msg: 'Please try again later.',
traceback: err,
});
} finally {
setIsLoadingSession(false);
}