1use core::future::Future;
6use core::time::Duration;
7
8use futures::StreamExt;
9use futures::pin_mut;
10use mnesis::Version;
11use mnesis_store::store::RawEventStore;
12use mnesis_store::wake::WakeSource;
13use mnesis_store::{AppendError, Step, StreamKey, Subscription};
14use tokio::time::timeout;
15
16use crate::row::{
17 ConformanceRow, SubId, append_event, append_rows, assert_strictly_increasing, drain_all,
18 drain_stream, envelope_for,
19};
20
21const WAIT: Duration = Duration::from_secs(10);
24
25pub async fn check_empty_read_yields_none<S, C, F, Fut>(factory: &F)
27where
28 S: RawEventStore + WakeSource,
29 C: Send,
30 F: Fn() -> Fut + Send + Sync,
31 Fut: Future<Output = (S, C)> + Send,
32{
33 let (store, _guard) = factory().await;
34 let got = drain_stream(&store, &StreamKey::from_slice(b"missing"), Version::INITIAL).await;
35 assert!(
36 got.is_empty(),
37 "reading an absent stream must yield an empty stream, got {} rows",
38 got.len(),
39 );
40}
41
42pub async fn check_append_then_read_round_trips<S, C, F, Fut>(factory: &F)
46where
47 S: RawEventStore + WakeSource,
48 C: Send,
49 F: Fn() -> Fut + Send + Sync,
50 Fut: Future<Output = (S, C)> + Send,
51{
52 let (store, _guard) = factory().await;
53 let id = StreamKey::from_slice(b"round-trip");
54 let rows = vec![
55 ConformanceRow::new(1, "Created", vec![]),
56 ConformanceRow::new(2, "user.signed_up", vec![0]).with_schema_version(7),
57 ConformanceRow::new(3, "ÉvénementUTF8", vec![0; 64]).with_metadata(vec![1, 2, 3]),
58 ConformanceRow::new(4, "with spaces 123", vec![0xff; 64]).with_schema_version(u32::MAX),
59 ConformanceRow::new(5, "E", (0..=255u8).collect()),
60 ConformanceRow::new(
61 6,
62 "E",
63 (0..4096u32)
64 .map(|i| u8::try_from(i % 256).unwrap_or(0))
65 .collect(),
66 ),
67 ];
68 append_rows(&store, &id, &rows).await;
69 let got = drain_stream(&store, &id, Version::INITIAL).await;
70 assert_eq!(
71 got, rows,
72 "rows must round-trip byte-for-byte in insertion order"
73 );
74}
75
76pub async fn check_versions_strictly_monotonic_and_fused<S, C, F, Fut>(factory: &F)
78where
79 S: RawEventStore + WakeSource,
80 C: Send,
81 F: Fn() -> Fut + Send + Sync,
82 Fut: Future<Output = (S, C)> + Send,
83{
84 let (store, _guard) = factory().await;
85 let id = StreamKey::from_slice(b"monotonic");
86 let rows: Vec<_> = (1..=64u64)
87 .map(|v| ConformanceRow::new(v, "E", vec![]))
88 .collect();
89 append_rows(&store, &id, &rows).await;
90
91 let stream = store
92 .read_stream(&id, Version::INITIAL)
93 .await
94 .unwrap_or_else(|e| panic!("read_stream failed: {e:?}"));
95 pin_mut!(stream);
96 let mut versions = Vec::new();
97 while let Some(item) = stream.next().await {
98 versions.push(
99 item.unwrap_or_else(|e| panic!("item errored: {e:?}"))
100 .version()
101 .as_u64(),
102 );
103 }
104 let want: Vec<u64> = (1..=64).collect();
105 assert_eq!(
106 versions, want,
107 "versions must be exactly 1..=64, strictly increasing"
108 );
109 for i in 0..8 {
110 assert!(
111 stream.next().await.is_none(),
112 "fused-after-None violated on repeat #{i}",
113 );
114 }
115}
116
117pub async fn check_large_stream_completes<S, C, F, Fut>(factory: &F)
120where
121 S: RawEventStore + WakeSource,
122 C: Send,
123 F: Fn() -> Fut + Send + Sync,
124 Fut: Future<Output = (S, C)> + Send,
125{
126 let (store, _guard) = factory().await;
127 let id = StreamKey::from_slice(b"large");
128 let rows: Vec<_> = (1..=1500u64)
129 .map(|v| ConformanceRow::new(v, "E", vec![]))
130 .collect();
131 append_rows(&store, &id, &rows).await;
132 let got = drain_stream(&store, &id, Version::INITIAL).await;
133 let versions: Vec<u64> = got.iter().map(|r| r.version).collect();
134 let want: Vec<u64> = (1..=1500).collect();
135 assert_eq!(
136 versions, want,
137 "1500-event stream must drain exactly 1..=1500"
138 );
139}
140
141pub async fn check_read_stream_from_is_inclusive<S, C, F, Fut>(factory: &F)
143where
144 S: RawEventStore + WakeSource,
145 C: Send,
146 F: Fn() -> Fut + Send + Sync,
147 Fut: Future<Output = (S, C)> + Send,
148{
149 let (store, _guard) = factory().await;
150 let id = StreamKey::from_slice(b"inclusive");
151 let rows: Vec<_> = (1..=5u64)
152 .map(|v| ConformanceRow::new(v, "E", vec![]))
153 .collect();
154 append_rows(&store, &id, &rows).await;
155 let got = drain_stream(&store, &id, Version::new(3).expect("v3")).await;
156 let versions: Vec<u64> = got.iter().map(|r| r.version).collect();
157 assert_eq!(
158 versions,
159 vec![3, 4, 5],
160 "read_stream(from=3) is inclusive: yields 3,4,5"
161 );
162}
163
164pub async fn check_append_conflict_is_surfaced<S, C, F, Fut>(factory: &F)
167where
168 S: RawEventStore + WakeSource,
169 C: Send,
170 F: Fn() -> Fut + Send + Sync,
171 Fut: Future<Output = (S, C)> + Send,
172{
173 let (store, _guard) = factory().await;
174 let id = StreamKey::from_slice(b"conflict");
175 append_rows(
176 &store,
177 &id,
178 &[
179 ConformanceRow::new(1, "E", vec![1]),
180 ConformanceRow::new(2, "E", vec![2]),
181 ],
182 )
183 .await;
184
185 let env = envelope_for(&ConformanceRow::new(1, "E", vec![9]));
187 let err = store
188 .append(&id, None, &[env])
189 .await
190 .expect_err("appending with a stale expected_version must fail");
191 match err {
192 AppendError::Conflict { actual, .. } => {
193 assert_eq!(
194 actual,
195 Version::new(2),
196 "Conflict must carry the actual head (2)",
197 );
198 }
199 other => panic!("expected Conflict, got {other:?}"),
200 }
201
202 let got = drain_stream(&store, &id, Version::INITIAL).await;
203 assert_eq!(got.len(), 2, "a conflicted append must not land any event");
204 assert_eq!(got[0].payload, vec![1]);
205 assert_eq!(got[1].payload, vec![2]);
206}
207
208pub async fn check_append_retry_after_conflict_succeeds<S, C, F, Fut>(factory: &F)
211where
212 S: RawEventStore + WakeSource,
213 C: Send,
214 F: Fn() -> Fut + Send + Sync,
215 Fut: Future<Output = (S, C)> + Send,
216{
217 let (store, _guard) = factory().await;
218 let id = StreamKey::from_slice(b"retry");
219 append_rows(&store, &id, &[ConformanceRow::new(1, "E", vec![1])]).await;
220
221 let stale = envelope_for(&ConformanceRow::new(1, "E", vec![9]));
223 store
224 .append(&id, None, &[stale])
225 .await
226 .expect_err("stale append must conflict");
227
228 let retry = envelope_for(&ConformanceRow::new(2, "E", vec![2]));
230 store
231 .append(&id, Version::new(1), &[retry])
232 .await
233 .expect("retry with corrected expected_version must succeed");
234
235 let got = drain_stream(&store, &id, Version::INITIAL).await;
236 let versions: Vec<u64> = got.iter().map(|r| r.version).collect();
237 assert_eq!(versions, vec![1, 2], "retry lands exactly one new event");
238}
239
240pub async fn check_all_empty_store_yields_none<S, C, F, Fut>(factory: &F)
242where
243 S: RawEventStore + WakeSource,
244 C: Send,
245 F: Fn() -> Fut + Send + Sync,
246 Fut: Future<Output = (S, C)> + Send,
247{
248 let (store, _guard) = factory().await;
249 let got = drain_all(&store, None).await;
250 assert!(
251 got.is_empty(),
252 "empty store: read_all(None) must yield nothing"
253 );
254}
255
256pub async fn check_all_global_order_across_streams<S, C, F, Fut>(factory: &F)
259where
260 S: RawEventStore + WakeSource,
261 C: Send,
262 F: Fn() -> Fut + Send + Sync,
263 Fut: Future<Output = (S, C)> + Send,
264{
265 let (store, _guard) = factory().await;
266 let a = StreamKey::from_slice(b"a");
267 let b = StreamKey::from_slice(b"b");
268 append_event(&store, &a, 1, b"a1").await;
269 append_event(&store, &a, 2, b"a2").await;
270 append_event(&store, &b, 1, b"b1").await;
271 append_event(&store, &a, 3, b"a3").await;
272 append_event(&store, &a, 4, b"a4").await;
273
274 let got = drain_all(&store, None).await;
275 let payloads: Vec<Vec<u8>> = got.iter().map(|(_, p)| p.clone()).collect();
276 assert_eq!(
277 payloads,
278 vec![
279 b"a1".to_vec(),
280 b"a2".to_vec(),
281 b"b1".to_vec(),
282 b"a3".to_vec(),
283 b"a4".to_vec()
284 ],
285 "read_all(None) must yield every event across streams in append order",
286 );
287 assert_strictly_increasing(&got);
288}
289
290pub async fn check_all_from_is_exclusive<S, C, F, Fut>(factory: &F)
292where
293 S: RawEventStore + WakeSource,
294 C: Send,
295 F: Fn() -> Fut + Send + Sync,
296 Fut: Future<Output = (S, C)> + Send,
297{
298 let (store, _guard) = factory().await;
299 let a = StreamKey::from_slice(b"a");
300 append_event(&store, &a, 1, b"a1").await;
301 append_event(&store, &a, 2, b"a2").await;
302 append_event(&store, &a, 3, b"a3").await;
303
304 let full = drain_all(&store, None).await;
305 assert_eq!(full.len(), 3);
306 let checkpoint = full[0].0;
307
308 let rest = drain_all(&store, Some(checkpoint)).await;
309 let payloads: Vec<Vec<u8>> = rest.iter().map(|(_, p)| p.clone()).collect();
310 assert_eq!(
311 payloads,
312 vec![b"a2".to_vec(), b"a3".to_vec()],
313 "read_all(Some(p)) is EXCLUSIVE",
314 );
315 assert!(
316 rest[0].0 > checkpoint,
317 "resumed position must be strictly after checkpoint"
318 );
319}
320
321pub async fn check_all_multi_resume_cycles<S, C, F, Fut>(factory: &F)
324where
325 S: RawEventStore + WakeSource,
326 C: Send,
327 F: Fn() -> Fut + Send + Sync,
328 Fut: Future<Output = (S, C)> + Send,
329{
330 let (store, _guard) = factory().await;
331 let a = StreamKey::from_slice(b"a");
332 let b = StreamKey::from_slice(b"b");
333 let mut va = 0u64;
334 let mut vb = 0u64;
335 let mut expected: Vec<Vec<u8>> = Vec::new();
336 for i in 0..10u64 {
337 if i % 2 == 0 {
338 va += 1;
339 let p = format!("a{va}").into_bytes();
340 append_event(&store, &a, va, &p).await;
341 expected.push(p);
342 } else {
343 vb += 1;
344 let p = format!("b{vb}").into_bytes();
345 append_event(&store, &b, vb, &p).await;
346 expected.push(p);
347 }
348 }
349
350 let full = drain_all(&store, None).await;
351 let full_payloads: Vec<Vec<u8>> = full.iter().map(|(_, p)| p.clone()).collect();
352 assert_eq!(
353 full_payloads, expected,
354 "single-shot read_all(None) must match append order"
355 );
356
357 let mut acc: Vec<(S::AllPosition, Vec<u8>)> = Vec::new();
358 let mut checkpoint: Option<S::AllPosition> = None;
359 loop {
360 let stream = store
361 .read_all(checkpoint)
362 .await
363 .unwrap_or_else(|e| panic!("open read_all cycle failed: {e:?}"));
364 pin_mut!(stream);
365 let mut taken = 0;
366 let mut advanced = false;
367 while let Some(item) = stream.next().await {
368 let (pos, env) = item.unwrap_or_else(|e| panic!("cycle item errored: {e:?}"));
369 acc.push((pos, env.payload().to_vec()));
370 checkpoint = Some(pos);
371 advanced = true;
372 taken += 1;
373 if taken == 3 {
374 break;
375 }
376 }
377 if !advanced {
378 break;
379 }
380 }
381
382 let acc_payloads: Vec<Vec<u8>> = acc.iter().map(|(_, p)| p.clone()).collect();
383 assert_eq!(
384 acc_payloads, full_payloads,
385 "multi-resume cycles must reconstruct the full stream exactly",
386 );
387 assert_strictly_increasing(&acc);
388}
389
390pub async fn check_all_boundary_then_new_append<S, C, F, Fut>(factory: &F)
393where
394 S: RawEventStore + WakeSource,
395 C: Send,
396 F: Fn() -> Fut + Send + Sync,
397 Fut: Future<Output = (S, C)> + Send,
398{
399 let (store, _guard) = factory().await;
400 let a = StreamKey::from_slice(b"a");
401 let b = StreamKey::from_slice(b"b");
402 append_event(&store, &a, 1, b"a1").await;
403 append_event(&store, &b, 1, b"b1").await;
404
405 let full = drain_all(&store, None).await;
406 assert_eq!(full.len(), 2);
407 let last = full.last().expect("non-empty").0;
408
409 let empty = drain_all(&store, Some(last)).await;
410 assert!(
411 empty.is_empty(),
412 "nothing is strictly after the last position"
413 );
414
415 append_event(&store, &a, 2, b"a2").await;
416 let after = drain_all(&store, Some(last)).await;
417 let payloads: Vec<Vec<u8>> = after.iter().map(|(_, p)| p.clone()).collect();
418 assert_eq!(
419 payloads,
420 vec![b"a2".to_vec()],
421 "same checkpoint surfaces exactly the new event"
422 );
423 assert!(
424 after[0].0 > last,
425 "new position must be strictly after the prior last"
426 );
427}
428
429pub async fn check_read_stream_inclusive_read_all_exclusive_coexist<S, C, F, Fut>(factory: &F)
432where
433 S: RawEventStore + WakeSource,
434 C: Send,
435 F: Fn() -> Fut + Send + Sync,
436 Fut: Future<Output = (S, C)> + Send,
437{
438 let (store, _guard) = factory().await;
439 let a = StreamKey::from_slice(b"a");
440 append_event(&store, &a, 1, b"a1").await;
441 append_event(&store, &a, 2, b"a2").await;
442 append_event(&store, &a, 3, b"a3").await;
443
444 let got = drain_stream(&store, &a, Version::new(2).expect("v2")).await;
445 let versions: Vec<u64> = got.iter().map(|r| r.version).collect();
446 assert_eq!(versions, vec![2, 3], "read_stream(from=2) is INCLUSIVE");
447
448 let full = drain_all(&store, None).await;
449 assert_eq!(full.len(), 3);
450 let pos_of_a2 = full[1].0;
451 let after = drain_all(&store, Some(pos_of_a2)).await;
452 let payloads: Vec<Vec<u8>> = after.iter().map(|(_, p)| p.clone()).collect();
453 assert_eq!(
454 payloads,
455 vec![b"a3".to_vec()],
456 "read_all(from=pos(a2)) is EXCLUSIVE"
457 );
458}
459
460async fn next_step<St, T, E>(stream: &mut core::pin::Pin<&mut St>, what: &str) -> Step<T>
463where
464 St: futures::Stream<Item = Result<Step<T>, E>>,
465 E: core::fmt::Debug,
466{
467 timeout(WAIT, stream.next())
468 .await
469 .unwrap_or_else(|_| panic!("{what}: subscription hung (lost wake?)"))
470 .unwrap_or_else(|| panic!("{what}: subscription ended (must never return None)"))
471 .unwrap_or_else(|e| panic!("{what}: subscription item errored: {e:?}"))
472}
473
474pub async fn check_subscription_backlog_then_caught_up_then_live<S, C, F, Fut>(factory: &F)
477where
478 S: RawEventStore + WakeSource,
479 S::Stream: Unpin,
480 C: Send,
481 F: Fn() -> Fut + Send + Sync,
482 Fut: Future<Output = (S, C)> + Send,
483{
484 let (raw, _guard) = factory().await;
485 let store = raw.into_store();
486 let id = SubId::new("sub-proto");
487 for v in 1..=3u64 {
488 append_event(&store, &id.key(), v, format!("p{v}").as_bytes()).await;
489 }
490
491 let sub = Subscription::new(&store);
492 let stream = sub
493 .subscribe(&id, None)
494 .unwrap_or_else(|e| panic!("register failed: {e:?}"));
495 pin_mut!(stream);
496
497 for want in 1..=3u64 {
498 match next_step(&mut stream, "backlog").await {
499 Step::Event(env) => assert_eq!(
500 env.version().as_u64(),
501 want,
502 "backlog must replay in version order",
503 ),
504 Step::CaughtUp => panic!("CaughtUp before the backlog drained (at v{want})"),
505 }
506 }
507 match next_step(&mut stream, "boundary").await {
508 Step::CaughtUp => {}
509 Step::Event(env) => panic!(
510 "expected CaughtUp after backlog, got Event v{}",
511 env.version()
512 ),
513 }
514
515 append_event(&store, &id.key(), 4, b"p4").await;
517 match next_step(&mut stream, "live").await {
518 Step::Event(env) => assert_eq!(env.version().as_u64(), 4, "live event must be v4"),
519 Step::CaughtUp => panic!("CaughtUp must be emitted exactly once"),
520 }
521}
522
523pub async fn check_subscription_resume_strict_after<S, C, F, Fut>(factory: &F)
526where
527 S: RawEventStore + WakeSource,
528 S::Stream: Unpin,
529 C: Send,
530 F: Fn() -> Fut + Send + Sync,
531 Fut: Future<Output = (S, C)> + Send,
532{
533 let (raw, _guard) = factory().await;
534 let store = raw.into_store();
535 let id = SubId::new("sub-resume");
536 for v in 1..=5u64 {
537 append_event(&store, &id.key(), v, b"p").await;
538 }
539
540 let sub = Subscription::new(&store);
541 let stream = sub
542 .subscribe(&id, Some(Version::new(3).expect("v3")))
543 .unwrap_or_else(|e| panic!("register failed: {e:?}"));
544 pin_mut!(stream);
545
546 match next_step(&mut stream, "resume").await {
547 Step::Event(env) => assert_eq!(
548 env.version().as_u64(),
549 4,
550 "resume from Some(3) must deliver v4 first (strict-after, no dup)",
551 ),
552 Step::CaughtUp => panic!("expected v4 before CaughtUp"),
553 }
554}
555
556pub async fn check_subscription_all_backlog_then_caught_up_then_live<S, C, F, Fut>(factory: &F)
559where
560 S: RawEventStore + WakeSource,
561 S::AllStream: Unpin,
562 C: Send,
563 F: Fn() -> Fut + Send + Sync,
564 Fut: Future<Output = (S, C)> + Send,
565{
566 let (raw, _guard) = factory().await;
567 let store = raw.into_store();
568 let a = StreamKey::from_slice(b"a");
569 let b = StreamKey::from_slice(b"b");
570 append_event(&store, &a, 1, b"a1").await;
571 append_event(&store, &b, 1, b"b1").await;
572 append_event(&store, &a, 2, b"a2").await;
573
574 let sub = Subscription::new(&store);
575 let stream = sub
576 .subscribe_all(None)
577 .unwrap_or_else(|e| panic!("register failed: {e:?}"));
578 pin_mut!(stream);
579
580 let mut backlog: Vec<(S::AllPosition, Vec<u8>)> = Vec::new();
581 while let Step::Event((pos, env)) = next_step(&mut stream, "all backlog").await {
582 backlog.push((pos, env.payload().to_vec()));
583 }
584 let payloads: Vec<Vec<u8>> = backlog.iter().map(|(_, p)| p.clone()).collect();
585 assert_eq!(
586 payloads,
587 vec![b"a1".to_vec(), b"b1".to_vec(), b"a2".to_vec()],
588 "$all backlog must replay in position order",
589 );
590 assert_strictly_increasing(&backlog);
591 let last = backlog.last().expect("non-empty").0;
592
593 append_event(&store, &b, 2, b"b2").await;
594 match next_step(&mut stream, "all live").await {
595 Step::Event((pos, env)) => {
596 assert_eq!(
597 env.payload(),
598 b"b2",
599 "live $all event must be the new append"
600 );
601 assert!(
602 pos > last,
603 "live position must be strictly after the backlog"
604 );
605 }
606 Step::CaughtUp => panic!("CaughtUp must be emitted exactly once"),
607 }
608}
609
610pub async fn check_subscription_absent_stream_waits_then_delivers<S, C, F, Fut>(factory: &F)
614where
615 S: RawEventStore + WakeSource,
616 S::Stream: Unpin,
617 C: Send,
618 F: Fn() -> Fut + Send + Sync,
619 Fut: Future<Output = (S, C)> + Send,
620{
621 let (raw, _guard) = factory().await;
622 let store = raw.into_store();
623 let id = SubId::new("ghost");
624
625 let sub = Subscription::new(&store);
626 let stream = sub
627 .subscribe(&id, None)
628 .unwrap_or_else(|e| panic!("register failed: {e:?}"));
629 pin_mut!(stream);
630
631 match next_step(&mut stream, "absent-stream boundary").await {
633 Step::CaughtUp => {}
634 Step::Event(env) => panic!("absent stream must have no backlog, got v{}", env.version()),
635 }
636
637 append_event(&store, &id.key(), 1, b"first").await;
639 match next_step(&mut stream, "absent-stream first event").await {
640 Step::Event(env) => {
641 assert_eq!(env.version().as_u64(), 1, "the first event must be v1");
642 assert_eq!(env.payload(), b"first");
643 }
644 Step::CaughtUp => panic!("CaughtUp must be emitted exactly once"),
645 }
646}
647
648pub async fn check_two_subscribers_same_stream_both_receive<S, C, F, Fut>(factory: &F)
651where
652 S: RawEventStore + WakeSource,
653 S::Stream: Unpin,
654 C: Send,
655 F: Fn() -> Fut + Send + Sync,
656 Fut: Future<Output = (S, C)> + Send,
657{
658 let (raw, _guard) = factory().await;
659 let store = raw.into_store();
660 let id = SubId::new("fanout");
661 append_event(&store, &id.key(), 1, b"p1").await;
662
663 let sub = Subscription::new(&store);
664 let stream_a = sub
665 .subscribe(&id, None)
666 .unwrap_or_else(|e| panic!("register a failed: {e:?}"));
667 let stream_b = sub
668 .subscribe(&id, None)
669 .unwrap_or_else(|e| panic!("register b failed: {e:?}"));
670 pin_mut!(stream_a);
671 pin_mut!(stream_b);
672
673 match next_step(&mut stream_a, "fanout backlog a").await {
675 Step::Event(env) => assert_eq!(env.version().as_u64(), 1, "subscriber a backlog"),
676 Step::CaughtUp => panic!("subscriber a: CaughtUp before backlog"),
677 }
678 match next_step(&mut stream_a, "fanout boundary a").await {
679 Step::CaughtUp => {}
680 Step::Event(env) => panic!("subscriber a: expected CaughtUp, got v{}", env.version()),
681 }
682 match next_step(&mut stream_b, "fanout backlog b").await {
683 Step::Event(env) => assert_eq!(env.version().as_u64(), 1, "subscriber b backlog"),
684 Step::CaughtUp => panic!("subscriber b: CaughtUp before backlog"),
685 }
686 match next_step(&mut stream_b, "fanout boundary b").await {
687 Step::CaughtUp => {}
688 Step::Event(env) => panic!("subscriber b: expected CaughtUp, got v{}", env.version()),
689 }
690
691 append_event(&store, &id.key(), 2, b"p2").await;
693 match next_step(&mut stream_a, "fanout live a").await {
694 Step::Event(env) => assert_eq!(
695 env.version().as_u64(),
696 2,
697 "subscriber a must receive the live event — fan-out, not a queue",
698 ),
699 Step::CaughtUp => panic!("subscriber a: CaughtUp must be emitted exactly once"),
700 }
701 match next_step(&mut stream_b, "fanout live b").await {
702 Step::Event(env) => assert_eq!(
703 env.version().as_u64(),
704 2,
705 "subscriber b must receive the live event — fan-out, not a queue",
706 ),
707 Step::CaughtUp => panic!("subscriber b: CaughtUp must be emitted exactly once"),
708 }
709}
710
711pub async fn check_subscription_large_backlog_crosses_chunk_seam<S, C, F, Fut>(factory: &F)
714where
715 S: RawEventStore + WakeSource,
716 S::Stream: Unpin,
717 C: Send,
718 F: Fn() -> Fut + Send + Sync,
719 Fut: Future<Output = (S, C)> + Send,
720{
721 const N: u64 = 2500; let (raw, _guard) = factory().await;
723 let store = raw.into_store();
724 let id = SubId::new("sub-chunk");
725 let rows: Vec<_> = (1..=N)
726 .map(|v| ConformanceRow::new(v, "E", vec![]))
727 .collect();
728 append_rows(&store, &id.key(), &rows).await;
729
730 let sub = Subscription::new(&store);
731 let stream = sub
732 .subscribe(&id, None)
733 .unwrap_or_else(|e| panic!("register failed: {e:?}"));
734 pin_mut!(stream);
735
736 let mut versions = Vec::with_capacity(usize::try_from(N).unwrap_or(usize::MAX));
737 while let Step::Event(env) = next_step(&mut stream, "chunk backlog").await {
738 versions.push(env.version().as_u64());
739 }
740 let want: Vec<u64> = (1..=N).collect();
741 assert_eq!(
742 versions, want,
743 "backlog across chunk seams must be exactly 1..=N — no gap, no duplicate",
744 );
745}
746
747pub async fn check_subscription_beyond_head_filters_below_bound<S, C, F, Fut>(factory: &F)
755where
756 S: RawEventStore + WakeSource,
757 S::Stream: Unpin,
758 C: Send,
759 F: Fn() -> Fut + Send + Sync,
760 Fut: Future<Output = (S, C)> + Send,
761{
762 let (raw, _guard) = factory().await;
763 let store = raw.into_store();
764 let id = SubId::new("beyond-head");
765 append_event(&store, &id.key(), 1, b"p1").await;
766 append_event(&store, &id.key(), 2, b"p2").await;
767
768 let sub = Subscription::new(&store);
770 let stream = sub
771 .subscribe(&id, Some(Version::new(5).expect("v5")))
772 .unwrap_or_else(|e| panic!("register failed: {e:?}"));
773 pin_mut!(stream);
774 match next_step(&mut stream, "beyond-head boundary").await {
775 Step::CaughtUp => {}
776 Step::Event(env) => panic!(
777 "subscribing beyond the head must have an empty backlog, got v{}",
778 env.version(),
779 ),
780 }
781
782 for v in 3..=6u64 {
785 append_event(&store, &id.key(), v, format!("p{v}").as_bytes()).await;
786 }
787 match next_step(&mut stream, "beyond-head first delivery").await {
788 Step::Event(env) => assert_eq!(
789 env.version().as_u64(),
790 6,
791 "the first delivered event must be from+1 (6) — below-bound live appends must be filtered, never delivered",
792 ),
793 Step::CaughtUp => panic!("CaughtUp must be emitted exactly once"),
794 }
795}