surrealpatch/tests/http_integration.rs

1653 lines
51 KiB
Rust
Raw Normal View History

// RUST_LOG=warn cargo make ci-http-integration
mod common;
mod http_integration {
use std::time::Duration;
use http::{header, Method};
use reqwest::Client;
use serde_json::json;
use surrealdb::headers::{AUTH_DB, AUTH_NS};
use test_log::test;
use ulid::Ulid;
use super::common::{self, StartServerArguments, PASS, USER};
#[test(tokio::test)]
async fn basic_auth() -> Result<(), Box<dyn std::error::Error>> {
let (addr, _server) = common::start_server_with_guests().await.unwrap();
let url = &format!("http://{addr}/sql");
// Prepare HTTP client
let mut headers = reqwest::header::HeaderMap::new();
let ns = Ulid::new().to_string();
let db = Ulid::new().to_string();
headers.insert("surreal-ns", ns.parse()?);
headers.insert("surreal-db", db.parse()?);
headers.insert(header::ACCEPT, "application/json".parse()?);
let client = reqwest::Client::builder()
.connect_timeout(Duration::from_millis(10))
.default_headers(headers)
.build()?;
// Request without credentials, gives an anonymous session
{
let res = client.post(url).body("CREATE foo").send().await?;
assert_eq!(res.status(), 200);
let body = res.text().await?;
assert!(body.contains("Not enough permissions"), "body: {body}");
}
// Request with invalid credentials, returns 401
{
let res =
client.post(url).basic_auth("user", Some("pass")).body("CREATE foo").send().await?;
assert_eq!(res.status(), 401);
}
// Request with valid root credentials, gives a ROOT session
{
let res =
client.post(url).basic_auth(USER, Some(PASS)).body("CREATE foo").send().await?;
assert_eq!(res.status(), 200);
let body = res.text().await?;
assert!(body.contains(r#"[{"result":[{"id":"foo:"#), "body: {body}");
}
// Prepare users with identical credentials on ROOT, NAMESPACE and DATABASE levels
{
let res =
client.post(url).basic_auth(USER, Some(PASS))
.body(format!("DEFINE USER {USER} ON ROOT PASSWORD '{PASS}' ROLES OWNER;
DEFINE USER {USER} ON NAMESPACE PASSWORD '{PASS}' ROLES OWNER;
DEFINE USER {USER} ON DATABASE PASSWORD '{PASS}' ROLES OWNER",
)).send().await?;
assert_eq!(res.status(), 200);
}
// Request with ROOT level access to access ROOT, returns 200 and succeeds
{
let res =
client.post(url).basic_auth(USER, Some(PASS)).body("INFO FOR ROOT").send().await?;
assert_eq!(res.status(), 200);
let body: serde_json::Value = serde_json::from_str(&res.text().await?).unwrap();
assert_eq!(body[0]["status"], "OK", "body: {body}");
}
// Request with ROOT level access to access NS, returns 200 and succeeds
{
let res =
client.post(url).basic_auth(USER, Some(PASS)).body("INFO FOR NS").send().await?;
assert_eq!(res.status(), 200);
let body: serde_json::Value = serde_json::from_str(&res.text().await?).unwrap();
assert_eq!(body[0]["status"], "OK", "body: {body}");
}
// Request with ROOT level access to access DB, returns 200 and succeeds
{
let res =
client.post(url).basic_auth(USER, Some(PASS)).body("INFO FOR DB").send().await?;
assert_eq!(res.status(), 200);
let body: serde_json::Value = serde_json::from_str(&res.text().await?).unwrap();
assert_eq!(body[0]["status"], "OK", "body: {body}");
}
// Request with NS level access to access ROOT, returns 200 but fails
{
let res = client
.post(url)
.header(&AUTH_NS, &ns)
.basic_auth(USER, Some(PASS))
.body("INFO FOR ROOT")
.send()
.await?;
assert_eq!(res.status(), 200);
let body: serde_json::Value = serde_json::from_str(&res.text().await?).unwrap();
assert_eq!(body[0]["status"], "ERR", "body: {body}");
assert_eq!(
body[0]["result"], "IAM error: Not enough permissions to perform this action",
"body: {body}"
);
}
// Request with NS level access to access NS, returns 200 and succeeds
{
let res = client
.post(url)
.header(&AUTH_NS, &ns)
.basic_auth(USER, Some(PASS))
.body("INFO FOR NS")
.send()
.await?;
assert_eq!(res.status(), 200);
let body: serde_json::Value = serde_json::from_str(&res.text().await?).unwrap();
assert_eq!(body[0]["status"], "OK", "body: {body}");
}
// Request with NS level access to access DB, returns 200 and succeeds
{
let res = client
.post(url)
.header(&AUTH_NS, &ns)
.basic_auth(USER, Some(PASS))
.body("INFO FOR DB")
.send()
.await?;
assert_eq!(res.status(), 200);
let body: serde_json::Value = serde_json::from_str(&res.text().await?).unwrap();
assert_eq!(body[0]["status"], "OK", "body: {body}");
}
// Request with DB level access to access ROOT, returns 200 but fails
{
let res = client
.post(url)
.header(&AUTH_NS, &ns)
.header(&AUTH_DB, &db)
.basic_auth(USER, Some(PASS))
.body("INFO FOR ROOT")
.send()
.await?;
assert_eq!(res.status(), 200);
let body: serde_json::Value = serde_json::from_str(&res.text().await?).unwrap();
assert_eq!(body[0]["status"], "ERR", "body: {body}");
assert_eq!(
body[0]["result"], "IAM error: Not enough permissions to perform this action",
"body: {body}"
);
}
// Request with DB level access to access NS, returns 200 but fails
{
let res = client
.post(url)
.header(&AUTH_NS, &ns)
.header(&AUTH_DB, &db)
.basic_auth(USER, Some(PASS))
.body("INFO FOR NS")
.send()
.await?;
assert_eq!(res.status(), 200);
let body: serde_json::Value = serde_json::from_str(&res.text().await?).unwrap();
assert_eq!(body[0]["status"], "ERR", "body: {body}");
assert_eq!(
body[0]["result"], "IAM error: Not enough permissions to perform this action",
"body: {body}"
);
}
// Request with DB level access to access DB, returns 200 and succeeds
{
let res = client
.post(url)
.header(&AUTH_NS, &ns)
.header(&AUTH_DB, &db)
.basic_auth(USER, Some(PASS))
.body("INFO FOR DB")
.send()
.await?;
assert_eq!(res.status(), 200);
let body: serde_json::Value = serde_json::from_str(&res.text().await?).unwrap();
assert_eq!(body[0]["status"], "OK", "body: {body}");
}
// Request with DB level access missing NS level header, returns 401
{
let res = client
.post(url)
.header(&AUTH_DB, &db)
.basic_auth(USER, Some(PASS))
.body("INFO FOR DB")
.send()
.await?;
assert_eq!(res.status(), 401);
}
Ok(())
}
#[test(tokio::test)]
async fn bearer_auth() -> Result<(), Box<dyn std::error::Error>> {
let (addr, _server) = common::start_server_with_guests().await.unwrap();
let url = &format!("http://{addr}/sql");
2023-11-21 09:08:13 +00:00
let ns = Ulid::new().to_string();
let db = Ulid::new().to_string();
// Prepare HTTP client
let mut headers = reqwest::header::HeaderMap::new();
headers.insert("surreal-ns", ns.parse()?);
headers.insert("surreal-db", db.parse()?);
headers.insert(header::ACCEPT, "application/json".parse()?);
let client = reqwest::Client::builder()
.connect_timeout(Duration::from_millis(10))
.default_headers(headers)
.build()?;
// Create user
{
let res = client
.post(url)
.basic_auth(USER, Some(PASS))
.body(r#"DEFINE USER user ON DB PASSWORD 'pass' ROLES OWNER"#)
.send()
.await?;
let body = res.text().await?;
assert!(body.contains(r#""status":"OK"#), "body: {body}");
}
// Signin with user and get the token
let token: String;
{
let req_body = serde_json::to_string(
json!({
2023-11-21 09:08:13 +00:00
"ns": ns,
"db": db,
"user": "user",
"pass": "pass",
})
.as_object()
.unwrap(),
)
.unwrap();
let res = client.post(format!("http://{addr}/signin")).body(req_body).send().await?;
assert_eq!(res.status(), 200, "body: {}", res.text().await?);
let body: serde_json::Value = serde_json::from_str(&res.text().await?).unwrap();
token = body["token"].as_str().unwrap().to_owned();
}
// Request with valid token, gives a USER session
{
let res = client.post(url).bearer_auth(&token).body("CREATE foo").send().await?;
assert_eq!(res.status(), 200, "body: {}", res.text().await?);
let body = res.text().await?;
assert!(body.contains(r#"[{"result":[{"id":"foo:"#), "body: {body}");
// Check the selected namespace and database
let res = client
.post(url)
2023-11-21 09:08:13 +00:00
.header("NS", Ulid::new().to_string())
.header("DB", Ulid::new().to_string())
.bearer_auth(&token)
.body("SELECT * FROM session::ns(); SELECT * FROM session::db()")
.send()
.await?;
assert_eq!(res.status(), 200, "body: {}", res.text().await?);
let body = res.text().await?;
assert!(body.contains(&format!(r#""result":["{ns}"]"#)), "body: {body}");
assert!(body.contains(&format!(r#""result":["{db}"]"#)), "body: {body}");
}
// Request with invalid token, returns 401
{
let res = client.post(url).bearer_auth("token").body("CREATE foo").send().await?;
assert_eq!(res.status(), 401, "body: {}", res.text().await?);
}
Ok(())
}
#[test(tokio::test)]
async fn client_ip_extractor() -> Result<(), Box<dyn std::error::Error>> {
// TODO: test the client IP extractor
Ok(())
}
#[test(tokio::test)]
async fn export_endpoint() -> Result<(), Box<dyn std::error::Error>> {
let (addr, _server) = common::start_server_with_defaults().await.unwrap();
let url = &format!("http://{addr}/export");
// Prepare HTTP client
let mut headers = reqwest::header::HeaderMap::new();
headers.insert("surreal-ns", Ulid::new().to_string().parse()?);
headers.insert("surreal-db", Ulid::new().to_string().parse()?);
headers.insert(header::ACCEPT, "application/json".parse()?);
let client = reqwest::Client::builder()
.connect_timeout(Duration::from_millis(10))
.default_headers(headers)
.build()?;
// Create some data
{
let res = client
.post(format!("http://{addr}/sql"))
.basic_auth(USER, Some(PASS))
.body("CREATE foo")
.send()
.await?;
assert_eq!(res.status(), 200, "body: {}", res.text().await?);
}
// When no auth is provided, the endpoint returns a 403
{
let res = client.get(url).send().await?;
assert_eq!(res.status(), 403, "body: {}", res.text().await?);
}
// When auth is provided, it returns the contents of the DB
{
let res = client.get(url).basic_auth(USER, Some(PASS)).send().await?;
assert_eq!(res.status(), 200, "body: {}", res.text().await?);
let body = res.text().await?;
assert!(body.contains("DEFINE TABLE foo"), "body: {body}");
}
Ok(())
}
#[test(tokio::test)]
async fn health_endpoint() -> Result<(), Box<dyn std::error::Error>> {
let (addr, _server) = common::start_server_with_defaults().await.unwrap();
let url = &format!("http://{addr}/health");
let res = Client::default().get(url).send().await?;
assert_eq!(res.status(), 200, "response: {res:#?}");
Ok(())
}
#[test(tokio::test)]
async fn no_server_id_headers() -> Result<(), Box<dyn std::error::Error>> {
// default server has the id headers
{
let (addr, _server) = common::start_server_with_defaults().await.unwrap();
let url = &format!("http://{addr}/health");
let res = Client::default().get(url).send().await?;
assert!(res.headers().contains_key("server"));
assert!(res.headers().contains_key("surreal-version"));
}
// turn on the no-identification-headers option to suppress headers
{
let mut start_server_arguments = StartServerArguments::default();
start_server_arguments.args.push_str(" --no-identification-headers");
let (addr, _server) = common::start_server(start_server_arguments).await.unwrap();
let url = &format!("http://{addr}/health");
let res = Client::default().get(url).send().await?;
assert!(!res.headers().contains_key("server"));
assert!(!res.headers().contains_key("surreal-version"));
}
Ok(())
}
#[test(tokio::test)]
async fn import_endpoint() -> Result<(), Box<dyn std::error::Error>> {
let (addr, _server) = common::start_server_with_defaults().await.unwrap();
let url = &format!("http://{addr}/import");
// Prepare HTTP client
let mut headers = reqwest::header::HeaderMap::new();
headers.insert("surreal-ns", Ulid::new().to_string().parse()?);
headers.insert("surreal-db", Ulid::new().to_string().parse()?);
headers.insert(header::ACCEPT, "application/json".parse()?);
let client = reqwest::Client::builder()
.connect_timeout(Duration::from_millis(10))
.default_headers(headers)
.build()?;
// When no auth is provided, the endpoint returns a 403
{
let res = client.post(url).body("").send().await?;
assert_eq!(res.status(), 403, "body: {}", res.text().await?);
}
// When auth is provided, it persists the import data
{
let data = r#"
-- --------------------------------
-- OPTION
-- ------------------------------
OPTION IMPORT;
-- ------------------------------
-- TABLE: foo
-- ------------------------------
DEFINE TABLE foo SCHEMALESS PERMISSIONS NONE;
-- ------------------------------
-- TRANSACTION
-- ------------------------------
BEGIN TRANSACTION;
-- ------------------------------
-- TABLE DATA: foo
-- ------------------------------
2024-06-12 09:15:09 +00:00
INSERT { id: foo:bvklxkhtxumyrfzqoc5i };
-- ------------------------------
-- TRANSACTION
-- ------------------------------
COMMIT TRANSACTION;
"#;
let res = client.post(url).basic_auth(USER, Some(PASS)).body(data).send().await?;
assert_eq!(res.status(), 200, "body: {}", res.text().await?);
// Check that the data was persisted
let res = client
.post(format!("http://{addr}/sql"))
.basic_auth(USER, Some(PASS))
.body("SELECT * FROM foo")
.send()
.await?;
assert_eq!(res.status(), 200, "body: {}", res.text().await?);
let body = res.text().await?;
assert!(body.contains("foo:bvklxkhtxumyrfzqoc5i"), "body: {body}");
}
Ok(())
}
#[test(tokio::test)]
async fn rpc_endpoint() -> Result<(), Box<dyn std::error::Error>> {
let (addr, _server) = common::start_server_with_defaults().await.unwrap();
let url = &format!("http://{addr}/rpc");
// Prepare HTTP client
let mut headers = reqwest::header::HeaderMap::new();
headers.insert("surreal-ns", Ulid::new().to_string().parse()?);
headers.insert("surreal-db", Ulid::new().to_string().parse()?);
headers.insert(header::ACCEPT, "application/json".parse()?);
let client = reqwest::Client::builder()
.connect_timeout(Duration::from_millis(10))
.default_headers(headers)
.build()?;
// Test WebSocket upgrade
{
let res = client
.get(url)
.header(header::CONNECTION, "Upgrade")
.header(header::UPGRADE, "websocket")
.header(header::SEC_WEBSOCKET_VERSION, "13")
.header(header::SEC_WEBSOCKET_KEY, "dGhlIHNhbXBsZSBub25jZQ==")
.send()
.await?
.upgrade()
.await;
assert!(res.is_ok(), "upgrade err: {}", res.unwrap_err());
}
Ok(())
}
#[test(tokio::test)]
async fn signin_endpoint() -> Result<(), Box<dyn std::error::Error>> {
let (addr, _server) = common::start_server_with_defaults().await.unwrap();
let url = &format!("http://{addr}/signin");
let ns = Ulid::new().to_string();
let db = Ulid::new().to_string();
// Prepare HTTP client
let mut headers = reqwest::header::HeaderMap::new();
headers.insert("surreal-ns", ns.parse()?);
headers.insert("surreal-db", db.parse()?);
headers.insert(header::ACCEPT, "application/json".parse()?);
let client = reqwest::Client::builder()
.connect_timeout(Duration::from_millis(10))
.default_headers(headers)
.build()?;
// Create a DB user
{
let res = client
.post(format!("http://{addr}/sql"))
.basic_auth(USER, Some(PASS))
.body(r#"DEFINE USER user_db ON DB PASSWORD 'pass_db'"#)
.send()
.await?;
assert!(res.status().is_success(), "body: {}", res.text().await?);
}
// Signin with valid DB credentials and get the token
{
let req_body = serde_json::to_string(
json!({
"ns": ns,
"db": db,
"user": "user_db",
"pass": "pass_db",
})
.as_object()
.unwrap(),
)
.unwrap();
let res = client.post(url).body(req_body).send().await?;
assert_eq!(res.status(), 200, "body: {}", res.text().await?);
let body: serde_json::Value = serde_json::from_str(&res.text().await?).unwrap();
assert!(!body["token"].as_str().unwrap().to_string().is_empty(), "body: {body}");
}
// Signin with invalid DB credentials returns 401
{
let req_body = serde_json::to_string(
json!({
"ns": ns,
"db": db,
"user": "user_db",
"pass": "invalid_pass",
})
.as_object()
.unwrap(),
)
.unwrap();
let res = client.post(url).body(req_body).send().await?;
assert_eq!(res.status(), 401, "body: {}", res.text().await?);
}
// Create a NS user
{
let res = client
.post(format!("http://{addr}/sql"))
.basic_auth(USER, Some(PASS))
.body(r#"DEFINE USER user_ns ON NS PASSWORD 'pass_ns'"#)
.send()
.await?;
assert!(res.status().is_success(), "body: {}", res.text().await?);
}
// Signin with valid NS credentials specifying NS and DB and get the token
// This should fail because authentication will be attempted at DB level
{
let req_body = serde_json::to_string(
json!({
"ns": ns,
"db": db,
"user": "user_ns",
"pass": "pass_ns",
})
.as_object()
.unwrap(),
)
.unwrap();
let res = client.post(url).body(req_body).send().await?;
assert_eq!(res.status(), 401, "body: {}", res.text().await?);
}
// Signin with valid NS credentials specifying NS and get the token
{
let req_body = serde_json::to_string(
json!({
"ns": ns,
"user": "user_ns",
"pass": "pass_ns",
})
.as_object()
.unwrap(),
)
.unwrap();
let res = client.post(url).body(req_body).send().await?;
assert_eq!(res.status(), 200, "body: {}", res.text().await?);
let body: serde_json::Value = serde_json::from_str(&res.text().await?).unwrap();
assert!(!body["token"].as_str().unwrap().to_string().is_empty(), "body: {body}");
}
// Signin with invalid NS credentials returns 401
{
let req_body = serde_json::to_string(
json!({
"ns": ns,
"db": db,
"user": "user_ns",
"pass": "invalid_pass",
})
.as_object()
.unwrap(),
)
.unwrap();
let res = client.post(url).body(req_body).send().await?;
assert_eq!(res.status(), 401, "body: {}", res.text().await?);
}
// Create a ROOT user
{
let res = client
.post(format!("http://{addr}/sql"))
.basic_auth(USER, Some(PASS))
.body(r#"DEFINE USER user_root ON ROOT PASSWORD 'pass_root'"#)
.send()
.await?;
assert!(res.status().is_success(), "body: {}", res.text().await?);
}
// Signin with valid ROOT credentials specifying NS and DB and get the token
// This should fail because authentication will be attempted at DB level
{
let req_body = serde_json::to_string(
json!({
"ns": ns,
"db": db,
"user": "user_root",
"pass": "pass_root",
})
.as_object()
.unwrap(),
)
.unwrap();
let res = client.post(url).body(req_body).send().await?;
assert_eq!(res.status(), 401, "body: {}", res.text().await?);
}
// Signin with valid ROOT credentials specifying NS and get the token
// This should fail because authentication will be attempted at NS level
{
let req_body = serde_json::to_string(
json!({
"ns": ns,
"user": "user_root",
"pass": "pass_root",
})
.as_object()
.unwrap(),
)
.unwrap();
let res = client.post(url).body(req_body).send().await?;
assert_eq!(res.status(), 401, "body: {}", res.text().await?);
}
// Signin with valid ROOT credentials without specifying NS nor DB and get the token
{
let req_body = serde_json::to_string(
json!({
"user": "user_root",
"pass": "pass_root",
})
.as_object()
.unwrap(),
)
.unwrap();
let res = client.post(url).body(req_body).send().await?;
assert_eq!(res.status(), 200, "body: {}", res.text().await?);
let body: serde_json::Value = serde_json::from_str(&res.text().await?).unwrap();
assert!(!body["token"].as_str().unwrap().to_string().is_empty(), "body: {body}");
}
// Signin with invalid ROOT credentials returns 401
{
let req_body = serde_json::to_string(
json!({
"ns": ns,
"db": db,
"user": "user_root",
"pass": "invalid_pass",
})
.as_object()
.unwrap(),
)
.unwrap();
let res = client.post(url).body(req_body).send().await?;
assert_eq!(res.status(), 401, "body: {}", res.text().await?);
}
Ok(())
}
#[test(tokio::test)]
async fn signup_endpoint() -> Result<(), Box<dyn std::error::Error>> {
let (addr, _server) = common::start_server_with_defaults().await.unwrap();
let url = &format!("http://{addr}/signup");
2023-11-21 09:08:13 +00:00
let ns = Ulid::new().to_string();
let db = Ulid::new().to_string();
// Prepare HTTP client
let mut headers = reqwest::header::HeaderMap::new();
headers.insert("surreal-ns", ns.parse()?);
headers.insert("surreal-db", db.parse()?);
headers.insert(header::ACCEPT, "application/json".parse()?);
let client = reqwest::Client::builder()
.connect_timeout(Duration::from_millis(10))
.default_headers(headers)
.build()?;
// Define a record access method
{
let res = client
.post(format!("http://{addr}/sql"))
.basic_auth(USER, Some(PASS))
.body(
r#"
DEFINE ACCESS user ON DATABASE TYPE RECORD
SIGNUP ( CREATE user SET email = $email, pass = crypto::argon2::generate($pass) )
SIGNIN ( SELECT * FROM user WHERE email = $email AND crypto::argon2::compare(pass, $pass) )
DURATION FOR SESSION 12h
;
"#,
)
.send()
.await?;
assert!(res.status().is_success(), "body: {}", res.text().await?);
}
// Signup using the defined record access method
{
let req_body = serde_json::to_string(
json!({
2023-11-21 09:08:13 +00:00
"ns": ns,
"db": db,
"ac": "user",
"email": "email@email.com",
"pass": "pass",
})
.as_object()
.unwrap(),
)
.unwrap();
let res = client.post(url).body(req_body).send().await?;
assert_eq!(res.status(), 200, "body: {}", res.text().await?);
let body: serde_json::Value = serde_json::from_str(&res.text().await?).unwrap();
assert!(
body["token"].as_str().unwrap().starts_with("eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzUxMiJ9"),
"body: {body}"
);
}
Ok(())
}
#[test(tokio::test)]
async fn sql_endpoint() -> Result<(), Box<dyn std::error::Error>> {
let (addr, _server) = common::start_server_with_guests().await.unwrap();
let url = &format!("http://{addr}/sql");
// Prepare HTTP client
let mut headers = reqwest::header::HeaderMap::new();
headers.insert("surreal-ns", Ulid::new().to_string().parse()?);
headers.insert("surreal-db", Ulid::new().to_string().parse()?);
headers.insert(header::ACCEPT, "application/json".parse()?);
let client = reqwest::Client::builder()
.connect_timeout(Duration::from_millis(10))
.default_headers(headers)
.build()?;
// Options method works
{
let res = client.request(Method::OPTIONS, url).send().await?;
assert_eq!(res.status(), 200);
}
// Creating a record without credentials is not allowed
{
let res = client.post(url).body("CREATE foo").send().await?;
assert_eq!(res.status(), 200);
let body = res.text().await?;
assert!(body.contains("Not enough permissions"), "body: {body}");
}
// Creating a record with Accept JSON encoding is allowed
{
let res =
client.post(url).basic_auth(USER, Some(PASS)).body("CREATE foo").send().await?;
assert_eq!(res.status(), 200);
let body: serde_json::Value = serde_json::from_str(&res.text().await?).unwrap();
assert_eq!(body[0]["status"], "OK", "body: {body}");
}
// Creating a record with Accept CBOR encoding is allowed
{
let res = client
.post(url)
.basic_auth(USER, Some(PASS))
.header(header::ACCEPT, "application/cbor")
.body("CREATE foo")
.send()
.await?;
assert_eq!(res.status(), 200);
let res = res.bytes().await?.to_vec();
let _: ciborium::Value = ciborium::from_reader(res.as_slice()).unwrap();
}
// Creating a record with Accept PACK encoding is allowed
{
let res = client
.post(url)
.basic_auth(USER, Some(PASS))
.header(header::ACCEPT, "application/pack")
.body("CREATE foo")
.send()
.await?;
assert_eq!(res.status(), 200);
let res = res.bytes().await?.to_vec();
let _: rmpv::Value = rmpv::decode::read_value(&mut res.as_slice()).unwrap();
}
// Creating a record with Accept Surrealdb encoding is allowed
{
let res = client
.post(url)
.basic_auth(USER, Some(PASS))
.header(header::ACCEPT, "application/surrealdb")
.body("CREATE foo")
.send()
.await?;
assert_eq!(res.status(), 200);
// TODO: parse the result
}
// Creating a record with an unsupported Accept header, returns a 415
{
let res = client
.post(url)
.basic_auth(USER, Some(PASS))
.header(header::ACCEPT, "text/plain")
.body("CREATE foo")
.send()
.await?;
assert_eq!(res.status(), 415);
}
// Test WebSocket upgrade
{
let res = client
.get(url)
.header(header::CONNECTION, "Upgrade")
.header(header::UPGRADE, "websocket")
.header(header::SEC_WEBSOCKET_VERSION, "13")
.header(header::SEC_WEBSOCKET_KEY, "dGhlIHNhbXBsZSBub25jZQ==")
.send()
.await?
.upgrade()
.await;
assert!(res.is_ok(), "upgrade err: {}", res.unwrap_err());
}
Ok(())
}
#[test(tokio::test)]
#[cfg(feature = "http-compression")]
async fn sql_endpoint_with_compression() -> Result<(), Box<dyn std::error::Error>> {
let (addr, _server) = common::start_server_with_defaults().await.unwrap();
let url = &format!("http://{addr}/sql");
// Prepare HTTP client
let mut headers = reqwest::header::HeaderMap::new();
headers.insert("surreal-ns", Ulid::new().to_string().parse()?);
headers.insert("surreal-db", Ulid::new().to_string().parse()?);
headers.insert(header::ACCEPT, "application/json".parse()?);
headers.insert(header::ACCEPT_ENCODING, "gzip".parse()?);
let client = reqwest::Client::builder()
.connect_timeout(Duration::from_millis(10))
.gzip(false) // So that the content-encoding header is not removed by Reqwest
.default_headers(headers.clone())
.build()?;
// Check that the content is gzip encoded
{
let res = client
.post(url)
.basic_auth(USER, Some(PASS))
.body("CREATE |foo:100|")
.send()
.await?;
assert_eq!(res.status(), 200);
assert_eq!(res.headers()["content-encoding"], "gzip");
}
Ok(())
}
#[test(tokio::test)]
async fn sync_endpoint() -> Result<(), Box<dyn std::error::Error>> {
let (addr, _server) = common::start_server_with_defaults().await.unwrap();
let url = &format!("http://{addr}/sync");
// Prepare HTTP client
let mut headers = reqwest::header::HeaderMap::new();
headers.insert("surreal-ns", Ulid::new().to_string().parse()?);
headers.insert("surreal-db", Ulid::new().to_string().parse()?);
headers.insert(header::ACCEPT, "application/json".parse()?);
let client = reqwest::Client::builder()
.connect_timeout(Duration::from_millis(10))
.default_headers(headers)
.build()?;
// GET
{
let res = client.get(url).send().await?;
assert_eq!(res.status(), 200, "body: {}", res.text().await?);
let body = res.text().await?;
assert_eq!(body, r#"Save"#, "body: {body}");
}
// POST
{
let res = client.post(url).body("").send().await?;
assert_eq!(res.status(), 200, "body: {}", res.text().await?);
let body = res.text().await?;
assert_eq!(body, r#"Load"#, "body: {body}");
}
Ok(())
}
#[test(tokio::test)]
async fn version_endpoint() -> Result<(), Box<dyn std::error::Error>> {
let (addr, _server) = common::start_server_with_defaults().await.unwrap();
let url = &format!("http://{addr}/version");
let res = Client::default().get(url).send().await?;
assert_eq!(res.status(), 200, "response: {res:#?}");
let body = res.text().await?;
assert!(body.starts_with("surrealdb-"), "body: {body}");
Ok(())
}
///
/// Key endpoint tests
///
async fn seed_table(
client: &Client,
addr: &str,
table: &str,
num_records: usize,
) -> Result<(), Box<dyn std::error::Error>> {
let res = client
.post(format!("http://{addr}/sql"))
.basic_auth(USER, Some(PASS))
.body(format!("CREATE |`{table}`:1..{num_records}| SET default = 'content'"))
.send()
.await?;
let body: serde_json::Value = serde_json::from_str(&res.text().await?).unwrap();
assert_eq!(
body[0]["result"].as_array().unwrap().len(),
num_records,
"error seeding the table: {body}"
);
Ok(())
}
#[test(tokio::test)]
async fn key_endpoint_select_all() -> Result<(), Box<dyn std::error::Error>> {
let (addr, _server) = common::start_server_with_guests().await.unwrap();
let table_name = "table";
let num_records = 50;
let url = &format!("http://{addr}/key/{table_name}");
// Prepare HTTP client
let mut headers = reqwest::header::HeaderMap::new();
headers.insert("surreal-ns", Ulid::new().to_string().parse()?);
headers.insert("surreal-db", Ulid::new().to_string().parse()?);
headers.insert(header::ACCEPT, "application/json".parse()?);
let client = reqwest::Client::builder()
.connect_timeout(Duration::from_millis(10))
.default_headers(headers)
.build()?;
// Seed the table
seed_table(&client, &addr, table_name, num_records).await?;
// GET all records
{
let res = client.get(url).basic_auth(USER, Some(PASS)).send().await?;
assert_eq!(res.status(), 200, "body: {}", res.text().await?);
let body: serde_json::Value = serde_json::from_str(&res.text().await?).unwrap();
assert_eq!(body[0]["result"].as_array().unwrap().len(), num_records, "body: {body}");
}
// GET records with a limit
{
let res =
client.get(format!("{url}?limit=10")).basic_auth(USER, Some(PASS)).send().await?;
assert_eq!(res.status(), 200, "body: {}", res.text().await?);
let body: serde_json::Value = serde_json::from_str(&res.text().await?).unwrap();
assert_eq!(body[0]["result"].as_array().unwrap().len(), 10, "body: {body}");
}
// GET records with a start
{
let res =
client.get(format!("{url}?start=10")).basic_auth(USER, Some(PASS)).send().await?;
assert_eq!(res.status(), 200, "body: {}", res.text().await?);
let body: serde_json::Value = serde_json::from_str(&res.text().await?).unwrap();
assert_eq!(
body[0]["result"].as_array().unwrap().len(),
num_records - 10,
"body: {body}"
);
assert_eq!(body[0]["result"].as_array().unwrap()[0]["id"], "table:11", "body: {body}");
}
// GET records with a start and limit
{
let res = client
.get(format!("{url}?start=10&limit=10"))
.basic_auth(USER, Some(PASS))
.send()
.await?;
assert_eq!(res.status(), 200, "body: {}", res.text().await?);
let body: serde_json::Value = serde_json::from_str(&res.text().await?).unwrap();
assert_eq!(body[0]["result"].as_array().unwrap().len(), 10, "body: {body}");
assert_eq!(body[0]["result"].as_array().unwrap()[0]["id"], "table:11", "body: {body}");
}
// GET without authentication returns no records
{
let res = client.get(url).send().await?;
assert_eq!(res.status(), 200, "body: {}", res.text().await?);
let body: serde_json::Value = serde_json::from_str(&res.text().await?).unwrap();
assert_eq!(body[0]["result"].as_array().unwrap().len(), 0, "body: {body}");
}
Ok(())
}
#[test(tokio::test)]
async fn key_endpoint_create_all() -> Result<(), Box<dyn std::error::Error>> {
let (addr, _server) = common::start_server_with_guests().await.unwrap();
// Prepare HTTP client
let mut headers = reqwest::header::HeaderMap::new();
headers.insert("surreal-ns", Ulid::new().to_string().parse()?);
headers.insert("surreal-db", Ulid::new().to_string().parse()?);
headers.insert(header::ACCEPT, "application/json".parse()?);
let client = reqwest::Client::builder()
.connect_timeout(Duration::from_millis(10))
.default_headers(headers)
.build()?;
// Create record with random ID
{
let table_name = "table";
let url = &format!("http://{addr}/key/{table_name}");
// Verify there are no records
let res = client.get(url).basic_auth(USER, Some(PASS)).send().await?;
let body: serde_json::Value = serde_json::from_str(&res.text().await?).unwrap();
assert_eq!(body[0]["result"].as_array().unwrap().len(), 0, "body: {body}");
// Try to create the record
let res = client
.post(url)
.basic_auth(USER, Some(PASS))
.body(r#"{"name": "record_name"}"#)
.send()
.await?;
assert_eq!(res.status(), 200, "body: {}", res.text().await?);
// Verify the record was created
let body: serde_json::Value = serde_json::from_str(&res.text().await?).unwrap();
assert_eq!(body[0]["result"].as_array().unwrap().len(), 1, "body: {body}");
assert_eq!(
body[0]["result"].as_array().unwrap()[0]["name"],
"record_name",
"body: {body}"
);
}
// POST without authentication creates no records
{
let table_name = "table_noauth";
let url = &format!("http://{addr}/key/{table_name}");
// Try to create the record
let res = client.post(url).body(r#"{"name": "record_name"}"#).send().await?;
assert_eq!(res.status(), 200, "body: {}", res.text().await?);
// Verify the table is empty
let res = client.get(url).basic_auth(USER, Some(PASS)).send().await?;
let body: serde_json::Value = serde_json::from_str(&res.text().await?).unwrap();
assert_eq!(body[0]["result"].as_array().unwrap().len(), 0, "body: {body}");
}
Ok(())
}
#[test(tokio::test)]
async fn key_endpoint_update_all() -> Result<(), Box<dyn std::error::Error>> {
let (addr, _server) = common::start_server_with_guests().await.unwrap();
let table_name = "table";
let num_records = 10;
let url = &format!("http://{addr}/key/{table_name}");
// Prepare HTTP client
let mut headers = reqwest::header::HeaderMap::new();
headers.insert("surreal-ns", Ulid::new().to_string().parse()?);
headers.insert("surreal-db", Ulid::new().to_string().parse()?);
headers.insert(header::ACCEPT, "application/json".parse()?);
let client = reqwest::Client::builder()
.connect_timeout(Duration::from_millis(10))
.default_headers(headers)
.build()?;
seed_table(&client, &addr, table_name, num_records).await?;
// Update all records
{
// Try to update the records
let res = client
.put(url)
.basic_auth(USER, Some(PASS))
.body(r#"{"name": "record_name"}"#)
.send()
.await?;
assert_eq!(res.status(), 200, "body: {}", res.text().await?);
// Verify the records were updated
let res = client.get(url).basic_auth(USER, Some(PASS)).send().await?;
let body: serde_json::Value = serde_json::from_str(&res.text().await?).unwrap();
assert_eq!(body[0]["result"].as_array().unwrap().len(), num_records, "body: {body}");
// Verify the records have the new data
for record in body[0]["result"].as_array().unwrap() {
assert_eq!(record["name"], "record_name", "body: {body}");
}
// Verify the records don't have the original data
for record in body[0]["result"].as_array().unwrap() {
assert!(record["default"].is_null(), "body: {body}");
}
}
// Update all records without authentication
{
// Try to update the records
let res = client.put(url).body(r#"{"noauth": "yes"}"#).send().await?;
assert_eq!(res.status(), 200, "body: {}", res.text().await?);
// Verify the records were not updated
let res = client.get(url).basic_auth(USER, Some(PASS)).send().await?;
let body: serde_json::Value = serde_json::from_str(&res.text().await?).unwrap();
assert_eq!(body[0]["result"].as_array().unwrap().len(), num_records, "body: {body}");
// Verify the records don't have the new data
for record in body[0]["result"].as_array().unwrap() {
assert!(record["noauth"].is_null(), "body: {body}");
}
// Verify the records have the original data
for record in body[0]["result"].as_array().unwrap() {
assert_eq!(record["name"], "record_name", "body: {body}");
}
}
Ok(())
}
#[test(tokio::test)]
async fn key_endpoint_modify_all() -> Result<(), Box<dyn std::error::Error>> {
let (addr, _server) = common::start_server_with_guests().await.unwrap();
let table_name = Ulid::new().to_string();
let num_records = 10;
let url = &format!("http://{addr}/key/{table_name}");
// Prepare HTTP client
let mut headers = reqwest::header::HeaderMap::new();
headers.insert("surreal-ns", Ulid::new().to_string().parse()?);
headers.insert("surreal-db", Ulid::new().to_string().parse()?);
headers.insert(header::ACCEPT, "application/json".parse()?);
let client = reqwest::Client::builder()
.connect_timeout(Duration::from_millis(10))
.default_headers(headers)
.build()?;
seed_table(&client, &addr, &table_name, num_records).await?;
// Modify all records
{
// Try to modify the records
let res = client
.patch(url)
.basic_auth(USER, Some(PASS))
.body(r#"{"name": "record_name"}"#)
.send()
.await?;
assert_eq!(res.status(), 200, "body: {}", res.text().await?);
// Verify the records were modified
let res = client.get(url).basic_auth(USER, Some(PASS)).send().await?;
let body: serde_json::Value = serde_json::from_str(&res.text().await?).unwrap();
assert_eq!(body[0]["result"].as_array().unwrap().len(), num_records, "body: {body}");
// Verify the records have the new data
for record in body[0]["result"].as_array().unwrap() {
assert_eq!(record["name"], "record_name", "body: {body}");
}
// Verify the records also have the original data
for record in body[0]["result"].as_array().unwrap() {
assert_eq!(record["default"], "content", "body: {body}");
}
}
// Modify all records without authentication
{
// Try to modify the records
let res = client.patch(url).body(r#"{"noauth": "yes"}"#).send().await?;
assert_eq!(res.status(), 200, "body: {}", res.text().await?);
// Verify the records were not modified
let res = client.get(url).basic_auth(USER, Some(PASS)).send().await?;
let body: serde_json::Value = serde_json::from_str(&res.text().await?).unwrap();
assert_eq!(body[0]["result"].as_array().unwrap().len(), num_records, "body: {body}");
// Verify the records don't have the new data
for record in body[0]["result"].as_array().unwrap() {
assert!(record["noauth"].is_null(), "body: {body}");
}
// Verify the records have the original data
for record in body[0]["result"].as_array().unwrap() {
assert_eq!(record["name"], "record_name", "body: {body}");
}
}
Ok(())
}
#[test(tokio::test)]
async fn key_endpoint_delete_all() -> Result<(), Box<dyn std::error::Error>> {
let (addr, _server) = common::start_server_with_guests().await.unwrap();
let table_name = "table";
let num_records = 10;
let url = &format!("http://{addr}/key/{table_name}");
// Prepare HTTP client
let mut headers = reqwest::header::HeaderMap::new();
headers.insert("surreal-ns", Ulid::new().to_string().parse()?);
headers.insert("surreal-db", Ulid::new().to_string().parse()?);
headers.insert(header::ACCEPT, "application/json".parse()?);
let client = reqwest::Client::builder()
.connect_timeout(Duration::from_millis(10))
.default_headers(headers)
.build()?;
// Delete all records
{
seed_table(&client, &addr, table_name, num_records).await?;
// Verify there are records
let res = client.get(url).basic_auth(USER, Some(PASS)).send().await?;
let body: serde_json::Value = serde_json::from_str(&res.text().await?).unwrap();
assert_eq!(body[0]["result"].as_array().unwrap().len(), num_records, "body: {body}");
// Try to delete the records
let res = client.delete(url).basic_auth(USER, Some(PASS)).send().await?;
assert_eq!(res.status(), 200, "body: {}", res.text().await?);
// Verify the records were deleted
let res = client.get(url).basic_auth(USER, Some(PASS)).send().await?;
let body: serde_json::Value = serde_json::from_str(&res.text().await?).unwrap();
assert_eq!(body[0]["result"].as_array().unwrap().len(), 0, "body: {body}");
}
// Delete all records without authentication
{
seed_table(&client, &addr, table_name, num_records).await?;
// Try to delete the records
let res = client.delete(url).send().await?;
assert_eq!(res.status(), 200, "body: {}", res.text().await?);
// Verify the records were not deleted
let res = client.get(url).basic_auth(USER, Some(PASS)).send().await?;
let body: serde_json::Value = serde_json::from_str(&res.text().await?).unwrap();
assert_eq!(body[0]["result"].as_array().unwrap().len(), num_records, "body: {body}");
}
Ok(())
}
#[test(tokio::test)]
async fn key_endpoint_select_one() -> Result<(), Box<dyn std::error::Error>> {
let (addr, _server) = common::start_server_with_guests().await.unwrap();
let table_name = "table";
let url = &format!("http://{addr}/key/{table_name}/1");
// Prepare HTTP client
let mut headers = reqwest::header::HeaderMap::new();
headers.insert("surreal-ns", Ulid::new().to_string().parse()?);
headers.insert("surreal-db", Ulid::new().to_string().parse()?);
headers.insert(header::ACCEPT, "application/json".parse()?);
let client = reqwest::Client::builder()
.connect_timeout(Duration::from_millis(10))
.default_headers(headers)
.build()?;
// Seed the table
seed_table(&client, &addr, table_name, 1).await?;
// GET one record
{
let res = client.get(url).basic_auth(USER, Some(PASS)).send().await?;
assert_eq!(res.status(), 200, "body: {}", res.text().await?);
let body: serde_json::Value = serde_json::from_str(&res.text().await?).unwrap();
assert_eq!(body[0]["result"].as_array().unwrap().len(), 1, "body: {body}");
}
// GET without authentication returns no record
{
let res = client.get(url).send().await?;
assert_eq!(res.status(), 200, "body: {}", res.text().await?);
let body: serde_json::Value = serde_json::from_str(&res.text().await?).unwrap();
assert_eq!(body[0]["result"].as_array().unwrap().len(), 0, "body: {body}");
}
Ok(())
}
#[test(tokio::test)]
async fn key_endpoint_create_one() -> Result<(), Box<dyn std::error::Error>> {
let (addr, _server) = common::start_server_with_guests().await.unwrap();
let table_name = "table";
// Prepare HTTP client
let mut headers = reqwest::header::HeaderMap::new();
headers.insert("surreal-ns", Ulid::new().to_string().parse()?);
headers.insert("surreal-db", Ulid::new().to_string().parse()?);
headers.insert(header::ACCEPT, "application/json".parse()?);
let client = reqwest::Client::builder()
.connect_timeout(Duration::from_millis(10))
.default_headers(headers)
.build()?;
// Create record with known ID
{
let url = &format!("http://{addr}/key/{table_name}/new_id");
// Try to create the record
let res = client
.post(url)
.basic_auth(USER, Some(PASS))
.body(r#"{"name": "record_name"}"#)
.send()
.await?;
assert_eq!(res.status(), 200, "body: {}", res.text().await?);
// Verify the record was created with the given ID
let body: serde_json::Value = serde_json::from_str(&res.text().await?).unwrap();
assert_eq!(body[0]["result"].as_array().unwrap().len(), 1, "body: {body}");
assert_eq!(
body[0]["result"].as_array().unwrap()[0]["id"],
"table:new_id",
"body: {body}"
);
}
// Create record with known ID and query params
{
let url = &format!(
"http://{addr}/key/{table_name}/new_id_query?{params}",
params = "age=45&elems=[1,2,3]&other={test: true}"
);
// Try to create the record
let res = client
.post(url)
.basic_auth(USER, Some(PASS))
.body(r#"{ age: $age, elems: $elems, other: $other }"#)
.send()
.await?;
assert_eq!(res.status(), 200, "body: {}", res.text().await?);
// Verify the record was created with the given ID
let body: serde_json::Value = serde_json::from_str(&res.text().await?).unwrap();
assert_eq!(body[0]["result"].as_array().unwrap().len(), 1, "body: {body}");
assert_eq!(
body[0]["result"].as_array().unwrap()[0]["id"],
"table:new_id_query",
"body: {body}"
);
assert_eq!(body[0]["result"].as_array().unwrap()[0]["age"], 45, "body: {body}");
assert_eq!(
body[0]["result"].as_array().unwrap()[0]["elems"].as_array().unwrap().len(),
3,
"body: {body}"
);
assert_eq!(
body[0]["result"].as_array().unwrap()[0]["other"].as_object().unwrap()["test"],
true,
"body: {body}"
);
}
// POST without authentication creates no records
{
let url = &format!("http://{addr}/key/{table_name}/noauth_id");
// Try to create the record
let res = client.post(url).body(r#"{"name": "record_name"}"#).send().await?;
assert_eq!(res.status(), 200, "body: {}", res.text().await?);
// Verify the table is empty
let res = client.get(url).basic_auth(USER, Some(PASS)).send().await?;
let body: serde_json::Value = serde_json::from_str(&res.text().await?).unwrap();
assert_eq!(body[0]["result"].as_array().unwrap().len(), 0, "body: {body}");
}
Ok(())
}
#[test(tokio::test)]
async fn key_endpoint_update_one() -> Result<(), Box<dyn std::error::Error>> {
let (addr, _server) = common::start_server_with_guests().await.unwrap();
let table_name = "table";
let url = &format!("http://{addr}/key/{table_name}/1");
// Prepare HTTP client
let mut headers = reqwest::header::HeaderMap::new();
headers.insert("surreal-ns", Ulid::new().to_string().parse()?);
headers.insert("surreal-db", Ulid::new().to_string().parse()?);
headers.insert(header::ACCEPT, "application/json".parse()?);
let client = reqwest::Client::builder()
.connect_timeout(Duration::from_millis(10))
.default_headers(headers)
.build()?;
seed_table(&client, &addr, table_name, 1).await?;
// Update one record
{
// Try to update the record
let res = client
.put(url)
.basic_auth(USER, Some(PASS))
.body(r#"{"name": "record_name"}"#)
.send()
.await?;
assert_eq!(res.status(), 200, "body: {}", res.text().await?);
// Verify the record was updated
let res = client.get(url).basic_auth(USER, Some(PASS)).send().await?;
let body: serde_json::Value = serde_json::from_str(&res.text().await?).unwrap();
assert_eq!(body[0]["result"].as_array().unwrap()[0]["id"], "table:1", "body: {body}");
// Verify the record has the new data
assert_eq!(
body[0]["result"].as_array().unwrap()[0]["name"],
"record_name",
"body: {body}"
);
// Verify the record doesn't have the original data
assert!(body[0]["result"].as_array().unwrap()[0]["default"].is_null(), "body: {body}");
}
// Update one record without authentication
{
// Try to update the record
let res = client.put(url).body(r#"{"noauth": "yes"}"#).send().await?;
assert_eq!(res.status(), 200, "body: {}", res.text().await?);
// Verify the record was not updated
let res = client.get(url).basic_auth(USER, Some(PASS)).send().await?;
let body: serde_json::Value = serde_json::from_str(&res.text().await?).unwrap();
assert_eq!(body[0]["result"].as_array().unwrap()[0]["id"], "table:1", "body: {body}");
// Verify the record doesn't have the new data
assert!(body[0]["result"].as_array().unwrap()[0]["noauth"].is_null(), "body: {body}");
// Verify the record has the original data
assert_eq!(
body[0]["result"].as_array().unwrap()[0]["name"],
"record_name",
"body: {body}"
);
}
Ok(())
}
#[test(tokio::test)]
async fn key_endpoint_modify_one() -> Result<(), Box<dyn std::error::Error>> {
let (addr, _server) = common::start_server_with_guests().await.unwrap();
let table_name = "table";
let url = &format!("http://{addr}/key/{table_name}/1");
// Prepare HTTP client
let mut headers = reqwest::header::HeaderMap::new();
headers.insert("surreal-ns", Ulid::new().to_string().parse()?);
headers.insert("surreal-db", Ulid::new().to_string().parse()?);
headers.insert(header::ACCEPT, "application/json".parse()?);
let client = reqwest::Client::builder()
.connect_timeout(Duration::from_millis(10))
.default_headers(headers)
.build()?;
seed_table(&client, &addr, table_name, 1).await?;
// Modify one record
{
// Try to modify one record
let res = client
.patch(url)
.basic_auth(USER, Some(PASS))
.body(r#"{"name": "record_name"}"#)
.send()
.await?;
assert_eq!(res.status(), 200, "body: {}", res.text().await?);
// Verify the records were modified
let res = client.get(url).basic_auth(USER, Some(PASS)).send().await?;
let body: serde_json::Value = serde_json::from_str(&res.text().await?).unwrap();
assert_eq!(body[0]["result"].as_array().unwrap()[0]["id"], "table:1", "body: {body}");
// Verify the record has the new data
assert_eq!(
body[0]["result"].as_array().unwrap()[0]["name"],
"record_name",
"body: {body}"
);
// Verify the record has the original data too
assert_eq!(
body[0]["result"].as_array().unwrap()[0]["default"],
"content",
"body: {body}"
);
}
// Modify one record without authentication
{
// Try to modify the record
let res = client.patch(url).body(r#"{"noauth": "yes"}"#).send().await?;
assert_eq!(res.status(), 200, "body: {}", res.text().await?);
// Verify the record was not modified
let res = client.get(url).basic_auth(USER, Some(PASS)).send().await?;
let body: serde_json::Value = serde_json::from_str(&res.text().await?).unwrap();
assert_eq!(body[0]["result"].as_array().unwrap()[0]["id"], "table:1", "body: {body}");
// Verify the record doesn't have the new data
assert!(body[0]["result"].as_array().unwrap()[0]["noauth"].is_null(), "body: {body}");
// Verify the record has the original data too
assert_eq!(
body[0]["result"].as_array().unwrap()[0]["default"],
"content",
"body: {body}"
);
}
Ok(())
}
#[test(tokio::test)]
async fn key_endpoint_delete_one() -> Result<(), Box<dyn std::error::Error>> {
let (addr, _server) = common::start_server_with_guests().await.unwrap();
let table_name = "table";
let base_url = &format!("http://{addr}/key/{table_name}");
// Prepare HTTP client
let mut headers = reqwest::header::HeaderMap::new();
headers.insert("surreal-ns", Ulid::new().to_string().parse()?);
headers.insert("surreal-db", Ulid::new().to_string().parse()?);
headers.insert(header::ACCEPT, "application/json".parse()?);
let client = reqwest::Client::builder()
.connect_timeout(Duration::from_millis(10))
.default_headers(headers)
.build()?;
// Delete all records
{
seed_table(&client, &addr, table_name, 2).await?;
// Verify there are records
let res = client.get(base_url).basic_auth(USER, Some(PASS)).send().await?;
let body: serde_json::Value = serde_json::from_str(&res.text().await?).unwrap();
assert_eq!(body[0]["result"].as_array().unwrap().len(), 2, "body: {body}");
// Try to delete the record
let res =
client.delete(format!("{base_url}/1")).basic_auth(USER, Some(PASS)).send().await?;
assert_eq!(res.status(), 200, "body: {}", res.text().await?);
// Verify only one record was deleted
let res = client.get(base_url).basic_auth(USER, Some(PASS)).send().await?;
let body: serde_json::Value = serde_json::from_str(&res.text().await?).unwrap();
assert_eq!(body[0]["result"].as_array().unwrap().len(), 1, "body: {body}");
assert_eq!(body[0]["result"].as_array().unwrap()[0]["id"], "table:2", "body: {body}");
}
// Delete one record without authentication
{
// Try to delete the record
let res = client.delete(format!("{base_url}/2")).send().await?;
assert_eq!(res.status(), 200, "body: {}", res.text().await?);
// Verify the record was not deleted
let res = client.get(base_url).basic_auth(USER, Some(PASS)).send().await?;
let body: serde_json::Value = serde_json::from_str(&res.text().await?).unwrap();
assert_eq!(body[0]["result"].as_array().unwrap().len(), 1, "body: {body}");
}
Ok(())
}
}