From b6ec1f1c6dbace826e329a5fe5fbf1aa717c420e Mon Sep 17 00:00:00 2001 From: Tamo Date: Thu, 12 Jan 2023 18:56:27 +0100 Subject: [PATCH 1/3] add functionnal + error tests on the swap_indexes route --- meilisearch/tests/integration.rs | 1 + meilisearch/tests/swap_indexes/errors.rs | 78 +++++ meilisearch/tests/swap_indexes/mod.rs | 357 +++++++++++++++++++++++ 3 files changed, 436 insertions(+) create mode 100644 meilisearch/tests/swap_indexes/errors.rs create mode 100644 meilisearch/tests/swap_indexes/mod.rs diff --git a/meilisearch/tests/integration.rs b/meilisearch/tests/integration.rs index 25b4e49b6..4383aea57 100644 --- a/meilisearch/tests/integration.rs +++ b/meilisearch/tests/integration.rs @@ -8,6 +8,7 @@ mod search; mod settings; mod snapshot; mod stats; +mod swap_indexes; mod tasks; // Tests are isolated by features in different modules to allow better readability, test diff --git a/meilisearch/tests/swap_indexes/errors.rs b/meilisearch/tests/swap_indexes/errors.rs new file mode 100644 index 000000000..066e586ce --- /dev/null +++ b/meilisearch/tests/swap_indexes/errors.rs @@ -0,0 +1,78 @@ +use meili_snap::*; +use serde_json::json; + +use crate::common::Server; + +#[actix_rt::test] +async fn swap_indexes_bad_format() { + let server = Server::new().await; + + let (response, code) = server.index_swap(json!("doggo")).await; + snapshot!(code, @"400 Bad Request"); + snapshot!(json_string!(response), @r###" + { + "message": "invalid type: String `\"doggo\"`, expected a Sequence at ``.", + "code": "bad_request", + "type": "invalid_request", + "link": "https://docs.meilisearch.com/errors#bad-request" + } + "###); + let (response, code) = server.index_swap(json!(["doggo"])).await; + snapshot!(code, @"400 Bad Request"); + snapshot!(json_string!(response), @r###" + { + "message": "invalid type: String `\"doggo\"`, expected a Map at `[0]`.", + "code": "bad_request", + "type": "invalid_request", + "link": "https://docs.meilisearch.com/errors#bad-request" + } + "###); +} + +#[actix_rt::test] +async fn swap_indexes_bad_indexes() { + let server = Server::new().await; + + let (response, code) = server.index_swap(json!([{ "indexes": "doggo"}])).await; + snapshot!(code, @"400 Bad Request"); + snapshot!(json_string!(response), @r###" + { + "message": "invalid type: String `\"doggo\"`, expected a Sequence at `[0].indexes`.", + "code": "invalid_swap_indexes", + "type": "invalid_request", + "link": "https://docs.meilisearch.com/errors#invalid-swap-indexes" + } + "###); + let (response, code) = server.index_swap(json!([{ "indexes": ["doggo"]}])).await; + snapshot!(code, @"400 Bad Request"); + snapshot!(json_string!(response), @r###" + { + "message": "Two indexes must be given for each swap. The list `[\"doggo\"]` contains 1 indexes.", + "code": "invalid_swap_indexes", + "type": "invalid_request", + "link": "https://docs.meilisearch.com/errors#invalid-swap-indexes" + } + "###); + let (response, code) = server.index_swap(json!([{ "indexes": ["doggo", "doggo"]}])).await; + snapshot!(code, @"400 Bad Request"); + snapshot!(json_string!(response), @r###" + { + "message": "Indexes must be declared only once during a swap. `doggo` was specified several times.", + "code": "invalid_swap_duplicate_index_found", + "type": "invalid_request", + "link": "https://docs.meilisearch.com/errors#invalid-swap-duplicate-index-found" + } + "###); + let (response, code) = server + .index_swap(json!([{ "indexes": ["doggo", "catto"]}, { "indexes": ["girafo", "doggo"]}])) + .await; + snapshot!(code, @"400 Bad Request"); + snapshot!(json_string!(response), @r###" + { + "message": "Indexes must be declared only once during a swap. `doggo` was specified several times.", + "code": "invalid_swap_duplicate_index_found", + "type": "invalid_request", + "link": "https://docs.meilisearch.com/errors#invalid-swap-duplicate-index-found" + } + "###); +} diff --git a/meilisearch/tests/swap_indexes/mod.rs b/meilisearch/tests/swap_indexes/mod.rs new file mode 100644 index 000000000..78f1de92e --- /dev/null +++ b/meilisearch/tests/swap_indexes/mod.rs @@ -0,0 +1,357 @@ +mod errors; + +use meili_snap::{json_string, snapshot}; +use serde_json::json; + +use crate::common::{GetAllDocumentsOptions, Server}; + +#[actix_rt::test] +async fn swap_indexes() { + let server = Server::new().await; + let a = server.index("a"); + let (_, code) = a.add_documents(json!({ "id": 1, "index": "a"}), None).await; + snapshot!(code, @"202 Accepted"); + let b = server.index("b"); + let (res, code) = b.add_documents(json!({ "id": 1, "index": "b"}), None).await; + snapshot!(code, @"202 Accepted"); + snapshot!(res["taskUid"], @"1"); + server.wait_task(1).await; + + let (tasks, code) = server.tasks().await; + snapshot!(code, @"200 OK"); + snapshot!(json_string!(tasks, { ".results[].duration" => "[duration]", ".results[].enqueuedAt" => "[date]", ".results[].startedAt" => "[date]", ".results[].finishedAt" => "[date]" }), @r###" + { + "results": [ + { + "uid": 1, + "indexUid": "b", + "status": "succeeded", + "type": "documentAdditionOrUpdate", + "canceledBy": null, + "details": { + "receivedDocuments": 1, + "indexedDocuments": 1 + }, + "error": null, + "duration": "[duration]", + "enqueuedAt": "[date]", + "startedAt": "[date]", + "finishedAt": "[date]" + }, + { + "uid": 0, + "indexUid": "a", + "status": "succeeded", + "type": "documentAdditionOrUpdate", + "canceledBy": null, + "details": { + "receivedDocuments": 1, + "indexedDocuments": 1 + }, + "error": null, + "duration": "[duration]", + "enqueuedAt": "[date]", + "startedAt": "[date]", + "finishedAt": "[date]" + } + ], + "limit": 20, + "from": 1, + "next": null + } + "###); + + let (res, code) = server.index_swap(json!([{ "indexes": ["a", "b"] }])).await; + snapshot!(code, @"202 Accepted"); + snapshot!(res["taskUid"], @"2"); + server.wait_task(2).await; + + let (tasks, code) = server.tasks().await; + snapshot!(code, @"200 OK"); + + // Notice how the task 0 which was initially representing the creation of the index `A` now represents the creation of the index `B`. + snapshot!(json_string!(tasks, { ".results[].duration" => "[duration]", ".results[].enqueuedAt" => "[date]", ".results[].startedAt" => "[date]", ".results[].finishedAt" => "[date]" }), @r###" + { + "results": [ + { + "uid": 2, + "indexUid": null, + "status": "succeeded", + "type": "indexSwap", + "canceledBy": null, + "details": { + "swaps": [ + { + "indexes": [ + "a", + "b" + ] + } + ] + }, + "error": null, + "duration": "[duration]", + "enqueuedAt": "[date]", + "startedAt": "[date]", + "finishedAt": "[date]" + }, + { + "uid": 1, + "indexUid": "a", + "status": "succeeded", + "type": "documentAdditionOrUpdate", + "canceledBy": null, + "details": { + "receivedDocuments": 1, + "indexedDocuments": 1 + }, + "error": null, + "duration": "[duration]", + "enqueuedAt": "[date]", + "startedAt": "[date]", + "finishedAt": "[date]" + }, + { + "uid": 0, + "indexUid": "b", + "status": "succeeded", + "type": "documentAdditionOrUpdate", + "canceledBy": null, + "details": { + "receivedDocuments": 1, + "indexedDocuments": 1 + }, + "error": null, + "duration": "[duration]", + "enqueuedAt": "[date]", + "startedAt": "[date]", + "finishedAt": "[date]" + } + ], + "limit": 20, + "from": 2, + "next": null + } + "###); + + // BUT, the data in `a` should now points to the data that was in `b`. + // And the opposite is true as well + let (res, _) = a.get_all_documents(GetAllDocumentsOptions::default()).await; + snapshot!(res["results"], @r###"[{"id":1,"index":"b"}]"###); + let (res, _) = b.get_all_documents(GetAllDocumentsOptions::default()).await; + snapshot!(res["results"], @r###"[{"id":1,"index":"a"}]"###); + + // ================ + // And now we're going to attempt the famous and dangerous DOUBLE index swap 🤞 + + let c = server.index("c"); + let (res, code) = c.add_documents(json!({ "id": 1, "index": "c"}), None).await; + snapshot!(code, @"202 Accepted"); + snapshot!(res["taskUid"], @"3"); + let d = server.index("d"); + let (res, code) = d.add_documents(json!({ "id": 1, "index": "d"}), None).await; + snapshot!(code, @"202 Accepted"); + snapshot!(res["taskUid"], @"4"); + server.wait_task(4).await; + + // ensure the index creation worked properly + let (tasks, code) = server.tasks_filter(json!({ "limit": 2 })).await; + snapshot!(code, @"200 OK"); + snapshot!(json_string!(tasks, { ".results[].duration" => "[duration]", ".results[].enqueuedAt" => "[date]", ".results[].startedAt" => "[date]", ".results[].finishedAt" => "[date]" }), @r###" + { + "results": [ + { + "uid": 4, + "indexUid": "d", + "status": "succeeded", + "type": "documentAdditionOrUpdate", + "canceledBy": null, + "details": { + "receivedDocuments": 1, + "indexedDocuments": 1 + }, + "error": null, + "duration": "[duration]", + "enqueuedAt": "[date]", + "startedAt": "[date]", + "finishedAt": "[date]" + }, + { + "uid": 3, + "indexUid": "c", + "status": "succeeded", + "type": "documentAdditionOrUpdate", + "canceledBy": null, + "details": { + "receivedDocuments": 1, + "indexedDocuments": 1 + }, + "error": null, + "duration": "[duration]", + "enqueuedAt": "[date]", + "startedAt": "[date]", + "finishedAt": "[date]" + } + ], + "limit": 2, + "from": 4, + "next": 2 + } + "###); + + // It's happening 😲 + + let (res, code) = + server.index_swap(json!([{ "indexes": ["a", "b"] }, { "indexes": ["c", "d"] } ])).await; + snapshot!(res["taskUid"], @"5"); + snapshot!(code, @"202 Accepted"); + server.wait_task(5).await; + + // ensure the index creation worked properly + let (tasks, code) = server.tasks().await; + snapshot!(code, @"200 OK"); + + // What should we check for each tasks in this test: + // Task number; + // 0. should have the indexUid `a` again + // 1. should have the indexUid `b` again + // 2. stays unchanged + // 3. now have the indexUid `d` instead of `c` + // 4. now have the indexUid `c` instead of `d` + snapshot!(json_string!(tasks, { ".results[].duration" => "[duration]", ".results[].enqueuedAt" => "[date]", ".results[].startedAt" => "[date]", ".results[].finishedAt" => "[date]" }), @r###" + { + "results": [ + { + "uid": 5, + "indexUid": null, + "status": "succeeded", + "type": "indexSwap", + "canceledBy": null, + "details": { + "swaps": [ + { + "indexes": [ + "a", + "b" + ] + }, + { + "indexes": [ + "c", + "d" + ] + } + ] + }, + "error": null, + "duration": "[duration]", + "enqueuedAt": "[date]", + "startedAt": "[date]", + "finishedAt": "[date]" + }, + { + "uid": 4, + "indexUid": "c", + "status": "succeeded", + "type": "documentAdditionOrUpdate", + "canceledBy": null, + "details": { + "receivedDocuments": 1, + "indexedDocuments": 1 + }, + "error": null, + "duration": "[duration]", + "enqueuedAt": "[date]", + "startedAt": "[date]", + "finishedAt": "[date]" + }, + { + "uid": 3, + "indexUid": "d", + "status": "succeeded", + "type": "documentAdditionOrUpdate", + "canceledBy": null, + "details": { + "receivedDocuments": 1, + "indexedDocuments": 1 + }, + "error": null, + "duration": "[duration]", + "enqueuedAt": "[date]", + "startedAt": "[date]", + "finishedAt": "[date]" + }, + { + "uid": 2, + "indexUid": null, + "status": "succeeded", + "type": "indexSwap", + "canceledBy": null, + "details": { + "swaps": [ + { + "indexes": [ + "b", + "a" + ] + } + ] + }, + "error": null, + "duration": "[duration]", + "enqueuedAt": "[date]", + "startedAt": "[date]", + "finishedAt": "[date]" + }, + { + "uid": 1, + "indexUid": "b", + "status": "succeeded", + "type": "documentAdditionOrUpdate", + "canceledBy": null, + "details": { + "receivedDocuments": 1, + "indexedDocuments": 1 + }, + "error": null, + "duration": "[duration]", + "enqueuedAt": "[date]", + "startedAt": "[date]", + "finishedAt": "[date]" + }, + { + "uid": 0, + "indexUid": "a", + "status": "succeeded", + "type": "documentAdditionOrUpdate", + "canceledBy": null, + "details": { + "receivedDocuments": 1, + "indexedDocuments": 1 + }, + "error": null, + "duration": "[duration]", + "enqueuedAt": "[date]", + "startedAt": "[date]", + "finishedAt": "[date]" + } + ], + "limit": 20, + "from": 5, + "next": null + } + "###); + + // - The data in `a` should point to `a`. + // - The data in `b` should point to `b`. + // - The data in `c` should point to `d`. + // - The data in `d` should point to `c`. + let (res, _) = a.get_all_documents(GetAllDocumentsOptions::default()).await; + snapshot!(res["results"], @r###"[{"id":1,"index":"a"}]"###); + let (res, _) = b.get_all_documents(GetAllDocumentsOptions::default()).await; + snapshot!(res["results"], @r###"[{"id":1,"index":"b"}]"###); + let (res, _) = c.get_all_documents(GetAllDocumentsOptions::default()).await; + snapshot!(res["results"], @r###"[{"id":1,"index":"d"}]"###); + let (res, _) = d.get_all_documents(GetAllDocumentsOptions::default()).await; + snapshot!(res["results"], @r###"[{"id":1,"index":"c"}]"###); +} From 82bdb545377d3f77ed78808ecac1e571fe50415a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lo=C3=AFc=20Lecrenier?= Date: Wed, 18 Jan 2023 09:40:41 +0100 Subject: [PATCH 2/3] Update the index swap tests after git rebase --- meilisearch/tests/swap_indexes/errors.rs | 8 ++++---- meilisearch/tests/swap_indexes/mod.rs | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/meilisearch/tests/swap_indexes/errors.rs b/meilisearch/tests/swap_indexes/errors.rs index 066e586ce..a2697a37d 100644 --- a/meilisearch/tests/swap_indexes/errors.rs +++ b/meilisearch/tests/swap_indexes/errors.rs @@ -11,7 +11,7 @@ async fn swap_indexes_bad_format() { snapshot!(code, @"400 Bad Request"); snapshot!(json_string!(response), @r###" { - "message": "invalid type: String `\"doggo\"`, expected a Sequence at ``.", + "message": "Invalid value type: expected an array, but found a string: `\"doggo\"`", "code": "bad_request", "type": "invalid_request", "link": "https://docs.meilisearch.com/errors#bad-request" @@ -21,7 +21,7 @@ async fn swap_indexes_bad_format() { snapshot!(code, @"400 Bad Request"); snapshot!(json_string!(response), @r###" { - "message": "invalid type: String `\"doggo\"`, expected a Map at `[0]`.", + "message": "Invalid value type at `[0]`: expected an object, but found a string: `\"doggo\"`", "code": "bad_request", "type": "invalid_request", "link": "https://docs.meilisearch.com/errors#bad-request" @@ -37,7 +37,7 @@ async fn swap_indexes_bad_indexes() { snapshot!(code, @"400 Bad Request"); snapshot!(json_string!(response), @r###" { - "message": "invalid type: String `\"doggo\"`, expected a Sequence at `[0].indexes`.", + "message": "Invalid value type at `[0].indexes`: expected an array, but found a string: `\"doggo\"`", "code": "invalid_swap_indexes", "type": "invalid_request", "link": "https://docs.meilisearch.com/errors#invalid-swap-indexes" @@ -47,7 +47,7 @@ async fn swap_indexes_bad_indexes() { snapshot!(code, @"400 Bad Request"); snapshot!(json_string!(response), @r###" { - "message": "Two indexes must be given for each swap. The list `[\"doggo\"]` contains 1 indexes.", + "message": "Two indexes must be given for each swap. The list `[IndexUid(\"doggo\")]` contains 1 indexes.", "code": "invalid_swap_indexes", "type": "invalid_request", "link": "https://docs.meilisearch.com/errors#invalid-swap-indexes" diff --git a/meilisearch/tests/swap_indexes/mod.rs b/meilisearch/tests/swap_indexes/mod.rs index 78f1de92e..42d92e2af 100644 --- a/meilisearch/tests/swap_indexes/mod.rs +++ b/meilisearch/tests/swap_indexes/mod.rs @@ -155,7 +155,7 @@ async fn swap_indexes() { server.wait_task(4).await; // ensure the index creation worked properly - let (tasks, code) = server.tasks_filter(json!({ "limit": 2 })).await; + let (tasks, code) = server.tasks_filter("limit=2").await; snapshot!(code, @"200 OK"); snapshot!(json_string!(tasks, { ".results[].duration" => "[duration]", ".results[].enqueuedAt" => "[date]", ".results[].startedAt" => "[date]", ".results[].finishedAt" => "[date]" }), @r###" { From 00f6af647521c0965ed9cfb178ecf3f0d0c60282 Mon Sep 17 00:00:00 2001 From: Tamo Date: Wed, 18 Jan 2023 17:26:48 +0100 Subject: [PATCH 3/3] fix a wrong error message --- meilisearch/src/error.rs | 4 ++-- meilisearch/tests/swap_indexes/errors.rs | 18 +++++++++++++++++- 2 files changed, 19 insertions(+), 3 deletions(-) diff --git a/meilisearch/src/error.rs b/meilisearch/src/error.rs index 9c77f4d3e..fcfe4b7fc 100644 --- a/meilisearch/src/error.rs +++ b/meilisearch/src/error.rs @@ -24,8 +24,8 @@ pub enum MeilisearchHttpError { MissingPayload(PayloadType), #[error("The provided payload reached the size limit.")] PayloadTooLarge, - #[error("Two indexes must be given for each swap. The list `{:?}` contains {} indexes.", - .0, .0.len() + #[error("Two indexes must be given for each swap. The list `[{}]` contains {} indexes.", + .0.iter().map(|uid| format!("\"{uid}\"")).collect::>().join(", "), .0.len() )] SwapIndexPayloadWrongLength(Vec), #[error(transparent)] diff --git a/meilisearch/tests/swap_indexes/errors.rs b/meilisearch/tests/swap_indexes/errors.rs index a2697a37d..848f347f8 100644 --- a/meilisearch/tests/swap_indexes/errors.rs +++ b/meilisearch/tests/swap_indexes/errors.rs @@ -17,6 +17,7 @@ async fn swap_indexes_bad_format() { "link": "https://docs.meilisearch.com/errors#bad-request" } "###); + let (response, code) = server.index_swap(json!(["doggo"])).await; snapshot!(code, @"400 Bad Request"); snapshot!(json_string!(response), @r###" @@ -43,16 +44,30 @@ async fn swap_indexes_bad_indexes() { "link": "https://docs.meilisearch.com/errors#invalid-swap-indexes" } "###); + let (response, code) = server.index_swap(json!([{ "indexes": ["doggo"]}])).await; snapshot!(code, @"400 Bad Request"); snapshot!(json_string!(response), @r###" { - "message": "Two indexes must be given for each swap. The list `[IndexUid(\"doggo\")]` contains 1 indexes.", + "message": "Two indexes must be given for each swap. The list `[\"doggo\"]` contains 1 indexes.", "code": "invalid_swap_indexes", "type": "invalid_request", "link": "https://docs.meilisearch.com/errors#invalid-swap-indexes" } "###); + + let (response, code) = + server.index_swap(json!([{ "indexes": ["doggo", "crabo", "croco"]}])).await; + snapshot!(code, @"400 Bad Request"); + snapshot!(json_string!(response), @r###" + { + "message": "Two indexes must be given for each swap. The list `[\"doggo\", \"crabo\", \"croco\"]` contains 3 indexes.", + "code": "invalid_swap_indexes", + "type": "invalid_request", + "link": "https://docs.meilisearch.com/errors#invalid-swap-indexes" + } + "###); + let (response, code) = server.index_swap(json!([{ "indexes": ["doggo", "doggo"]}])).await; snapshot!(code, @"400 Bad Request"); snapshot!(json_string!(response), @r###" @@ -63,6 +78,7 @@ async fn swap_indexes_bad_indexes() { "link": "https://docs.meilisearch.com/errors#invalid-swap-duplicate-index-found" } "###); + let (response, code) = server .index_swap(json!([{ "indexes": ["doggo", "catto"]}, { "indexes": ["girafo", "doggo"]}])) .await;