use super::*;
#[ntex::test]
async fn updates_cannot_move_a_row_between_organisations_or_write_a_password() {
let db = TempDatabase::create("stamped").await;
let root = temp_dir("stamped");
write_files(
&root,
&[(
"main.toml",
&format!(
"[server]\nbase_path = \"/api\"\n\n[database]\nurl = \"{}\"\n",
db.url
),
)],
);
let state = load_state(&root).await;
let app = init_http_app!(state);
let register = |email: &'static str| {
req_json(
"POST",
"/api/auth/register",
json!({ "email": email, "password": "pw" }),
)
};
let mallory = read_json(test::call_service(&app, register("mallory@example.com")).await).await;
let mallory_token = mallory["token"].as_str().unwrap().to_string();
let mallory_id = mallory["user"]["id"].as_str().unwrap().to_string();
let victim = read_json(test::call_service(&app, register("victim@example.com")).await).await;
let victim_token = victim["token"].as_str().unwrap().to_string();
let create_org = |token: &str, name: &str| {
bearer(
test::TestRequest::post()
.uri("/api/organization")
.header(CONTENT_TYPE, "application/json")
.set_payload(json!({ "name": name }).to_string()),
token,
)
.to_request()
};
let mine = read_json(test::call_service(&app, create_org(&mallory_token, "Mine")).await).await;
let mine_id = mine["id"].as_str().unwrap().to_string();
let theirs =
read_json(test::call_service(&app, create_org(&victim_token, "Theirs")).await).await;
let theirs_id = theirs["id"].as_str().unwrap().to_string();
let memberships = read_json(
test::call_service(
&app,
bearer(
test::TestRequest::get()
.uri("/api/membership")
.header("x-organization", mine_id.clone()),
&mallory_token,
)
.to_request(),
)
.await,
)
.await;
let membership_id = memberships[0]["id"].as_str().unwrap().to_string();
let moved = test::call_service(
&app,
bearer(
test::TestRequest::patch()
.uri(&format!("/api/membership/{membership_id}"))
.header("x-organization", mine_id.clone())
.header(CONTENT_TYPE, "application/json")
.set_payload(json!({ "organization_id": theirs_id }).to_string()),
&mallory_token,
)
.to_request(),
)
.await;
assert_eq!(moved.status().as_u16(), 200);
assert_eq!(read_json(moved).await["organization_id"], mine_id);
let probe = test::call_service(
&app,
bearer(
test::TestRequest::get()
.uri("/api/membership")
.header("x-organization", theirs_id.clone()),
&mallory_token,
)
.to_request(),
)
.await;
assert_eq!(probe.status().as_u16(), 403);
let patched = test::call_service(
&app,
bearer(
test::TestRequest::patch()
.uri(&format!("/api/user/{mallory_id}"))
.header(CONTENT_TYPE, "application/json")
.set_payload(
json!({
"display_name": "Mallory",
"password_hash": "$argon2id$v=19$m=1,t=1,p=1$c2FsdA$forged"
})
.to_string(),
),
&mallory_token,
)
.to_request(),
)
.await;
assert_eq!(patched.status().as_u16(), 200);
let login = test::call_service(
&app,
req_json(
"POST",
"/api/auth/login",
json!({ "email": "mallory@example.com", "password": "pw" }),
),
)
.await;
assert_eq!(login.status().as_u16(), 200);
fs::remove_dir_all(root).unwrap();
db.cleanup().await;
}
#[ntex::test]
async fn org_members_can_read_each_other_but_strangers_cannot() {
let db = TempDatabase::create("user-comembers").await;
let root = temp_dir("user-comembers");
write_files(
&root,
&[(
"main.toml",
&format!(
"[server]\nbase_path = \"/api\"\n\n[database]\nurl = \"{}\"\n",
db.url
),
)],
);
let state = load_state(&root).await;
let app = init_http_app!(state);
let register = |email: &str| {
req_json(
"POST",
"/api/auth/register",
json!({"email": email, "password": "pw"}),
)
};
let admin = read_json(test::call_service(&app, register("admin@example.com")).await).await;
let admin_token = admin["token"].as_str().unwrap().to_string();
let colleague =
read_json(test::call_service(&app, register("colleague@example.com")).await).await;
let colleague_token = colleague["token"].as_str().unwrap().to_string();
let colleague_id = colleague["user"]["id"].as_str().unwrap().to_string();
let stranger =
read_json(test::call_service(&app, register("stranger@example.com")).await).await;
let stranger_token = stranger["token"].as_str().unwrap().to_string();
let org = read_json(
test::call_service(
&app,
bearer(
test::TestRequest::post()
.uri("/api/organization")
.header(CONTENT_TYPE, "application/json")
.set_payload(json!({"name":"Acme"}).to_string()),
&admin_token,
)
.to_request(),
)
.await,
)
.await;
let org_id = org["id"].as_str().unwrap().to_string();
let added = test::call_service(
&app,
bearer(
test::TestRequest::post()
.uri("/api/membership")
.header("x-organization", org_id.clone())
.header(CONTENT_TYPE, "application/json")
.set_payload(json!({"user_id": colleague_id, "role": "member"}).to_string()),
&admin_token,
)
.to_request(),
)
.await;
assert_eq!(added.status().as_u16(), 201);
let members = read_json(
test::call_service(
&app,
bearer(
test::TestRequest::get()
.uri("/api/membership?expand=user")
.header("x-organization", org_id.clone()),
&admin_token,
)
.to_request(),
)
.await,
)
.await;
let colleague_row = members
.as_array()
.unwrap()
.iter()
.find(|m| m["user_id"] == colleague_id)
.expect("the colleague's membership");
assert_eq!(colleague_row["user"]["email"], "colleague@example.com");
let read_as = |token: &str, id: &str| {
bearer(
test::TestRequest::get().uri(&format!("/api/user/{id}")),
token,
)
.to_request()
};
let seen = test::call_service(&app, read_as(&colleague_token, &colleague_id)).await;
assert_eq!(seen.status().as_u16(), 200);
let seen = test::call_service(&app, read_as(&admin_token, &colleague_id)).await;
assert_eq!(seen.status().as_u16(), 200);
let refused = test::call_service(&app, read_as(&stranger_token, &colleague_id)).await;
assert_eq!(refused.status().as_u16(), 404);
let listed = read_json(
test::call_service(
&app,
bearer(test::TestRequest::get().uri("/api/user"), &stranger_token).to_request(),
)
.await,
)
.await;
let listed = listed.as_array().unwrap();
assert_eq!(listed.len(), 1, "only themselves");
assert_eq!(listed[0]["email"], "stranger@example.com");
fs::remove_dir_all(root).unwrap();
db.cleanup().await;
}
#[ntex::test]
async fn org_admins_can_list_and_read_owner_scoped_rows_for_everyone() {
let db = TempDatabase::create("org-admin-owner-bypass").await;
let root = temp_dir("org-admin-owner-bypass");
write_files(
&root,
&[
(
"main.toml",
&format!(
"[server]\nbase_path = \"/api\"\n\n[database]\nurl = \"{}\"\n",
db.url
),
),
(
"models/note.toml",
r#"
[resource]
name = "note"
[permissions]
list = "owner"
read = "owner"
create = "member"
update = "owner"
delete = "owner"
[fields.title]
type = "string"
required = true
[fields.owner_id]
type = "reference"
references = "user"
required = true
"#,
),
],
);
let state = load_state(&root).await;
let app = init_http_app!(state);
let register = |email: &str| {
req_json(
"POST",
"/api/auth/register",
json!({"email": email, "password": "pw"}),
)
};
let admin = read_json(test::call_service(&app, register("admin@example.com")).await).await;
let admin_token = admin["token"].as_str().unwrap().to_string();
let member = read_json(test::call_service(&app, register("member@example.com")).await).await;
let member_token = member["token"].as_str().unwrap().to_string();
let org = read_json(
test::call_service(
&app,
bearer(
test::TestRequest::post()
.uri("/api/organization")
.header(CONTENT_TYPE, "application/json")
.set_payload(json!({"name":"Acme"}).to_string()),
&admin_token,
)
.to_request(),
)
.await,
)
.await;
let org_id = org["id"].as_str().unwrap().to_string();
let as_admin = |req: test::TestRequest| {
bearer(req.header("x-organization", org_id.clone()), &admin_token).to_request()
};
let as_member = |req: test::TestRequest| {
bearer(req.header("x-organization", org_id.clone()), &member_token).to_request()
};
let joined = test::call_service(
&app,
as_admin(
test::TestRequest::post()
.uri("/api/membership")
.header(CONTENT_TYPE, "application/json")
.set_payload(json!({"email":"member@example.com","role":"member"}).to_string()),
),
)
.await;
assert_eq!(joined.status().as_u16(), 201);
let mine = test::call_service(
&app,
as_admin(
test::TestRequest::post()
.uri("/api/note")
.header(CONTENT_TYPE, "application/json")
.set_payload(json!({"title":"Admin note"}).to_string()),
),
)
.await;
assert_eq!(mine.status().as_u16(), 201);
let mine_id = read_json(mine).await["id"].as_str().unwrap().to_string();
let theirs = test::call_service(
&app,
as_member(
test::TestRequest::post()
.uri("/api/note")
.header(CONTENT_TYPE, "application/json")
.set_payload(json!({"title":"Member note"}).to_string()),
),
)
.await;
assert_eq!(theirs.status().as_u16(), 201);
let theirs_id = read_json(theirs).await["id"].as_str().unwrap().to_string();
let listed = read_json(
test::call_service(&app, as_admin(test::TestRequest::get().uri("/api/note"))).await,
)
.await;
let rows = listed.as_array().unwrap();
assert_eq!(rows.len(), 2);
assert!(rows.iter().any(|row| row["id"] == mine_id));
assert!(rows.iter().any(|row| row["id"] == theirs_id));
let fetched = read_json(
test::call_service(
&app,
as_admin(test::TestRequest::get().uri(&format!("/api/note/{theirs_id}"))),
)
.await,
)
.await;
assert_eq!(fetched["id"], theirs_id);
fs::remove_dir_all(root).unwrap();
db.cleanup().await;
}
#[ntex::test]
async fn org_admins_only_see_global_owner_rows_for_the_active_org() {
let db = TempDatabase::create("global-owner-org-scope").await;
let root = temp_dir("global-owner-org-scope");
write_files(
&root,
&[
(
"main.toml",
&format!(
"[server]\nbase_path = \"/api\"\n\n[database]\nurl = \"{}\"\n",
db.url
),
),
(
"models/note.toml",
r#"
[resource]
name = "note"
scope = "global"
[permissions]
list = "owner"
read = "owner"
create = "authenticated"
update = "owner"
delete = "owner"
[fields.title]
type = "string"
required = true
[fields.owner_id]
type = "reference"
references = "user"
required = true
"#,
),
],
);
let state = load_state(&root).await;
let app = init_http_app!(state);
let register = |email: &str| {
req_json(
"POST",
"/api/auth/register",
json!({"email": email, "password": "pw"}),
)
};
let admin = read_json(test::call_service(&app, register("admin@example.com")).await).await;
let admin_token = admin["token"].as_str().unwrap().to_string();
let member = read_json(test::call_service(&app, register("member@example.com")).await).await;
let member_token = member["token"].as_str().unwrap().to_string();
let outsider =
read_json(test::call_service(&app, register("outsider@example.com")).await).await;
let outsider_token = outsider["token"].as_str().unwrap().to_string();
let create_org = |token: &str, name: &str| {
bearer(
test::TestRequest::post()
.uri("/api/organization")
.header(CONTENT_TYPE, "application/json")
.set_payload(json!({ "name": name }).to_string()),
token,
)
.to_request()
};
let org_a = read_json(test::call_service(&app, create_org(&admin_token, "Alpha")).await).await;
let org_a_id = org_a["id"].as_str().unwrap().to_string();
let org_b =
read_json(test::call_service(&app, create_org(&outsider_token, "Beta")).await).await;
let org_b_id = org_b["id"].as_str().unwrap().to_string();
let as_admin = |req: test::TestRequest| {
bearer(req.header("x-organization", org_a_id.clone()), &admin_token).to_request()
};
let as_outsider = |req: test::TestRequest| {
bearer(
req.header("x-organization", org_b_id.clone()),
&outsider_token,
)
.to_request()
};
let joined = test::call_service(
&app,
as_admin(
test::TestRequest::post()
.uri("/api/membership")
.header(CONTENT_TYPE, "application/json")
.set_payload(json!({"email":"member@example.com","role":"member"}).to_string()),
),
)
.await;
assert_eq!(joined.status().as_u16(), 201);
let create_note = |token: &str| {
bearer(
test::TestRequest::post()
.uri("/api/note")
.header(CONTENT_TYPE, "application/json")
.set_payload(json!({"title":"Note"}).to_string()),
token,
)
.to_request()
};
let mine = read_json(test::call_service(&app, create_note(&admin_token)).await).await;
let member_note = read_json(test::call_service(&app, create_note(&member_token)).await).await;
let outsider_note =
read_json(test::call_service(&app, create_note(&outsider_token)).await).await;
let listed = read_json(
test::call_service(&app, as_admin(test::TestRequest::get().uri("/api/note"))).await,
)
.await;
let rows = listed.as_array().unwrap();
assert_eq!(rows.len(), 2);
assert!(rows.iter().any(|row| row["id"] == mine["id"]));
assert!(rows.iter().any(|row| row["id"] == member_note["id"]));
assert!(!rows.iter().any(|row| row["id"] == outsider_note["id"]));
let refused = test::call_service(
&app,
as_admin(test::TestRequest::get().uri(&format!(
"/api/note/{}",
outsider_note["id"].as_str().unwrap()
))),
)
.await;
assert_eq!(refused.status().as_u16(), 404);
let outsider_reads_own = test::call_service(
&app,
as_outsider(test::TestRequest::get().uri(&format!(
"/api/note/{}",
outsider_note["id"].as_str().unwrap()
))),
)
.await;
assert_eq!(outsider_reads_own.status().as_u16(), 200);
fs::remove_dir_all(root).unwrap();
db.cleanup().await;
}
#[ntex::test]
async fn a_member_is_added_by_email_through_the_builtin_hook() {
let db = TempDatabase::create("join-hook").await;
let root = temp_dir("join-hook");
write_files(
&root,
&[(
"main.toml",
&format!(
"[server]\nbase_path = \"/api\"\n\n[database]\nurl = \"{}\"\n",
db.url
),
)],
);
let state = load_state(&root).await;
let app = init_http_app!(state);
let register = |email: &str| {
req_json(
"POST",
"/api/auth/register",
json!({"email": email, "password": "pw"}),
)
};
let admin = read_json(test::call_service(&app, register("boss@example.com")).await).await;
let admin_token = admin["token"].as_str().unwrap().to_string();
let newcomer =
read_json(test::call_service(&app, register("New.Hire@example.com")).await).await;
let newcomer_id = newcomer["user"]["id"].as_str().unwrap().to_string();
let org = read_json(
test::call_service(
&app,
bearer(
test::TestRequest::post()
.uri("/api/organization")
.header(CONTENT_TYPE, "application/json")
.set_payload(json!({"name":"Acme"}).to_string()),
&admin_token,
)
.to_request(),
)
.await,
)
.await;
let org_id = org["id"].as_str().unwrap().to_string();
let add = |body: serde_json::Value| {
bearer(
test::TestRequest::post()
.uri("/api/membership")
.header("x-organization", org_id.clone())
.header(CONTENT_TYPE, "application/json")
.set_payload(body.to_string()),
&admin_token,
)
.to_request()
};
let created = test::call_service(
&app,
add(json!({"email":"new.hire@example.com","role":"member"})),
)
.await;
assert_eq!(created.status().as_u16(), 201);
let created = read_json(created).await;
assert_eq!(created["user_id"], newcomer_id);
assert_eq!(created["role"], "member");
assert!(created.get("email").is_none());
let again = test::call_service(&app, add(json!({"email":"new.hire@example.com"}))).await;
assert_eq!(again.status().as_u16(), 409);
let unknown = test::call_service(&app, add(json!({"email":"ghost@example.com"}))).await;
assert_eq!(unknown.status().as_u16(), 404);
let empty = test::call_service(&app, add(json!({"role":"member"}))).await;
assert_eq!(empty.status().as_u16(), 422);
let by_id = test::call_service(
&app,
bearer(
test::TestRequest::post()
.uri("/api/membership")
.header("x-organization", org_id.clone())
.header(CONTENT_TYPE, "application/json")
.set_payload(json!({"user_id": newcomer_id, "role":"member"}).to_string()),
&admin_token,
)
.to_request(),
)
.await;
assert_eq!(
by_id.status().as_u16(),
409,
"already a member, by either name"
);
fs::remove_dir_all(root).unwrap();
db.cleanup().await;
}
#[ntex::test]
async fn expanding_a_relation_respects_the_target_permissions() {
let db = TempDatabase::create("expand-auth").await;
let root = temp_dir("expand-auth");
write_files(
&root,
&[
(
"main.toml",
&format!(
"[server]\nbase_path = \"/api\"\n\n[database]\nurl = \"{}\"\n",
db.url
),
),
(
"models/post.toml",
r#"
[resource]
name = "post"
[permissions]
list = "member"
read = "member"
create = "member"
[fields.title]
type = "string"
required = true
[fields.owner_id]
type = "reference"
references = "user"
"#,
),
(
"models/user.toml",
r#"
[resource]
name = "user"
scope = "global"
timestamps = true
[permissions]
list = "authenticated"
read = "owner"
create = "public"
update = "owner"
delete = "private"
[auth]
identity_field = "email"
password_field = "password_hash"
[fields.email]
type = "string"
required = true
unique = true
[fields.password_hash]
type = "string"
hidden = true
"#,
),
],
);
let state = load_state(&root).await;
let app = init_http_app!(state);
let author = read_json(
test::call_service(
&app,
req_json(
"POST",
"/api/auth/register",
json!({"email":"author@example.com","password":"pw"}),
),
)
.await,
)
.await;
let author_token = author["token"].as_str().unwrap().to_string();
let author_id = author["user"]["id"].as_str().unwrap().to_string();
let reader = read_json(
test::call_service(
&app,
req_json(
"POST",
"/api/auth/register",
json!({"email":"reader@example.com","password":"pw"}),
),
)
.await,
)
.await;
let reader_token = reader["token"].as_str().unwrap().to_string();
let reader_id = reader["user"]["id"].as_str().unwrap().to_string();
let org = read_json(
test::call_service(
&app,
bearer(
test::TestRequest::post()
.uri("/api/organization")
.header(CONTENT_TYPE, "application/json")
.set_payload(json!({"name":"Newsroom"}).to_string()),
&author_token,
)
.to_request(),
)
.await,
)
.await;
let org_id = org["id"].as_str().unwrap().to_string();
let added = test::call_service(
&app,
bearer(
test::TestRequest::post()
.uri("/api/membership")
.header("x-organization", org_id.clone())
.header(CONTENT_TYPE, "application/json")
.set_payload(json!({"user_id": reader_id, "role": "member"}).to_string()),
&author_token,
)
.to_request(),
)
.await;
assert_eq!(added.status().as_u16(), 201);
let created = test::call_service(
&app,
bearer(
test::TestRequest::post()
.uri("/api/post")
.header("x-organization", org_id.clone())
.header(CONTENT_TYPE, "application/json")
.set_payload(json!({"title":"Scoop"}).to_string()),
&author_token,
)
.to_request(),
)
.await;
assert_eq!(created.status().as_u16(), 201);
let expand = |token: &str| {
bearer(
test::TestRequest::get()
.uri("/api/post?expand=owner")
.header("x-organization", org_id.clone()),
token,
)
.to_request()
};
let mine = read_json(test::call_service(&app, expand(&author_token)).await).await;
assert_eq!(mine[0]["owner"]["id"], author_id);
assert_eq!(mine[0]["owner"]["email"], "author@example.com");
let theirs = read_json(test::call_service(&app, expand(&reader_token)).await).await;
assert_eq!(theirs[0]["title"], "Scoop");
assert!(theirs[0]["owner"].is_null());
fs::remove_dir_all(root).unwrap();
db.cleanup().await;
}
#[ntex::test]
async fn roles_are_a_set_that_admins_hold_all_of_and_cannot_resign_from() {
let db = TempDatabase::create("roles").await;
let root = temp_dir("roles");
write_files(
&root,
&[
(
"main.toml",
&format!(
"[server]\nbase_path = \"/api\"\n\n[database]\nurl = \"{}\"\n",
db.url
),
),
(
"models/order.toml",
r#"
[resource]
name = "order"
scope = "organization"
[permissions]
list = "member"
read = "member"
create = "role:buyer"
update = "role:buyer"
delete = "role:admin"
[fields.reference]
type = "string"
"#,
),
],
);
let state = load_state(&root).await;
let app = init_http_app!(state);
let register = |email: &'static str| {
req_json(
"POST",
"/api/auth/register",
json!({ "email": email, "password": "pw" }),
)
};
let founder = read_json(test::call_service(&app, register("founder@example.com")).await).await;
let founder_token = founder["token"].as_str().unwrap().to_string();
let staff = read_json(test::call_service(&app, register("staff@example.com")).await).await;
let staff_token = staff["token"].as_str().unwrap().to_string();
let org = read_json(
test::call_service(
&app,
bearer(
test::TestRequest::post()
.uri("/api/organization")
.header(CONTENT_TYPE, "application/json")
.set_payload(json!({ "name": "Acme" }).to_string()),
&founder_token,
)
.to_request(),
)
.await,
)
.await;
let org_id = org["id"].as_str().unwrap().to_string();
let as_founder = |req: test::TestRequest| {
bearer(req.header("x-organization", org_id.clone()), &founder_token).to_request()
};
let as_staff = |req: test::TestRequest| {
bearer(req.header("x-organization", org_id.clone()), &staff_token).to_request()
};
let created = test::call_service(
&app,
as_founder(
test::TestRequest::post()
.uri("/api/order")
.header(CONTENT_TYPE, "application/json")
.set_payload(json!({ "reference": "PO-1" }).to_string()),
),
)
.await;
assert_eq!(created.status().as_u16(), 201);
let membership = read_json(
test::call_service(
&app,
as_founder(
test::TestRequest::post()
.uri("/api/membership")
.header(CONTENT_TYPE, "application/json")
.set_payload(
json!({ "email": "staff@example.com", "role": "support" }).to_string(),
),
),
)
.await,
)
.await;
let staff_membership = membership["id"].as_str().unwrap().to_string();
let refused = test::call_service(
&app,
as_staff(
test::TestRequest::post()
.uri("/api/order")
.header(CONTENT_TYPE, "application/json")
.set_payload(json!({ "reference": "PO-2" }).to_string()),
),
)
.await;
assert_eq!(refused.status().as_u16(), 403);
let granted = test::call_service(
&app,
as_founder(
test::TestRequest::post()
.uri("/api/membership_role")
.header(CONTENT_TYPE, "application/json")
.set_payload(
json!({ "membership_id": staff_membership, "role": "buyer" }).to_string(),
),
),
)
.await;
assert_eq!(granted.status().as_u16(), 201);
let grant_id = read_json(granted).await["id"].as_str().unwrap().to_string();
let allowed = test::call_service(
&app,
as_staff(
test::TestRequest::post()
.uri("/api/order")
.header(CONTENT_TYPE, "application/json")
.set_payload(json!({ "reference": "PO-3" }).to_string()),
),
)
.await;
assert_eq!(allowed.status().as_u16(), 201);
let held = read_json(
test::call_service(
&app,
as_founder(test::TestRequest::get().uri("/api/membership_role")),
)
.await,
)
.await;
assert_eq!(held.as_array().unwrap().len(), 1);
assert_eq!(
read_json(
test::call_service(
&app,
as_founder(
test::TestRequest::get().uri(&format!("/api/membership/{staff_membership}"))
),
)
.await
)
.await["role"],
"support"
);
let again = test::call_service(
&app,
as_founder(
test::TestRequest::post()
.uri("/api/membership_role")
.header(CONTENT_TYPE, "application/json")
.set_payload(
json!({ "membership_id": staff_membership, "role": "buyer" }).to_string(),
),
),
)
.await;
assert_eq!(again.status().as_u16(), 409);
let shadow = test::call_service(
&app,
as_founder(
test::TestRequest::post()
.uri("/api/membership_role")
.header(CONTENT_TYPE, "application/json")
.set_payload(
json!({ "membership_id": staff_membership, "role": "support" }).to_string(),
),
),
)
.await;
assert_eq!(shadow.status().as_u16(), 409);
let revoked = test::call_service(
&app,
as_founder(test::TestRequest::delete().uri(&format!("/api/membership_role/{grant_id}"))),
)
.await;
assert_eq!(revoked.status().as_u16(), 204);
let refused_again = test::call_service(
&app,
as_staff(
test::TestRequest::post()
.uri("/api/order")
.header(CONTENT_TYPE, "application/json")
.set_payload(json!({ "reference": "PO-4" }).to_string()),
),
)
.await;
assert_eq!(refused_again.status().as_u16(), 403);
let own_membership = read_json(
test::call_service(
&app,
as_founder(test::TestRequest::get().uri("/api/membership")),
)
.await,
)
.await;
let founder_membership = own_membership
.as_array()
.unwrap()
.iter()
.find(|row| row["role"] == "admin")
.unwrap()["id"]
.as_str()
.unwrap()
.to_string();
let self_demote = test::call_service(
&app,
as_founder(
test::TestRequest::patch()
.uri(&format!("/api/membership/{founder_membership}"))
.header(CONTENT_TYPE, "application/json")
.set_payload(json!({ "role": "support" }).to_string()),
),
)
.await;
assert_eq!(self_demote.status().as_u16(), 403);
let self_remove = test::call_service(
&app,
as_founder(
test::TestRequest::delete().uri(&format!("/api/membership/{founder_membership}")),
),
)
.await;
assert_eq!(self_remove.status().as_u16(), 403);
let harmless = test::call_service(
&app,
as_founder(
test::TestRequest::patch()
.uri(&format!("/api/membership/{founder_membership}"))
.header(CONTENT_TYPE, "application/json")
.set_payload(json!({ "role": "admin" }).to_string()),
),
)
.await;
assert_eq!(harmless.status().as_u16(), 200);
let promote = test::call_service(
&app,
as_founder(
test::TestRequest::patch()
.uri(&format!("/api/membership/{staff_membership}"))
.header(CONTENT_TYPE, "application/json")
.set_payload(json!({ "role": "admin" }).to_string()),
),
)
.await;
assert_eq!(promote.status().as_u16(), 200);
let demote_other = test::call_service(
&app,
as_staff(
test::TestRequest::patch()
.uri(&format!("/api/membership/{founder_membership}"))
.header(CONTENT_TYPE, "application/json")
.set_payload(json!({ "role": "support" }).to_string()),
),
)
.await;
assert_eq!(demote_other.status().as_u16(), 200);
db.cleanup().await;
}