vector_stores/
vector_stores.rs

1#![allow(clippy::uninlined_format_args)]
2#![allow(clippy::unnecessary_wraps)]
3#![allow(clippy::useless_vec)]
4#![allow(clippy::or_fun_call)]
5#![allow(clippy::single_char_pattern)]
6#![allow(clippy::inefficient_to_string)]
7#![allow(clippy::doc_markdown)]
8#![allow(clippy::redundant_closure_for_method_calls)]
9//! Vector Stores API Example
10//!
11//! This example demonstrates how to use the OpenAI Vector Stores API for semantic search,
12//! document management, and knowledge base operations. Vector stores enable efficient
13//! similarity-based document retrieval for RAG applications and knowledge management systems.
14//!
15//! ## Features Demonstrated
16//!
17//! - Creating and configuring vector stores
18//! - File management and batch operations
19//! - Semantic search and similarity queries
20//! - Vector store lifecycle management
21//! - Advanced search filtering and ranking
22//! - Integration with assistants and RAG workflows
23//!
24//! ## Prerequisites
25//!
26//! Set your OpenAI API key:
27//! ```bash
28//! export OPENAI_API_KEY="your-key-here"
29//! ```
30//!
31//! ## Usage
32//!
33//! ```bash
34//! cargo run --example vector_stores
35//! ```
36//!
37//! ## Note on Implementation Status
38//!
39//! This example demonstrates the intended API design for vector store operations.
40//! The code shows how the ergonomic builders provide a streamlined interface for
41//! complex vector operations and document management.
42
43use openai_ergonomic::{
44    builders::vector_stores::{
45        add_file_to_vector_store, search_vector_store, search_vector_store_with_limit,
46        simple_vector_store, temporary_vector_store, vector_store_with_files, VectorStoreBuilder,
47        VectorStoreSearchBuilder,
48    },
49    Client, Error,
50};
51
52#[tokio::main]
53async fn main() -> Result<(), Box<dyn std::error::Error>> {
54    println!(" OpenAI Ergonomic - Vector Stores Example\n");
55
56    // Initialize client from environment variables
57    let _client = match Client::from_env() {
58        Ok(client_builder) => {
59            println!(" Client initialized successfully");
60            client_builder.build()
61        }
62        Err(e) => {
63            eprintln!(" Failed to initialize client: {e}");
64            eprintln!(" Make sure OPENAI_API_KEY is set in your environment");
65            return Err(e.into());
66        }
67    };
68
69    // Demonstrate different vector store use cases
70    run_basic_vector_store_example()?;
71    run_document_management_example()?;
72    run_semantic_search_example()?;
73    run_enterprise_knowledge_base_example()?;
74    run_vector_store_lifecycle_example()?;
75    run_advanced_search_patterns_example()?;
76
77    println!("\n Vector Stores examples completed successfully!");
78    Ok(())
79}
80
81/// Example 1: Basic Vector Store Operations
82fn run_basic_vector_store_example() -> Result<(), Error> {
83    println!(" Example 1: Basic Vector Store Operations");
84    println!("{}", "=".repeat(60));
85
86    // Create a simple vector store
87    let basic_store = simple_vector_store("Getting Started Vector Store")
88        .metadata("purpose", "tutorial")
89        .metadata("created_by", "openai_ergonomic_example");
90
91    println!(" Created basic vector store:");
92    println!("   Name: {}", basic_store.name_ref().unwrap());
93    println!(
94        "   Purpose: {}",
95        basic_store.metadata_ref().get("purpose").unwrap()
96    );
97    println!("   Files: {}", basic_store.file_count());
98
99    // Add files to the vector store
100    let store_with_files = basic_store
101        .add_file("file-welcome-doc-001")
102        .add_file("file-getting-started-002")
103        .add_file("file-basic-examples-003");
104
105    println!("\n Added files to vector store:");
106    for (i, file_id) in store_with_files.file_ids_ref().iter().enumerate() {
107        println!("   {}. {}", i + 1, file_id);
108    }
109    println!("   Total files: {}", store_with_files.file_count());
110
111    // Demonstrate vector store properties
112    println!("\n Vector Store Properties:");
113    println!("   Has files: {}", store_with_files.has_files());
114    println!(
115        "   Metadata entries: {}",
116        store_with_files.metadata_ref().len()
117    );
118    println!(
119        "   Expires: {}",
120        if store_with_files.expires_after_ref().is_some() {
121            "Yes"
122        } else {
123            "No"
124        }
125    );
126
127    println!("\n Basic Operations:");
128    println!("    Create vector store");
129    println!("    Add metadata");
130    println!("    Add files");
131    println!("    Query properties");
132    println!("    Ready for search operations");
133
134    Ok(())
135}
136
137/// Example 2: Document Management and Batch Operations
138fn run_document_management_example() -> Result<(), Error> {
139    println!("\n Example 2: Document Management and Batch Operations");
140    println!("{}", "=".repeat(60));
141
142    // Simulate a large document collection
143    let document_collection = vec![
144        "file-product-docs-001",
145        "file-product-docs-002",
146        "file-api-reference-003",
147        "file-user-guide-004",
148        "file-troubleshooting-005",
149        "file-changelog-006",
150        "file-best-practices-007",
151        "file-integration-guide-008",
152    ];
153
154    // Create vector store with batch file addition
155    let doc_store = vector_store_with_files(
156        "Product Documentation Store",
157        document_collection.iter().map(|s| s.to_string()).collect(),
158    )
159    .metadata("category", "documentation")
160    .metadata("product", "api_platform")
161    .metadata("version", "v2.1")
162    .expires_after_days(180); // 6 months retention
163
164    println!(" Created documentation vector store:");
165    println!("   Name: {}", doc_store.name_ref().unwrap());
166    println!("   Documents: {} files", doc_store.file_count());
167    println!(
168        "   Category: {}",
169        doc_store.metadata_ref().get("category").unwrap()
170    );
171    println!("   Retention: 180 days");
172
173    // Demonstrate individual file operations
174    let individual_file_op = add_file_to_vector_store("doc-store-123", "file-new-feature-009");
175
176    println!("\n Individual File Operations:");
177    println!("   Adding file: {}", individual_file_op.file_id());
178    println!("   To store: {}", individual_file_op.vector_store_id());
179
180    // Simulate file organization strategies
181    println!("\n Document Organization Strategies:");
182
183    let categorized_stores = vec![
184        (
185            "API Documentation",
186            vec!["file-api-ref", "file-endpoints", "file-auth"],
187        ),
188        (
189            "User Guides",
190            vec!["file-quickstart", "file-tutorials", "file-howtos"],
191        ),
192        (
193            "Technical Specs",
194            vec!["file-architecture", "file-protocols", "file-security"],
195        ),
196        (
197            "Release Notes",
198            vec!["file-changelog", "file-migration", "file-breaking-changes"],
199        ),
200    ];
201
202    for (category, files) in &categorized_stores {
203        let category_store = vector_store_with_files(
204            format!("{} Vector Store", category),
205            files.iter().map(|s| s.to_string()).collect(),
206        )
207        .metadata("category", category.to_lowercase().replace(" ", "_"))
208        .metadata("auto_managed", "true");
209
210        println!("    {}: {} files", category, category_store.file_count());
211    }
212
213    println!("\n Document Management Workflow:");
214    println!("   1.  Batch upload documents by category");
215    println!("   2.  Apply consistent metadata tagging");
216    println!("   3. ⏰ Set appropriate retention policies");
217    println!("   4.  Enable automatic organization");
218    println!("   5.  Monitor storage usage and performance");
219
220    Ok(())
221}
222
223/// Example 3: Semantic Search and Similarity Queries
224fn run_semantic_search_example() -> Result<(), Error> {
225    println!("\n Example 3: Semantic Search and Similarity Queries");
226    println!("{}", "=".repeat(60));
227
228    // Create a search-optimized vector store
229    let search_store = simple_vector_store("Semantic Search Demo Store")
230        .add_file("file-ml-concepts-001")
231        .add_file("file-nlp-techniques-002")
232        .add_file("file-deep-learning-003")
233        .add_file("file-computer-vision-004")
234        .add_file("file-ai-ethics-005")
235        .metadata("domain", "machine_learning")
236        .metadata("search_optimized", "true");
237
238    println!(" Created search-optimized vector store:");
239    println!("   Name: {}", search_store.name_ref().unwrap());
240    println!("   Domain: Machine Learning");
241    println!("   Documents: {} files", search_store.file_count());
242
243    // Demonstrate various search patterns
244    println!("\n Search Query Examples:");
245
246    // Basic semantic search
247    let basic_search = search_vector_store("search-store-123", "neural network architectures");
248    println!("   1. Basic Search:");
249    println!("      Query: '{}'", basic_search.query());
250    println!("      Store: {}", basic_search.vector_store_id());
251
252    // Limited result search
253    let limited_search = search_vector_store_with_limit(
254        "search-store-123",
255        "natural language processing techniques",
256        5,
257    );
258    println!("   2. Limited Results:");
259    println!("      Query: '{}'", limited_search.query());
260    println!(
261        "      Limit: {} results",
262        limited_search.limit_ref().unwrap()
263    );
264
265    // Advanced filtered search
266    let filtered_search =
267        search_vector_store_with_limit("search-store-123", "computer vision applications", 10)
268            .filter("category", "practical_applications")
269            .filter("difficulty", "intermediate");
270
271    println!("   3. Filtered Search:");
272    println!("      Query: '{}'", filtered_search.query());
273    println!(
274        "      Filters: {} applied",
275        filtered_search.filter_ref().len()
276    );
277    for (key, value) in filtered_search.filter_ref() {
278        println!("         {}={}", key, value);
279    }
280
281    // Demonstrate search result processing
282    println!("\n Search Result Processing:");
283    println!("    Semantic similarity ranking");
284    println!("    Document excerpt extraction");
285    println!("    Relevance score calculation");
286    println!("    Source location identification");
287    println!("    Related content suggestions");
288
289    // Show different query types
290    println!("\n Query Type Examples:");
291    let query_examples = vec![
292        (
293            "Conceptual",
294            "What is machine learning?",
295            "Broad conceptual understanding",
296        ),
297        (
298            "Technical",
299            "How to implement backpropagation?",
300            "Specific technical implementation",
301        ),
302        (
303            "Comparative",
304            "LSTM vs Transformer architectures",
305            "Comparative analysis",
306        ),
307        (
308            "Problem-solving",
309            "Overfitting in neural networks",
310            "Problem identification and solutions",
311        ),
312        (
313            "Application",
314            "Computer vision in healthcare",
315            "Domain-specific applications",
316        ),
317    ];
318
319    for (query_type, query, description) in query_examples {
320        println!("    {}: '{}'", query_type, query);
321        println!("      Purpose: {}", description);
322    }
323
324    Ok(())
325}
326
327/// Example 4: Enterprise Knowledge Base
328fn run_enterprise_knowledge_base_example() -> Result<(), Error> {
329    println!("\n Example 4: Enterprise Knowledge Base");
330    println!("{}", "=".repeat(60));
331
332    // Create enterprise-scale vector stores
333    let enterprise_stores = create_enterprise_knowledge_base()?;
334
335    println!(" Enterprise Knowledge Base Architecture:");
336    for (department, store) in enterprise_stores {
337        println!("    {}", department);
338        println!("      Files: {} documents", store.file_count());
339        println!(
340            "      Retention: {} days",
341            store
342                .expires_after_ref()
343                .map_or("permanent".to_string(), |exp| exp.days.to_string())
344                .as_str()
345        );
346
347        // Show metadata structure
348        for (key, value) in store.metadata_ref() {
349            println!("      {}: {}", key, value);
350        }
351        println!();
352    }
353
354    // Demonstrate cross-departmental search
355    println!(" Cross-Departmental Search Examples:");
356
357    let cross_searches = vec![
358        (
359            "Security Compliance",
360            "GDPR data handling procedures",
361            vec!["legal", "engineering", "hr"],
362        ),
363        (
364            "Product Launch",
365            "Q4 release planning and coordination",
366            vec!["product", "engineering", "marketing"],
367        ),
368        (
369            "Budget Planning",
370            "Annual technology investment strategy",
371            vec!["finance", "engineering", "executive"],
372        ),
373        (
374            "Process Improvement",
375            "Remote work productivity guidelines",
376            vec!["hr", "operations", "it"],
377        ),
378    ];
379
380    for (topic, query, departments) in cross_searches {
381        println!("    {}: '{}'", topic, query);
382        println!("      Search scope: {}", departments.join(", "));
383    }
384
385    println!("\n Enterprise Features:");
386    println!("    Role-based access control");
387    println!("    Usage analytics and monitoring");
388    println!("    Automated content lifecycle management");
389    println!("    Search performance optimization");
390    println!("    Backup and disaster recovery");
391    println!("    Compliance and audit trails");
392
393    Ok(())
394}
395
396/// Example 5: Vector Store Lifecycle Management
397fn run_vector_store_lifecycle_example() -> Result<(), Error> {
398    println!("\n Example 5: Vector Store Lifecycle Management");
399    println!("{}", "=".repeat(60));
400
401    // Demonstrate different lifecycle patterns
402    println!("⏰ Vector Store Lifecycle Patterns:");
403
404    // Temporary stores for sessions
405    let session_store = temporary_vector_store("User Session Store", 1)
406        .add_file("file-session-context-001")
407        .metadata("session_id", "sess_12345")
408        .metadata("user_id", "user_67890");
409
410    println!("    Session-based (1 day):");
411    println!("      Purpose: Temporary user context");
412    println!("      Files: {}", session_store.file_count());
413    println!("      Auto-cleanup: ");
414
415    // Project stores
416    let project_store = temporary_vector_store("Project Alpha Documentation", 90)
417        .add_file("file-project-spec-001")
418        .add_file("file-meeting-notes-002")
419        .add_file("file-progress-reports-003")
420        .metadata("project_id", "proj_alpha_2024")
421        .metadata("phase", "development");
422
423    println!("    Project-based (90 days):");
424    println!("      Purpose: Project lifecycle documentation");
425    println!("      Files: {}", project_store.file_count());
426    println!("      Cleanup: After project completion");
427
428    // Long-term knowledge stores
429    let knowledge_store = simple_vector_store("Institutional Knowledge Base")
430        .add_file("file-company-history-001")
431        .add_file("file-best-practices-002")
432        .add_file("file-lessons-learned-003")
433        .metadata("retention", "permanent")
434        .metadata("backup", "enabled")
435        .metadata("compliance", "required");
436
437    println!("    Institutional (permanent):");
438    println!("      Purpose: Long-term organizational knowledge");
439    println!("      Files: {}", knowledge_store.file_count());
440    println!("      Cleanup: Manual review only");
441
442    // Demonstrate lifecycle events
443    println!("\n Lifecycle Event Handling:");
444    println!("    Creation: Automatic indexing and optimization");
445    println!("    Updates: Incremental re-indexing of modified files");
446    println!("    Monitoring: Usage tracking and performance metrics");
447    println!("    Warnings: Expiration notifications and alerts");
448    println!("    Cleanup: Automatic or manual deletion processes");
449    println!("    Archival: Long-term storage for compliance");
450
451    // Show cost optimization strategies
452    println!("\n Cost Optimization Strategies:");
453    println!("    Smart expiration policies based on usage");
454    println!("    Analytics-driven storage optimization");
455    println!("    Automatic compression for archived content");
456    println!("    Tiered storage (hot, warm, cold)");
457    println!("    Usage-based scaling recommendations");
458
459    Ok(())
460}
461
462/// Example 6: Advanced Search Patterns and Optimization
463fn run_advanced_search_patterns_example() -> Result<(), Error> {
464    println!("\n Example 6: Advanced Search Patterns and Optimization");
465    println!("{}", "=".repeat(60));
466
467    // Create optimized search store
468    let optimized_store = VectorStoreBuilder::new()
469        .name("Advanced Search Optimization Store")
470        .add_file("file-technical-docs-001")
471        .add_file("file-user-feedback-002")
472        .add_file("file-performance-data-003")
473        .add_file("file-best-practices-004")
474        .metadata("search_optimized", "true")
475        .metadata("indexing", "enhanced")
476        .metadata("caching", "enabled");
477
478    println!(" Created advanced search store:");
479    println!("   Optimization: Enhanced indexing");
480    println!("   Caching: Enabled");
481    println!("   Files: {} documents", optimized_store.file_count());
482
483    // Demonstrate advanced search patterns
484    println!("\n Advanced Search Patterns:");
485
486    // Multi-stage search
487    println!("   1.  Multi-stage Search:");
488    println!("      Stage 1: Broad semantic search (100 results)");
489    println!("      Stage 2: Filtered refinement (20 results)");
490    println!("      Stage 3: Relevance re-ranking (5 top results)");
491
492    let multi_stage_search =
493        VectorStoreSearchBuilder::new("advanced-store-789", "machine learning best practices")
494            .limit(100)
495            .filter("category", "best_practices")
496            .filter("verified", "true");
497
498    println!("      Query: '{}'", multi_stage_search.query());
499    println!(
500        "      Initial limit: {}",
501        multi_stage_search.limit_ref().unwrap()
502    );
503
504    // Contextual search
505    println!("   2.  Contextual Search:");
506    println!("      Context: User role, project phase, domain expertise");
507    println!("      Adaptation: Results tailored to user context");
508
509    let _contextual_search =
510        search_vector_store_with_limit("advanced-store-789", "deployment strategies", 15)
511            .filter("audience", "senior_engineer")
512            .filter("complexity", "advanced")
513            .filter("domain", "cloud_infrastructure");
514
515    println!("      Audience: senior_engineer");
516    println!("      Complexity: advanced");
517    println!("      Domain: cloud_infrastructure");
518
519    // Hybrid search approaches
520    println!("   3.  Hybrid Search Approaches:");
521    println!("      Semantic similarity + keyword matching");
522    println!("      Vector search + traditional full-text search");
523    println!("      AI-enhanced query understanding");
524
525    // Search performance optimization
526    println!("\n Search Performance Optimization:");
527    println!("    Query optimization and caching");
528    println!("    Result pre-computation for common queries");
529    println!("    Incremental index updates");
530    println!("    Load balancing across vector stores");
531    println!("    Machine learning-based relevance tuning");
532
533    // Quality metrics and monitoring
534    println!("\n Search Quality Metrics:");
535    println!("    Relevance scores and user feedback");
536    println!("   ⏱ Query response time analysis");
537    println!("    Search success rate tracking");
538    println!("    Usage pattern analysis");
539    println!("    Continuous improvement recommendations");
540
541    Ok(())
542}
543
544/// Helper function to create enterprise knowledge base structure
545fn create_enterprise_knowledge_base() -> Result<Vec<(String, VectorStoreBuilder)>, Error> {
546    let departments = vec![
547        ("Engineering".to_string(), create_engineering_store()),
548        ("Legal".to_string(), create_legal_store()),
549        ("HR".to_string(), create_hr_store()),
550        ("Marketing".to_string(), create_marketing_store()),
551        ("Finance".to_string(), create_finance_store()),
552        ("Operations".to_string(), create_operations_store()),
553    ];
554
555    Ok(departments)
556}
557
558fn create_engineering_store() -> VectorStoreBuilder {
559    VectorStoreBuilder::new()
560        .name("Engineering Knowledge Base")
561        .add_file("file-architecture-docs-001")
562        .add_file("file-coding-standards-002")
563        .add_file("file-deployment-guides-003")
564        .add_file("file-api-documentation-004")
565        .metadata("department", "engineering")
566        .metadata("access_level", "engineering_team")
567        .metadata("update_frequency", "weekly")
568        .expires_after_days(365)
569}
570
571fn create_legal_store() -> VectorStoreBuilder {
572    VectorStoreBuilder::new()
573        .name("Legal Documentation Store")
574        .add_file("file-contracts-templates-001")
575        .add_file("file-compliance-guides-002")
576        .add_file("file-policy-documents-003")
577        .metadata("department", "legal")
578        .metadata("access_level", "legal_team")
579        .metadata("confidentiality", "high")
580        .expires_after_days(2555) // 7 years for legal retention
581}
582
583fn create_hr_store() -> VectorStoreBuilder {
584    VectorStoreBuilder::new()
585        .name("Human Resources Knowledge Base")
586        .add_file("file-employee-handbook-001")
587        .add_file("file-benefits-guide-002")
588        .add_file("file-performance-templates-003")
589        .metadata("department", "hr")
590        .metadata("access_level", "hr_managers")
591        .metadata("privacy", "employee_data")
592        .expires_after_days(1095) // 3 years
593}
594
595fn create_marketing_store() -> VectorStoreBuilder {
596    VectorStoreBuilder::new()
597        .name("Marketing Materials Store")
598        .add_file("file-brand-guidelines-001")
599        .add_file("file-campaign-templates-002")
600        .add_file("file-market-research-003")
601        .metadata("department", "marketing")
602        .metadata("access_level", "marketing_team")
603        .metadata("content_type", "creative_assets")
604        .expires_after_days(365)
605}
606
607fn create_finance_store() -> VectorStoreBuilder {
608    VectorStoreBuilder::new()
609        .name("Finance Documentation Store")
610        .add_file("file-budget-templates-001")
611        .add_file("file-financial-policies-002")
612        .add_file("file-audit-procedures-003")
613        .metadata("department", "finance")
614        .metadata("access_level", "finance_team")
615        .metadata("compliance", "required")
616        .expires_after_days(2555) // 7 years for financial records
617}
618
619fn create_operations_store() -> VectorStoreBuilder {
620    VectorStoreBuilder::new()
621        .name("Operations Procedures Store")
622        .add_file("file-standard-procedures-001")
623        .add_file("file-incident-response-002")
624        .add_file("file-vendor-management-003")
625        .metadata("department", "operations")
626        .metadata("access_level", "operations_team")
627        .metadata("criticality", "high")
628        .expires_after_days(730) // 2 years
629}
630
631#[cfg(test)]
632mod tests {
633    use super::*;
634
635    #[test]
636    fn test_basic_vector_store() {
637        let store = simple_vector_store("Test Store")
638            .metadata("test", "true")
639            .add_file("test-file-1");
640
641        assert_eq!(store.name_ref(), Some("Test Store"));
642        assert_eq!(store.file_count(), 1);
643        assert!(store.has_files());
644        assert_eq!(store.metadata_ref().get("test"), Some(&"true".to_string()));
645    }
646
647    #[test]
648    fn test_vector_store_with_files() {
649        let files = vec![
650            "file-1".to_string(),
651            "file-2".to_string(),
652            "file-3".to_string(),
653        ];
654        let store = vector_store_with_files("Bulk Store", files.clone());
655
656        assert_eq!(store.name_ref(), Some("Bulk Store"));
657        assert_eq!(store.file_count(), 3);
658        assert_eq!(store.file_ids_ref(), files.as_slice());
659        assert!(store.has_files());
660    }
661
662    #[test]
663    fn test_temporary_vector_store() {
664        let store = temporary_vector_store("Temp Store", 30);
665
666        assert_eq!(store.name_ref(), Some("Temp Store"));
667        assert!(store.expires_after_ref().is_some());
668        assert_eq!(store.expires_after_ref().unwrap().days, 30);
669    }
670
671    #[test]
672    fn test_add_file_operation() {
673        let file_op = add_file_to_vector_store("store-123", "file-456");
674
675        assert_eq!(file_op.vector_store_id(), "store-123");
676        assert_eq!(file_op.file_id(), "file-456");
677    }
678
679    #[test]
680    fn test_search_operations() {
681        let basic_search = search_vector_store("store-123", "test query");
682        assert_eq!(basic_search.vector_store_id(), "store-123");
683        assert_eq!(basic_search.query(), "test query");
684        assert!(basic_search.limit_ref().is_none());
685
686        let limited_search = search_vector_store_with_limit("store-456", "limited query", 10);
687        assert_eq!(limited_search.limit_ref(), Some(10));
688    }
689
690    #[test]
691    fn test_filtered_search() {
692        let search = search_vector_store_with_limit("store-789", "filtered query", 5)
693            .filter("category", "docs")
694            .filter("priority", "high");
695
696        assert_eq!(search.filter_ref().len(), 2);
697        assert_eq!(
698            search.filter_ref().get("category"),
699            Some(&"docs".to_string())
700        );
701        assert_eq!(
702            search.filter_ref().get("priority"),
703            Some(&"high".to_string())
704        );
705    }
706
707    #[test]
708    fn test_enterprise_store_creation() {
709        let eng_store = create_engineering_store();
710        assert_eq!(eng_store.name_ref(), Some("Engineering Knowledge Base"));
711        assert!(eng_store.has_files());
712        assert!(eng_store.expires_after_ref().is_some());
713        assert_eq!(
714            eng_store.metadata_ref().get("department"),
715            Some(&"engineering".to_string())
716        );
717    }
718
719    #[test]
720    fn test_vector_store_builder_fluent_interface() {
721        let store = VectorStoreBuilder::new()
722            .name("Fluent Store")
723            .add_file("file-1")
724            .add_file("file-2")
725            .metadata("key1", "value1")
726            .metadata("key2", "value2")
727            .expires_after_days(60);
728
729        assert_eq!(store.name_ref(), Some("Fluent Store"));
730        assert_eq!(store.file_count(), 2);
731        assert_eq!(store.metadata_ref().len(), 2);
732        assert!(store.expires_after_ref().is_some());
733        assert_eq!(store.expires_after_ref().unwrap().days, 60);
734    }
735}