use iam_service::models::CreateRoleRequest; use iam_service::services::{RoleService, TenantService}; use sqlx::PgPool; use uuid::Uuid; #[tokio::test] async fn set_user_roles_is_idempotent_and_validates_tenant_roles( ) -> Result<(), Box> { let database_url = match std::env::var("DATABASE_URL") { Ok(v) if !v.trim().is_empty() => v, _ => return Ok(()), }; let pool = PgPool::connect(&database_url).await?; let tenant_service = TenantService::new(pool.clone()); let role_service = RoleService::new(pool.clone()); let tenant = tenant_service .create_tenant(iam_service::models::CreateTenantRequest { name: format!("smoke-{}", Uuid::new_v4()), config: None, }) .await?; let user_id: Uuid = sqlx::query_scalar( r#" INSERT INTO users (tenant_id, email, password_hash) VALUES ($1, $2, $3) RETURNING id "#, ) .bind(tenant.id) .bind(format!("smoke-{}@example.com", Uuid::new_v4())) .bind("hash") .fetch_one(&pool) .await?; let role1 = role_service .create_role( tenant.id, CreateRoleRequest { name: "R1".into(), description: Some("role1".into()), }, ) .await?; let role2 = role_service .create_role( tenant.id, CreateRoleRequest { name: "R2".into(), description: Some("role2".into()), }, ) .await?; let r1 = role_service .set_roles_for_user(tenant.id, user_id, vec![role1.id, role2.id]) .await?; assert_eq!(r1.len(), 2); let r2 = role_service .set_roles_for_user(tenant.id, user_id, vec![role1.id, role2.id]) .await?; assert_eq!(r2.len(), 2); let roles = role_service.list_roles_for_user(tenant.id, user_id).await?; assert_eq!(roles.len(), 2); let other_tenant = tenant_service .create_tenant(iam_service::models::CreateTenantRequest { name: format!("smoke-{}", Uuid::new_v4()), config: None, }) .await?; let other_role = role_service .create_role( other_tenant.id, CreateRoleRequest { name: "Other".into(), description: None, }, ) .await?; let bad = role_service .set_roles_for_user(tenant.id, user_id, vec![other_role.id]) .await; assert!(bad.is_err()); let _ = sqlx::query("DELETE FROM tenants WHERE id = $1") .bind(tenant.id) .execute(&pool) .await; let _ = sqlx::query("DELETE FROM tenants WHERE id = $1") .bind(other_tenant.id) .execute(&pool) .await; Ok(()) }