anytype 0.3.2

An ergonomic Anytype API client in rust
Documentation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
//! Integration Tests for anytype
//!
//! Covers:
//! - Public API contracts for spaces, types, properties, objects, search, tags, members
//! - Getters and setters for all PropertyFormats
//! - Mutation tests for object create/update/delete with cleanup
//! - Search and filters
//! - Error handling, validation, and integration surface correctness
//! - Property formats and custom property types
//!
//! ## Running
//!
//! ```bash
//! source .test-env
//! cargo test -p anytype --test integration
//! ```

mod common;

// =============================================================================
// Property Format Getters and Setters
// =============================================================================

mod property_formats {
    use anytype::{prelude::*, test_util::*};

    use super::common::{unique_test_name /*with_test_context_unit*/};

    /// Test creating objects with various property formats
    #[tokio::test]
    #[test_log::test]
    async fn test_property_format_setters() {
        with_test_context_unit(|ctx| async move {
            let name = unique_test_name("Property Format Test");

            // Create object with various property values
            let obj = ctx
                .client
                .new_object(&ctx.space_id, "page")
                .name(&name)
                .description("Testing property format setters")
                .set_text("description", "Test description via set_text")
                .create()
                .await
                .expect("Failed to create object with properties");
            ctx.register_object(&obj.id);

            // Verify the object was created
            assert_eq!(obj.name.as_deref(), Some(name.as_str()));
        })
        .await
    }

    /// Test reading property values from objects
    #[tokio::test]
    #[test_log::test]
    async fn test_property_format_getters() {
        with_test_context_unit(|ctx| async move {
            // Get an existing object with properties
            let objects = ctx
                .client
                .objects(&ctx.space_id)
                .limit(1)
                .list()
                .await
                .expect("Failed to list objects");

            if let Some(obj) = objects.iter().next() {
                // Verify we can read properties
                for prop in &obj.properties {
                    // Property should have valid structure
                    assert!(!prop.id.is_empty(), "Property ID should not be empty");
                    assert!(!prop.key.is_empty(), "Property key should not be empty");

                    // Test value accessors based on format
                    match prop.format() {
                        PropertyFormat::Text => {
                            let _ = prop.value.as_str();
                        }
                        PropertyFormat::Number => {
                            let _ = prop.value.as_number();
                        }
                        PropertyFormat::Checkbox => {
                            let _ = prop.value.as_bool();
                        }
                        PropertyFormat::Date => {
                            let _ = prop.value.as_date();
                        }
                        PropertyFormat::Select => {
                            let _ = prop.value.as_str();
                        }
                        PropertyFormat::MultiSelect
                        | PropertyFormat::Objects
                        | PropertyFormat::Files => {
                            let _ = prop.value.as_array();
                        }
                        _ => {}
                    }
                }
            }
        })
        .await
    }

    /// Test all property format types are recognized
    #[tokio::test]
    #[test_log::test]
    async fn test_property_format_coverage() {
        with_test_context_unit(|ctx| async move {
            let properties = ctx
                .client
                .properties(&ctx.space_id)
                .list()
                .await
                .expect("Failed to list properties");

            // Collect unique formats
            let mut found_formats: Vec<PropertyFormat> = Vec::new();
            for prop in properties.iter() {
                if !found_formats.iter().any(|f| *f == prop.format()) {
                    found_formats.push(prop.format());
                }
            }

            eprintln!("Found {} unique property formats:", found_formats.len());
            for format in &found_formats {
                eprintln!("  - {:?}", format);
            }

            // Should have at least Text format
            assert!(
                found_formats.contains(&PropertyFormat::Text),
                "Should have Text format"
            );
        })
        .await
    }
}

// =============================================================================
// Object CRUD Mutations
// =============================================================================

mod object_crud {
    use anytype::test_util::*;

    use super::common::unique_test_name;

    /// Test full object lifecycle: create, read, update, delete
    #[tokio::test]
    #[test_log::test]
    async fn test_object_crud_lifecycle() {
        with_test_context_unit(|ctx| async move {
            let original_name = unique_test_name("CRUD Test");
            let updated_name = format!("{} (Updated)", original_name);

            // CREATE
            let created = ctx
                .client
                .new_object(&ctx.space_id, "page")
                .name(&original_name)
                .body("# Test Content\n\nThis is test content.")
                .description("Created for CRUD test")
                .create()
                .await
                .expect("Failed to create object");
            ctx.register_object(&created.id);

            assert_eq!(created.name.as_deref(), Some(original_name.as_str()));
            let object_id = created.id.clone();

            // READ
            let read = ctx
                .client
                .object(&ctx.space_id, &object_id)
                .get()
                .await
                .expect("Failed to read object");
            assert_eq!(read.id, object_id);
            assert_eq!(read.name.as_deref(), Some(original_name.as_str()));

            // UPDATE
            let updated = ctx
                .client
                .update_object(&ctx.space_id, &object_id)
                .name(&updated_name)
                .body("# Updated Content\n\nThis content was updated.")
                .update()
                .await
                .expect("Failed to update object");
            assert_eq!(updated.name.as_deref(), Some(updated_name.as_str()));

            // Verify update persisted
            let verified = ctx
                .client
                .object(&ctx.space_id, &object_id)
                .get()
                .await
                .expect("Failed to verify update");
            assert_eq!(verified.name.as_deref(), Some(updated_name.as_str()));

            // DELETE (archive)
            let deleted = ctx
                .client
                .object(&ctx.space_id, &object_id)
                .delete()
                .await
                .expect("Failed to delete object");
            // Note: archived status may not be immediately reflected
            eprintln!("Delete returned archived={}", deleted.archived);
        })
        .await
    }

    /// Test creating multiple objects and batch operations
    #[tokio::test]
    #[test_log::test]
    async fn test_create_multiple_objects() {
        with_test_context_unit(|ctx| async move {
            let base_name = unique_test_name("Multi");
            let mut created_ids = Vec::new();

            // Create 3 objects
            for i in 1..=3 {
                let name = format!("{} Object {}", base_name, i);
                let obj = ctx
                    .client
                    .new_object(&ctx.space_id, "page")
                    .name(&name)
                    .create()
                    .await
                    .expect("Failed to create object");
                ctx.register_object(&obj.id);
                created_ids.push(obj.id);
            }

            assert_eq!(created_ids.len(), 3, "Should have created 3 objects");

            // Verify all can be read
            for id in &created_ids {
                let obj = ctx
                    .client
                    .object(&ctx.space_id, id)
                    .get()
                    .await
                    .expect("Failed to read created object");
                assert_eq!(&obj.id, id);
            }
        })
        .await
    }

    /// Test object body content handling
    #[tokio::test]
    #[test_log::test]
    async fn test_object_body_content() {
        with_test_context_unit(|ctx| async move {
            let name = unique_test_name("Body Content Test");
            let body_content = r#"# Heading 1

        This is a paragraph with **bold** and *italic* text.

        ## Heading 2

        - List item 1
        - List item 2
        - List item 3

        ```rust
        fn main() {
            println!("Hello, world!");
        }
        ```
        "#;

            // Create with body
            let obj = ctx
                .client
                .new_object(&ctx.space_id, "page")
                .name(&name)
                .body(body_content)
                .create()
                .await
                .expect("Failed to create object with body");
            ctx.register_object(&obj.id);

            // Read back and verify content
            let read = ctx
                .client
                .object(&ctx.space_id, &obj.id)
                .get()
                .await
                .expect("Failed to read object");

            // Content should be present as markdown or snippet
            assert!(
                read.markdown.is_some() || read.snippet.is_some(),
                "Should have content (markdown or snippet)"
            );
        })
        .await
    }
}

// =============================================================================
// Search and Filters
// =============================================================================

mod search_and_filters {
    use std::time::Duration;

    use anytype::{prelude::*, test_util::*};

    /// Test global search functionality
    #[tokio::test]
    #[test_log::test]
    async fn test_global_search() {
        with_test_context_unit(|ctx| async move {
            let results = ctx
                .client
                .search_global()
                .limit(10)
                .execute()
                .await
                .expect("Failed to execute global search");

            // Global search should return results
            eprintln!("Global search returned {} results", results.len());
        })
        .await
    }

    /// Test search within a specific space
    #[tokio::test]
    #[test_log::test]
    async fn test_space_search() {
        with_test_context_unit(|ctx| async move {
            let results = ctx
                .client
                .search_in(&ctx.space_id)
                .limit(10)
                .execute()
                .await
                .expect("Failed to execute space search");

            eprintln!("Space search returned {} results", results.len());

            // All results should be from the specified space
            for obj in &results {
                assert_eq!(
                    obj.space_id, ctx.space_id,
                    "Result should be from search space"
                );
            }
        })
        .await
    }

    /// Test search with text query
    #[tokio::test]
    #[test_log::test]
    async fn test_search_with_text() {
        with_test_context_unit(|ctx| async move {
            // Create a uniquely named object to search for
            let unique_term = format!("SearchTest{}", chrono::Utc::now().timestamp_millis());
            let obj = ctx
                .client
                .new_object(&ctx.space_id, "page")
                .name(&unique_term)
                .create()
                .await
                .expect("Failed to create searchable object");
            ctx.register_object(&obj.id);

            // Small delay to allow indexing
            tokio::time::sleep(Duration::from_millis(100)).await;

            // Search for the unique term
            let results = ctx
                .client
                .search_in(&ctx.space_id)
                .text(&unique_term)
                .execute()
                .await
                .expect("Failed to execute text search");

            eprintln!(
                "Text search for '{}' returned {} results",
                unique_term,
                results.len()
            );
        })
        .await
    }

    /// Test search with type filter
    #[tokio::test]
    #[test_log::test]
    async fn test_search_with_type_filter() {
        with_test_context_unit(|ctx| async move {
            let results = ctx
                .client
                .search_in(&ctx.space_id)
                .types(["page"])
                .limit(10)
                .execute()
                .await
                .expect("Failed to execute type-filtered search");

            // All results should be of type "page"
            for obj in &results {
                if let Some(ref typ) = obj.r#type {
                    assert_eq!(typ.key, "page", "Result should be of type 'page'");
                }
            }
        })
        .await
    }

    /// Test object list with filters
    #[tokio::test]
    #[test_log::test]
    async fn test_object_list_filters() {
        with_test_context_unit(|ctx| async move {
            // Test not_empty filter
            let results = ctx
                .client
                .objects(&ctx.space_id)
                .filter(Filter::not_empty("name"))
                .limit(5)
                .list()
                .await
                .expect("Failed to list with not_empty filter");

            eprintln!("not_empty(name) returned {} results", results.len());

            // All results should have non-empty names
            for obj in results.iter() {
                assert!(
                    obj.name.as_ref().map(|n| !n.is_empty()).unwrap_or(false),
                    "Object should have non-empty name"
                );
            }
        })
        .await
    }

    /// Test object list with is_empty filter
    #[tokio::test]
    #[test_log::test]
    async fn test_object_list_empty_filter() {
        with_test_context_unit(|ctx| async move {
            // Test is_empty filter on description
            let results = ctx
                .client
                .objects(&ctx.space_id)
                .filter(Filter::is_empty("description"))
                .limit(5)
                .list()
                .await
                .expect("Failed to list with is_empty filter");

            eprintln!("is_empty(description) returned {} results", results.len());
        })
        .await
    }
}

// =============================================================================
// Error Handling and Validation
// =============================================================================

mod error_handling {
    use anytype::{prelude::*, test_util::*};

    /// Test that invalid space ID returns appropriate error
    #[tokio::test]
    #[test_log::test]
    async fn test_invalid_space_id() {
        with_test_context_unit(|ctx| async move {
            let result = ctx.client.space("invalid-space-id-12345").get().await;

            match result {
                Err(AnytypeError::NotFound { obj_type, .. }) if &obj_type == "Space" => {
                    eprintln!("Correctly received NotFound for invalid space");
                }
                Err(AnytypeError::Validation { .. }) => {
                    eprintln!("Correctly received Validation error for invalid space");
                }
                Err(e) => {
                    eprintln!("Received error: {:?}", e);
                    // Accept any error for invalid space
                }
                Ok(_) => {
                    panic!("Expected error for invalid space ID");
                }
            }
        })
        .await
    }

    /// Test that invalid object ID returns appropriate error
    #[tokio::test]
    #[test_log::test]
    async fn test_invalid_object_id() {
        with_test_context_unit(|ctx| async move {
            let result = ctx
                .client
                .object(&ctx.space_id, "invalid-object-id-12345")
                .get()
                .await;

            match result {
                Err(AnytypeError::NotFound { obj_type, .. }) if &obj_type == "Object" => {
                    eprintln!("Correctly received NotFound for invalid object");
                }
                Err(AnytypeError::Validation { .. }) => {
                    eprintln!("Correctly received Validation error for invalid object");
                }
                Err(e) => {
                    eprintln!("Received error: {:?}", e);
                    // Accept any error for invalid object
                }
                Ok(_) => {
                    panic!("Expected error for invalid object ID");
                }
            }
        })
        .await
    }

    /// Test that invalid property ID returns appropriate error
    #[tokio::test]
    #[test_log::test]
    async fn test_invalid_property_id() {
        with_test_context_unit(|ctx| async move {
            let result = ctx
                .client
                .property(&ctx.space_id, "invalid-property-id-12345")
                .get()
                .await;

            match result {
                Err(AnytypeError::NotFound { obj_type, .. }) if &obj_type == "Property" => {
                    eprintln!("Correctly received NotFound for invalid property");
                }
                Err(AnytypeError::Validation { .. }) => {
                    eprintln!("Correctly received Validation error for invalid property");
                }
                Err(e) => {
                    eprintln!("Received error: {:?}", e);
                }
                Ok(_) => {
                    panic!("Expected error for invalid property ID");
                }
            }
        })
        .await
    }

    /// Test that invalid type ID returns appropriate error
    #[tokio::test]
    #[test_log::test]
    async fn test_invalid_type_id() {
        with_test_context_unit(|ctx| async move {
            let result = ctx
                .client
                .get_type(&ctx.space_id, "invalid-type-id-12345")
                .get()
                .await;

            match result {
                Err(AnytypeError::NotFound { obj_type, .. }) if &obj_type == "Type" => {
                    eprintln!("Correctly received NotFound for invalid type");
                }
                Err(AnytypeError::Validation { .. }) => {
                    eprintln!("Correctly received Validation error for invalid type");
                }
                Err(e) => {
                    eprintln!("Received error: {:?}", e);
                }
                Ok(_) => {
                    panic!("Expected error for invalid type ID");
                }
            }
        })
        .await
    }

    /// Test update without changes returns validation error
    #[tokio::test]
    #[test_log::test]
    async fn test_update_without_changes() {
        with_test_context_unit(|ctx| async move {
            // Get an existing object
            let objects = ctx
                .client
                .objects(&ctx.space_id)
                .limit(1)
                .list()
                .await
                .expect("Failed to list objects");

            if let Some(obj) = objects.iter().next() {
                // Try to update without setting any fields
                let result = ctx
                    .client
                    .update_object(&ctx.space_id, &obj.id)
                    .update()
                    .await;

                match result {
                    Err(AnytypeError::Validation { message }) => {
                        eprintln!("Correctly received validation error: {}", message);
                        assert!(
                            message.contains("must set at least one field"),
                            "Error should mention missing fields"
                        );
                    }
                    Err(e) => {
                        eprintln!("Received error: {:?}", e);
                    }
                    Ok(_) => {
                        panic!("Expected validation error for update without changes");
                    }
                }
            }
        })
        .await
    }

    /// Test that empty name is handled correctly
    #[tokio::test]
    #[test_log::test]
    async fn test_create_with_empty_name() {
        with_test_context_unit(|ctx| async move {
            // Creating with empty name may succeed (for Note types) or fail
            let result = ctx
                .client
                .new_object(&ctx.space_id, "page")
                .name("")
                .create()
                .await;

            match result {
                Ok(obj) => {
                    ctx.register_object(&obj.id);
                    eprintln!("Object created with empty name (ID: {})", obj.id);
                }
                Err(e) => {
                    eprintln!("Create with empty name failed (expected): {:?}", e);
                }
            }
        })
        .await
    }
}

// =============================================================================
// Property Formats and Custom Property Types
// =============================================================================

mod custom_properties {
    use std::collections::HashMap;

    use anytype::{prelude::*, test_util::*};
    use common::unique_test_name;

    use super::*;

    /// Test property key stability across list and get
    #[tokio::test]
    #[test_log::test]
    async fn test_property_key_stability() {
        with_test_context_unit(|ctx| async move {
            let properties = ctx
                .client
                .properties(&ctx.space_id)
                .list()
                .await
                .expect("Failed to list properties");

            // For each property, verify get returns same key
            for prop in properties.iter().take(5) {
                let fetched = ctx
                    .client
                    .property(&ctx.space_id, &prop.id)
                    .get()
                    .await
                    .expect("Failed to get property");

                assert_eq!(prop.key, fetched.key, "Property key should be stable");
                assert_eq!(
                    prop.format(),
                    fetched.format(),
                    "Property format should be stable"
                );
            }
        })
        .await
    }

    /// Test creating custom property
    #[tokio::test]
    #[test_log::test]
    async fn test_create_custom_property() {
        with_test_context_unit(|ctx| async move {
            let prop_name = unique_test_name("CustomProp");
            let prop_key = format!("custom_prop_{}", chrono::Utc::now().timestamp_millis());

            let result = ctx
                .client
                .new_property(&ctx.space_id, &prop_name, PropertyFormat::Text)
                .key(&prop_key)
                .create()
                .await;

            match result {
                Ok(prop) => {
                    ctx.register_property(&prop.id);
                    eprintln!("Created custom property: {} ({})", prop.name, prop.id);
                    assert_eq!(prop.name, prop_name);
                    assert_eq!(prop.format(), PropertyFormat::Text);
                }
                Err(e) => {
                    eprintln!("Failed to create custom property: {:?}", e);
                    // May fail if property already exists
                }
            }
        })
        .await
    }

    /// Test property format enum completeness
    #[tokio::test]
    #[test_log::test]
    async fn test_property_format_enum() {
        with_test_context_unit(|ctx| async move {
            let properties = ctx
                .client
                .properties(&ctx.space_id)
                .list()
                .await
                .expect("Failed to list properties");

            assert!(!properties.is_empty());

            // Track which formats we've seen
            let mut format_counts: HashMap<String, usize> = HashMap::new();

            for prop in properties.iter() {
                let format_name = format!("{:?}", prop.format());
                *format_counts.entry(format_name).or_insert(0) += 1;
            }

            eprintln!("Property format distribution:");
            for (format, count) in &format_counts {
                eprintln!("  {}: {}", format, count);
            }

            // Verify at least some formats are present
            assert!(!format_counts.is_empty(), "Should have at least one format");
        })
        .await
    }
}

// =============================================================================
// Pagination Tests
// =============================================================================

mod pagination {

    use anytype::test_util::*;
    use serial_test::serial;

    /// Test pagination limit is respected
    #[tokio::test]
    #[test_log::test]
    #[serial]
    async fn test_pagination_limit() {
        with_test_context_unit(|ctx| async move {
            let limit = 3;
            let results = ctx
                .client
                .objects(&ctx.space_id)
                .limit(limit)
                .list()
                .await
                .expect("Failed to list with limit");

            assert!(
                results.len() <= limit as usize,
                "Results should respect limit: got {} for limit {}",
                results.len(),
                limit
            );
        })
        .await
    }

    /// Test pagination offset
    #[tokio::test]
    #[test_log::test]
    #[serial]
    #[ignore]
    async fn test_pagination_offset() {
        with_test_context_unit(|ctx| async move {
            // Get first page
            let page1 = ctx
                .client
                .objects(&ctx.space_id)
                .limit(2)
                .offset(0)
                .list()
                .await
                .expect("Failed to get page 1");

            // Get second page
            let page2 = ctx
                .client
                .objects(&ctx.space_id)
                .limit(2)
                .offset(2)
                .list()
                .await
                .expect("Failed to get page 2");

            // Pages should be different (if enough data exists)
            if !page1.is_empty() && !page2.is_empty() {
                let page1_ids: Vec<&str> = page1.iter().map(|o| o.id.as_str()).collect();
                let page2_ids: Vec<&str> = page2.iter().map(|o| o.id.as_str()).collect();

                for id in &page2_ids {
                    assert!(
                        !page1_ids.contains(id),
                        "Page 2 should not contain items from page 1"
                    );
                }
            }
        })
        .await
    }

    /// Test collect_all for pagination
    #[tokio::test]
    #[test_log::test]
    #[serial]
    async fn test_collect_all() {
        with_test_context_unit(|ctx| async move {
            // Use small limit to force pagination
            let all_objects = ctx
                .client
                .objects(&ctx.space_id)
                .limit(5)
                .list()
                .await
                .expect("Failed to list objects")
                .collect_all()
                .await
                .expect("Failed to collect all");

            eprintln!("collect_all returned {} objects", all_objects.len());
        })
        .await
    }
}