mod common;
use common::{get, post, signup, As};
use serde_json::json;
#[tokio::test]
#[ignore = "needs MongoDB on 127.0.0.1:27017"]
async fn mixed_success_and_failure_in_one_request() {
let server = common::boot().await;
let host = &server.host;
let (owner_id, token) = signup(host, "batch_user", "pw").await;
let locked = post(
host,
"/classes/Item",
&As::user(&token),
&json!({ "name": "locked", "ACL": { owner_id.clone(): { "read": true, "write": true } } }),
)
.await;
let locked_id = locked.body["objectId"]
.as_str()
.expect("objectId")
.to_string();
let response = post(
host,
"/batch",
&As::anonymous(),
&json!({
"requests": [
{ "method": "POST", "path": "/parse/classes/Item", "body": { "name": "one" } },
{ "method": "PUT", "path": format!("/parse/classes/Item/{locked_id}"),
"body": { "name": "hijacked" } },
{ "method": "GET", "path": "/parse/classes/Item",
"body": { "where": { "name": "one" } } },
],
}),
)
.await;
assert_eq!(response.status, 200, "{}", response.raw);
let results = response.body.as_array().expect("an array body").clone();
assert_eq!(results.len(), 3, "{}", response.raw);
assert!(
results[0]["success"]["objectId"].is_string(),
"{}",
response.raw
);
assert!(results[0]["success"]["createdAt"].is_string());
assert_eq!(results[1]["error"]["code"], json!(101), "{}", response.raw);
assert_eq!(results[1]["error"]["error"], json!("Object not found."));
assert!(results[1].get("success").is_none());
let found = results[2]["success"]["results"]
.as_array()
.expect("results array");
assert_eq!(found.len(), 1, "{}", response.raw);
assert_eq!(found[0]["name"], json!("one"));
let after = get(
host,
&format!("/classes/Item/{locked_id}"),
&As::user(&token),
)
.await;
assert_eq!(after.body["name"], json!("locked"), "{}", after.raw);
}
#[tokio::test]
#[ignore = "needs MongoDB on 127.0.0.1:27017"]
async fn sub_requests_inherit_the_outer_auth() {
let server = common::boot().await;
let host = &server.host;
let (_id, token) = signup(host, "batch_auth", "pw").await;
let as_master = post(
host,
"/batch",
&As::master(),
&json!({ "requests": [{ "method": "GET", "path": "/parse/schemas" }] }),
)
.await;
let results = as_master.body.as_array().expect("array").clone();
assert!(
results[0]["success"]["results"].is_array(),
"{}",
as_master.raw
);
let as_user = post(
host,
"/batch",
&As::user(&token),
&json!({ "requests": [{ "method": "GET", "path": "/parse/schemas" }] }),
)
.await;
let results = as_user.body.as_array().expect("array").clone();
assert_eq!(results[0]["error"]["error"], json!("Permission denied"));
assert!(results[0]["error"].get("code").is_none(), "{}", as_user.raw);
}
#[tokio::test]
#[ignore = "needs MongoDB on 127.0.0.1:27017"]
async fn the_malformed_shapes_are_refused_before_anything_runs() {
let server = common::boot().await;
let host = &server.host;
for (body, code, message) in [
(json!({}), 107, "requests must be an array"),
(json!({ "requests": {} }), 107, "requests must be an array"),
(
json!({ "requests": [{ "method": "POST" }] }),
107,
"batch request path must be a string",
),
(
json!({ "requests": ["nope"] }),
107,
"batch request path must be a string",
),
(
json!({ "requests": [{ "method": "POST", "path": "/parse/batch" }] }),
107,
"nested batch requests are not allowed",
),
(
json!({ "requests": [{ "method": "GET", "path": "/elsewhere/classes/Post" }] }),
107,
"cannot route batch path /elsewhere/classes/Post",
),
] {
let r = post(host, "/batch", &As::master(), &body).await;
assert_eq!(r.code(), Some(code), "{}", r.raw);
assert_eq!(r.error(), message, "{}", r.raw);
}
let mixed = post(
host,
"/batch",
&As::master(),
&json!({
"requests": [
{ "method": "POST", "path": "/parse/classes/Never", "body": { "n": 1 } },
{ "method": "POST" },
],
}),
)
.await;
assert_eq!(mixed.code(), Some(107), "{}", mixed.raw);
let listed = get(host, "/classes/Never", &As::master()).await;
assert!(
listed.results().is_empty(),
"validation runs over the whole array before anything executes: {}",
listed.raw
);
}
#[tokio::test]
#[ignore = "needs MongoDB on 127.0.0.1:27017"]
async fn a_transactional_batch_is_refused() {
let server = common::boot().await;
let host = &server.host;
let r = post(
host,
"/batch",
&As::master(),
&json!({
"transaction": true,
"requests": [{ "method": "POST", "path": "/parse/classes/Tx", "body": { "n": 1 } }],
}),
)
.await;
assert_eq!(r.code(), Some(108), "{}", r.raw);
assert!(
r.error().contains("transaction"),
"the message has to name the limitation: {}",
r.raw
);
let listed = get(host, "/classes/Tx", &As::master()).await;
assert!(listed.results().is_empty(), "nothing ran: {}", listed.raw);
}
#[tokio::test]
#[ignore = "needs MongoDB on 127.0.0.1:27017"]
async fn an_unroutable_sub_request_fails_only_itself() {
let server = common::boot().await;
let host = &server.host;
let r = post(
host,
"/batch",
&As::master(),
&json!({
"requests": [
{ "method": "POST", "path": "/parse/functions/nope", "body": {} },
{ "method": "POST", "path": "/parse/classes/Fine", "body": { "n": 1 } },
],
}),
)
.await;
assert_eq!(r.status, 200, "{}", r.raw);
let results = r.body.as_array().expect("array").clone();
assert_eq!(results[0]["error"]["code"], json!(107), "{}", r.raw);
assert_eq!(
results[0]["error"]["error"],
json!("cannot route POST /functions/nope")
);
assert!(results[1]["success"]["objectId"].is_string(), "{}", r.raw);
}
#[tokio::test]
#[ignore = "needs MongoDB on 127.0.0.1:27017"]
async fn the_batch_request_limit_is_disabled_by_default_and_master_bypasses_it() {
let server = common::boot().await;
let requests: Vec<_> = (0..5)
.map(|n| json!({ "method": "POST", "path": "/parse/classes/Many", "body": { "n": n } }))
.collect();
let r = post(
&server.host,
"/batch",
&As::anonymous(),
&json!({ "requests": requests.clone() }),
)
.await;
assert_eq!(r.status, 200, "{}", r.raw);
assert_eq!(r.body.as_array().map(Vec::len), Some(5));
let limited = {
let mut config = parse_rust_server::ServerConfig::new(common::APP_ID, common::MASTER_KEY);
config.batch_request_limit = 2;
common::boot_with(&server.database, config).await
};
let refused = post(
&limited,
"/batch",
&As::anonymous(),
&json!({ "requests": requests.clone() }),
)
.await;
assert_eq!(refused.code(), Some(107), "{}", refused.raw);
assert_eq!(
refused.error(),
"Batch request contains 5 sub-requests, which exceeds the limit of 2."
);
let allowed = post(
&limited,
"/batch",
&As::master(),
&json!({ "requests": requests }),
)
.await;
assert_eq!(
allowed.status, 200,
"master bypasses the limit: {}",
allowed.raw
);
}