1use crate::{
6 models::{PaginationQuery, PluginModel},
7 repositories::{PaginatedResult, PluginRepositoryTrait, RepositoryError},
8};
9
10use async_trait::async_trait;
11
12use std::collections::HashMap;
13use tokio::sync::{Mutex, MutexGuard};
14
15#[derive(Debug, Clone)]
17struct CompiledCodeEntry {
18 code: String,
19 source_hash: Option<String>,
20}
21
22#[derive(Debug)]
23pub struct InMemoryPluginRepository {
24 store: Mutex<HashMap<String, PluginModel>>,
25 compiled_cache: Mutex<HashMap<String, CompiledCodeEntry>>,
26}
27
28impl Clone for InMemoryPluginRepository {
29 fn clone(&self) -> Self {
30 let data = self
32 .store
33 .try_lock()
34 .map(|guard| guard.clone())
35 .unwrap_or_else(|_| HashMap::new());
36
37 let compiled = self
38 .compiled_cache
39 .try_lock()
40 .map(|guard| guard.clone())
41 .unwrap_or_else(|_| HashMap::new());
42
43 Self {
44 store: Mutex::new(data),
45 compiled_cache: Mutex::new(compiled),
46 }
47 }
48}
49
50impl InMemoryPluginRepository {
51 pub fn new() -> Self {
52 Self {
53 store: Mutex::new(HashMap::new()),
54 compiled_cache: Mutex::new(HashMap::new()),
55 }
56 }
57
58 pub async fn get_by_id(&self, id: &str) -> Result<Option<PluginModel>, RepositoryError> {
59 let store = Self::acquire_lock(&self.store).await?;
60 Ok(store.get(id).cloned())
61 }
62
63 async fn acquire_lock<T>(lock: &Mutex<T>) -> Result<MutexGuard<T>, RepositoryError> {
64 Ok(lock.lock().await)
65 }
66}
67
68impl Default for InMemoryPluginRepository {
69 fn default() -> Self {
70 Self::new()
71 }
72}
73
74#[async_trait]
75impl PluginRepositoryTrait for InMemoryPluginRepository {
76 async fn get_by_id(&self, id: &str) -> Result<Option<PluginModel>, RepositoryError> {
77 let store = Self::acquire_lock(&self.store).await?;
78 Ok(store.get(id).cloned())
79 }
80
81 async fn add(&self, plugin: PluginModel) -> Result<(), RepositoryError> {
82 let mut store = Self::acquire_lock(&self.store).await?;
83 store.insert(plugin.id.clone(), plugin);
84 Ok(())
85 }
86
87 async fn update(&self, plugin: PluginModel) -> Result<PluginModel, RepositoryError> {
88 let mut store = Self::acquire_lock(&self.store).await?;
89 if !store.contains_key(&plugin.id) {
90 return Err(RepositoryError::NotFound(format!(
91 "Plugin with id {} not found",
92 plugin.id
93 )));
94 }
95 store.insert(plugin.id.clone(), plugin.clone());
96 Ok(plugin)
97 }
98
99 async fn list_paginated(
100 &self,
101 query: PaginationQuery,
102 ) -> Result<PaginatedResult<PluginModel>, RepositoryError> {
103 let total = self.count().await?;
104 let start = ((query.page - 1) * query.per_page) as usize;
105
106 let items = self
107 .store
108 .lock()
109 .await
110 .values()
111 .skip(start)
112 .take(query.per_page as usize)
113 .cloned()
114 .collect();
115
116 Ok(PaginatedResult {
117 items,
118 total: total as u64,
119 page: query.page,
120 per_page: query.per_page,
121 })
122 }
123
124 async fn count(&self) -> Result<usize, RepositoryError> {
125 let store = self.store.lock().await;
126 Ok(store.len())
127 }
128
129 async fn has_entries(&self) -> Result<bool, RepositoryError> {
130 let store = Self::acquire_lock(&self.store).await?;
131 Ok(!store.is_empty())
132 }
133
134 async fn drop_all_entries(&self) -> Result<(), RepositoryError> {
135 let mut store = Self::acquire_lock(&self.store).await?;
136 store.clear();
137 Ok(())
138 }
139
140 async fn get_compiled_code(&self, plugin_id: &str) -> Result<Option<String>, RepositoryError> {
143 let cache = Self::acquire_lock(&self.compiled_cache).await?;
144 Ok(cache.get(plugin_id).map(|e| e.code.clone()))
145 }
146
147 async fn store_compiled_code(
148 &self,
149 plugin_id: &str,
150 compiled_code: &str,
151 source_hash: Option<&str>,
152 ) -> Result<(), RepositoryError> {
153 let mut cache = Self::acquire_lock(&self.compiled_cache).await?;
154 cache.insert(
155 plugin_id.to_string(),
156 CompiledCodeEntry {
157 code: compiled_code.to_string(),
158 source_hash: source_hash.map(|s| s.to_string()),
159 },
160 );
161 Ok(())
162 }
163
164 async fn invalidate_compiled_code(&self, plugin_id: &str) -> Result<(), RepositoryError> {
165 let mut cache = Self::acquire_lock(&self.compiled_cache).await?;
166 cache.remove(plugin_id);
167 Ok(())
168 }
169
170 async fn invalidate_all_compiled_code(&self) -> Result<(), RepositoryError> {
171 let mut cache = Self::acquire_lock(&self.compiled_cache).await?;
172 cache.clear();
173 Ok(())
174 }
175
176 async fn has_compiled_code(&self, plugin_id: &str) -> Result<bool, RepositoryError> {
177 let cache = Self::acquire_lock(&self.compiled_cache).await?;
178 Ok(cache.contains_key(plugin_id))
179 }
180
181 async fn get_source_hash(&self, plugin_id: &str) -> Result<Option<String>, RepositoryError> {
182 let cache = Self::acquire_lock(&self.compiled_cache).await?;
183 Ok(cache.get(plugin_id).and_then(|e| e.source_hash.clone()))
184 }
185}
186
187#[cfg(test)]
188mod tests {
189 use crate::{config::PluginFileConfig, constants::DEFAULT_PLUGIN_TIMEOUT_SECONDS};
190
191 use super::*;
192 use std::{sync::Arc, time::Duration};
193
194 fn create_test_plugin(id: &str) -> PluginModel {
199 PluginModel {
200 id: id.to_string(),
201 path: format!("path/{id}"),
202 timeout: Duration::from_secs(DEFAULT_PLUGIN_TIMEOUT_SECONDS),
203 emit_logs: false,
204 emit_traces: false,
205 raw_response: false,
206 allow_get_invocation: false,
207 config: None,
208 forward_logs: false,
209 }
210 }
211
212 fn create_test_plugin_with_options(
213 id: &str,
214 emit_logs: bool,
215 emit_traces: bool,
216 raw_response: bool,
217 ) -> PluginModel {
218 PluginModel {
219 id: id.to_string(),
220 path: format!("path/{id}"),
221 timeout: Duration::from_secs(DEFAULT_PLUGIN_TIMEOUT_SECONDS),
222 emit_logs,
223 emit_traces,
224 raw_response,
225 allow_get_invocation: false,
226 config: None,
227 forward_logs: false,
228 }
229 }
230
231 #[tokio::test]
236 async fn test_new_creates_empty_repository() {
237 let repo = InMemoryPluginRepository::new();
238
239 assert_eq!(repo.count().await.unwrap(), 0);
240 assert!(!repo.has_entries().await.unwrap());
241 }
242
243 #[tokio::test]
244 async fn test_default_creates_empty_repository() {
245 let repo = InMemoryPluginRepository::default();
246
247 assert_eq!(repo.count().await.unwrap(), 0);
248 assert!(!repo.has_entries().await.unwrap());
249 }
250
251 #[tokio::test]
252 async fn test_add_and_get_by_id() {
253 let repo = Arc::new(InMemoryPluginRepository::new());
254
255 let plugin = create_test_plugin("test-plugin");
256 repo.add(plugin.clone()).await.unwrap();
257
258 let retrieved = repo.get_by_id("test-plugin").await.unwrap();
259 assert_eq!(retrieved, Some(plugin));
260 }
261
262 #[tokio::test]
263 async fn test_get_nonexistent_plugin() {
264 let repo = Arc::new(InMemoryPluginRepository::new());
265
266 let result = repo.get_by_id("nonexistent").await;
267 assert!(matches!(result, Ok(None)));
268 }
269
270 #[tokio::test]
271 async fn test_add_multiple_plugins() {
272 let repo = Arc::new(InMemoryPluginRepository::new());
273
274 for i in 1..=5 {
275 let plugin = create_test_plugin(&format!("plugin-{i}"));
276 repo.add(plugin).await.unwrap();
277 }
278
279 assert_eq!(repo.count().await.unwrap(), 5);
280
281 for i in 1..=5 {
282 let result = repo.get_by_id(&format!("plugin-{i}")).await.unwrap();
283 assert!(result.is_some());
284 }
285 }
286
287 #[tokio::test]
288 async fn test_add_overwrites_existing() {
289 let repo = Arc::new(InMemoryPluginRepository::new());
290
291 let plugin1 = create_test_plugin_with_options("test-plugin", false, false, false);
292 repo.add(plugin1).await.unwrap();
293
294 let plugin2 = create_test_plugin_with_options("test-plugin", true, true, true);
295 repo.add(plugin2.clone()).await.unwrap();
296
297 let retrieved = repo.get_by_id("test-plugin").await.unwrap().unwrap();
299 assert!(retrieved.emit_logs);
300 assert!(retrieved.emit_traces);
301 assert!(retrieved.raw_response);
302
303 assert_eq!(repo.count().await.unwrap(), 1);
305 }
306
307 #[tokio::test]
312 async fn test_update_existing_plugin() {
313 let repo = Arc::new(InMemoryPluginRepository::new());
314
315 let plugin = create_test_plugin("test-plugin");
316 repo.add(plugin).await.unwrap();
317
318 let updated_plugin = create_test_plugin_with_options("test-plugin", true, true, true);
319 let result = repo.update(updated_plugin.clone()).await;
320
321 assert!(result.is_ok());
322 let returned = result.unwrap();
323 assert_eq!(returned.id, "test-plugin");
324 assert!(returned.emit_logs);
325 assert!(returned.emit_traces);
326
327 let retrieved = repo.get_by_id("test-plugin").await.unwrap().unwrap();
329 assert!(retrieved.emit_logs);
330 }
331
332 #[tokio::test]
333 async fn test_update_nonexistent_plugin_returns_error() {
334 let repo = Arc::new(InMemoryPluginRepository::new());
335
336 let plugin = create_test_plugin("nonexistent");
337 let result = repo.update(plugin).await;
338
339 assert!(result.is_err());
340 match result {
341 Err(RepositoryError::NotFound(msg)) => {
342 assert!(msg.contains("nonexistent"));
343 }
344 _ => panic!("Expected NotFound error"),
345 }
346 }
347
348 #[tokio::test]
349 async fn test_update_preserves_other_plugins() {
350 let repo = Arc::new(InMemoryPluginRepository::new());
351
352 repo.add(create_test_plugin("plugin-1")).await.unwrap();
353 repo.add(create_test_plugin("plugin-2")).await.unwrap();
354 repo.add(create_test_plugin("plugin-3")).await.unwrap();
355
356 let updated = create_test_plugin_with_options("plugin-2", true, false, false);
357 repo.update(updated).await.unwrap();
358
359 let p1 = repo.get_by_id("plugin-1").await.unwrap().unwrap();
361 assert!(!p1.emit_logs);
362
363 let p3 = repo.get_by_id("plugin-3").await.unwrap().unwrap();
364 assert!(!p3.emit_logs);
365
366 let p2 = repo.get_by_id("plugin-2").await.unwrap().unwrap();
368 assert!(p2.emit_logs);
369 }
370
371 #[tokio::test]
376 async fn test_count_empty_repository() {
377 let repo = InMemoryPluginRepository::new();
378 assert_eq!(repo.count().await.unwrap(), 0);
379 }
380
381 #[tokio::test]
382 async fn test_count_with_entries() {
383 let repo = Arc::new(InMemoryPluginRepository::new());
384
385 for i in 1..=10 {
386 repo.add(create_test_plugin(&format!("plugin-{i}")))
387 .await
388 .unwrap();
389 }
390
391 assert_eq!(repo.count().await.unwrap(), 10);
392 }
393
394 #[tokio::test]
395 async fn test_count_after_drop_all() {
396 let repo = Arc::new(InMemoryPluginRepository::new());
397
398 for i in 1..=5 {
399 repo.add(create_test_plugin(&format!("plugin-{i}")))
400 .await
401 .unwrap();
402 }
403
404 assert_eq!(repo.count().await.unwrap(), 5);
405
406 repo.drop_all_entries().await.unwrap();
407
408 assert_eq!(repo.count().await.unwrap(), 0);
409 }
410
411 #[tokio::test]
416 async fn test_list_paginated_first_page() {
417 let repo = Arc::new(InMemoryPluginRepository::new());
418
419 for i in 1..=10 {
420 repo.add(create_test_plugin(&format!("plugin-{i:02}")))
421 .await
422 .unwrap();
423 }
424
425 let query = PaginationQuery {
426 page: 1,
427 per_page: 3,
428 };
429
430 let result = repo.list_paginated(query).await.unwrap();
431
432 assert_eq!(result.items.len(), 3);
433 assert_eq!(result.total, 10);
434 assert_eq!(result.page, 1);
435 assert_eq!(result.per_page, 3);
436 }
437
438 #[tokio::test]
439 async fn test_list_paginated_middle_page() {
440 let repo = Arc::new(InMemoryPluginRepository::new());
441
442 for i in 1..=10 {
443 repo.add(create_test_plugin(&format!("plugin-{i:02}")))
444 .await
445 .unwrap();
446 }
447
448 let query = PaginationQuery {
449 page: 2,
450 per_page: 3,
451 };
452
453 let result = repo.list_paginated(query).await.unwrap();
454
455 assert_eq!(result.items.len(), 3);
456 assert_eq!(result.total, 10);
457 assert_eq!(result.page, 2);
458 }
459
460 #[tokio::test]
461 async fn test_list_paginated_last_partial_page() {
462 let repo = Arc::new(InMemoryPluginRepository::new());
463
464 for i in 1..=10 {
465 repo.add(create_test_plugin(&format!("plugin-{i:02}")))
466 .await
467 .unwrap();
468 }
469
470 let query = PaginationQuery {
471 page: 4,
472 per_page: 3,
473 };
474
475 let result = repo.list_paginated(query).await.unwrap();
476
477 assert_eq!(result.items.len(), 1);
479 assert_eq!(result.total, 10);
480 }
481
482 #[tokio::test]
483 async fn test_list_paginated_empty_repository() {
484 let repo = Arc::new(InMemoryPluginRepository::new());
485
486 let query = PaginationQuery {
487 page: 1,
488 per_page: 10,
489 };
490
491 let result = repo.list_paginated(query).await.unwrap();
492
493 assert_eq!(result.items.len(), 0);
494 assert_eq!(result.total, 0);
495 }
496
497 #[tokio::test]
498 async fn test_list_paginated_page_beyond_data() {
499 let repo = Arc::new(InMemoryPluginRepository::new());
500
501 for i in 1..=5 {
502 repo.add(create_test_plugin(&format!("plugin-{i}")))
503 .await
504 .unwrap();
505 }
506
507 let query = PaginationQuery {
508 page: 10, per_page: 2,
510 };
511
512 let result = repo.list_paginated(query).await.unwrap();
513
514 assert_eq!(result.items.len(), 0);
515 assert_eq!(result.total, 5);
516 }
517
518 #[tokio::test]
519 async fn test_list_paginated_large_per_page() {
520 let repo = Arc::new(InMemoryPluginRepository::new());
521
522 for i in 1..=5 {
523 repo.add(create_test_plugin(&format!("plugin-{i}")))
524 .await
525 .unwrap();
526 }
527
528 let query = PaginationQuery {
529 page: 1,
530 per_page: 100, };
532
533 let result = repo.list_paginated(query).await.unwrap();
534
535 assert_eq!(result.items.len(), 5);
536 assert_eq!(result.total, 5);
537 }
538
539 #[tokio::test]
544 async fn test_has_entries_empty() {
545 let repo = InMemoryPluginRepository::new();
546 assert!(!repo.has_entries().await.unwrap());
547 }
548
549 #[tokio::test]
550 async fn test_has_entries_with_data() {
551 let repo = Arc::new(InMemoryPluginRepository::new());
552 repo.add(create_test_plugin("test")).await.unwrap();
553 assert!(repo.has_entries().await.unwrap());
554 }
555
556 #[tokio::test]
557 async fn test_drop_all_entries_clears_store() {
558 let repo = Arc::new(InMemoryPluginRepository::new());
559
560 for i in 1..=5 {
561 repo.add(create_test_plugin(&format!("plugin-{i}")))
562 .await
563 .unwrap();
564 }
565
566 assert!(repo.has_entries().await.unwrap());
567 assert_eq!(repo.count().await.unwrap(), 5);
568
569 repo.drop_all_entries().await.unwrap();
570
571 assert!(!repo.has_entries().await.unwrap());
572 assert_eq!(repo.count().await.unwrap(), 0);
573 }
574
575 #[tokio::test]
576 async fn test_drop_all_entries_on_empty_repo() {
577 let repo = InMemoryPluginRepository::new();
578
579 let result = repo.drop_all_entries().await;
581 assert!(result.is_ok());
582 }
583
584 #[tokio::test]
589 async fn test_store_and_get_compiled_code() {
590 let repo = InMemoryPluginRepository::new();
591
592 repo.store_compiled_code("plugin-1", "compiled code here", None)
593 .await
594 .unwrap();
595
596 let result = repo.get_compiled_code("plugin-1").await.unwrap();
597 assert_eq!(result, Some("compiled code here".to_string()));
598 }
599
600 #[tokio::test]
601 async fn test_get_compiled_code_nonexistent() {
602 let repo = InMemoryPluginRepository::new();
603
604 let result = repo.get_compiled_code("nonexistent").await.unwrap();
605 assert_eq!(result, None);
606 }
607
608 #[tokio::test]
609 async fn test_store_compiled_code_with_source_hash() {
610 let repo = InMemoryPluginRepository::new();
611
612 repo.store_compiled_code("plugin-1", "code", Some("abc123hash"))
613 .await
614 .unwrap();
615
616 let code = repo.get_compiled_code("plugin-1").await.unwrap();
617 assert_eq!(code, Some("code".to_string()));
618
619 let hash = repo.get_source_hash("plugin-1").await.unwrap();
620 assert_eq!(hash, Some("abc123hash".to_string()));
621 }
622
623 #[tokio::test]
624 async fn test_store_compiled_code_overwrites_existing() {
625 let repo = InMemoryPluginRepository::new();
626
627 repo.store_compiled_code("plugin-1", "old code", Some("oldhash"))
628 .await
629 .unwrap();
630
631 repo.store_compiled_code("plugin-1", "new code", Some("newhash"))
632 .await
633 .unwrap();
634
635 let code = repo.get_compiled_code("plugin-1").await.unwrap();
636 assert_eq!(code, Some("new code".to_string()));
637
638 let hash = repo.get_source_hash("plugin-1").await.unwrap();
639 assert_eq!(hash, Some("newhash".to_string()));
640 }
641
642 #[tokio::test]
643 async fn test_has_compiled_code() {
644 let repo = InMemoryPluginRepository::new();
645
646 assert!(!repo.has_compiled_code("plugin-1").await.unwrap());
647
648 repo.store_compiled_code("plugin-1", "code", None)
649 .await
650 .unwrap();
651
652 assert!(repo.has_compiled_code("plugin-1").await.unwrap());
653 assert!(!repo.has_compiled_code("plugin-2").await.unwrap());
654 }
655
656 #[tokio::test]
657 async fn test_invalidate_compiled_code() {
658 let repo = InMemoryPluginRepository::new();
659
660 repo.store_compiled_code("plugin-1", "code1", None)
661 .await
662 .unwrap();
663 repo.store_compiled_code("plugin-2", "code2", None)
664 .await
665 .unwrap();
666
667 assert!(repo.has_compiled_code("plugin-1").await.unwrap());
668 assert!(repo.has_compiled_code("plugin-2").await.unwrap());
669
670 repo.invalidate_compiled_code("plugin-1").await.unwrap();
671
672 assert!(!repo.has_compiled_code("plugin-1").await.unwrap());
673 assert!(repo.has_compiled_code("plugin-2").await.unwrap());
674 }
675
676 #[tokio::test]
677 async fn test_invalidate_compiled_code_nonexistent() {
678 let repo = InMemoryPluginRepository::new();
679
680 let result = repo.invalidate_compiled_code("nonexistent").await;
682 assert!(result.is_ok());
683 }
684
685 #[tokio::test]
686 async fn test_invalidate_all_compiled_code() {
687 let repo = InMemoryPluginRepository::new();
688
689 for i in 1..=5 {
690 repo.store_compiled_code(&format!("plugin-{i}"), &format!("code-{i}"), None)
691 .await
692 .unwrap();
693 }
694
695 for i in 1..=5 {
696 assert!(repo
697 .has_compiled_code(&format!("plugin-{i}"))
698 .await
699 .unwrap());
700 }
701
702 repo.invalidate_all_compiled_code().await.unwrap();
703
704 for i in 1..=5 {
705 assert!(!repo
706 .has_compiled_code(&format!("plugin-{i}"))
707 .await
708 .unwrap());
709 }
710 }
711
712 #[tokio::test]
713 async fn test_invalidate_all_compiled_code_empty() {
714 let repo = InMemoryPluginRepository::new();
715
716 let result = repo.invalidate_all_compiled_code().await;
718 assert!(result.is_ok());
719 }
720
721 #[tokio::test]
722 async fn test_get_source_hash() {
723 let repo = InMemoryPluginRepository::new();
724
725 repo.store_compiled_code("plugin-1", "code", None)
727 .await
728 .unwrap();
729 let hash = repo.get_source_hash("plugin-1").await.unwrap();
730 assert_eq!(hash, None);
731
732 repo.store_compiled_code("plugin-2", "code", Some("sha256:abc"))
734 .await
735 .unwrap();
736 let hash = repo.get_source_hash("plugin-2").await.unwrap();
737 assert_eq!(hash, Some("sha256:abc".to_string()));
738 }
739
740 #[tokio::test]
741 async fn test_get_source_hash_nonexistent() {
742 let repo = InMemoryPluginRepository::new();
743
744 let hash = repo.get_source_hash("nonexistent").await.unwrap();
745 assert_eq!(hash, None);
746 }
747
748 #[tokio::test]
753 async fn test_clone_copies_store_data() {
754 let repo = InMemoryPluginRepository::new();
755 repo.add(create_test_plugin("plugin-1")).await.unwrap();
756 repo.add(create_test_plugin("plugin-2")).await.unwrap();
757
758 let cloned = repo.clone();
759
760 assert_eq!(cloned.count().await.unwrap(), 2);
762 assert!(cloned.get_by_id("plugin-1").await.unwrap().is_some());
763 assert!(cloned.get_by_id("plugin-2").await.unwrap().is_some());
764 }
765
766 #[tokio::test]
767 async fn test_clone_copies_compiled_cache() {
768 let repo = InMemoryPluginRepository::new();
769 repo.store_compiled_code("plugin-1", "code1", Some("hash1"))
770 .await
771 .unwrap();
772
773 let cloned = repo.clone();
774
775 assert!(cloned.has_compiled_code("plugin-1").await.unwrap());
777 assert_eq!(
778 cloned.get_compiled_code("plugin-1").await.unwrap(),
779 Some("code1".to_string())
780 );
781 assert_eq!(
782 cloned.get_source_hash("plugin-1").await.unwrap(),
783 Some("hash1".to_string())
784 );
785 }
786
787 #[tokio::test]
788 async fn test_clone_is_independent() {
789 let repo = InMemoryPluginRepository::new();
790 repo.add(create_test_plugin("plugin-1")).await.unwrap();
791
792 let cloned = repo.clone();
793
794 repo.add(create_test_plugin("plugin-2")).await.unwrap();
796
797 assert_eq!(repo.count().await.unwrap(), 2);
800 assert_eq!(cloned.count().await.unwrap(), 1);
802 }
803
804 #[tokio::test]
809 async fn test_plugin_model_try_from_config() {
810 let config = PluginFileConfig {
811 id: "test-plugin".to_string(),
812 path: "test-path".to_string(),
813 timeout: None,
814 emit_logs: false,
815 emit_traces: false,
816 raw_response: false,
817 allow_get_invocation: false,
818 config: None,
819 forward_logs: false,
820 };
821
822 let result = PluginModel::try_from(config);
823 assert!(result.is_ok());
824
825 let plugin = result.unwrap();
826 assert_eq!(plugin.id, "test-plugin");
827 assert_eq!(plugin.path, "test-path");
828 assert_eq!(
829 plugin.timeout,
830 Duration::from_secs(DEFAULT_PLUGIN_TIMEOUT_SECONDS)
831 );
832 }
833
834 #[tokio::test]
835 async fn test_plugin_model_try_from_config_with_timeout() {
836 let mut config_map = serde_json::Map::new();
837 config_map.insert("key".to_string(), serde_json::json!("value"));
838
839 let config = PluginFileConfig {
840 id: "test-plugin".to_string(),
841 path: "test-path".to_string(),
842 timeout: Some(120),
843 emit_logs: true,
844 emit_traces: true,
845 raw_response: true,
846 allow_get_invocation: true,
847 config: Some(config_map),
848 forward_logs: true,
849 };
850
851 let result = PluginModel::try_from(config);
852 assert!(result.is_ok());
853
854 let plugin = result.unwrap();
855 assert_eq!(plugin.timeout, Duration::from_secs(120));
856 assert!(plugin.emit_logs);
857 assert!(plugin.emit_traces);
858 assert!(plugin.raw_response);
859 assert!(plugin.allow_get_invocation);
860 assert!(plugin.config.is_some());
861 assert!(plugin.forward_logs);
862 }
863
864 #[tokio::test]
869 async fn test_compiled_cache_independent_of_plugin_store() {
870 let repo = InMemoryPluginRepository::new();
871
872 repo.store_compiled_code("plugin-1", "compiled", None)
874 .await
875 .unwrap();
876
877 assert!(repo.get_by_id("plugin-1").await.unwrap().is_none());
879
880 assert!(repo.has_compiled_code("plugin-1").await.unwrap());
882 }
883
884 #[tokio::test]
885 async fn test_drop_all_entries_does_not_clear_compiled_cache() {
886 let repo = InMemoryPluginRepository::new();
887
888 repo.add(create_test_plugin("plugin-1")).await.unwrap();
889 repo.store_compiled_code("plugin-1", "compiled", None)
890 .await
891 .unwrap();
892
893 repo.drop_all_entries().await.unwrap();
894
895 assert!(repo.get_by_id("plugin-1").await.unwrap().is_none());
897
898 assert!(repo.has_compiled_code("plugin-1").await.unwrap());
900 }
901
902 #[tokio::test]
903 async fn test_invalidate_all_compiled_does_not_clear_store() {
904 let repo = InMemoryPluginRepository::new();
905
906 repo.add(create_test_plugin("plugin-1")).await.unwrap();
907 repo.store_compiled_code("plugin-1", "compiled", None)
908 .await
909 .unwrap();
910
911 repo.invalidate_all_compiled_code().await.unwrap();
912
913 assert!(!repo.has_compiled_code("plugin-1").await.unwrap());
915
916 assert!(repo.get_by_id("plugin-1").await.unwrap().is_some());
918 }
919}