1use std::collections::HashMap;
18use std::sync::Arc;
19
20use launchdarkly_server_sdk_evaluation::{Flag, FlagBuilder, Segment};
21use parking_lot::{Mutex, RwLock};
22use tokio::sync::{broadcast, mpsc};
23
24use crate::data_source::DataSource;
25use crate::data_source_builders::{BuildError, DataSourceFactory};
26use crate::data_system_builders::{
27 BuildError as DataSystemBuildError, DataSourceBuildContext, FDv2SynchronizerConfig,
28};
29use crate::fdv2::data_system::SynchronizerFactory;
30use crate::fdv2::model::{ChangeSetKind, Selector};
31use crate::fdv2::source::{FDv2SourceEvent, FDv2SourceEventFuture, FDv2SourceResult, Synchronizer};
32use crate::service_endpoints;
33use crate::stores::change_set::{ChangeSet, ItemChange};
34use crate::stores::store::DataStore;
35use crate::stores::store_types::{AllData, PatchTarget, StorageItem};
36
37enum FlagOrigin {
39 Builder(FlagBuilder),
40 Preconfigured,
41}
42
43struct TestDataInner {
44 flag_origins: HashMap<String, FlagOrigin>,
45 current_flags: HashMap<String, Flag>,
46 flag_versions: HashMap<String, u64>,
47 current_segments: HashMap<String, Segment>,
48 segment_versions: HashMap<String, u64>,
49 instances: Vec<Arc<RwLock<dyn DataStore>>>,
50 fdv2_senders: Vec<mpsc::UnboundedSender<ChangeSet>>,
51}
52
53impl TestDataInner {
54 fn broadcast_flag(&mut self, flag: &Flag) {
56 self.broadcast(ItemChange::Flag {
57 key: flag.key.clone(),
58 item: StorageItem::Item(flag.clone()),
59 });
60 }
61
62 fn broadcast_segment(&mut self, segment: &Segment) {
64 self.broadcast(ItemChange::Segment {
65 key: segment.key.clone(),
66 item: StorageItem::Item(segment.clone()),
67 });
68 }
69
70 fn broadcast(&mut self, change: ItemChange) {
73 self.fdv2_senders.retain(|sender| {
74 let change_set = ChangeSet {
75 kind: ChangeSetKind::Partial,
76 changes: vec![change.clone()],
77 selector: None,
78 };
79 sender.send(change_set).is_ok()
80 });
81 }
82}
83
84#[derive(Clone)]
100pub struct TestData {
101 inner: Arc<Mutex<TestDataInner>>,
102}
103
104impl TestData {
105 pub fn new() -> Self {
107 Self {
108 inner: Arc::new(Mutex::new(TestDataInner {
109 flag_origins: HashMap::new(),
110 current_flags: HashMap::new(),
111 flag_versions: HashMap::new(),
112 current_segments: HashMap::new(),
113 segment_versions: HashMap::new(),
114 instances: Vec::new(),
115 fdv2_senders: Vec::new(),
116 })),
117 }
118 }
119
120 pub fn flag(&self, key: &str) -> FlagBuilder {
128 let inner = self.inner.lock();
129 match inner.flag_origins.get(key) {
130 Some(FlagOrigin::Builder(builder)) => builder.clone(),
131 _ => FlagBuilder::new(key),
132 }
133 }
134
135 pub fn update(&self, builder: FlagBuilder) {
140 let mut inner = self.inner.lock();
141
142 let key = builder.key().to_owned();
143 let stored_builder = builder.clone();
144 let mut flag = builder.build();
145
146 let version = inner.flag_versions.entry(key.clone()).or_insert(0);
147 *version += 1;
148 flag.version = *version;
149
150 inner
151 .flag_origins
152 .insert(key.clone(), FlagOrigin::Builder(stored_builder));
153 inner.current_flags.insert(key.clone(), flag.clone());
154
155 for store in &inner.instances {
156 let mut store = store.write();
157 let _ = store.upsert(&key, PatchTarget::Flag(StorageItem::Item(flag.clone())));
158 }
159
160 inner.broadcast_flag(&flag);
161 }
162
163 pub fn use_preconfigured_flag(&self, mut flag: Flag) {
171 let mut inner = self.inner.lock();
172
173 let key = flag.key.clone();
174 let version = inner.flag_versions.entry(key.clone()).or_insert(0);
175 *version += 1;
176 flag.version = *version;
177
178 inner
179 .flag_origins
180 .insert(key.clone(), FlagOrigin::Preconfigured);
181 inner.current_flags.insert(key.clone(), flag.clone());
182
183 for store in &inner.instances {
184 let mut store = store.write();
185 let _ = store.upsert(&key, PatchTarget::Flag(StorageItem::Item(flag.clone())));
186 }
187
188 inner.broadcast_flag(&flag);
189 }
190
191 pub fn use_preconfigured_segment(&self, mut segment: Segment) {
195 let mut inner = self.inner.lock();
196
197 let key = segment.key.clone();
198 let version = inner.segment_versions.entry(key.clone()).or_insert(0);
199 *version += 1;
200 segment.version = *version;
201
202 inner.current_segments.insert(key.clone(), segment.clone());
203
204 for store in &inner.instances {
205 let mut store = store.write();
206 let _ = store.upsert(
207 &key,
208 PatchTarget::Segment(StorageItem::Item(segment.clone())),
209 );
210 }
211
212 inner.broadcast_segment(&segment);
213 }
214}
215
216impl Default for TestData {
217 fn default() -> Self {
218 Self::new()
219 }
220}
221
222impl DataSourceFactory for TestData {
223 fn build(
224 &self,
225 _endpoints: &service_endpoints::ServiceEndpoints,
226 _sdk_key: &str,
227 _tags: Option<String>,
228 ) -> Result<Arc<dyn DataSource>, BuildError> {
229 Ok(Arc::new(TestDataSource {
230 inner: self.inner.clone(),
231 }))
232 }
233
234 fn to_owned(&self) -> Box<dyn DataSourceFactory> {
235 Box::new(self.clone())
236 }
237}
238
239struct TestDataSource {
240 inner: Arc<Mutex<TestDataInner>>,
241}
242
243impl DataSource for TestDataSource {
244 fn subscribe(
245 &self,
246 data_store: Arc<RwLock<dyn DataStore>>,
247 init_complete: Arc<dyn Fn(bool) + Send + Sync>,
248 shutdown_receiver: broadcast::Receiver<()>,
249 ) {
250 let mut inner = self.inner.lock();
251
252 let all_data = AllData {
254 flags: inner.current_flags.clone(),
255 segments: inner.current_segments.clone(),
256 };
257
258 {
259 let mut store = data_store.write();
260 store.init(all_data);
261 }
262
263 inner.instances.push(data_store.clone());
265
266 (init_complete)(true);
267
268 let inner_ref = self.inner.clone();
270 let store_ref = data_store.clone();
271 tokio::spawn(async move {
272 let mut shutdown = shutdown_receiver;
273 let _ = shutdown.recv().await;
274 let mut inner = inner_ref.lock();
275 inner.instances.retain(|s| !Arc::ptr_eq(s, &store_ref));
276 });
277 }
278}
279
280impl FDv2SynchronizerConfig for TestData {
281 fn build_synchronizer(
282 &self,
283 _context: &DataSourceBuildContext,
284 ) -> Result<Box<dyn SynchronizerFactory>, DataSystemBuildError> {
285 Ok(Box::new(TestDataSynchronizerFactory {
286 inner: self.inner.clone(),
287 }))
288 }
289
290 fn to_owned(&self) -> Box<dyn FDv2SynchronizerConfig> {
291 Box::new(self.clone())
292 }
293}
294
295struct TestDataSynchronizerFactory {
296 inner: Arc<Mutex<TestDataInner>>,
297}
298
299impl SynchronizerFactory for TestDataSynchronizerFactory {
300 fn create(&self) -> Box<dyn Synchronizer> {
301 let mut inner = self.inner.lock();
302
303 let mut changes = Vec::new();
305 for flag in inner.current_flags.values() {
306 changes.push(ItemChange::Flag {
307 key: flag.key.clone(),
308 item: StorageItem::Item(flag.clone()),
309 });
310 }
311 for segment in inner.current_segments.values() {
312 changes.push(ItemChange::Segment {
313 key: segment.key.clone(),
314 item: StorageItem::Item(segment.clone()),
315 });
316 }
317 let initial = ChangeSet {
318 kind: ChangeSetKind::Full,
319 changes,
320 selector: None,
321 };
322
323 let (sender, receiver) = mpsc::unbounded_channel();
325 inner.fdv2_senders.push(sender);
326
327 Box::new(TestDataSynchronizer {
328 initial: Some(initial),
329 receiver,
330 })
331 }
332}
333
334struct TestDataSynchronizer {
335 initial: Option<ChangeSet>,
336 receiver: mpsc::UnboundedReceiver<ChangeSet>,
337}
338
339impl Synchronizer for TestDataSynchronizer {
340 fn next(&mut self, _selector: Selector) -> FDv2SourceEventFuture<'_> {
341 if let Some(change_set) = self.initial.take() {
342 return Box::pin(async move {
343 FDv2SourceEvent {
344 result: FDv2SourceResult::ChangeSet(change_set),
345 fdv1_fallback: None,
346 }
347 });
348 }
349 Box::pin(async move {
350 match self.receiver.recv().await {
351 Some(change_set) => FDv2SourceEvent {
352 result: FDv2SourceResult::ChangeSet(change_set),
353 fdv1_fallback: None,
354 },
355 None => std::future::pending().await,
357 }
358 })
359 }
360
361 fn name(&self) -> &str {
362 "test-data"
363 }
364}
365
366#[cfg(test)]
367mod tests {
368 use super::*;
369 use crate::stores::store::InMemoryDataStore;
370 use launchdarkly_server_sdk_evaluation::FlagBuilder;
371 use std::sync::atomic::{AtomicBool, Ordering};
372
373 fn make_store() -> Arc<RwLock<dyn DataStore>> {
374 Arc::new(RwLock::new(InMemoryDataStore::new()))
375 }
376
377 fn subscribe_store(td: &TestData, store: &Arc<RwLock<dyn DataStore>>) -> broadcast::Sender<()> {
378 let factory: &dyn DataSourceFactory = td;
379 let endpoints = crate::ServiceEndpointsBuilder::new().build().unwrap();
380 let ds = factory.build(&endpoints, "fake-key", None).unwrap();
381
382 let (shutdown_tx, shutdown_rx) = broadcast::channel(1);
383 ds.subscribe(store.clone(), Arc::new(|_| {}), shutdown_rx);
384 shutdown_tx
385 }
386
387 #[test]
388 fn flag_returns_default_builder_for_unknown_key() {
389 let td = TestData::new();
390 let builder = td.flag("unknown");
391 assert_eq!(builder.key(), "unknown");
392 let flag = builder.build();
393 assert_eq!(flag.key, "unknown");
394 }
395
396 #[test]
397 fn flag_returns_cloned_builder_after_update() {
398 let td = TestData::new();
399 td.update(FlagBuilder::new("my-flag").variation_for_all(false));
400 let builder = td.flag("my-flag");
401 assert_eq!(builder.key(), "my-flag");
402 }
403
404 #[test]
405 fn flag_returns_default_builder_for_preconfigured_flag() {
406 let td = TestData::new();
407 let flag = FlagBuilder::new("preconf").build();
408 td.use_preconfigured_flag(flag);
409 let builder = td.flag("preconf");
410 assert_eq!(builder.key(), "preconf");
411 }
412
413 #[test]
414 fn update_increments_version_each_call() {
415 let td = TestData::new();
416 td.update(FlagBuilder::new("my-flag"));
417 td.update(FlagBuilder::new("my-flag"));
418 td.update(FlagBuilder::new("my-flag"));
419
420 let inner = td.inner.lock();
421 let flag = inner.current_flags.get("my-flag").unwrap();
422 assert_eq!(flag.version, 3);
423 }
424
425 #[test]
426 fn use_preconfigured_flag_increments_version_each_call() {
427 let td = TestData::new();
428 td.use_preconfigured_flag(FlagBuilder::new("my-flag").build());
429 td.use_preconfigured_flag(FlagBuilder::new("my-flag").build());
430 td.use_preconfigured_flag(FlagBuilder::new("my-flag").build());
431
432 let inner = td.inner.lock();
433 let flag = inner.current_flags.get("my-flag").unwrap();
434 assert_eq!(flag.version, 3);
435 }
436
437 #[test]
438 fn flag_builder_increments_version_each_call() {
439 let td = TestData::new();
440 td.update(td.flag("my-flag").variation_for_all(true));
441 td.update(td.flag("my-flag").variation_for_all(false));
442 td.update(td.flag("my-flag").variation_for_all(true));
443
444 let inner = td.inner.lock();
445 let flag = inner.current_flags.get("my-flag").unwrap();
446 assert_eq!(flag.version, 3);
447 }
448
449 #[tokio::test]
450 async fn update_propagates_to_connected_store() {
451 let td = TestData::new();
452 let store = make_store();
453 let _shutdown = subscribe_store(&td, &store);
454
455 td.update(FlagBuilder::new("my-flag").variation_for_all(true));
456
457 let s = store.read();
458 let flag = s.flag("my-flag").unwrap();
459 assert_eq!(flag.key, "my-flag");
460 }
461
462 #[tokio::test]
463 async fn subscribe_initializes_store_with_all_current_data() {
464 let td = TestData::new();
465 td.update(FlagBuilder::new("flag-1").variation_for_all(true));
466 td.update(FlagBuilder::new("flag-2").variation_for_all(false));
467
468 let store = make_store();
469 let _shutdown = subscribe_store(&td, &store);
470
471 let s = store.read();
472 assert!(s.flag("flag-1").is_some());
473 assert!(s.flag("flag-2").is_some());
474 }
475
476 #[tokio::test]
477 async fn subscribe_calls_init_complete_true() {
478 let td = TestData::new();
479 let store = make_store();
480
481 let factory: &dyn DataSourceFactory = &td;
482 let endpoints = crate::ServiceEndpointsBuilder::new().build().unwrap();
483 let ds = factory.build(&endpoints, "fake-key", None).unwrap();
484
485 let initialized = Arc::new(AtomicBool::new(false));
486 let init_clone = initialized.clone();
487 let (_shutdown_tx, shutdown_rx) = broadcast::channel(1);
488
489 ds.subscribe(
490 store,
491 Arc::new(move |success| init_clone.store(success, Ordering::SeqCst)),
492 shutdown_rx,
493 );
494
495 assert!(initialized.load(Ordering::SeqCst));
496 }
497
498 #[tokio::test]
499 async fn multiple_stores_receive_updates() {
500 let td = TestData::new();
501 let store1 = make_store();
502 let store2 = make_store();
503 let _shutdown1 = subscribe_store(&td, &store1);
504 let _shutdown2 = subscribe_store(&td, &store2);
505
506 td.update(FlagBuilder::new("shared-flag"));
507
508 assert!(store1.read().flag("shared-flag").is_some());
509 assert!(store2.read().flag("shared-flag").is_some());
510 }
511
512 #[tokio::test]
513 async fn use_preconfigured_flag_propagates() {
514 let td = TestData::new();
515 let store = make_store();
516 let _shutdown = subscribe_store(&td, &store);
517
518 let flag = FlagBuilder::new("preconf").variation_for_all(true).build();
519 td.use_preconfigured_flag(flag);
520
521 let s = store.read();
522 let stored_flag = s.flag("preconf").unwrap();
523 assert_eq!(stored_flag.version, 1);
524 }
525
526 #[tokio::test]
527 async fn use_preconfigured_segment_propagates() {
528 let td = TestData::new();
529 let store = make_store();
530 let _shutdown = subscribe_store(&td, &store);
531
532 let segment: Segment = serde_json::from_str(
533 r#"{
534 "key": "seg-1",
535 "included": ["alice"],
536 "excluded": [],
537 "rules": [],
538 "salt": "salty",
539 "version": 999
540 }"#,
541 )
542 .unwrap();
543
544 td.use_preconfigured_segment(segment);
545
546 let s = store.read();
547 let stored = s.segment("seg-1").unwrap();
548 assert_eq!(stored.version, 1); }
550
551 #[test]
552 fn version_counters_are_independent_per_flag() {
553 let td = TestData::new();
554 td.update(FlagBuilder::new("a"));
555 td.update(FlagBuilder::new("a"));
556 td.update(FlagBuilder::new("b"));
557
558 let inner = td.inner.lock();
559 assert_eq!(inner.current_flags.get("a").unwrap().version, 2);
560 assert_eq!(inner.current_flags.get("b").unwrap().version, 1);
561 }
562
563 #[tokio::test]
564 async fn data_source_factory_build_returns_working_data_source() {
565 let td = TestData::new();
566 td.update(FlagBuilder::new("factory-flag"));
567
568 let factory: &dyn DataSourceFactory = &td;
569 let endpoints = crate::ServiceEndpointsBuilder::new().build().unwrap();
570 let ds = factory.build(&endpoints, "key", None).unwrap();
571
572 let store = make_store();
573 let (_tx, rx) = broadcast::channel(1);
574 ds.subscribe(store.clone(), Arc::new(|_| {}), rx);
575
576 assert!(store.read().flag("factory-flag").is_some());
577 }
578
579 #[tokio::test]
580 async fn data_source_factory_to_owned_shares_state() {
581 let td = TestData::new();
582 let owned = DataSourceFactory::to_owned(&td);
583
584 td.update(FlagBuilder::new("shared-state"));
585
586 let endpoints = crate::ServiceEndpointsBuilder::new().build().unwrap();
587 let ds = owned.build(&endpoints, "key", None).unwrap();
588
589 let store = make_store();
590 let (_tx, rx) = broadcast::channel(1);
591 ds.subscribe(store.clone(), Arc::new(|_| {}), rx);
592
593 assert!(store.read().flag("shared-state").is_some());
594 }
595
596 #[tokio::test]
597 async fn shutdown_unregisters_store() {
598 let td = TestData::new();
599 let store = make_store();
600 let shutdown_tx = subscribe_store(&td, &store);
601
602 assert_eq!(td.inner.lock().instances.len(), 1);
603
604 let _ = shutdown_tx.send(());
605 tokio::time::sleep(std::time::Duration::from_millis(50)).await;
607
608 assert_eq!(td.inner.lock().instances.len(), 0);
609 }
610
611 fn build_fdv2_synchronizer(td: &TestData) -> Box<dyn crate::fdv2::source::Synchronizer> {
612 use crate::fdv2::request_headers::RequestHeaders;
613
614 let endpoints = crate::ServiceEndpointsBuilder::new().build().unwrap();
615 let headers = RequestHeaders::new("fake-key", None, "test-instance");
616 let context = DataSourceBuildContext {
617 endpoints: &endpoints,
618 headers: &headers,
619 };
620 FDv2SynchronizerConfig::build_synchronizer(td, &context)
621 .unwrap()
622 .create()
623 }
624
625 fn flag_keys(cs: &crate::stores::change_set::ChangeSet) -> Vec<&str> {
626 cs.changes
627 .iter()
628 .filter_map(|c| match c {
629 crate::stores::change_set::ItemChange::Flag { key, .. } => Some(key.as_str()),
630 _ => None,
631 })
632 .collect()
633 }
634
635 #[tokio::test]
636 async fn fdv2_synchronizer_emits_full_basis_then_partials() {
637 use crate::fdv2::model::ChangeSetKind;
638 use crate::fdv2::source::FDv2SourceResult;
639
640 let td = TestData::new();
641 td.update(FlagBuilder::new("flag-1").variation_for_all(true));
642
643 let mut sync = build_fdv2_synchronizer(&td);
644
645 let event = sync.next(None).await;
647 match event.result {
648 FDv2SourceResult::ChangeSet(cs) => {
649 assert_eq!(cs.kind, ChangeSetKind::Full);
650 assert_eq!(flag_keys(&cs), vec!["flag-1"]);
651 }
652 other => panic!("expected a change set, got {other:?}"),
653 }
654
655 td.update(FlagBuilder::new("flag-2").variation_for_all(false));
657 let event = sync.next(None).await;
658 match event.result {
659 FDv2SourceResult::ChangeSet(cs) => {
660 assert_eq!(cs.kind, ChangeSetKind::Partial);
661 assert_eq!(flag_keys(&cs), vec!["flag-2"]);
662 }
663 other => panic!("expected a change set, got {other:?}"),
664 }
665 }
666}