Skip to content

Commit fb10311

Browse files
committed
fix(store): scope context frame iteration correctly when using last_id
1 parent 96fe0d9 commit fb10311

File tree

2 files changed

+67
-18
lines changed

2 files changed

+67
-18
lines changed

src/store/mod.rs

Lines changed: 20 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -522,26 +522,28 @@ impl Store {
522522
) -> Box<dyn Iterator<Item = Frame> + '_> {
523523
match context_id {
524524
Some(ctx_id) => {
525-
let mut start_key = Vec::with_capacity(32);
526-
start_key.extend(ctx_id.as_bytes());
527-
if let Some(last_id) = last_id {
528-
start_key.extend(last_id.as_bytes());
529-
}
530-
531-
let range = match last_id {
532-
Some(_) => (Bound::Excluded(start_key), Bound::Unbounded),
533-
None => (
534-
Bound::Included(ctx_id.as_bytes().to_vec()),
535-
Bound::Excluded(idx_context_key_range_end(ctx_id)),
536-
),
525+
let start_key = if let Some(last_id) = last_id {
526+
// explicitly combine context_id + last_id
527+
let mut v = Vec::with_capacity(32);
528+
v.extend(ctx_id.as_bytes());
529+
v.extend(last_id.as_bytes());
530+
Bound::Excluded(v)
531+
} else {
532+
Bound::Included(ctx_id.as_bytes().to_vec())
537533
};
538534

539-
Box::new(self.idx_context.range(range).filter_map(move |r| {
540-
let (key, _) = r.ok()?;
541-
let frame_id_bytes = &key[16..];
542-
let frame_id = Scru128Id::from_bytes(frame_id_bytes.try_into().ok()?);
543-
self.get(&frame_id)
544-
}))
535+
let end_key = Bound::Excluded(idx_context_key_range_end(ctx_id));
536+
537+
Box::new(
538+
self.idx_context
539+
.range((start_key, end_key))
540+
.filter_map(move |r| {
541+
let (key, _) = r.ok()?;
542+
let frame_id_bytes = &key[16..];
543+
let frame_id = Scru128Id::from_bytes(frame_id_bytes.try_into().ok()?);
544+
self.get(&frame_id)
545+
}),
546+
)
545547
}
546548
None => {
547549
let range = match last_id {

src/store/tests.rs

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -844,6 +844,53 @@ mod tests_context {
844844
"No context with last_id failed"
845845
);
846846
}
847+
848+
#[test]
849+
fn test_iter_frames_context_scope_with_last_id() {
850+
let temp_dir = tempfile::tempdir().unwrap();
851+
let store = Store::new(temp_dir.path().to_path_buf());
852+
853+
// Create two distinct contexts
854+
let ctx1 = store
855+
.append(Frame::builder("xs.context", ZERO_CONTEXT).build())
856+
.unwrap()
857+
.id;
858+
let ctx2 = store
859+
.append(Frame::builder("xs.context", ZERO_CONTEXT).build())
860+
.unwrap()
861+
.id;
862+
863+
// Add frames in context 1
864+
let ctx1_frame1 = store.append(Frame::builder("test", ctx1).build()).unwrap();
865+
let ctx1_frame2 = store.append(Frame::builder("test", ctx1).build()).unwrap();
866+
867+
// Add frames in context 2
868+
let ctx2_frame1 = store.append(Frame::builder("test", ctx2).build()).unwrap();
869+
let ctx2_frame2 = store.append(Frame::builder("test", ctx2).build()).unwrap();
870+
871+
// Attempt to iterate from ctx1_frame1 in ctx1
872+
let frames_ctx1: Vec<_> = store
873+
.iter_frames(Some(ctx1), Some(&ctx1_frame1.id))
874+
.collect();
875+
876+
// Verify we ONLY get ctx1_frame2
877+
assert_eq!(frames_ctx1, vec![ctx1_frame2.clone()]);
878+
879+
// Attempt to iterate from ctx1_frame1 but incorrectly across contexts
880+
let frames_cross_context: Vec<_> = store
881+
.iter_frames(Some(ctx1), Some(&ctx1_frame2.id))
882+
.collect();
883+
884+
// This should yield NO frames, as ctx1_frame2 is the last in ctx1
885+
assert!(
886+
frames_cross_context.is_empty(),
887+
"Iterator incorrectly traversed beyond context boundary"
888+
);
889+
890+
// Additionally, ensure iterating in ctx2 doesn't return frames from ctx1
891+
let frames_ctx2: Vec<_> = store.iter_frames(Some(ctx2), None).collect();
892+
assert_eq!(frames_ctx2, vec![ctx2_frame1, ctx2_frame2]);
893+
}
847894
}
848895

849896
mod tests_ttl_expire {

0 commit comments

Comments
 (0)