1#![allow(clippy::used_underscore_binding)]
111#![allow(clippy::pub_underscore_fields)]
112
113#[cfg(feature = "couch_rs_derive")]
115#[allow(unused_imports)]
116#[macro_use]
117extern crate couch_rs_derive;
118
119#[cfg(feature = "couch_rs_derive")]
120#[doc(hidden)]
121pub use couch_rs_derive::*;
122pub use http;
124pub use std::borrow::Cow;
125
126#[allow(unused_macros)]
129#[macro_use]
130mod macros {
131 macro_rules! mod_use {
133 ($module:ident) => {
134 mod $module;
135 pub use self::$module::*;
136 };
137 }
138
139 macro_rules! json_extr {
142 ($e:expr) => {
143 serde_json::from_value($e.to_owned()).unwrap_or_default()
144 };
145 }
146
147 macro_rules! dtj {
150 ($e:expr) => {
151 js!(&$e.get_data())
152 };
153 }
154
155 macro_rules! js {
157 ($e:expr) => {
158 serde_json::to_string(&$e).unwrap()
159 };
160 }
161
162 macro_rules! s {
164 ($e:expr) => {
165 String::from($e)
166 };
167 }
168
169 macro_rules! tspec_ms {
171 ($tspec:ident) => {{ $tspec.sec * 1000 + $tspec.nsec as i64 / 1000000 }};
172 }
173
174 macro_rules! msnow {
176 () => {{
177 let tm = time::now().to_timespec();
178 tspec_ms!(tm)
179 }};
180 }
181
182 macro_rules! url_encode {
184 ($id:ident) => {{ url::form_urlencoded::byte_serialize($id.as_bytes()).collect::<String>() }};
185 }
186}
187
188mod client;
189pub mod database;
191
192pub mod typed;
194
195pub mod document;
197pub mod error;
199pub mod management;
201pub mod model;
204pub mod types;
206
207mod changes;
208
209pub use client::Client;
210
211#[allow(unused_mut, unused_variables)]
212#[cfg(feature = "integration-tests")]
213#[cfg(test)]
214mod couch_rs_tests {
215 use crate as couch_rs;
216 use couch_rs::{CouchDocument, document::TypedCouchDocument, types::document::DocumentId};
217 use serde::{Deserialize, Serialize};
218 use std::borrow::Cow;
219
220 #[derive(Serialize, Deserialize, CouchDocument, Default, Debug)]
221 pub struct TestDoc {
222 #[serde(skip_serializing_if = "String::is_empty")]
223 pub _id: DocumentId,
224 #[serde(skip_serializing_if = "String::is_empty")]
225 pub _rev: String,
226 pub first_name: String,
227 pub last_name: String,
228 }
229
230 #[derive(serde::Serialize, serde::Deserialize, Clone, Debug)]
231 struct TestDocImplementing {
232 my_id: String,
233 my_rev: String,
234 first_name: String,
235 last_name: String,
236 }
237 impl TypedCouchDocument for TestDocImplementing {
238 fn get_id(&self) -> Cow<'_, str> {
239 Cow::Borrowed(&self.my_id)
240 }
241
242 fn get_rev(&self) -> Cow<'_, str> {
243 Cow::Borrowed(&self.my_rev)
244 }
245
246 fn set_rev(&mut self, rev: &str) {
247 self.my_rev = rev.to_string();
248 }
249
250 fn set_id(&mut self, id: &str) {
251 self.my_id = id.to_string();
252 }
253
254 fn merge_ids(&mut self, other: &Self) {
255 self.my_id = other.my_id.clone();
256 }
257 }
258
259 mod client_tests {
260 use crate::{
261 client::Client,
262 couch_rs_tests::{TestDoc, TestDocImplementing},
263 document::TypedCouchDocument,
264 error::CouchError,
265 };
266 use reqwest::StatusCode;
267 use serde_json::json;
268
269 #[tokio::test]
270 async fn should_check_couchdbs_status() {
271 let client = Client::new_local_test().unwrap();
272 let status = client.check_status().await;
273 assert!(status.is_ok());
274 assert_eq!("The Apache Software Foundation", status.unwrap().vendor.name);
275 }
276
277 #[tokio::test]
278 async fn should_create_test_db() {
279 let client = Client::new_local_test().unwrap();
280 let dbw = client.db("should_create_test_db").await;
281 assert!(dbw.is_ok());
282
283 client
284 .destroy_db("should_create_test_db")
285 .await
286 .expect("can not destroy db");
287 }
288
289 #[tokio::test]
290 async fn should_create_test_db_with_a_complex_name() {
291 let client = Client::new_local_test().unwrap();
297 let dbname = "abcdefghijklmnopqrstuvwxyz+0123456789_$()-/";
298 let dbw = client.db(dbname).await;
299 assert!(dbw.is_ok());
300 assert!(client.exists(dbname).await.is_ok());
301 let info = client.get_info(dbname).await.expect("can not get db info");
302 assert_eq!(info.db_name, dbname);
303 client.destroy_db(dbname).await.expect("can not destroy db");
304 }
305
306 #[tokio::test]
307 async fn should_get_information_on_test_db() {
308 let client = Client::new_local_test().unwrap();
309 let dbname = "should_get_information_on_test_db";
310 let dbw = client.db(dbname).await;
311 assert!(dbw.is_ok());
312 assert!(client.exists(dbname).await.is_ok());
313 let info = client.get_info(dbname).await.expect("can not get db info");
314 assert_eq!(info.db_name, dbname);
315 client.destroy_db(dbname).await.expect("can not destroy db");
316 }
317
318 #[tokio::test]
319 async fn should_not_exist() {
320 let client = Client::new_local_test().unwrap();
321 let dbname = "should_not_exist";
322 let dbw = client.exists(dbname).await;
323 assert!(!client.exists(dbname).await.unwrap());
324 }
325
326 #[tokio::test]
327 async fn should_create_a_document() {
328 let client = Client::new_local_test().unwrap();
329 let dbw = client.db("should_create_a_document").await;
330 assert!(dbw.is_ok());
331 let db = dbw.unwrap();
332
333 let mut doc = json!({
334 "thing": true
335 });
336 let ndoc_result = db.create(&mut doc).await;
337
338 assert!(ndoc_result.is_ok());
339
340 let details = ndoc_result.unwrap();
341 assert_eq!(details.rev, doc.get("_rev").unwrap().as_str().unwrap());
342
343 client
344 .destroy_db("should_create_a_document")
345 .await
346 .expect("can not destroy db");
347 }
348
349 #[tokio::test]
350 async fn should_create_a_typed_document() {
351 let client = Client::new_local_test().unwrap();
352 let dbw = client.db("should_create_a_typed_document").await;
353 assert!(dbw.is_ok());
354 let db = dbw.unwrap();
355 let mut my_doc = TestDoc {
356 _id: String::new(),
357 _rev: String::new(),
358 first_name: "John".to_string(),
359 last_name: "Doe".to_string(),
360 };
361
362 let ndoc_result = db.create(&mut my_doc).await;
363
364 assert!(ndoc_result.is_ok());
365
366 let details = ndoc_result.unwrap();
367 assert_eq!(details.rev, my_doc._rev);
368 assert!(!my_doc._id.is_empty());
369 assert!(my_doc._rev.starts_with("1-"));
370
371 client
372 .destroy_db("should_create_a_typed_document")
373 .await
374 .expect("can not destroy db");
375 }
376
377 #[tokio::test]
378 async fn should_keep_id_creating_a_typed_document_deriving() {
379 const UNIQUE_ID: &str = "unique_id";
380
381 let client = Client::new_local_test().unwrap();
382 let dbw = client.db("should_keep_id_creating_a_typed_document").await;
383 assert!(dbw.is_ok());
384 let db = dbw.unwrap();
385 let mut my_doc = TestDoc {
386 _id: UNIQUE_ID.to_string(),
387 _rev: String::new(),
388 first_name: "John".to_string(),
389 last_name: "Doe".to_string(),
390 };
391
392 let ndoc_result = db.create(&mut my_doc).await;
393
394 assert!(ndoc_result.is_ok());
395
396 let details = ndoc_result.unwrap();
397 assert_eq!(details.rev, my_doc._rev);
398 assert!(!my_doc._id.is_empty());
399 assert!(my_doc._rev.starts_with("1-"));
400
401 let document: TestDoc = db.get(UNIQUE_ID).await.expect("can not get doc");
402
403 client
404 .destroy_db("should_keep_id_creating_a_typed_document")
405 .await
406 .expect("can not destroy db");
407 }
408
409 #[tokio::test]
410 async fn should_keep_id_creating_a_typed_document_implementing() {
411 create_read_remove(Some("id".to_string()), None).await;
412 }
413
414 #[tokio::test]
415 async fn should_ignore_rev_creating_a_typed_document_implementing() {
416 create_read_remove(Some("id".to_string()), Some("something".to_string())).await;
417 }
418
419 #[tokio::test]
420 async fn should_update_id_creating_a_typed_document_implementing() {
421 create_read_remove(None, None).await;
422 }
423
424 async fn create_read_remove(id: Option<String>, rev: Option<String>) {
425 let client = Client::new_local_test().unwrap();
426 let dbw = client.db("create_read_remove_with_rev").await;
427 assert!(dbw.is_ok());
428 let db = dbw.unwrap();
429 let (id, autogenerated_id) = if let Some(id) = id {
430 (id, false)
431 } else {
432 (String::new(), true)
433 };
434 let rev = rev.unwrap_or_default();
435
436 let mut my_doc = TestDocImplementing {
437 my_id: id.clone(),
438 my_rev: rev.clone(),
439 first_name: "John".to_string(),
440 last_name: "Doe".to_string(),
441 };
442
443 let details = db
444 .create(&mut my_doc)
445 .await
446 .unwrap_or_else(|err| panic!("can not create doc with rev '{rev}': {err}"));
447
448 assert_eq!(details.rev, my_doc.my_rev);
449 if autogenerated_id {
450 assert!(!my_doc.get_id().is_empty(), "Found empty _id for document {my_doc:?}");
451 assert_ne!(
452 my_doc.my_id, id,
453 "generated id and original id (empty) should be different"
454 );
455 } else {
456 assert_eq!(my_doc.my_id, id);
457 }
458 assert!(!my_doc.get_rev().is_empty(), "Found empty _rev for document {my_doc:?}");
459
460 let document: TestDocImplementing = db.get(&my_doc.my_id).await.expect("can not get doc");
461 assert!(db.remove(&document).await.is_ok(), "can not remove doc {document:?}");
462
463 client
464 .destroy_db("create_read_remove_with_rev")
465 .await
466 .expect("can not destroy db");
467 }
468
469 #[tokio::test]
470 async fn should_keep_id_bulk_creating_a_typed_document_implementing() {
471 const UNIQUE_ID: &str = "unique_id";
472 let client = Client::new_local_test().unwrap();
473 let dbw = client
474 .db("should_keep_id_bulk_creating_a_typed_document_implementing")
475 .await;
476 assert!(dbw.is_ok());
477 let db = dbw.unwrap();
478 let mut my_doc = TestDocImplementing {
479 my_id: UNIQUE_ID.to_string(),
480 my_rev: String::default(),
481 first_name: "John".to_string(),
482 last_name: "Doe".to_string(),
483 };
484
485 let mut docs = vec![my_doc];
486 let results = db
487 .bulk_docs(&mut docs)
488 .await
489 .unwrap_or_else(|err| panic!("can not create doc: {err}"));
490 let my_doc = docs.into_iter().next().expect("no doc found");
491 let details = results
492 .into_iter()
493 .collect::<Result<Vec<_>, CouchError>>()
494 .expect("operation failed")
495 .into_iter()
496 .next()
497 .expect("no result found");
498 assert_eq!(details.rev, my_doc.my_rev);
499 assert_eq!(my_doc.my_id, UNIQUE_ID);
500 assert!(!my_doc.get_rev().is_empty(), "Found empty _rev for document {my_doc:?}");
501
502 let document: TestDocImplementing = db.get(UNIQUE_ID).await.expect("can not get doc");
503 assert!(db.remove(&document).await.is_ok(), "can not remove doc");
504
505 client
506 .destroy_db("should_keep_id_bulk_creating_a_typed_document_implementing")
507 .await
508 .expect("can not destroy db");
509 }
510
511 #[tokio::test]
512 async fn should_create_bulk_documents() {
513 let client = Client::new_local_test().unwrap();
514 let dbname = "should_create_bulk_documents";
515 let dbw = client.db(dbname).await;
516 assert!(dbw.is_ok());
517 let db = dbw.unwrap();
518
519 let mut docs = vec![
520 json!({
521 "_id":"first",
522 "thing": true
523 }),
524 json!({
525 "_id":"first",
526 "thing": false
527 }),
528 ];
529 let ndoc_result = db.bulk_docs(&mut docs).await;
530
531 assert!(ndoc_result.is_ok());
532
533 let mut ndoc_result = ndoc_result.unwrap().into_iter();
534 let first_result = ndoc_result.next().unwrap();
535 assert!(first_result.is_ok());
536 let mut docs = docs.into_iter();
537 let first_doc = docs.next().unwrap();
538 assert_eq!(
539 first_doc.as_object().unwrap().get("_rev").unwrap().as_str().unwrap(),
540 first_result.unwrap().rev.as_str()
541 );
542
543 let second_result = ndoc_result.next().unwrap();
544 assert!(second_result.is_err());
545 assert_eq!(second_result.err().unwrap().status(), Some(StatusCode::CONFLICT));
546
547 client.destroy_db(dbname).await.expect("can not destroy db");
548 }
549
550 #[tokio::test]
551 async fn should_destroy_the_db() {
552 let client = Client::new_local_test().unwrap();
553 client.db("should_destroy_the_db").await.expect("can not create db");
554
555 assert!(client.destroy_db("should_destroy_the_db").await.unwrap());
556 }
557 }
558
559 mod database_tests {
560 use crate::{
561 client::Client,
562 database::Database,
563 document::{DocumentCollection, TypedCouchDocument},
564 error::{CouchResult, CouchResultExt},
565 management::{ClusterSetup, EnsureDbsExist},
566 types,
567 types::{
568 find::FindQuery,
569 query::{QueriesParams, QueryParams},
570 view::{CouchFunc, CouchViews, ViewCollection},
571 },
572 };
573 use serde_json::{Value, json};
574 use tokio::sync::{
575 mpsc,
576 mpsc::{Receiver, Sender},
577 };
578
579 async fn setup(dbname: &str) -> (Client, Database, Value) {
580 let client = Client::new_local_test().unwrap();
581 let dbw = client.db(dbname).await;
582 assert!(dbw.is_ok());
583 let db = dbw.unwrap();
584
585 let mut doc = json!({
586 "thing": true
587 });
588 let ndoc_result = db.create(&mut doc).await;
589
590 assert!(ndoc_result.is_ok());
591
592 let details = ndoc_result.unwrap();
593 assert_eq!(details.rev, doc.get("_rev").unwrap().as_str().unwrap());
594 (client, db, doc)
595 }
596
597 async fn setup_multiple(dbname: &str, nr_of_docs: usize) -> (Client, Database, Vec<Value>) {
598 let client = Client::new_local_test().unwrap();
599 let dbw = client.db(dbname).await;
600 assert!(dbw.is_ok());
601 let db = dbw.unwrap();
602 let mut docs = vec![];
603
604 for _ in 0..nr_of_docs {
605 let mut doc = json!({
606 "thing": true
607 });
608 let ndoc_result = db.create(&mut doc).await;
609
610 assert!(ndoc_result.is_ok());
611
612 let details = ndoc_result.unwrap();
613 assert_eq!(details.rev, doc.get("_rev").unwrap().as_str().unwrap());
614
615 docs.push(doc);
616 }
617
618 (client, db, docs)
619 }
620
621 async fn teardown(client: Client, dbname: &str) {
622 assert!(client.destroy_db(dbname).await.unwrap());
623 }
624
625 #[tokio::test]
626 async fn should_update_a_document() {
627 let (client, db, mut doc) = setup("should_update_a_document").await;
628
629 doc["thing"] = json!(false);
630
631 let save_result = db.save(&mut doc).await;
632 assert!(save_result.is_ok());
633 let details = save_result.unwrap();
634 assert_eq!(doc["_rev"], details.rev);
635
636 teardown(client, "should_update_a_document").await;
637 }
638
639 #[tokio::test]
640 async fn should_handle_a_document_plus() {
641 let dbname = "should_handle_a_document_plus";
642 let (client, db, mut doc) = setup(dbname).await;
643
644 assert!(db.remove(&doc).await.is_ok());
645 assert_eq!(db.get_all_raw().await.unwrap().rows.len(), 0);
647
648 let id = "1+2";
650 let mut created = json!({ "_id": id });
651 let details = db.create(&mut created).await.unwrap();
652 assert_eq!(details.id, id);
653
654 let save_result = db.save(&mut created).await;
656 assert!(save_result.is_ok());
657 assert_eq!(db.get_all_raw().await.unwrap().rows.len(), 1);
659
660 assert!(db.remove(&created).await.is_ok());
662 assert_eq!(db.get_all_raw().await.unwrap().rows.len(), 0);
664
665 teardown(client, dbname).await;
666 }
667
668 #[tokio::test]
669 async fn should_remove_a_document() {
670 let (client, db, doc) = setup("should_remove_a_document").await;
671 assert!(db.remove(&doc).await.is_ok());
672
673 teardown(client, "should_remove_a_document").await;
674 }
675
676 #[tokio::test]
677 async fn should_recognize_a_non_existent_document() {
678 let (client, db, doc) = setup("should_recognize_a_non_existent_document").await;
679 let result = db.get_raw("non_existent").await;
680 assert!(result.expect_err("should be a 404").is_not_found());
681 teardown(client, "should_recognize_a_non_existent_document").await;
682 }
683
684 #[tokio::test]
685 async fn should_turn_a_non_existent_document_into_an_option() {
686 let (client, db, doc) = setup("should_turn_a_non_existent_document_into_an_option").await;
687 let result = db.get_raw("non_existent").await;
688 let maybe_doc = result.into_option();
689 assert!(maybe_doc.expect("should not be an error").is_none());
690 teardown(client, "should_turn_a_non_existent_document_into_an_option").await;
691 }
692
693 #[tokio::test]
694 async fn should_get_a_single_document() {
695 let (client, ..) = setup("should_get_a_single_document").await;
696 teardown(client, "should_get_a_single_document").await;
697 }
698
699 #[tokio::test]
700 async fn should_get_a_document_with_a_space_in_id() {
701 let (client, db, _) = setup("should_get_a_document_with_a_space_in_id").await;
702 let space_doc_result = db
703 .create(&mut json!({
704 "_id": "some crazy name"
705 }))
706 .await;
707 assert!(space_doc_result.is_ok());
708
709 let doc_result = db.get_raw("some crazy name").await;
710 assert!(doc_result.is_ok());
711
712 teardown(client, "should_get_a_document_with_a_space_in_id").await;
713 }
714
715 async fn setup_create_indexes(dbname: &str) -> (Client, Database, Value) {
716 let (client, db, doc) = setup(dbname).await;
717
718 let spec = types::index::IndexFields::new(vec![types::find::SortSpec::Simple(s!("thing"))]);
719
720 let res = db.insert_index("thing-index", spec, None, None).await;
721
722 assert!(res.is_ok());
723
724 (client, db, doc)
725 }
726
727 #[tokio::test]
728 async fn should_create_index_in_db() {
729 let (client, db, _) = setup_create_indexes("should_create_index_in_db").await;
730 teardown(client, "should_create_index_in_db").await;
731 }
732
733 #[tokio::test]
734 async fn should_list_indexes_in_db() {
735 let (client, db, _) = setup_create_indexes("should_list_indexes_in_db").await;
736
737 let index_list = db.read_indexes().await.unwrap();
738 assert!(index_list.indexes.len() > 1);
739 let findex = &index_list.indexes[1];
740
741 assert_eq!(findex.name.as_str(), "thing-index");
742 teardown(client, "should_list_indexes_in_db").await;
743 }
744
745 #[tokio::test]
746 async fn should_insert_index_in_db() {
747 let (client, db, _) = setup("should_insert_index_in_db").await;
748
749 let spec = types::index::IndexFields::new(vec![types::find::SortSpec::Simple(s!("thing"))]);
750
751 let res = db.insert_index("thing-index", spec, None, None).await;
752 assert!(res.is_ok());
753
754 teardown(client, "should_insert_index_in_db").await;
755 }
756
757 #[tokio::test]
758 async fn should_find_documents_in_db() {
759 let (client, db, doc) = setup_create_indexes("should_find_documents_in_db").await;
760 let query = FindQuery::new_from_value(json!({
761 "selector": {
762 "thing": true
763 },
764 "limit": 1,
765 "sort": [{
766 "thing": "desc"
767 }]
768 }));
769
770 let documents_res = db.find_raw(&query).await;
771
772 assert!(documents_res.is_ok());
773 let documents = documents_res.unwrap();
774 assert_eq!(documents.rows.len(), 1);
775
776 teardown(client, "should_find_documents_in_db").await;
777 }
778
779 #[tokio::test]
780 async fn should_bulk_get_a_document() {
781 let (client, db, doc) = setup("should_bulk_get_a_document").await;
782 let id = doc.get_id().into_owned();
783
784 let collection = db.get_bulk_raw(vec![id]).await.unwrap();
785 assert_eq!(collection.rows.len(), 1);
786 assert!(db.remove(&doc).await.is_ok());
787
788 teardown(client, "should_bulk_get_a_document").await;
789 }
790
791 #[tokio::test]
792 async fn should_bulk_get_invalid_documents() {
793 let (client, db, doc) = setup("should_bulk_get_invalid_documents").await;
794 let id = doc.get_id().into_owned();
795 let invalid_id = "does_not_exist".to_string();
796
797 let collection = db.get_bulk_raw(vec![id, invalid_id]).await.unwrap();
798 assert_eq!(collection.rows.len(), 1);
799 assert!(db.remove(&doc).await.is_ok());
800
801 teardown(client, "should_bulk_get_invalid_documents").await;
802 }
803
804 #[tokio::test]
805 async fn should_get_all_documents_with_keys() {
806 let (client, db, doc) = setup("should_get_all_documents_with_keys").await;
807 let id = doc.get_id().into_owned();
808
809 let params = QueryParams::from_keys(vec![id]);
810
811 let collection = db.get_all_params_raw(Some(params)).await.unwrap();
812 assert_eq!(collection.rows.len(), 1);
813 assert!(db.remove(&doc).await.is_ok());
814
815 teardown(client, "should_get_all_documents_with_keys").await;
816 }
817
818 #[tokio::test]
819 async fn should_query_documents_with_keys() {
820 let db_name = "should_query_documents_with_keys";
821 let (client, db, doc) = setup(db_name).await;
822 let id = doc.get_id().into_owned();
823 let view_name = "testViewAll";
824 db.create_view(
825 view_name,
826 CouchViews::new(
827 view_name,
828 CouchFunc {
829 map: r"function(doc) {{
830 emit(doc._id, null);
831 }}"
832 .to_string(),
833 reduce: None,
834 },
835 ),
836 )
837 .await
838 .unwrap();
839 let mut second_doc = json!({
840 "thing": true
841 });
842 let details = db.create(&mut second_doc).await.unwrap();
843 let ndoc_id = details.id;
844 let single_view_name = "testViewSingle";
845 db.create_view(
846 single_view_name,
847 CouchViews::new(
848 single_view_name,
849 CouchFunc {
850 map: format!(
851 r#"function(doc) {{
852 if(doc._id === "{ndoc_id}") {{
853 emit(doc._id, null);
854 }}
855 }}"#
856 )
857 .to_string(),
858 reduce: None,
859 },
860 ),
861 )
862 .await
863 .unwrap();
864
865 assert_eq!(
867 db.query_raw(
868 view_name,
869 view_name,
870 Some(QueryParams::from_keys(vec![id.clone().into()]))
871 )
872 .await
873 .unwrap()
874 .rows
875 .len(),
876 1
877 );
878 assert_eq!(
879 db.query_raw(
880 single_view_name,
881 single_view_name,
882 Some(QueryParams::from_keys(vec![id.into()])),
883 )
884 .await
885 .unwrap()
886 .rows
887 .len(),
888 0
889 );
890
891 assert!(db.remove(&second_doc).await.is_ok());
892 assert!(db.remove(&doc).await.is_ok());
893
894 teardown(client, db_name).await;
895 }
896
897 #[tokio::test]
898 async fn should_query_documents_with_key() {
899 let db_name = "should_query_documents_with_key";
900 let (client, db, doc) = setup(db_name).await;
901 let id = doc.get_id().into_owned();
902 let view_name = "testViewAll";
903 db.create_view(
904 view_name,
905 CouchViews::new(
906 view_name,
907 CouchFunc {
908 map: r"function(doc) {{
909 emit(doc._id, null);
910 }}"
911 .to_string(),
912 reduce: None,
913 },
914 ),
915 )
916 .await
917 .unwrap();
918
919 let design_info = db.get_design_info(view_name).await.unwrap();
921 assert_eq!(design_info.name, view_name);
922
923 let mut ndoc = json!({
924 "thing": true
925 });
926 let details = db.create(&mut ndoc).await.unwrap();
927 let ndoc_id = ndoc.get_id().into_owned();
928 let single_view_name = "testViewSingle";
929 db.create_view(
930 single_view_name,
931 CouchViews::new(
932 single_view_name,
933 CouchFunc {
934 map: format!(
935 r#"function(doc) {{
936 if(doc._id === "{ndoc_id}") {{
937 emit(doc._id, null);
938 }}
939 }}"#
940 )
941 .to_string(),
942 reduce: None,
943 },
944 ),
945 )
946 .await
947 .unwrap();
948
949 let one_key = QueryParams {
951 key: Some(doc.get_id().into()),
952 ..Default::default()
953 };
954
955 assert_eq!(
956 db.query_raw(view_name, view_name, Some(one_key.clone()))
957 .await
958 .unwrap()
959 .rows
960 .len(),
961 1
962 );
963 assert_eq!(
964 db.query_raw(single_view_name, single_view_name, Some(one_key))
965 .await
966 .unwrap()
967 .rows
968 .len(),
969 0
970 );
971
972 assert!(db.remove(&ndoc).await.is_ok());
973 assert!(db.remove(&doc).await.is_ok());
974
975 teardown(client, db_name).await;
976 }
977
978 #[tokio::test]
979 async fn should_query_documents_with_defaultparams() {
980 let dbname = "should_query_documents_with_defaultparams";
981 let (client, db, doc) = setup(dbname).await;
982 let id = doc.get_id().into_owned();
983 let view_name = "testViewAll";
984 db.create_view(
985 view_name,
986 CouchViews::new(
987 view_name,
988 CouchFunc {
989 map: r"function(doc) {{
990 emit(doc._id, null);
991 }}"
992 .to_string(),
993 reduce: None,
994 },
995 ),
996 )
997 .await
998 .unwrap();
999 let mut ndoc = json!({
1000 "thing": true
1001 });
1002 let details = db.create(&mut ndoc).await.unwrap();
1003 let ndoc_id = ndoc.get_id().into_owned();
1004 let single_view_name = "testViewSingle";
1005 db.create_view(
1006 single_view_name,
1007 CouchViews::new(
1008 single_view_name,
1009 CouchFunc {
1010 map: format!(
1011 r#"function(doc) {{
1012 if(doc._id === "{ndoc_id}") {{
1013 emit(doc._id, null);
1014 }}
1015 }}"#
1016 )
1017 .to_string(),
1018 reduce: None,
1019 },
1020 ),
1021 )
1022 .await
1023 .unwrap();
1024
1025 let query_result = db.query_raw(view_name, view_name, None).await;
1026
1027 assert_eq!(query_result.unwrap().rows.len(), 2);
1029 assert_eq!(
1030 db.query_raw(single_view_name, single_view_name, None)
1031 .await
1032 .unwrap()
1033 .rows
1034 .len(),
1035 1
1036 );
1037 assert_eq!(
1039 db.query_raw(view_name, view_name, Some(QueryParams::default()))
1040 .await
1041 .unwrap()
1042 .rows
1043 .len(),
1044 2
1045 );
1046 assert_eq!(
1047 db.query_raw(single_view_name, single_view_name, Some(QueryParams::default()))
1048 .await
1049 .unwrap()
1050 .rows
1051 .len(),
1052 1
1053 );
1054
1055 assert!(db.remove(&ndoc).await.is_ok());
1056 assert!(db.remove(&doc).await.is_ok());
1057
1058 teardown(client, dbname).await;
1059 }
1060
1061 #[tokio::test]
1062 async fn should_get_many_all_documents_with_keys() {
1063 let dbname = "should_get_many_all_documents_with_keys";
1064 let (client, db, docs) = setup_multiple(dbname, 4).await;
1065 let doc = docs.first().unwrap();
1066
1067 let params1 = QueryParams {
1068 key: Some(doc.get_id().into_owned()),
1069 ..Default::default()
1070 };
1071 let params2 = QueryParams {
1072 include_docs: Some(true),
1073 ..Default::default()
1074 };
1075 let mut params3 = QueryParams::default();
1076
1077 let params = vec![params1, params2, params3];
1078 let collections = db.query_many_all_docs(QueriesParams::new(params)).await.unwrap();
1079
1080 assert_eq!(collections.len(), 3);
1081 assert_eq!(collections.first().unwrap().rows.len(), 1);
1082 assert!(collections.first().unwrap().rows.first().unwrap().doc.is_none());
1084 assert_eq!(collections.get(1).unwrap().rows.len(), 4);
1086 assert!(collections.get(1).unwrap().rows.first().unwrap().doc.is_some());
1087 assert_eq!(collections.get(2).unwrap().rows.len(), 4);
1089 assert!(collections.get(2).unwrap().rows.first().unwrap().doc.is_none());
1090
1091 for doc in docs {
1092 assert!(db.remove(&doc).await.is_ok());
1093 }
1094
1095 teardown(client, dbname).await;
1096 }
1097
1098 #[tokio::test]
1099 async fn should_handle_null_view_keys() {
1100 let dbname = "should_handle_null_view_keys";
1101 let (client, db, docs) = setup_multiple(dbname, 4).await;
1102 let doc = docs.first().unwrap();
1103 let count_by_id = r"function (doc) {
1104 emit(doc._id, null);
1105 }";
1106 let view_name = "should_handle_null_view_keys";
1107 assert!(
1116 db.create_view(
1117 view_name,
1118 CouchViews::new(view_name, CouchFunc::new(count_by_id, Some("_count"))),
1119 )
1120 .await
1121 .is_ok()
1122 );
1123
1124 assert!(db.query_raw(view_name, view_name, None).await.is_ok());
1125
1126 teardown(client, dbname).await;
1127 }
1128
1129 #[tokio::test]
1130 async fn should_handle_null_values() {
1131 let dbname = "should_handle_null_values";
1132 let nr_of_docs = 4;
1133 let (client, db, docs) = setup_multiple(dbname, nr_of_docs).await;
1134 let doc = docs.first().unwrap();
1135 let count_by_id = r"function (doc) {
1137 emit(doc._id, null);
1138 }";
1139 let view_name = "should_handle_null_values";
1140 assert!(
1147 db.create_view(view_name, CouchViews::new(view_name, CouchFunc::new(count_by_id, None)),)
1148 .await
1149 .is_ok(),
1150 "problems creating view"
1151 );
1152
1153 let options = QueryParams::from_keys(vec!["doesnotexist".to_string()]);
1155 let result: CouchResult<ViewCollection<String, String, Value>> =
1157 db.query(view_name, view_name, Some(options)).await;
1158
1159 match result {
1160 Ok(_) => {}
1161 Err(e) => {
1162 panic!("problems executing query: {e}");
1163 }
1164 }
1165
1166 let result: CouchResult<ViewCollection<String, String, Value>> = db.query(view_name, view_name, None).await;
1168
1169 match result {
1170 Ok(entries) => {
1171 panic!("previous query should have failed, but succeeded");
1172 }
1173 Err(e) => {}
1174 }
1175
1176 let result: CouchResult<ViewCollection<String, Value, Value>> = db.query(view_name, view_name, None).await;
1178
1179 match result {
1180 Ok(entries) => {
1181 assert_eq!(nr_of_docs, entries.rows.len());
1182 }
1183 Err(e) => {
1184 panic!("{}", e)
1185 }
1186 }
1187 teardown(client, dbname).await;
1188 }
1189
1190 #[tokio::test]
1191 async fn should_bulk_insert_and_get_many_docs() {
1192 let (client, db, _doc) = setup("should_bulk_insert_and_get_many_docs").await;
1193 let mut docs: Vec<Value> = (0..2000)
1194 .map(|idx| {
1195 json!({
1196 "_id": format!("bd_{}", idx),
1197 "count": idx,
1198 })
1199 })
1200 .collect();
1201
1202 db.bulk_docs(&mut docs).await.expect("should insert 2000 documents");
1203
1204 let (tx, mut rx): (Sender<DocumentCollection<Value>>, Receiver<DocumentCollection<Value>>) =
1206 mpsc::channel(1000);
1207
1208 let t = tokio::spawn(async move {
1210 db.get_all_batched(tx, 0, 0).await.expect("can not launch batch fetch");
1211 });
1212
1213 let mut retrieved = 0;
1214 while let Some(all_docs) = rx.recv().await {
1215 retrieved += all_docs.total_rows;
1216 }
1217
1218 assert_eq!(retrieved, 2001);
1220
1221 t.await.unwrap();
1223 teardown(client, "should_bulk_insert_and_get_many_docs").await;
1224 }
1225
1226 #[tokio::test]
1227 async fn should_bulk_upsert_docs() {
1228 let (client, db, _doc) = setup("should_bulk_upsert_docs").await;
1229 let count = 3;
1230 let mut docs: Vec<Value> = (0..count)
1231 .map(|idx| {
1232 json!({
1233 "_id": format!("bd_{}", idx),
1234 "value": "hello",
1235 "count": idx,
1236 })
1237 })
1238 .collect();
1239
1240 db.bulk_docs(&mut docs).await.expect("should insert documents");
1241
1242 for doc in &mut docs {
1243 doc.as_object_mut()
1244 .unwrap()
1245 .insert("updated".to_string(), Value::Bool(true));
1246 }
1247
1248 let res = db.bulk_upsert(&mut docs).await.expect("should upsert documents");
1249
1250 for i in 0..count {
1251 assert_eq!(
1252 res[i].as_ref().unwrap().rev,
1253 docs[i].get_rev(),
1254 "Received rev for item {}: '{}' does not match document rev: '{}'",
1255 i,
1256 res[i].as_ref().unwrap().rev,
1257 docs[i].get_rev()
1258 );
1259 }
1260 let ids: Vec<String> = (0..count).map(|idx| format!("bd_{idx}")).collect();
1261 let docs = db.get_bulk::<Value>(ids).await.expect("should get documents");
1262
1263 for i in 0..count {
1264 assert!(docs[i].get_rev() == res[i].as_ref().unwrap().rev);
1265 assert!(
1266 docs[i]
1267 .as_object()
1268 .expect("should be an object")
1269 .get("updated")
1270 .expect("should have updated key")
1271 == true
1272 );
1273 }
1274 teardown(client, "should_bulk_upsert_docs").await;
1275 }
1276
1277 #[tokio::test]
1278 async fn should_retrieve_membership() {
1279 let client = Client::new_local_test().unwrap();
1280 let membership = client
1281 .membership()
1282 .await
1283 .expect("unable to retrieve cluster membership");
1284 dbg!(membership);
1285 }
1286
1287 #[tokio::test]
1288 async fn should_retrieve_cluster_setup_status() {
1289 let client = Client::new_local_test().unwrap();
1290 let cluster_setup = client
1291 .cluster_setup(EnsureDbsExist::default())
1292 .await
1293 .expect("unable to retrieve cluster setup status");
1294 assert_eq!(cluster_setup, ClusterSetup::ClusterEnabled);
1295 }
1296 }
1297}