Files
galaxy/app/src/terminal/view/load_ai_conversation_tests.rs
T

220 lines
7.0 KiB
Rust

use chrono::{Local, TimeZone};
use warp_terminal::model::BlockIndex;
use super::find_block_indices_for_exchange_timestamps;
/// Helper: create a `DateTime<Local>` from a unix timestamp in seconds.
fn ts(secs: i64) -> chrono::DateTime<Local> {
Local.timestamp_opt(secs, 0).unwrap()
}
fn bi(idx: usize) -> BlockIndex {
BlockIndex::from(idx)
}
// ── All blocks in increasing timestamp order ──────────────────────────
#[test]
fn sorted_blocks_exchange_before_all_blocks() {
// Exchange at t=1, blocks at t=10, t=20, t=30.
// Should find the first block (t=10).
let blocks = vec![(bi(0), ts(10)), (bi(1), ts(20)), (bi(2), ts(30))];
let exchanges = vec![ts(1)];
let result = find_block_indices_for_exchange_timestamps(&blocks, &exchanges);
assert_eq!(result, vec![Some(bi(0))]);
}
#[test]
fn sorted_blocks_exchange_between_blocks() {
// Exchange at t=15, blocks at t=10, t=20, t=30.
// Should find t=20 (first block >= exchange).
let blocks = vec![(bi(0), ts(10)), (bi(1), ts(20)), (bi(2), ts(30))];
let exchanges = vec![ts(15)];
let result = find_block_indices_for_exchange_timestamps(&blocks, &exchanges);
assert_eq!(result, vec![Some(bi(1))]);
}
#[test]
fn sorted_blocks_exchange_equal_to_block() {
// Exchange at t=20, blocks at t=10, t=20, t=30.
// Should find t=20 (>= includes equality).
let blocks = vec![(bi(0), ts(10)), (bi(1), ts(20)), (bi(2), ts(30))];
let exchanges = vec![ts(20)];
let result = find_block_indices_for_exchange_timestamps(&blocks, &exchanges);
assert_eq!(result, vec![Some(bi(1))]);
}
#[test]
fn sorted_blocks_exchange_after_all_blocks() {
// Exchange at t=100, blocks at t=10, t=20, t=30.
// No block >= 100, should be None.
let blocks = vec![(bi(0), ts(10)), (bi(1), ts(20)), (bi(2), ts(30))];
let exchanges = vec![ts(100)];
let result = find_block_indices_for_exchange_timestamps(&blocks, &exchanges);
assert_eq!(result, vec![None]);
}
#[test]
fn sorted_blocks_multiple_exchanges() {
// Blocks: t=10, t=20, t=30, t=40.
// Exchanges: t=5 (→ bi(0)), t=15 (→ bi(1)), t=25 (→ bi(2)), t=35 (→ bi(3)), t=45 (→ None).
let blocks = vec![
(bi(0), ts(10)),
(bi(1), ts(20)),
(bi(2), ts(30)),
(bi(3), ts(40)),
];
let exchanges = vec![ts(5), ts(15), ts(25), ts(35), ts(45)];
let result = find_block_indices_for_exchange_timestamps(&blocks, &exchanges);
assert_eq!(
result,
vec![Some(bi(0)), Some(bi(1)), Some(bi(2)), Some(bi(3)), None]
);
}
// ── Sorted tail appended after prefix ─────────────────────────────────
// Simulate restore_conversation_after_view_creation: the terminal already
// has blocks (the prefix), then insert_restored_block appends the
// conversation's command blocks as a sorted tail.
// Prefix: [t=40 @0, t=50 @1] (pre-existing terminal blocks)
// Tail: [t=10 @2, t=30 @3] (conversation blocks, sorted)
//
// The search should only match against the tail. The backwards iteration
// with break stops at the boundary so prefix blocks are never considered.
#[test]
fn sorted_tail_exchange_before_tail() {
// Exchange at t=5: all tail blocks are >= 5; smallest is t=10 @2.
let blocks = vec![
(bi(0), ts(40)),
(bi(1), ts(50)),
(bi(2), ts(10)),
(bi(3), ts(30)),
];
let result = find_block_indices_for_exchange_timestamps(&blocks, &[ts(5)]);
assert_eq!(result, vec![Some(bi(2))]);
}
#[test]
fn sorted_tail_exchange_between_tail_blocks() {
// Exchange at t=15: t=30 @3 >= 15 (best), t=10 @2 < 15 → break.
let blocks = vec![
(bi(0), ts(40)),
(bi(1), ts(50)),
(bi(2), ts(10)),
(bi(3), ts(30)),
];
let result = find_block_indices_for_exchange_timestamps(&blocks, &[ts(15)]);
assert_eq!(result, vec![Some(bi(3))]);
}
#[test]
fn sorted_tail_exchange_after_tail() {
// Exchange at t=35: t=30 @3 < 35 → break immediately.
// No tail block matches, so None (AI block appended at end).
// Prefix blocks t=40, t=50 are intentionally not considered.
let blocks = vec![
(bi(0), ts(40)),
(bi(1), ts(50)),
(bi(2), ts(10)),
(bi(3), ts(30)),
];
let result = find_block_indices_for_exchange_timestamps(&blocks, &[ts(35)]);
assert_eq!(result, vec![None]);
}
#[test]
fn sorted_tail_exchange_equals_tail_block() {
// Exchange at t=10: t=30 @3 >= 10 (best), t=10 @2 >= 10 (better, 10 < 30) → best = @2.
// t=50 @1… never reached because next step would be t=50 which is still >= 10,
// but we already found t=10 which is the exact match.
let blocks = vec![
(bi(0), ts(40)),
(bi(1), ts(50)),
(bi(2), ts(10)),
(bi(3), ts(30)),
];
let result = find_block_indices_for_exchange_timestamps(&blocks, &[ts(10)]);
assert_eq!(result, vec![Some(bi(2))]);
}
#[test]
fn sorted_tail_equal_timestamps_pick_first_inserted_block() {
// When restored commands fall back to the same exchange/message timestamp,
// insert the AI block before the first command from that exchange.
let blocks = vec![
(bi(0), ts(40)),
(bi(1), ts(50)),
(bi(2), ts(10)),
(bi(3), ts(10)),
];
let result = find_block_indices_for_exchange_timestamps(&blocks, &[ts(10)]);
assert_eq!(result, vec![Some(bi(2))]);
}
#[test]
fn sorted_tail_multiple_exchanges() {
// Prefix: [t=40 @0, t=50 @1], Tail: [t=10 @2, t=30 @3]
let blocks = vec![
(bi(0), ts(40)),
(bi(1), ts(50)),
(bi(2), ts(10)),
(bi(3), ts(30)),
];
let exchanges = vec![ts(5), ts(15), ts(35), ts(45), ts(55)];
let result = find_block_indices_for_exchange_timestamps(&blocks, &exchanges);
assert_eq!(
result,
vec![
Some(bi(2)), // t=10 (first tail block)
Some(bi(3)), // t=30
None, // past tail, appended at end
None, // past tail
None, // past tail
]
);
}
// ── Edge cases ────────────────────────────────────────────────────────
#[test]
fn empty_blocks_returns_none_for_all_exchanges() {
let blocks: Vec<(BlockIndex, chrono::DateTime<Local>)> = vec![];
let exchanges = vec![ts(10), ts(20)];
let result = find_block_indices_for_exchange_timestamps(&blocks, &exchanges);
assert_eq!(result, vec![None, None]);
}
#[test]
fn empty_exchanges_returns_empty() {
let blocks = vec![(bi(0), ts(10))];
let exchanges: Vec<chrono::DateTime<Local>> = vec![];
let result = find_block_indices_for_exchange_timestamps(&blocks, &exchanges);
assert!(result.is_empty());
}
#[test]
fn single_block_at_same_time_as_exchange() {
let blocks = vec![(bi(0), ts(42))];
let exchanges = vec![ts(42)];
let result = find_block_indices_for_exchange_timestamps(&blocks, &exchanges);
assert_eq!(result, vec![Some(bi(0))]);
}