use iam_service::application::services::TenantService; use sqlx::PgPool; use uuid::Uuid; #[tokio::test] async fn tenant_enabled_apps_roundtrip_and_version_conflict() -> 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 test_app = format!("testapp{}", Uuid::new_v4().to_string().replace('-', "")); let tenant_id: Uuid = sqlx::query_scalar( r#" INSERT INTO tenants (name, status, config) VALUES ($1, 'active', '{}'::jsonb) RETURNING id "#, ) .bind(format!("smoke-{}", Uuid::new_v4())) .fetch_one(&pool) .await?; let actor_user_id = Uuid::new_v4(); async fn cleanup(pool: &PgPool, tenant_id: Uuid, test_app: &str) { let _ = sqlx::query("DELETE FROM tenants WHERE id = $1") .bind(tenant_id) .execute(pool) .await; let _ = sqlx::query("DELETE FROM apps WHERE id = $1") .bind(test_app) .execute(pool) .await; } let _ = sqlx::query( r#" INSERT INTO apps (id, name, description) VALUES ($1, 'TestApp', 'Test') "#, ) .bind(&test_app) .execute(&pool) .await?; let _ = sqlx::query( r#" INSERT INTO tenant_entitlements (tenant_id, enabled_apps, version) VALUES ($1, '{}'::text[], 0) ON CONFLICT (tenant_id) DO NOTHING "#, ) .bind(tenant_id) .execute(&pool) .await?; let r1 = tenant_service .set_enabled_apps(tenant_id, vec![test_app.clone()], Some(0), actor_user_id) .await; if r1.is_err() { cleanup(&pool, tenant_id, &test_app).await; } let (apps, v1, _) = r1?; assert_eq!(apps, vec![test_app.clone()]); assert_eq!(v1, 1); let r2 = tenant_service .set_enabled_apps(tenant_id, vec![test_app.clone()], Some(0), actor_user_id) .await; assert!(r2.is_err()); let r3 = tenant_service .set_enabled_apps(tenant_id, vec!["dms".to_string()], None, actor_user_id) .await; assert!(r3.is_err()); cleanup(&pool, tenant_id, &test_app).await; Ok(()) }