* chore(clippy): enable and fix many stricter lints These ensure that the codebase uses more idiomatic, performant, and concise language constructions. * chore: make non-Clippy compiler warnings also deny by default
1322 lines
48 KiB
Rust
1322 lines
48 KiB
Rust
use crate::common::{
|
|
api_common::{ApiProject, ApiTeams},
|
|
database::{
|
|
ADMIN_USER_PAT, ENEMY_USER_ID_PARSED, ENEMY_USER_PAT,
|
|
FRIEND_USER_ID_PARSED, MOD_USER_ID, MOD_USER_PAT, USER_USER_ID,
|
|
USER_USER_ID_PARSED, generate_random_name,
|
|
},
|
|
dummy_data::{
|
|
DummyImage, DummyOrganizationZeta, DummyProjectAlpha, DummyProjectBeta,
|
|
},
|
|
};
|
|
use actix_http::StatusCode;
|
|
use ariadne::ids::UserId;
|
|
use common::{
|
|
api_v3::ApiV3,
|
|
database::{FRIEND_USER_ID, FRIEND_USER_PAT, USER_USER_PAT},
|
|
environment::{
|
|
TestEnvironment, with_test_environment, with_test_environment_all,
|
|
},
|
|
permissions::{PermissionsTest, PermissionsTestContext},
|
|
};
|
|
use labrinth::models::teams::{OrganizationPermissions, ProjectPermissions};
|
|
use serde_json::json;
|
|
|
|
pub mod common;
|
|
|
|
#[actix_rt::test]
|
|
async fn create_organization() {
|
|
with_test_environment(
|
|
None,
|
|
|test_env: TestEnvironment<ApiV3>| async move {
|
|
let api = &test_env.api;
|
|
let zeta_organization_slug =
|
|
&test_env.dummy.organization_zeta.organization_id;
|
|
|
|
// Failed creations title:
|
|
// - too short title
|
|
// - too long title
|
|
for title in ["a", &"a".repeat(100)] {
|
|
let resp = api
|
|
.create_organization(
|
|
title,
|
|
"theta",
|
|
"theta_description",
|
|
USER_USER_PAT,
|
|
)
|
|
.await;
|
|
assert_status!(&resp, StatusCode::BAD_REQUEST);
|
|
}
|
|
|
|
// Failed creations slug:
|
|
// - slug collision with zeta
|
|
// - too short slug
|
|
// - too long slug
|
|
// - not url safe slug
|
|
for slug in [
|
|
zeta_organization_slug,
|
|
"a",
|
|
&"a".repeat(100),
|
|
"not url safe%&^!#$##!@#$%^&*()",
|
|
] {
|
|
let resp = api
|
|
.create_organization(
|
|
"Theta Org",
|
|
slug,
|
|
"theta_description",
|
|
USER_USER_PAT,
|
|
)
|
|
.await;
|
|
assert_status!(&resp, StatusCode::BAD_REQUEST);
|
|
}
|
|
|
|
// Failed creations description:
|
|
// - too short desc
|
|
// - too long desc
|
|
for description in ["a", &"a".repeat(300)] {
|
|
let resp = api
|
|
.create_organization(
|
|
"Theta Org",
|
|
"theta",
|
|
description,
|
|
USER_USER_PAT,
|
|
)
|
|
.await;
|
|
assert_status!(&resp, StatusCode::BAD_REQUEST);
|
|
}
|
|
|
|
// Create 'theta' organization
|
|
let resp = api
|
|
.create_organization(
|
|
"Theta Org",
|
|
"theta",
|
|
"not url safe%&^!#$##!@#$%^&",
|
|
USER_USER_PAT,
|
|
)
|
|
.await;
|
|
assert_status!(&resp, StatusCode::OK);
|
|
|
|
// Get organization using slug
|
|
let theta = api
|
|
.get_organization_deserialized("theta", USER_USER_PAT)
|
|
.await;
|
|
assert_eq!(theta.name, "Theta Org");
|
|
assert_eq!(theta.slug, "theta");
|
|
assert_eq!(theta.description, "not url safe%&^!#$##!@#$%^&");
|
|
assert_status!(&resp, StatusCode::OK);
|
|
|
|
// Get created team
|
|
let members = api
|
|
.get_organization_members_deserialized("theta", USER_USER_PAT)
|
|
.await;
|
|
|
|
// Should only be one member, which is USER_USER_ID, and is the owner with full permissions
|
|
assert_eq!(members[0].user.id.to_string(), USER_USER_ID);
|
|
assert_eq!(
|
|
members[0].organization_permissions,
|
|
Some(OrganizationPermissions::all())
|
|
);
|
|
assert_eq!(members[0].role, "Member");
|
|
assert!(members[0].is_owner);
|
|
},
|
|
)
|
|
.await;
|
|
}
|
|
|
|
#[actix_rt::test]
|
|
async fn get_project_organization() {
|
|
with_test_environment(
|
|
None,
|
|
|test_env: TestEnvironment<ApiV3>| async move {
|
|
let api = &test_env.api;
|
|
let zeta_organization_id =
|
|
&test_env.dummy.organization_zeta.organization_id;
|
|
let alpha_project_id = &test_env.dummy.project_alpha.project_id;
|
|
|
|
// ADd alpha project to zeta organization
|
|
let resp = api
|
|
.organization_add_project(
|
|
zeta_organization_id,
|
|
alpha_project_id,
|
|
USER_USER_PAT,
|
|
)
|
|
.await;
|
|
assert_status!(&resp, StatusCode::OK);
|
|
|
|
// Get project organization
|
|
let zeta = api
|
|
.get_project_organization_deserialized(
|
|
alpha_project_id,
|
|
USER_USER_PAT,
|
|
)
|
|
.await;
|
|
assert_eq!(zeta.id.to_string(), zeta_organization_id.to_string());
|
|
},
|
|
)
|
|
.await;
|
|
}
|
|
|
|
#[actix_rt::test]
|
|
async fn patch_organization() {
|
|
with_test_environment(
|
|
None,
|
|
|test_env: TestEnvironment<ApiV3>| async move {
|
|
let api = &test_env.api;
|
|
|
|
let zeta_organization_id =
|
|
&test_env.dummy.organization_zeta.organization_id;
|
|
|
|
// Create 'theta' organization
|
|
let resp = api
|
|
.create_organization(
|
|
"Theta Org",
|
|
"theta",
|
|
"theta_description",
|
|
USER_USER_PAT,
|
|
)
|
|
.await;
|
|
assert_status!(&resp, StatusCode::OK);
|
|
|
|
// Failed patch to theta title:
|
|
// - too short title
|
|
// - too long title
|
|
for title in ["a", &"a".repeat(100)] {
|
|
let resp = api
|
|
.edit_organization(
|
|
"theta",
|
|
json!({
|
|
"name": title,
|
|
}),
|
|
USER_USER_PAT,
|
|
)
|
|
.await;
|
|
assert_status!(&resp, StatusCode::BAD_REQUEST);
|
|
}
|
|
|
|
// Failed patch to zeta slug:
|
|
// - slug collision with theta
|
|
// - too short slug
|
|
// - too long slug
|
|
// - not url safe slug
|
|
for title in [
|
|
"theta",
|
|
"a",
|
|
&"a".repeat(100),
|
|
"not url safe%&^!#$##!@#$%^&*()",
|
|
] {
|
|
let resp = api
|
|
.edit_organization(
|
|
zeta_organization_id,
|
|
json!({
|
|
"slug": title,
|
|
"description": "theta_description"
|
|
}),
|
|
USER_USER_PAT,
|
|
)
|
|
.await;
|
|
assert_status!(&resp, StatusCode::BAD_REQUEST);
|
|
}
|
|
|
|
// Failed patch to zeta description:
|
|
// - too short description
|
|
// - too long description
|
|
for description in ["a", &"a".repeat(300)] {
|
|
let resp = api
|
|
.edit_organization(
|
|
zeta_organization_id,
|
|
json!({
|
|
"description": description
|
|
}),
|
|
USER_USER_PAT,
|
|
)
|
|
.await;
|
|
assert_status!(&resp, StatusCode::BAD_REQUEST);
|
|
}
|
|
|
|
// Successful patch to many fields
|
|
let resp = api
|
|
.edit_organization(
|
|
zeta_organization_id,
|
|
json!({
|
|
"name": "new_title",
|
|
"slug": "new_slug",
|
|
"description": "not url safe%&^!#$##!@#$%^&" // not-URL-safe description should still work
|
|
}),
|
|
USER_USER_PAT,
|
|
)
|
|
.await;
|
|
assert_status!(&resp, StatusCode::NO_CONTENT);
|
|
|
|
// Get project using new slug
|
|
let new_title = api
|
|
.get_organization_deserialized("new_slug", USER_USER_PAT)
|
|
.await;
|
|
assert_eq!(new_title.name, "new_title");
|
|
assert_eq!(new_title.slug, "new_slug");
|
|
assert_eq!(new_title.description, "not url safe%&^!#$##!@#$%^&");
|
|
},
|
|
)
|
|
.await;
|
|
}
|
|
|
|
// add/remove icon
|
|
#[actix_rt::test]
|
|
async fn add_remove_icon() {
|
|
with_test_environment(
|
|
Some(10),
|
|
|test_env: TestEnvironment<ApiV3>| async move {
|
|
let api = &test_env.api;
|
|
let zeta_organization_id =
|
|
&test_env.dummy.organization_zeta.organization_id;
|
|
|
|
// Get project
|
|
let resp = test_env
|
|
.api
|
|
.get_organization_deserialized(
|
|
zeta_organization_id,
|
|
USER_USER_PAT,
|
|
)
|
|
.await;
|
|
assert_eq!(resp.icon_url, None);
|
|
|
|
// Icon edit
|
|
// Uses alpha organization to delete this icon
|
|
let resp = api
|
|
.edit_organization_icon(
|
|
zeta_organization_id,
|
|
Some(DummyImage::SmallIcon.get_icon_data()),
|
|
USER_USER_PAT,
|
|
)
|
|
.await;
|
|
assert_status!(&resp, StatusCode::NO_CONTENT);
|
|
|
|
// Get project
|
|
let zeta_org = api
|
|
.get_organization_deserialized(
|
|
zeta_organization_id,
|
|
USER_USER_PAT,
|
|
)
|
|
.await;
|
|
assert!(zeta_org.icon_url.is_some());
|
|
|
|
// Icon delete
|
|
// Uses alpha organization to delete added icon
|
|
let resp = api
|
|
.edit_organization_icon(
|
|
zeta_organization_id,
|
|
None,
|
|
USER_USER_PAT,
|
|
)
|
|
.await;
|
|
assert_status!(&resp, StatusCode::NO_CONTENT);
|
|
|
|
// Get project
|
|
let zeta_org = api
|
|
.get_organization_deserialized(
|
|
zeta_organization_id,
|
|
USER_USER_PAT,
|
|
)
|
|
.await;
|
|
assert!(zeta_org.icon_url.is_none());
|
|
},
|
|
)
|
|
.await;
|
|
}
|
|
|
|
// delete org
|
|
#[actix_rt::test]
|
|
async fn delete_org() {
|
|
with_test_environment(
|
|
None,
|
|
|test_env: TestEnvironment<ApiV3>| async move {
|
|
let api = &test_env.api;
|
|
let zeta_organization_id =
|
|
&test_env.dummy.organization_zeta.organization_id;
|
|
|
|
let resp = api
|
|
.delete_organization(zeta_organization_id, USER_USER_PAT)
|
|
.await;
|
|
assert_status!(&resp, StatusCode::NO_CONTENT);
|
|
|
|
// Get organization, which should no longer exist
|
|
let resp = api
|
|
.get_organization(zeta_organization_id, USER_USER_PAT)
|
|
.await;
|
|
assert_status!(&resp, StatusCode::NOT_FOUND);
|
|
},
|
|
)
|
|
.await;
|
|
}
|
|
|
|
// add/remove organization projects
|
|
#[actix_rt::test]
|
|
async fn add_remove_organization_projects() {
|
|
with_test_environment(
|
|
None,
|
|
|test_env: TestEnvironment<ApiV3>| async move {
|
|
let alpha_project_id: &str =
|
|
&test_env.dummy.project_alpha.project_id;
|
|
let alpha_project_slug: &str =
|
|
&test_env.dummy.project_alpha.project_slug;
|
|
let zeta_organization_id: &str =
|
|
&test_env.dummy.organization_zeta.organization_id;
|
|
|
|
// user's page should show alpha project
|
|
// It may contain more than one project, depending on dummy data, but should contain the alpha project
|
|
let projects = test_env
|
|
.api
|
|
.get_user_projects_deserialized_common(
|
|
USER_USER_ID,
|
|
USER_USER_PAT,
|
|
)
|
|
.await;
|
|
assert!(
|
|
projects
|
|
.iter()
|
|
.any(|p| p.id.to_string() == alpha_project_id)
|
|
);
|
|
|
|
// Add/remove project to organization, first by ID, then by slug
|
|
for alpha in [alpha_project_id, alpha_project_slug] {
|
|
let resp = test_env
|
|
.api
|
|
.organization_add_project(
|
|
zeta_organization_id,
|
|
alpha,
|
|
USER_USER_PAT,
|
|
)
|
|
.await;
|
|
assert_status!(&resp, StatusCode::OK);
|
|
|
|
// Get organization projects
|
|
let projects = test_env
|
|
.api
|
|
.get_organization_projects_deserialized(
|
|
zeta_organization_id,
|
|
USER_USER_PAT,
|
|
)
|
|
.await;
|
|
assert_eq!(projects[0].id.to_string(), alpha_project_id);
|
|
assert_eq!(
|
|
projects[0].slug,
|
|
Some(alpha_project_slug.to_string())
|
|
);
|
|
|
|
// Currently, intended behaviour is that user's page should NOT show organization projects.
|
|
// It may contain other projects, depending on dummy data, but should not contain the alpha project
|
|
let projects = test_env
|
|
.api
|
|
.get_user_projects_deserialized_common(
|
|
USER_USER_ID,
|
|
USER_USER_PAT,
|
|
)
|
|
.await;
|
|
assert!(
|
|
!projects
|
|
.iter()
|
|
.any(|p| p.id.to_string() == alpha_project_id)
|
|
);
|
|
|
|
// Remove project from organization
|
|
let resp = test_env
|
|
.api
|
|
.organization_remove_project(
|
|
zeta_organization_id,
|
|
alpha,
|
|
UserId(USER_USER_ID_PARSED as u64),
|
|
USER_USER_PAT,
|
|
)
|
|
.await;
|
|
assert_status!(&resp, StatusCode::OK);
|
|
|
|
// Get user's projects as user - should be 1, the alpha project,
|
|
// as we gave back ownership to the user when we removed it from the organization
|
|
// So user's page should show the alpha project (and possibly others)
|
|
let projects = test_env
|
|
.api
|
|
.get_user_projects_deserialized_common(
|
|
USER_USER_ID,
|
|
USER_USER_PAT,
|
|
)
|
|
.await;
|
|
assert!(
|
|
projects
|
|
.iter()
|
|
.any(|p| p.id.to_string() == alpha_project_id)
|
|
);
|
|
|
|
// Get organization projects
|
|
let projects = test_env
|
|
.api
|
|
.get_organization_projects_deserialized(
|
|
zeta_organization_id,
|
|
USER_USER_PAT,
|
|
)
|
|
.await;
|
|
assert!(projects.is_empty());
|
|
}
|
|
},
|
|
)
|
|
.await;
|
|
}
|
|
|
|
// Like above, but specifically regarding ownership transferring
|
|
#[actix_rt::test]
|
|
async fn add_remove_organization_project_ownership_to_user() {
|
|
with_test_environment(
|
|
None,
|
|
|test_env: TestEnvironment<ApiV3>| async move {
|
|
let DummyProjectAlpha {
|
|
project_id: alpha_project_id,
|
|
team_id: alpha_team_id,
|
|
..
|
|
} = &test_env.dummy.project_alpha;
|
|
let DummyProjectBeta {
|
|
project_id: beta_project_id,
|
|
team_id: beta_team_id,
|
|
..
|
|
} = &test_env.dummy.project_beta;
|
|
let DummyOrganizationZeta {
|
|
organization_id: zeta_organization_id,
|
|
team_id: zeta_team_id,
|
|
..
|
|
} = &test_env.dummy.organization_zeta;
|
|
|
|
// Add friend to alpha, beta, and zeta
|
|
for (team, organization) in [
|
|
(alpha_team_id, false),
|
|
(beta_team_id, false),
|
|
(zeta_team_id, true),
|
|
] {
|
|
let org_permissions = if organization {
|
|
Some(OrganizationPermissions::all())
|
|
} else {
|
|
None
|
|
};
|
|
let resp = test_env
|
|
.api
|
|
.add_user_to_team(
|
|
team,
|
|
FRIEND_USER_ID,
|
|
Some(ProjectPermissions::all()),
|
|
org_permissions,
|
|
USER_USER_PAT,
|
|
)
|
|
.await;
|
|
assert_status!(&resp, StatusCode::NO_CONTENT);
|
|
|
|
// Accept invites
|
|
let resp = test_env.api.join_team(team, FRIEND_USER_PAT).await;
|
|
assert_status!(&resp, StatusCode::NO_CONTENT);
|
|
}
|
|
|
|
// For each team, confirm there are two members, but only one owner of the project, and it is USER_USER_ID
|
|
for team in [alpha_team_id, beta_team_id, zeta_team_id] {
|
|
let members = test_env
|
|
.api
|
|
.get_team_members_deserialized(team, USER_USER_PAT)
|
|
.await;
|
|
assert_eq!(members.len(), 2);
|
|
let user_member =
|
|
members.iter().filter(|m| m.is_owner).collect::<Vec<_>>();
|
|
assert_eq!(user_member.len(), 1);
|
|
assert_eq!(user_member[0].user.id.to_string(), USER_USER_ID);
|
|
}
|
|
|
|
// Transfer ownership of beta project to FRIEND
|
|
let resp = test_env
|
|
.api
|
|
.transfer_team_ownership(
|
|
beta_team_id,
|
|
FRIEND_USER_ID,
|
|
USER_USER_PAT,
|
|
)
|
|
.await;
|
|
assert_status!(&resp, StatusCode::NO_CONTENT);
|
|
|
|
// Confirm there are still two users, but now FRIEND_USER_ID is the owner
|
|
let members = test_env
|
|
.api
|
|
.get_team_members_deserialized(beta_team_id, USER_USER_PAT)
|
|
.await;
|
|
assert_eq!(members.len(), 2);
|
|
let user_member =
|
|
members.iter().filter(|m| m.is_owner).collect::<Vec<_>>();
|
|
assert_eq!(user_member.len(), 1);
|
|
assert_eq!(user_member[0].user.id.to_string(), FRIEND_USER_ID);
|
|
|
|
// Add alpha, beta to zeta organization
|
|
for (project_id, pat) in [
|
|
(alpha_project_id, USER_USER_PAT),
|
|
(beta_project_id, FRIEND_USER_PAT),
|
|
] {
|
|
let resp = test_env
|
|
.api
|
|
.organization_add_project(
|
|
zeta_organization_id,
|
|
project_id,
|
|
pat,
|
|
)
|
|
.await;
|
|
assert_status!(&resp, StatusCode::OK);
|
|
|
|
// Get and confirm it has been added
|
|
let project = test_env
|
|
.api
|
|
.get_project_deserialized(project_id, pat)
|
|
.await;
|
|
assert_eq!(
|
|
&project.organization.unwrap().to_string(),
|
|
zeta_organization_id
|
|
);
|
|
}
|
|
|
|
// Alpha project should have:
|
|
// - 1 member, FRIEND_USER_ID
|
|
// -> User was removed entirely as a team_member as it is now the owner of the organization
|
|
// - No owner.
|
|
// -> For alpha, user was removed as owner when it was added to the organization
|
|
// -> Friend was never an owner of the alpha project
|
|
let members = test_env
|
|
.api
|
|
.get_team_members_deserialized(alpha_team_id, USER_USER_PAT)
|
|
.await;
|
|
assert_eq!(members.len(), 1);
|
|
assert_eq!(members[0].user.id.to_string(), FRIEND_USER_ID);
|
|
assert_eq!(members.iter().filter(|m| m.is_owner).count(), 0);
|
|
|
|
// Beta project should have:
|
|
// - No members
|
|
// -> User was removed entirely as a team_member as it is now the owner of the organization
|
|
// -> Friend was made owner of the beta project, but was removed as a member when it was added to the organization
|
|
// If you are owner of a projeect, you are removed from the team when it is added to an organization,
|
|
// so that your former permissions are not overriding the organization permissions by default.
|
|
let members = test_env
|
|
.api
|
|
.get_team_members_deserialized(beta_team_id, USER_USER_PAT)
|
|
.await;
|
|
assert!(members.is_empty());
|
|
|
|
// Transfer ownership of zeta organization to FRIEND
|
|
let resp = test_env
|
|
.api
|
|
.transfer_team_ownership(
|
|
zeta_team_id,
|
|
FRIEND_USER_ID,
|
|
USER_USER_PAT,
|
|
)
|
|
.await;
|
|
assert_status!(&resp, StatusCode::NO_CONTENT);
|
|
|
|
// Confirm there are no members of the alpha project OR the beta project
|
|
// - Friend was removed as a member of these projects when ownership was transferred to them
|
|
for team_id in [alpha_team_id, beta_team_id] {
|
|
let members = test_env
|
|
.api
|
|
.get_team_members_deserialized(team_id, USER_USER_PAT)
|
|
.await;
|
|
assert!(members.is_empty());
|
|
}
|
|
|
|
// As user, cannot add friend to alpha project, as they are the org owner
|
|
let resp = test_env
|
|
.api
|
|
.add_user_to_team(
|
|
alpha_team_id,
|
|
FRIEND_USER_ID,
|
|
None,
|
|
None,
|
|
USER_USER_PAT,
|
|
)
|
|
.await;
|
|
assert_status!(&resp, StatusCode::BAD_REQUEST);
|
|
|
|
// As friend, can add user to alpha project, as they are not the org owner
|
|
let resp = test_env
|
|
.api
|
|
.add_user_to_team(
|
|
alpha_team_id,
|
|
USER_USER_ID,
|
|
None,
|
|
None,
|
|
FRIEND_USER_PAT,
|
|
)
|
|
.await;
|
|
assert_status!(&resp, StatusCode::NO_CONTENT);
|
|
|
|
// At this point, friend owns the org
|
|
// Alpha member has user as a member, but not as an owner
|
|
// Neither project has an owner, as they are owned by the org
|
|
|
|
// Remove project from organization with a user that is not an organization member
|
|
// This should fail as we cannot give a project to a user that is not a member of the organization
|
|
let resp = test_env
|
|
.api
|
|
.organization_remove_project(
|
|
zeta_organization_id,
|
|
alpha_project_id,
|
|
UserId(ENEMY_USER_ID_PARSED as u64),
|
|
USER_USER_PAT,
|
|
)
|
|
.await;
|
|
assert_status!(&resp, StatusCode::BAD_REQUEST);
|
|
|
|
// Set user's permissions within the project that it is a member of to none (for a later test)
|
|
let resp = test_env
|
|
.api
|
|
.edit_team_member(
|
|
alpha_team_id,
|
|
USER_USER_ID,
|
|
json!({
|
|
"project_permissions": 0,
|
|
}),
|
|
FRIEND_USER_PAT,
|
|
)
|
|
.await;
|
|
assert_status!(&resp, StatusCode::NO_CONTENT);
|
|
|
|
// Remove project from organization with a user that is an organization member, and a project member
|
|
// This should succeed
|
|
let resp = test_env
|
|
.api
|
|
.organization_remove_project(
|
|
zeta_organization_id,
|
|
alpha_project_id,
|
|
UserId(USER_USER_ID_PARSED as u64),
|
|
USER_USER_PAT,
|
|
)
|
|
.await;
|
|
assert_status!(&resp, StatusCode::OK);
|
|
|
|
// Remove project from organization with a user that is an organization member, but not a project member
|
|
// This should succeed
|
|
let resp = test_env
|
|
.api
|
|
.organization_remove_project(
|
|
zeta_organization_id,
|
|
beta_project_id,
|
|
UserId(USER_USER_ID_PARSED as u64),
|
|
USER_USER_PAT,
|
|
)
|
|
.await;
|
|
assert_status!(&resp, StatusCode::OK);
|
|
|
|
// For each of alpha and beta, confirm:
|
|
// - There is one member of each project, the owner, USER_USER_ID
|
|
// - In addition to being the owner, they have full permissions (even though they were set to none earlier)
|
|
// - They no longer have an attached organization
|
|
for team_id in [alpha_team_id, beta_team_id] {
|
|
let members = test_env
|
|
.api
|
|
.get_team_members_deserialized(team_id, USER_USER_PAT)
|
|
.await;
|
|
assert_eq!(members.len(), 1);
|
|
let user_member =
|
|
members.iter().filter(|m| m.is_owner).collect::<Vec<_>>();
|
|
assert_eq!(user_member.len(), 1);
|
|
assert_eq!(user_member[0].user.id.to_string(), USER_USER_ID);
|
|
assert_eq!(
|
|
user_member[0].permissions.unwrap(),
|
|
ProjectPermissions::all()
|
|
);
|
|
}
|
|
|
|
for project_id in [alpha_project_id, beta_project_id] {
|
|
let project = test_env
|
|
.api
|
|
.get_project_deserialized(project_id, USER_USER_PAT)
|
|
.await;
|
|
assert!(project.organization.is_none());
|
|
}
|
|
},
|
|
)
|
|
.await;
|
|
}
|
|
|
|
#[actix_rt::test]
|
|
async fn delete_organization_means_all_projects_to_org_owner() {
|
|
with_test_environment(
|
|
None,
|
|
|test_env: TestEnvironment<ApiV3>| async move {
|
|
let DummyProjectAlpha {
|
|
project_id: alpha_project_id,
|
|
team_id: alpha_team_id,
|
|
..
|
|
} = &test_env.dummy.project_alpha;
|
|
let DummyProjectBeta {
|
|
project_id: beta_project_id,
|
|
team_id: beta_team_id,
|
|
..
|
|
} = &test_env.dummy.project_beta;
|
|
let DummyOrganizationZeta {
|
|
organization_id: zeta_organization_id,
|
|
team_id: zeta_team_id,
|
|
..
|
|
} = &test_env.dummy.organization_zeta;
|
|
|
|
// Create random project from enemy, ensure it wont get affected
|
|
let (enemy_project, _) = test_env
|
|
.api
|
|
.add_public_project("enemy_project", None, None, ENEMY_USER_PAT)
|
|
.await;
|
|
|
|
// Add FRIEND
|
|
let resp = test_env
|
|
.api
|
|
.add_user_to_team(
|
|
zeta_team_id,
|
|
FRIEND_USER_ID,
|
|
None,
|
|
None,
|
|
USER_USER_PAT,
|
|
)
|
|
.await;
|
|
assert_status!(&resp, StatusCode::NO_CONTENT);
|
|
|
|
// Accept invite
|
|
let resp =
|
|
test_env.api.join_team(zeta_team_id, FRIEND_USER_PAT).await;
|
|
assert_status!(&resp, StatusCode::NO_CONTENT);
|
|
|
|
// Confirm there is only one owner of the project, and it is USER_USER_ID
|
|
let members = test_env
|
|
.api
|
|
.get_team_members_deserialized(alpha_team_id, USER_USER_PAT)
|
|
.await;
|
|
let user_member =
|
|
members.iter().filter(|m| m.is_owner).collect::<Vec<_>>();
|
|
assert_eq!(user_member.len(), 1);
|
|
assert_eq!(user_member[0].user.id.to_string(), USER_USER_ID);
|
|
|
|
// Add alpha to zeta organization
|
|
let resp = test_env
|
|
.api
|
|
.organization_add_project(
|
|
zeta_organization_id,
|
|
alpha_project_id,
|
|
USER_USER_PAT,
|
|
)
|
|
.await;
|
|
assert_status!(&resp, StatusCode::OK);
|
|
|
|
// Add beta to zeta organization
|
|
test_env
|
|
.api
|
|
.organization_add_project(
|
|
zeta_organization_id,
|
|
beta_project_id,
|
|
USER_USER_PAT,
|
|
)
|
|
.await;
|
|
|
|
// Add friend as a member of the beta project
|
|
let resp = test_env
|
|
.api
|
|
.add_user_to_team(
|
|
beta_team_id,
|
|
FRIEND_USER_ID,
|
|
None,
|
|
None,
|
|
USER_USER_PAT,
|
|
)
|
|
.await;
|
|
assert_status!(&resp, StatusCode::NO_CONTENT);
|
|
|
|
// Try to accept invite
|
|
// This returns a failure, because since beta and FRIEND are in the organizations,
|
|
// they can be added to the project without an invite
|
|
let resp =
|
|
test_env.api.join_team(beta_team_id, FRIEND_USER_PAT).await;
|
|
assert_status!(&resp, StatusCode::BAD_REQUEST);
|
|
|
|
// Confirm there is NO owner of the project, as it is owned by the organization
|
|
let members = test_env
|
|
.api
|
|
.get_team_members_deserialized(alpha_team_id, USER_USER_PAT)
|
|
.await;
|
|
assert_eq!(members.iter().filter(|m| m.is_owner).count(), 0);
|
|
|
|
// Transfer ownership of zeta organization to FRIEND
|
|
let resp = test_env
|
|
.api
|
|
.transfer_team_ownership(
|
|
zeta_team_id,
|
|
FRIEND_USER_ID,
|
|
USER_USER_PAT,
|
|
)
|
|
.await;
|
|
assert_status!(&resp, StatusCode::NO_CONTENT);
|
|
|
|
// Confirm there is NO owner of the project, as it is owned by the organization
|
|
let members = test_env
|
|
.api
|
|
.get_team_members_deserialized(alpha_team_id, USER_USER_PAT)
|
|
.await;
|
|
assert_eq!(members.iter().filter(|m| m.is_owner).count(), 0);
|
|
|
|
// Delete organization
|
|
let resp = test_env
|
|
.api
|
|
.delete_organization(zeta_organization_id, FRIEND_USER_PAT)
|
|
.await;
|
|
assert_status!(&resp, StatusCode::NO_CONTENT);
|
|
|
|
// Confirm there is only one owner of the alpha project, and it is now FRIEND_USER_ID
|
|
let members = test_env
|
|
.api
|
|
.get_team_members_deserialized(alpha_team_id, USER_USER_PAT)
|
|
.await;
|
|
let user_member =
|
|
members.iter().filter(|m| m.is_owner).collect::<Vec<_>>();
|
|
assert_eq!(user_member.len(), 1);
|
|
assert_eq!(user_member[0].user.id.to_string(), FRIEND_USER_ID);
|
|
|
|
// Confirm there is only one owner of the beta project, and it is now FRIEND_USER_ID
|
|
let members = test_env
|
|
.api
|
|
.get_team_members_deserialized(beta_team_id, USER_USER_PAT)
|
|
.await;
|
|
let user_member =
|
|
members.iter().filter(|m| m.is_owner).collect::<Vec<_>>();
|
|
assert_eq!(user_member.len(), 1);
|
|
assert_eq!(user_member[0].user.id.to_string(), FRIEND_USER_ID);
|
|
|
|
// Confirm there is only one member of the enemy project, and it is STILL ENEMY_USER_ID
|
|
let enemy_project = test_env
|
|
.api
|
|
.get_project_deserialized(
|
|
&enemy_project.id.to_string(),
|
|
ENEMY_USER_PAT,
|
|
)
|
|
.await;
|
|
let members = test_env
|
|
.api
|
|
.get_team_members_deserialized(
|
|
&enemy_project.team_id.to_string(),
|
|
ENEMY_USER_PAT,
|
|
)
|
|
.await;
|
|
let user_member =
|
|
members.iter().filter(|m| m.is_owner).collect::<Vec<_>>();
|
|
assert_eq!(user_member.len(), 1);
|
|
assert_eq!(
|
|
user_member[0].user.id.to_string(),
|
|
ENEMY_USER_ID_PARSED.to_string()
|
|
);
|
|
},
|
|
)
|
|
.await;
|
|
}
|
|
|
|
#[actix_rt::test]
|
|
async fn permissions_patch_organization() {
|
|
with_test_environment(
|
|
None,
|
|
|test_env: TestEnvironment<ApiV3>| async move {
|
|
// For each permission covered by EDIT_DETAILS, ensure the permission is required
|
|
let api = &test_env.api;
|
|
let edit_details = OrganizationPermissions::EDIT_DETAILS;
|
|
let test_pairs = [
|
|
("name", json!("")), // generated in the test to not collide slugs
|
|
("description", json!("New description")),
|
|
];
|
|
|
|
for (key, value) in test_pairs {
|
|
let req_gen = |ctx: PermissionsTestContext| {
|
|
let value = value.clone();
|
|
async move {
|
|
api.edit_organization(
|
|
&ctx.organization_id.unwrap(),
|
|
json!({
|
|
key: if key == "name" {
|
|
json!(generate_random_name("randomslug"))
|
|
} else {
|
|
value.clone()
|
|
},
|
|
}),
|
|
ctx.test_pat.as_deref(),
|
|
)
|
|
.await
|
|
}
|
|
};
|
|
PermissionsTest::new(&test_env)
|
|
.simple_organization_permissions_test(edit_details, req_gen)
|
|
.await
|
|
.unwrap();
|
|
}
|
|
},
|
|
)
|
|
.await;
|
|
}
|
|
|
|
// Not covered by PATCH /organization
|
|
#[actix_rt::test]
|
|
async fn permissions_edit_details() {
|
|
with_test_environment(
|
|
Some(12),
|
|
|test_env: TestEnvironment<ApiV3>| async move {
|
|
let zeta_organization_id =
|
|
&test_env.dummy.organization_zeta.organization_id;
|
|
let zeta_team_id = &test_env.dummy.organization_zeta.team_id;
|
|
|
|
let api = &test_env.api;
|
|
let edit_details = OrganizationPermissions::EDIT_DETAILS;
|
|
|
|
// Icon edit
|
|
// Uses alpha organization to delete this icon
|
|
let req_gen = |ctx: PermissionsTestContext| async move {
|
|
api.edit_organization_icon(
|
|
&ctx.organization_id.unwrap(),
|
|
Some(DummyImage::SmallIcon.get_icon_data()),
|
|
ctx.test_pat.as_deref(),
|
|
)
|
|
.await
|
|
};
|
|
PermissionsTest::new(&test_env)
|
|
.with_existing_organization(zeta_organization_id, zeta_team_id)
|
|
.with_user(FRIEND_USER_ID, FRIEND_USER_PAT, true)
|
|
.simple_organization_permissions_test(edit_details, req_gen)
|
|
.await
|
|
.unwrap();
|
|
|
|
// Icon delete
|
|
// Uses alpha project to delete added icon
|
|
let req_gen = |ctx: PermissionsTestContext| async move {
|
|
api.edit_organization_icon(
|
|
&ctx.organization_id.unwrap(),
|
|
None,
|
|
ctx.test_pat.as_deref(),
|
|
)
|
|
.await
|
|
};
|
|
PermissionsTest::new(&test_env)
|
|
.with_existing_organization(zeta_organization_id, zeta_team_id)
|
|
.with_user(FRIEND_USER_ID, FRIEND_USER_PAT, true)
|
|
.simple_organization_permissions_test(edit_details, req_gen)
|
|
.await
|
|
.unwrap();
|
|
},
|
|
)
|
|
.await;
|
|
}
|
|
|
|
#[actix_rt::test]
|
|
async fn permissions_manage_invites() {
|
|
// Add member, remove member, edit member
|
|
with_test_environment_all(None, |test_env| async move {
|
|
let api = &test_env.api;
|
|
|
|
let zeta_organization_id =
|
|
&test_env.dummy.organization_zeta.organization_id;
|
|
let zeta_team_id = &test_env.dummy.organization_zeta.team_id;
|
|
|
|
let manage_invites = OrganizationPermissions::MANAGE_INVITES;
|
|
|
|
// Add member
|
|
let req_gen = |ctx: PermissionsTestContext| async move {
|
|
api.add_user_to_team(
|
|
&ctx.team_id.unwrap(),
|
|
MOD_USER_ID,
|
|
Some(ProjectPermissions::empty()),
|
|
Some(OrganizationPermissions::empty()),
|
|
ctx.test_pat.as_deref(),
|
|
)
|
|
.await
|
|
};
|
|
PermissionsTest::new(&test_env)
|
|
.with_existing_organization(zeta_organization_id, zeta_team_id)
|
|
.with_user(FRIEND_USER_ID, FRIEND_USER_PAT, true)
|
|
.simple_organization_permissions_test(manage_invites, req_gen)
|
|
.await
|
|
.unwrap();
|
|
|
|
// Edit member
|
|
let edit_member = OrganizationPermissions::EDIT_MEMBER;
|
|
let req_gen = |ctx: PermissionsTestContext| async move {
|
|
api.edit_team_member(
|
|
&ctx.team_id.unwrap(),
|
|
MOD_USER_ID,
|
|
json!({
|
|
"organization_permissions": 0,
|
|
}),
|
|
ctx.test_pat.as_deref(),
|
|
)
|
|
.await
|
|
};
|
|
PermissionsTest::new(&test_env)
|
|
.with_existing_organization(zeta_organization_id, zeta_team_id)
|
|
.with_user(FRIEND_USER_ID, FRIEND_USER_PAT, true)
|
|
.simple_organization_permissions_test(edit_member, req_gen)
|
|
.await
|
|
.unwrap();
|
|
|
|
// remove member
|
|
// requires manage_invites if they have not yet accepted the invite
|
|
let req_gen = |ctx: PermissionsTestContext| async move {
|
|
api.remove_from_team(
|
|
&ctx.team_id.unwrap(),
|
|
MOD_USER_ID,
|
|
ctx.test_pat.as_deref(),
|
|
)
|
|
.await
|
|
};
|
|
PermissionsTest::new(&test_env)
|
|
.with_existing_organization(zeta_organization_id, zeta_team_id)
|
|
.with_user(FRIEND_USER_ID, FRIEND_USER_PAT, true)
|
|
.simple_organization_permissions_test(manage_invites, req_gen)
|
|
.await
|
|
.unwrap();
|
|
|
|
// re-add member for testing
|
|
let resp = api
|
|
.add_user_to_team(
|
|
zeta_team_id,
|
|
MOD_USER_ID,
|
|
None,
|
|
None,
|
|
ADMIN_USER_PAT,
|
|
)
|
|
.await;
|
|
assert_status!(&resp, StatusCode::NO_CONTENT);
|
|
let resp = api.join_team(zeta_team_id, MOD_USER_PAT).await;
|
|
assert_status!(&resp, StatusCode::NO_CONTENT);
|
|
|
|
// remove existing member (requires remove_member)
|
|
let remove_member = OrganizationPermissions::REMOVE_MEMBER;
|
|
let req_gen = |ctx: PermissionsTestContext| async move {
|
|
api.remove_from_team(
|
|
&ctx.team_id.unwrap(),
|
|
MOD_USER_ID,
|
|
ctx.test_pat.as_deref(),
|
|
)
|
|
.await
|
|
};
|
|
|
|
PermissionsTest::new(&test_env)
|
|
.with_existing_organization(zeta_organization_id, zeta_team_id)
|
|
.with_user(FRIEND_USER_ID, FRIEND_USER_PAT, true)
|
|
.simple_organization_permissions_test(remove_member, req_gen)
|
|
.await
|
|
.unwrap();
|
|
})
|
|
.await;
|
|
}
|
|
|
|
#[actix_rt::test]
|
|
async fn permissions_add_remove_project() {
|
|
with_test_environment(
|
|
None,
|
|
|test_env: TestEnvironment<ApiV3>| async move {
|
|
let api = &test_env.api;
|
|
|
|
let alpha_project_id = &test_env.dummy.project_alpha.project_id;
|
|
let alpha_team_id = &test_env.dummy.project_alpha.team_id;
|
|
let zeta_organization_id =
|
|
&test_env.dummy.organization_zeta.organization_id;
|
|
let zeta_team_id = &test_env.dummy.organization_zeta.team_id;
|
|
|
|
let add_project = OrganizationPermissions::ADD_PROJECT;
|
|
|
|
// First, we add FRIEND_USER_ID to the alpha project and transfer ownership to them
|
|
// This is because the ownership of a project is needed to add it to an organization
|
|
let resp = api
|
|
.add_user_to_team(
|
|
alpha_team_id,
|
|
FRIEND_USER_ID,
|
|
None,
|
|
None,
|
|
USER_USER_PAT,
|
|
)
|
|
.await;
|
|
assert_status!(&resp, StatusCode::NO_CONTENT);
|
|
let resp = api.join_team(alpha_team_id, FRIEND_USER_PAT).await;
|
|
assert_status!(&resp, StatusCode::NO_CONTENT);
|
|
let resp = api
|
|
.transfer_team_ownership(
|
|
alpha_team_id,
|
|
FRIEND_USER_ID,
|
|
USER_USER_PAT,
|
|
)
|
|
.await;
|
|
assert_status!(&resp, StatusCode::NO_CONTENT);
|
|
|
|
// Now, FRIEND_USER_ID owns the alpha project
|
|
// Add alpha project to zeta organization
|
|
let req_gen = |ctx: PermissionsTestContext| async move {
|
|
api.organization_add_project(
|
|
&ctx.organization_id.unwrap(),
|
|
alpha_project_id,
|
|
ctx.test_pat.as_deref(),
|
|
)
|
|
.await
|
|
};
|
|
PermissionsTest::new(&test_env)
|
|
.with_existing_organization(zeta_organization_id, zeta_team_id)
|
|
.with_user(FRIEND_USER_ID, FRIEND_USER_PAT, true)
|
|
.simple_organization_permissions_test(add_project, req_gen)
|
|
.await
|
|
.unwrap();
|
|
|
|
// Remove alpha project from zeta organization
|
|
let remove_project = OrganizationPermissions::REMOVE_PROJECT;
|
|
let req_gen = |ctx: PermissionsTestContext| async move {
|
|
api.organization_remove_project(
|
|
&ctx.organization_id.unwrap(),
|
|
alpha_project_id,
|
|
UserId(FRIEND_USER_ID_PARSED as u64),
|
|
ctx.test_pat.as_deref(),
|
|
)
|
|
.await
|
|
};
|
|
PermissionsTest::new(&test_env)
|
|
.with_existing_organization(zeta_organization_id, zeta_team_id)
|
|
.with_user(FRIEND_USER_ID, FRIEND_USER_PAT, true)
|
|
.simple_organization_permissions_test(remove_project, req_gen)
|
|
.await
|
|
.unwrap();
|
|
},
|
|
)
|
|
.await;
|
|
}
|
|
|
|
#[actix_rt::test]
|
|
async fn permissions_delete_organization() {
|
|
with_test_environment(
|
|
None,
|
|
|test_env: TestEnvironment<ApiV3>| async move {
|
|
let api = &test_env.api;
|
|
let delete_organization =
|
|
OrganizationPermissions::DELETE_ORGANIZATION;
|
|
|
|
// Now, FRIEND_USER_ID owns the alpha project
|
|
// Add alpha project to zeta organization
|
|
let req_gen = |ctx: PermissionsTestContext| async move {
|
|
api.delete_organization(
|
|
&ctx.organization_id.unwrap(),
|
|
ctx.test_pat.as_deref(),
|
|
)
|
|
.await
|
|
};
|
|
PermissionsTest::new(&test_env)
|
|
.simple_organization_permissions_test(
|
|
delete_organization,
|
|
req_gen,
|
|
)
|
|
.await
|
|
.unwrap();
|
|
},
|
|
)
|
|
.await;
|
|
}
|
|
|
|
#[actix_rt::test]
|
|
async fn permissions_add_default_project_permissions() {
|
|
with_test_environment_all(None, |test_env| async move {
|
|
let zeta_organization_id =
|
|
&test_env.dummy.organization_zeta.organization_id;
|
|
let zeta_team_id = &test_env.dummy.organization_zeta.team_id;
|
|
|
|
let api = &test_env.api;
|
|
|
|
// Add member
|
|
let add_member_default_permissions =
|
|
OrganizationPermissions::MANAGE_INVITES
|
|
| OrganizationPermissions::EDIT_MEMBER_DEFAULT_PERMISSIONS;
|
|
|
|
// Failure test should include MANAGE_INVITES, as it is required to add
|
|
// default permissions on an invited user, but should still fail without EDIT_MEMBER_DEFAULT_PERMISSIONS
|
|
let failure_with_add_member = (OrganizationPermissions::all()
|
|
^ add_member_default_permissions)
|
|
| OrganizationPermissions::MANAGE_INVITES;
|
|
|
|
let req_gen = |ctx: PermissionsTestContext| async move {
|
|
api.add_user_to_team(
|
|
&ctx.team_id.unwrap(),
|
|
MOD_USER_ID,
|
|
Some(
|
|
ProjectPermissions::UPLOAD_VERSION
|
|
| ProjectPermissions::DELETE_VERSION,
|
|
),
|
|
Some(OrganizationPermissions::empty()),
|
|
ctx.test_pat.as_deref(),
|
|
)
|
|
.await
|
|
};
|
|
PermissionsTest::new(&test_env)
|
|
.with_existing_organization(zeta_organization_id, zeta_team_id)
|
|
.with_user(FRIEND_USER_ID, FRIEND_USER_PAT, true)
|
|
.with_failure_permissions(None, Some(failure_with_add_member))
|
|
.simple_organization_permissions_test(
|
|
add_member_default_permissions,
|
|
req_gen,
|
|
)
|
|
.await
|
|
.unwrap();
|
|
|
|
// Now that member is added, modify default permissions
|
|
let modify_member_default_permission =
|
|
OrganizationPermissions::EDIT_MEMBER
|
|
| OrganizationPermissions::EDIT_MEMBER_DEFAULT_PERMISSIONS;
|
|
|
|
// Failure test should include MANAGE_INVITES, as it is required to add
|
|
// default permissions on an invited user, but should still fail without EDIT_MEMBER_DEFAULT_PERMISSIONS
|
|
let failure_with_modify_member = (OrganizationPermissions::all()
|
|
^ add_member_default_permissions)
|
|
| OrganizationPermissions::EDIT_MEMBER;
|
|
|
|
let req_gen = |ctx: PermissionsTestContext| async move {
|
|
api.edit_team_member(
|
|
&ctx.team_id.unwrap(),
|
|
MOD_USER_ID,
|
|
json!({
|
|
"permissions": ProjectPermissions::EDIT_DETAILS.bits(),
|
|
}),
|
|
ctx.test_pat.as_deref(),
|
|
)
|
|
.await
|
|
};
|
|
PermissionsTest::new(&test_env)
|
|
.with_existing_organization(zeta_organization_id, zeta_team_id)
|
|
.with_user(FRIEND_USER_ID, FRIEND_USER_PAT, true)
|
|
.with_failure_permissions(None, Some(failure_with_modify_member))
|
|
.simple_organization_permissions_test(
|
|
modify_member_default_permission,
|
|
req_gen,
|
|
)
|
|
.await
|
|
.unwrap();
|
|
})
|
|
.await;
|
|
}
|
|
|
|
#[actix_rt::test]
|
|
async fn permissions_organization_permissions_consistency_test() {
|
|
with_test_environment(
|
|
None,
|
|
|test_env: TestEnvironment<ApiV3>| async move {
|
|
let api = &test_env.api;
|
|
// Ensuring that permission are as we expect them to be
|
|
// Full organization permissions test
|
|
let success_permissions = OrganizationPermissions::EDIT_DETAILS;
|
|
let req_gen = |ctx: PermissionsTestContext| async move {
|
|
api.edit_organization(
|
|
&ctx.organization_id.unwrap(),
|
|
json!({
|
|
"description": "Example description - changed.",
|
|
}),
|
|
ctx.test_pat.as_deref(),
|
|
)
|
|
.await
|
|
};
|
|
PermissionsTest::new(&test_env)
|
|
.full_organization_permissions_tests(
|
|
success_permissions,
|
|
req_gen,
|
|
)
|
|
.await
|
|
.unwrap();
|
|
},
|
|
)
|
|
.await;
|
|
}
|