#![cfg(feature = "cloud")]
use std::sync::Arc;
use redis_cloud::testing::{
AccountFixture, DatabaseFixture, Mock, MockCloudServer, SubscriptionFixture, TaskFixture,
UserFixture, method, path, query_param,
};
use serde_json::json;
use tower_mcp::Tool;
use wiremock::ResponseTemplate;
use wiremock::matchers::body_partial_json;
use redisctl_mcp::policy::{Policy, PolicyConfig, SafetyTier};
use redisctl_mcp::state::AppState;
use redisctl_mcp::tools::cloud;
#[cfg(feature = "cloud")]
fn full_policy_state(client: redis_cloud::CloudClient) -> Arc<AppState> {
let mut state = AppState::with_cloud_client(client);
state.policy = Arc::new(Policy::new(
PolicyConfig {
tier: SafetyTier::Full,
..Default::default()
},
std::collections::HashMap::new(),
"test-full".to_string(),
));
Arc::new(state)
}
async fn call_tool_text(tool: &Tool, input: serde_json::Value) -> String {
let result = tool.call(input).await;
result
.content
.first()
.and_then(|c: &tower_mcp::Content| c.as_text())
.unwrap_or_default()
.to_string()
}
async fn call_tool_json(tool: &Tool, input: serde_json::Value) -> serde_json::Value {
let text = call_tool_text(tool, input).await;
serde_json::from_str(&text).expect("valid JSON response")
}
#[tokio::test]
async fn test_list_subscriptions() {
let server = MockCloudServer::start().await;
let sub1 = SubscriptionFixture::new(123, "Production")
.status("active")
.cloud_provider("AWS")
.region("us-east-1")
.build();
let sub2 = SubscriptionFixture::new(456, "Development")
.status("active")
.cloud_provider("GCP")
.region("us-central1")
.build();
server.mock_subscriptions_list(vec![sub1, sub2]).await;
let client = server.client();
let state = Arc::new(AppState::with_cloud_client(client));
let tool = cloud::list_subscriptions(state);
let result = call_tool_json(&tool, json!({})).await;
assert!(result.get("subscriptions").is_some());
let subscriptions = result["subscriptions"].as_array().unwrap();
assert_eq!(subscriptions.len(), 2);
assert_eq!(subscriptions[0]["name"], "Production");
assert_eq!(subscriptions[1]["name"], "Development");
}
#[tokio::test]
async fn test_list_subscriptions_empty() {
let server = MockCloudServer::start().await;
server.mock_subscriptions_list(vec![]).await;
let client = server.client();
let state = Arc::new(AppState::with_cloud_client(client));
let tool = cloud::list_subscriptions(state);
let result = call_tool_json(&tool, json!({})).await;
let subscriptions = result["subscriptions"].as_array().unwrap();
assert_eq!(subscriptions.len(), 0);
}
#[tokio::test]
async fn test_get_subscription() {
let server = MockCloudServer::start().await;
let subscription = SubscriptionFixture::new(123, "Production")
.status("active")
.payment_method_type("credit-card")
.memory_storage("ram")
.cloud_provider("AWS")
.region("us-east-1")
.build();
server.mock_subscription_get(123, subscription).await;
let client = server.client();
let state = Arc::new(AppState::with_cloud_client(client));
let tool = cloud::get_subscription(state);
let result = call_tool_json(&tool, json!({"subscription_id": 123})).await;
assert_eq!(result["id"], 123);
assert_eq!(result["name"], "Production");
assert_eq!(result["status"], "active");
}
#[tokio::test]
async fn test_list_databases() {
let server = MockCloudServer::start().await;
let db1 = DatabaseFixture::new(1001, "cache-primary")
.memory_limit_in_gb(2.0)
.protocol("redis")
.replication(true)
.public_endpoint("redis-1001.c1.us-east-1.ec2.cloud.redislabs.com:12001")
.build();
let db2 = DatabaseFixture::new(1002, "cache-replica")
.memory_limit_in_gb(1.0)
.protocol("redis")
.replication(false)
.build();
server.mock_databases_list(123, vec![db1, db2]).await;
let client = server.client();
let state = Arc::new(AppState::with_cloud_client(client));
let tool = cloud::list_databases(state);
let result = call_tool_json(&tool, json!({"subscription_id": 123})).await;
let subscriptions = result["subscription"].as_array().unwrap();
assert_eq!(subscriptions.len(), 1);
let databases = subscriptions[0]["databases"].as_array().unwrap();
assert_eq!(databases.len(), 2);
assert_eq!(databases[0]["name"], "cache-primary");
assert_eq!(databases[1]["name"], "cache-replica");
}
#[tokio::test]
async fn test_get_database() {
let server = MockCloudServer::start().await;
let database = DatabaseFixture::new(1001, "cache-primary")
.memory_limit_in_gb(2.0)
.protocol("redis")
.replication(true)
.data_persistence("aof-every-1-second")
.throughput("operations-per-second", 25000)
.public_endpoint("redis-1001.c1.us-east-1.ec2.cloud.redislabs.com:12001")
.build();
server.mock_database_get(123, 1001, database).await;
let client = server.client();
let state = Arc::new(AppState::with_cloud_client(client));
let tool = cloud::get_database(state);
let result = call_tool_json(
&tool,
json!({
"subscription_id": 123,
"database_id": 1001
}),
)
.await;
assert_eq!(result["databaseId"], 1001);
assert_eq!(result["name"], "cache-primary");
assert_eq!(result["memoryLimitInGb"], 2.0);
assert_eq!(result["protocol"], "redis");
assert_eq!(result["replication"], true);
}
#[tokio::test]
async fn test_get_account() {
let server = MockCloudServer::start().await;
let account = AccountFixture::new(12345, "My Organization")
.marketplace_status("active")
.created_timestamp("2024-01-15T10:30:00Z")
.build();
server.mock_account(account).await;
let client = server.client();
let state = Arc::new(AppState::with_cloud_client(client));
let tool = cloud::get_account(state);
let result = call_tool_json(&tool, json!({})).await;
assert!(result.get("account").is_some());
assert_eq!(result["account"]["id"], 12345);
assert_eq!(result["account"]["name"], "My Organization");
}
#[tokio::test]
async fn test_list_tasks() {
let server = MockCloudServer::start().await;
let task1 = TaskFixture::completed("task-001", 123)
.command_type("subscriptionCreateRequest")
.description("Create subscription")
.build();
let task2 = TaskFixture::new("task-002")
.command_type("databaseCreateRequest")
.status("processing-in-progress")
.description("Create database")
.build();
server.mock_tasks_list(vec![task1, task2]).await;
let client = server.client();
let state = Arc::new(AppState::with_cloud_client(client));
let tool = cloud::list_tasks(state);
let result = call_tool_json(&tool, json!({})).await;
assert_eq!(result["count"], 2);
let tasks = result["tasks"].as_array().unwrap();
assert_eq!(tasks.len(), 2);
assert_eq!(tasks[0]["taskId"], "task-001");
assert_eq!(tasks[0]["status"], "processing-completed");
assert_eq!(tasks[1]["taskId"], "task-002");
assert_eq!(tasks[1]["status"], "processing-in-progress");
}
#[tokio::test]
async fn test_get_task() {
let server = MockCloudServer::start().await;
let task = TaskFixture::completed("task-001", 123)
.command_type("subscriptionCreateRequest")
.description("Create subscription completed successfully")
.build();
server.mock_task_get("task-001", task).await;
let client = server.client();
let state = Arc::new(AppState::with_cloud_client(client));
let tool = cloud::get_task(state);
let result = call_tool_json(&tool, json!({"task_id": "task-001"})).await;
assert_eq!(result["taskId"], "task-001");
assert_eq!(result["status"], "processing-completed");
assert_eq!(result["response"]["resourceId"], 123);
}
#[tokio::test]
async fn test_get_task_failed() {
let server = MockCloudServer::start().await;
let task = TaskFixture::failed("task-002", "Insufficient credits")
.command_type("subscriptionCreateRequest")
.build();
server.mock_task_get("task-002", task).await;
let client = server.client();
let state = Arc::new(AppState::with_cloud_client(client));
let tool = cloud::get_task(state);
let result = call_tool_json(&tool, json!({"task_id": "task-002"})).await;
assert_eq!(result["taskId"], "task-002");
assert_eq!(result["status"], "processing-error");
assert_eq!(result["response"]["error"], "Insufficient credits");
}
#[tokio::test]
async fn test_list_account_users() {
let server = MockCloudServer::start().await;
let user1 = UserFixture::new(1, "admin@example.com")
.name("Admin User")
.role("owner")
.build();
let user2 = UserFixture::new(2, "dev@example.com")
.name("Developer")
.role("member")
.build();
server.mock_users_list(vec![user1, user2]).await;
let client = server.client();
let state = Arc::new(AppState::with_cloud_client(client));
let tool = cloud::list_account_users(state);
let result = call_tool_json(&tool, json!({})).await;
let users = result["users"].as_array().unwrap();
assert_eq!(users.len(), 2);
assert_eq!(users[0]["email"], "admin@example.com");
assert_eq!(users[0]["role"], "owner");
assert_eq!(users[1]["email"], "dev@example.com");
assert_eq!(users[1]["role"], "member");
}
#[tokio::test]
async fn test_get_regions() {
let server = MockCloudServer::start().await;
server
.mock_regions(vec![
json!({"name": "us-east-1", "provider": "AWS"}),
json!({"name": "us-west-2", "provider": "AWS"}),
json!({"name": "us-central1", "provider": "GCP"}),
])
.await;
let client = server.client();
let state = Arc::new(AppState::with_cloud_client(client));
let tool = cloud::get_regions(state);
let result = call_tool_json(&tool, json!({})).await;
let regions = result["regions"].as_array().unwrap();
assert_eq!(regions.len(), 3);
}
#[tokio::test]
async fn test_get_modules() {
let server = MockCloudServer::start().await;
server
.mock_database_modules(vec![
json!({"name": "RedisJSON", "description": "JSON support"}),
json!({"name": "RediSearch", "description": "Full-text search"}),
json!({"name": "RedisTimeSeries", "description": "Time series data"}),
])
.await;
let client = server.client();
let state = Arc::new(AppState::with_cloud_client(client));
let tool = cloud::get_modules(state);
let result = call_tool_json(&tool, json!({})).await;
let modules = result["modules"].as_array().unwrap();
assert_eq!(modules.len(), 3);
assert_eq!(modules[0]["name"], "RedisJSON");
}
#[tokio::test]
async fn test_get_system_logs() {
let server = MockCloudServer::start().await;
server
.mock_path(
"GET",
"/logs",
ResponseTemplate::new(200).set_body_json(json!({
"entries": [
{
"id": 1,
"time": "2024-01-15T10:30:00Z",
"originator": "admin@example.com",
"apiKeyName": "default-api-key",
"resource": "subscription",
"resourceId": 123,
"action": "create-subscription"
},
{
"id": 2,
"time": "2024-01-15T10:25:00Z",
"originator": "admin@example.com",
"apiKeyName": "default-api-key",
"resource": "database",
"resourceId": 456,
"action": "update-database"
}
]
})),
)
.await;
let client = server.client();
let state = Arc::new(AppState::with_cloud_client(client));
let tool = cloud::get_system_logs(state);
let result = call_tool_json(&tool, json!({})).await;
assert!(result.get("entries").is_some());
let entries = result["entries"].as_array().unwrap();
assert_eq!(entries.len(), 2);
}
#[tokio::test]
async fn test_get_session_logs() {
let server = MockCloudServer::start().await;
server
.mock_path(
"GET",
"/session-logs",
ResponseTemplate::new(200).set_body_json(json!({
"entries": [
{
"id": "550e8400-e29b-41d4-a716-446655440001",
"time": "2024-01-15T10:30:00Z",
"user": "admin@example.com",
"action": "login"
},
{
"id": "550e8400-e29b-41d4-a716-446655440002",
"time": "2024-01-15T09:00:00Z",
"user": "dev@example.com",
"action": "logout"
}
]
})),
)
.await;
let client = server.client();
let state = Arc::new(AppState::with_cloud_client(client));
let tool = cloud::get_session_logs(state);
let result = call_tool_json(&tool, json!({})).await;
assert!(result.get("entries").is_some());
let entries = result["entries"].as_array().unwrap();
assert_eq!(entries.len(), 2);
}
#[tokio::test]
async fn test_create_aa_vpc_peering_destination_region() {
let server = MockCloudServer::start().await;
Mock::given(method("POST"))
.and(path("/subscriptions/123/regions/peerings"))
.and(body_partial_json(json!({
"sourceRegion": "us-east-1",
"destinationRegion": "us-west-2"
})))
.respond_with(ResponseTemplate::new(202).set_body_json(json!({
"taskId": "task-aa-001",
"commandType": "CREATE_AA_VPC_PEERING",
"status": "processing-in-progress"
})))
.mount(server.inner())
.await;
let client = server.client();
let state = Arc::new(AppState::with_cloud_client_write(client));
let tool = cloud::create_aa_vpc_peering(state);
let result = call_tool_json(
&tool,
json!({
"subscription_id": 123,
"provider": "AWS",
"aws_region": "us-east-1",
"destination_region": "us-west-2",
"aws_account_id": "123456789012",
"vpc_id": "vpc-abcdef01"
}),
)
.await;
assert_eq!(result["taskId"], "task-aa-001");
}
#[tokio::test]
async fn test_update_subscription_cidr_allowlist_request_shape() {
let server = MockCloudServer::start().await;
Mock::given(method("PUT"))
.and(path("/subscriptions/123/cidr"))
.and(body_partial_json(json!({
"cidrIps": ["10.0.0.0/8"]
})))
.respond_with(ResponseTemplate::new(202).set_body_json(json!({
"taskId": "task-cidr-update",
"commandType": "updateSubscriptionCidrAllowlist",
"status": "processing-in-progress",
"description": "Task in progress"
})))
.mount(server.inner())
.await;
let client = server.client();
let state = full_policy_state(client);
let tool = cloud::update_subscription_cidr_allowlist(state);
let result = call_tool_text(
&tool,
json!({
"subscription_id": 123,
"cidr_ips": ["10.0.0.0/8"]
}),
)
.await;
assert!(
result.contains("task-cidr-update") || result.contains("taskId"),
"Expected task response, got: {result}"
);
}
#[tokio::test]
async fn test_delete_active_active_regions_bodyful_delete() {
let server = MockCloudServer::start().await;
Mock::given(method("DELETE"))
.and(path("/subscriptions/123/regions"))
.and(body_partial_json(json!({
"regions": [{"region": "us-east-1"}]
})))
.respond_with(ResponseTemplate::new(202).set_body_json(json!({
"taskId": "task-delete-regions",
"commandType": "deleteActiveActiveRegions",
"status": "processing-in-progress",
"description": "Task in progress"
})))
.mount(server.inner())
.await;
let client = server.client();
let state = full_policy_state(client);
let tool = cloud::delete_active_active_regions(state);
let result = call_tool_text(
&tool,
json!({
"subscription_id": 123,
"regions": [{"region": "us-east-1"}]
}),
)
.await;
assert!(
result.contains("task-delete-regions") || result.contains("taskId"),
"Expected task response — bodyful DELETE body was not sent correctly: {result}"
);
}
#[tokio::test]
async fn test_get_redis_versions_with_subscription_id_query_param() {
let server = MockCloudServer::start().await;
Mock::given(method("GET"))
.and(path("/subscriptions/redis-versions"))
.and(query_param("subscriptionId", "123"))
.respond_with(ResponseTemplate::new(200).set_body_json(json!({
"redisVersions": [
{"version": "7.2", "default": true},
{"version": "7.0", "default": false}
]
})))
.mount(server.inner())
.await;
let client = server.client();
let state = Arc::new(AppState::with_cloud_client(client));
let tool = cloud::get_redis_versions(state);
let result = call_tool_text(&tool, json!({"subscription_id": 123})).await;
assert!(
result.contains("7.2") || result.contains("redisVersions"),
"Expected versions response with query param match, got: {result}"
);
}
#[tokio::test]
async fn test_get_vpc_peering_request_shape() {
let server = MockCloudServer::start().await;
Mock::given(method("GET"))
.and(path("/subscriptions/123/peerings"))
.respond_with(ResponseTemplate::new(200).set_body_json(json!({
"subscription": 123,
"peerings": []
})))
.mount(server.inner())
.await;
let client = server.client();
let state = Arc::new(AppState::with_cloud_client(client));
let tool = cloud::get_vpc_peering(state);
let result = call_tool_text(&tool, json!({"subscription_id": 123})).await;
assert!(
!result.contains("Failed"),
"GET peerings should have matched, got: {result}"
);
}
#[tokio::test]
async fn test_create_vpc_peering_request_shape() {
let server = MockCloudServer::start().await;
Mock::given(method("POST"))
.and(path("/subscriptions/123/peerings"))
.and(body_partial_json(json!({
"provider": "AWS",
"region": "us-east-1",
"awsAccountId": "123456789012",
"vpcId": "vpc-abc123"
})))
.respond_with(ResponseTemplate::new(202).set_body_json(json!({
"taskId": "task-create-peering",
"commandType": "createSubscriptionPeering",
"status": "processing-in-progress"
})))
.mount(server.inner())
.await;
let client = server.client();
let state = full_policy_state(client);
let tool = cloud::create_vpc_peering(state);
let result = call_tool_text(
&tool,
json!({
"subscription_id": 123,
"provider": "AWS",
"vpc_id": "vpc-abc123",
"aws_region": "us-east-1",
"aws_account_id": "123456789012",
"vpc_cidr": "10.0.0.0/16"
}),
)
.await;
assert!(
result.contains("task-create-peering") || result.contains("taskId"),
"Expected task response, got: {result}"
);
}
#[tokio::test]
async fn test_delete_vpc_peering_request_shape() {
let server = MockCloudServer::start().await;
Mock::given(method("DELETE"))
.and(path("/subscriptions/123/peerings/456"))
.respond_with(ResponseTemplate::new(202).set_body_json(json!({
"taskId": "task-delete-peering",
"commandType": "deleteSubscriptionPeering",
"status": "processing-in-progress"
})))
.mount(server.inner())
.await;
let client = server.client();
let state = full_policy_state(client);
let tool = cloud::delete_vpc_peering(state);
let result = call_tool_text(
&tool,
json!({
"subscription_id": 123,
"peering_id": 456
}),
)
.await;
assert!(
result.contains("task-delete-peering") || result.contains("taskId"),
"Expected task response for DELETE peering, got: {result}"
);
}
#[tokio::test]
async fn test_create_fixed_subscription_request_shape() {
let server = MockCloudServer::start().await;
Mock::given(method("POST"))
.and(path("/fixed/subscriptions"))
.and(body_partial_json(json!({
"name": "my-essentials",
"planId": 42
})))
.respond_with(ResponseTemplate::new(202).set_body_json(json!({
"taskId": "task-create-fixed-sub",
"commandType": "createFixedSubscription",
"status": "processing-in-progress"
})))
.mount(server.inner())
.await;
let client = server.client();
let state = full_policy_state(client);
let tool = cloud::create_fixed_subscription(state);
let result = call_tool_text(
&tool,
json!({
"name": "my-essentials",
"plan_id": 42
}),
)
.await;
assert!(
result.contains("task-create-fixed-sub") || result.contains("taskId"),
"Expected task response, got: {result}"
);
}
#[tokio::test]
async fn test_update_fixed_subscription_request_shape() {
let server = MockCloudServer::start().await;
Mock::given(method("PUT"))
.and(path("/fixed/subscriptions/789"))
.and(body_partial_json(json!({
"name": "updated-name"
})))
.respond_with(ResponseTemplate::new(202).set_body_json(json!({
"taskId": "task-update-fixed-sub",
"commandType": "updateFixedSubscription",
"status": "processing-in-progress"
})))
.mount(server.inner())
.await;
let client = server.client();
let state = full_policy_state(client);
let tool = cloud::update_fixed_subscription(state);
let result = call_tool_text(
&tool,
json!({
"subscription_id": 789,
"name": "updated-name"
}),
)
.await;
assert!(
result.contains("task-update-fixed-sub") || result.contains("taskId"),
"Expected task response, got: {result}"
);
}
#[tokio::test]
async fn test_delete_fixed_subscription_request_shape() {
let server = MockCloudServer::start().await;
Mock::given(method("DELETE"))
.and(path("/fixed/subscriptions/789"))
.respond_with(ResponseTemplate::new(202).set_body_json(json!({
"taskId": "task-delete-fixed-sub",
"commandType": "deleteFixedSubscription",
"status": "processing-in-progress"
})))
.mount(server.inner())
.await;
let client = server.client();
let state = full_policy_state(client);
let tool = cloud::delete_fixed_subscription(state);
let result = call_tool_text(&tool, json!({"subscription_id": 789})).await;
assert!(
result.contains("task-delete-fixed-sub") || result.contains("taskId"),
"Expected task response for DELETE fixed subscription, got: {result}"
);
}
#[tokio::test]
async fn test_create_acl_user_request_shape() {
let server = MockCloudServer::start().await;
Mock::given(method("POST"))
.and(path("/acl/users"))
.and(body_partial_json(json!({
"name": "test-user",
"role": "some-role"
})))
.respond_with(ResponseTemplate::new(202).set_body_json(json!({
"taskId": "task-create-acl-user",
"commandType": "createAclUser",
"status": "processing-in-progress"
})))
.mount(server.inner())
.await;
let client = server.client();
let state = full_policy_state(client);
let tool = cloud::create_acl_user(state);
let result = call_tool_text(
&tool,
json!({
"name": "test-user",
"role": "some-role",
"password": "s3cr3t"
}),
)
.await;
assert!(
result.contains("task-create-acl-user") || result.contains("taskId"),
"Expected task response, got: {result}"
);
}
#[tokio::test]
async fn test_create_redis_rule_request_shape() {
let server = MockCloudServer::start().await;
Mock::given(method("POST"))
.and(path("/acl/redisRules"))
.and(body_partial_json(json!({
"name": "my-rule",
"redisRule": "+@read"
})))
.respond_with(ResponseTemplate::new(202).set_body_json(json!({
"taskId": "task-create-redis-rule",
"commandType": "createRedisRule",
"status": "processing-in-progress"
})))
.mount(server.inner())
.await;
let client = server.client();
let state = full_policy_state(client);
let tool = cloud::create_redis_rule(state);
let result = call_tool_text(
&tool,
json!({
"name": "my-rule",
"redis_rule": "+@read"
}),
)
.await;
assert!(
result.contains("task-create-redis-rule") || result.contains("taskId"),
"Expected task response, got: {result}"
);
}
#[tokio::test]
async fn test_delete_acl_role_request_shape() {
let server = MockCloudServer::start().await;
Mock::given(method("DELETE"))
.and(path("/acl/roles/99"))
.respond_with(ResponseTemplate::new(202).set_body_json(json!({
"taskId": "task-delete-acl-role",
"commandType": "deleteAclRole",
"status": "processing-in-progress"
})))
.mount(server.inner())
.await;
let client = server.client();
let state = full_policy_state(client);
let tool = cloud::delete_acl_role(state);
let result = call_tool_text(&tool, json!({"role_id": 99})).await;
assert!(
result.contains("task-delete-acl-role") || result.contains("taskId"),
"Expected task response for DELETE ACL role, got: {result}"
);
}
#[tokio::test]
async fn test_update_subscription_request_shape() {
let server = MockCloudServer::start().await;
Mock::given(method("PUT"))
.and(path("/subscriptions/123"))
.and(body_partial_json(json!({"name": "Updated Subscription"})))
.respond_with(ResponseTemplate::new(202).set_body_json(json!({
"taskId": "task-update-sub",
"status": "processing-in-progress"
})))
.mount(server.inner())
.await;
let client = server.client();
let state = full_policy_state(client);
let tool = cloud::update_subscription(state);
let result = call_tool_text(
&tool,
json!({
"subscription_id": 123,
"name": "Updated Subscription"
}),
)
.await;
assert!(
result.contains("task-update-sub") || result.contains("taskId"),
"Expected task response, got: {result}"
);
}
#[tokio::test]
async fn test_get_subscription_cidr_allowlist_request_shape() {
let server = MockCloudServer::start().await;
Mock::given(method("GET"))
.and(path("/subscriptions/123/cidr"))
.respond_with(ResponseTemplate::new(200).set_body_json(json!({
"cidrIps": ["10.0.0.0/8"],
"securityGroupIds": []
})))
.mount(server.inner())
.await;
let client = server.client();
let state = Arc::new(AppState::with_cloud_client(client));
let tool = cloud::get_subscription_cidr_allowlist(state);
let result = call_tool_text(&tool, json!({"subscription_id": 123})).await;
assert!(
!result.contains("Failed"),
"Expected successful GET cidr response, got: {result}"
);
}
#[tokio::test]
async fn test_get_subscription_maintenance_windows_request_shape() {
let server = MockCloudServer::start().await;
Mock::given(method("GET"))
.and(path("/subscriptions/123/maintenance-windows"))
.respond_with(ResponseTemplate::new(200).set_body_json(json!({
"mode": "automatic",
"timeZone": "UTC",
"windows": []
})))
.mount(server.inner())
.await;
let client = server.client();
let state = Arc::new(AppState::with_cloud_client(client));
let tool = cloud::get_subscription_maintenance_windows(state);
let result = call_tool_json(&tool, json!({"subscription_id": 123})).await;
assert_eq!(result["mode"], "automatic");
}
#[tokio::test]
async fn test_update_subscription_maintenance_windows_request_shape() {
let server = MockCloudServer::start().await;
Mock::given(method("PUT"))
.and(path("/subscriptions/123/maintenance-windows"))
.and(body_partial_json(json!({
"mode": "automatic"
})))
.respond_with(ResponseTemplate::new(202).set_body_json(json!({
"taskId": "task-update-mw",
"status": "processing-in-progress"
})))
.mount(server.inner())
.await;
let client = server.client();
let state = full_policy_state(client);
let tool = cloud::update_subscription_maintenance_windows(state);
let result = call_tool_text(
&tool,
json!({
"subscription_id": 123,
"mode": "automatic"
}),
)
.await;
assert!(
result.contains("task-update-mw") || result.contains("taskId"),
"Expected task response, got: {result}"
);
}
#[tokio::test]
async fn test_get_active_active_regions_request_shape() {
let server = MockCloudServer::start().await;
Mock::given(method("GET"))
.and(path("/subscriptions/123/regions"))
.respond_with(ResponseTemplate::new(200).set_body_json(json!({
"subscriptionId": 123
})))
.mount(server.inner())
.await;
let client = server.client();
let state = Arc::new(AppState::with_cloud_client(client));
let tool = cloud::get_active_active_regions(state);
let result = call_tool_text(&tool, json!({"subscription_id": 123})).await;
assert!(
!result.contains("Failed"),
"Expected successful GET regions response, got: {result}"
);
}
#[tokio::test]
async fn test_add_active_active_region_request_shape() {
let server = MockCloudServer::start().await;
Mock::given(method("POST"))
.and(path("/subscriptions/123/regions"))
.and(body_partial_json(json!({
"deploymentCidr": "10.1.0.0/24"
})))
.respond_with(ResponseTemplate::new(202).set_body_json(json!({
"taskId": "task-add-region",
"status": "processing-in-progress"
})))
.mount(server.inner())
.await;
let client = server.client();
let state = full_policy_state(client);
let tool = cloud::add_active_active_region(state);
let result = call_tool_text(
&tool,
json!({
"subscription_id": 123,
"deployment_cidr": "10.1.0.0/24"
}),
)
.await;
assert!(
result.contains("task-add-region") || result.contains("taskId"),
"Expected task response for add region, got: {result}"
);
}
#[tokio::test]
async fn test_get_subscription_pricing_request_shape() {
let server = MockCloudServer::start().await;
Mock::given(method("GET"))
.and(path("/subscriptions/123/pricing"))
.respond_with(ResponseTemplate::new(200).set_body_json(json!({
"pricing": []
})))
.mount(server.inner())
.await;
let client = server.client();
let state = Arc::new(AppState::with_cloud_client(client));
let tool = cloud::get_subscription_pricing(state);
let result = call_tool_text(&tool, json!({"subscription_id": 123})).await;
assert!(
!result.contains("Failed"),
"Expected successful GET pricing response, got: {result}"
);
}
#[tokio::test]
async fn test_create_database_tag_request_shape() {
let server = MockCloudServer::start().await;
Mock::given(method("POST"))
.and(path("/subscriptions/123/databases/1001/tags"))
.and(body_partial_json(json!({
"key": "env",
"value": "prod"
})))
.respond_with(ResponseTemplate::new(201).set_body_json(json!({
"key": "env",
"value": "prod"
})))
.mount(server.inner())
.await;
let client = server.client();
let state = full_policy_state(client);
let tool = cloud::create_database_tag(state);
let result = call_tool_json(
&tool,
json!({
"subscription_id": 123,
"database_id": 1001,
"key": "env",
"value": "prod"
}),
)
.await;
assert_eq!(
result["key"], "env",
"Expected tag key in response, got: {result}"
);
}
#[tokio::test]
async fn test_update_database_tag_request_shape() {
let server = MockCloudServer::start().await;
Mock::given(method("PUT"))
.and(path("/subscriptions/123/databases/1001/tags/env"))
.and(body_partial_json(json!({
"value": "staging"
})))
.respond_with(ResponseTemplate::new(200).set_body_json(json!({
"key": "env",
"value": "staging"
})))
.mount(server.inner())
.await;
let client = server.client();
let state = full_policy_state(client);
let tool = cloud::update_database_tag(state);
let result = call_tool_json(
&tool,
json!({
"subscription_id": 123,
"database_id": 1001,
"tag_key": "env",
"value": "staging"
}),
)
.await;
assert_eq!(
result["value"], "staging",
"Expected updated tag value in response, got: {result}"
);
}
#[tokio::test]
async fn test_delete_database_tag_request_shape() {
let server = MockCloudServer::start().await;
Mock::given(method("DELETE"))
.and(path("/subscriptions/123/databases/1001/tags/env"))
.respond_with(ResponseTemplate::new(202).set_body_json(json!({
"taskId": "task-delete-tag",
"status": "processing-in-progress"
})))
.mount(server.inner())
.await;
let client = server.client();
let state = full_policy_state(client);
let tool = cloud::delete_database_tag(state);
let result = call_tool_text(
&tool,
json!({
"subscription_id": 123,
"database_id": 1001,
"tag_key": "env"
}),
)
.await;
assert!(
result.contains("task-delete-tag") || result.contains("taskId"),
"Expected task response for DELETE tag, got: {result}"
);
}
#[tokio::test]
async fn test_update_database_tags_request_shape() {
let server = MockCloudServer::start().await;
Mock::given(method("PUT"))
.and(path("/subscriptions/123/databases/1001/tags"))
.and(body_partial_json(json!({
"tags": [{"key": "env", "value": "prod"}]
})))
.respond_with(ResponseTemplate::new(200).set_body_json(json!({
"accountId": 12345
})))
.mount(server.inner())
.await;
let client = server.client();
let state = full_policy_state(client);
let tool = cloud::update_database_tags(state);
let result = call_tool_text(
&tool,
json!({
"subscription_id": 123,
"database_id": 1001,
"tags": [{"key": "env", "value": "prod"}]
}),
)
.await;
assert!(
!result.contains("Failed"),
"Expected successful update tags response — body_partial_json matcher was not satisfied: {result}"
);
}
#[tokio::test]
async fn test_upgrade_database_redis_version_request_shape() {
let server = MockCloudServer::start().await;
Mock::given(method("POST"))
.and(path("/subscriptions/123/databases/1001/upgrade"))
.and(body_partial_json(json!({
"targetRedisVersion": "7.4"
})))
.respond_with(ResponseTemplate::new(202).set_body_json(json!({
"taskId": "task-upgrade-version",
"status": "processing-in-progress"
})))
.mount(server.inner())
.await;
let client = server.client();
let state = full_policy_state(client);
let tool = cloud::upgrade_database_redis_version(state);
let result = call_tool_text(
&tool,
json!({
"subscription_id": 123,
"database_id": 1001,
"target_redis_version": "7.4"
}),
)
.await;
assert!(
result.contains("task-upgrade-version") || result.contains("taskId"),
"Expected task response for upgrade, got: {result}"
);
}
#[tokio::test]
async fn test_get_database_upgrade_status_request_shape() {
let server = MockCloudServer::start().await;
Mock::given(method("GET"))
.and(path("/subscriptions/123/databases/1001/upgrade"))
.respond_with(ResponseTemplate::new(200).set_body_json(json!({
"upgradeStatus": "completed",
"targetRedisVersion": "7.4"
})))
.mount(server.inner())
.await;
let client = server.client();
let state = Arc::new(AppState::with_cloud_client(client));
let tool = cloud::get_database_upgrade_status(state);
let result = call_tool_json(
&tool,
json!({
"subscription_id": 123,
"database_id": 1001
}),
)
.await;
assert_eq!(result["upgradeStatus"], "completed");
}
#[tokio::test]
async fn test_get_database_import_status_request_shape() {
let server = MockCloudServer::start().await;
Mock::given(method("GET"))
.and(path("/subscriptions/123/databases/1001/import"))
.respond_with(ResponseTemplate::new(200).set_body_json(json!({
"status": "completed",
"importedRdb": true
})))
.mount(server.inner())
.await;
let client = server.client();
let state = Arc::new(AppState::with_cloud_client(client));
let tool = cloud::get_database_import_status(state);
let result = call_tool_text(
&tool,
json!({
"subscription_id": 123,
"database_id": 1001
}),
)
.await;
assert!(
!result.contains("Failed"),
"Expected successful import status response, got: {result}"
);
}
#[tokio::test]
async fn test_get_available_database_versions_request_shape() {
let server = MockCloudServer::start().await;
Mock::given(method("GET"))
.and(path(
"/subscriptions/123/databases/1001/available-target-versions",
))
.respond_with(ResponseTemplate::new(200).set_body_json(json!({
"versions": ["7.2", "7.4"]
})))
.mount(server.inner())
.await;
let client = server.client();
let state = Arc::new(AppState::with_cloud_client(client));
let tool = cloud::get_available_database_versions(state);
let result = call_tool_text(
&tool,
json!({
"subscription_id": 123,
"database_id": 1001
}),
)
.await;
assert!(
!result.contains("Failed"),
"Expected successful available versions response, got: {result}"
);
}
#[tokio::test]
async fn test_get_database_certificate_request_shape() {
let server = MockCloudServer::start().await;
Mock::given(method("GET"))
.and(path("/subscriptions/123/databases/1001/certificate"))
.respond_with(ResponseTemplate::new(200).set_body_json(json!({
"publicCertificatePemString": "-----BEGIN CERTIFICATE-----\nMIIBx...\n-----END CERTIFICATE-----"
})))
.mount(server.inner())
.await;
let client = server.client();
let state = Arc::new(AppState::with_cloud_client(client));
let tool = cloud::get_database_certificate(state);
let result = call_tool_json(
&tool,
json!({
"subscription_id": 123,
"database_id": 1001
}),
)
.await;
assert!(
result.get("publicCertificatePemString").is_some(),
"Expected certificate PEM in response, got: {result}"
);
}
fn net_task_body() -> serde_json::Value {
json!({"taskId": "task-net-test", "status": "processing-in-progress"})
}
#[tokio::test]
async fn test_update_vpc_peering_request_shape() {
let server = MockCloudServer::start().await;
Mock::given(method("PUT"))
.and(path("/subscriptions/123/peerings/456"))
.and(body_partial_json(json!({"vpcCidr": "10.0.0.0/16"})))
.respond_with(ResponseTemplate::new(202).set_body_json(net_task_body()))
.mount(server.inner())
.await;
let client = server.client();
let state = full_policy_state(client);
let tool = cloud::update_vpc_peering(state);
let result = call_tool_text(
&tool,
json!({
"subscription_id": 123,
"peering_id": 456,
"vpc_cidr": "10.0.0.0/16"
}),
)
.await;
assert!(
!result.contains("Failed"),
"PUT peerings/456 with vpcCidr should have matched, got: {result}"
);
}
#[tokio::test]
async fn test_get_aa_vpc_peering_request_shape() {
let server = MockCloudServer::start().await;
Mock::given(method("GET"))
.and(path("/subscriptions/123/regions/peerings"))
.respond_with(ResponseTemplate::new(200).set_body_json(net_task_body()))
.mount(server.inner())
.await;
let client = server.client();
let state = Arc::new(AppState::with_cloud_client(client));
let tool = cloud::get_aa_vpc_peering(state);
let result = call_tool_text(&tool, json!({"subscription_id": 123})).await;
assert!(
!result.contains("Failed"),
"GET regions/peerings should have matched, got: {result}"
);
}
#[tokio::test]
async fn test_update_aa_vpc_peering_request_shape() {
let server = MockCloudServer::start().await;
Mock::given(method("PUT"))
.and(path("/subscriptions/123/regions/peerings/456"))
.and(body_partial_json(json!({"vpcCidr": "10.1.0.0/16"})))
.respond_with(ResponseTemplate::new(202).set_body_json(net_task_body()))
.mount(server.inner())
.await;
let client = server.client();
let state = full_policy_state(client);
let tool = cloud::update_aa_vpc_peering(state);
let result = call_tool_text(
&tool,
json!({
"subscription_id": 123,
"peering_id": 456,
"vpc_cidr": "10.1.0.0/16"
}),
)
.await;
assert!(
!result.contains("Failed"),
"PUT regions/peerings/456 with vpcCidr should have matched, got: {result}"
);
}
#[tokio::test]
async fn test_delete_aa_vpc_peering_request_shape() {
let server = MockCloudServer::start().await;
Mock::given(method("DELETE"))
.and(path("/subscriptions/123/regions/peerings/456"))
.respond_with(ResponseTemplate::new(202).set_body_json(net_task_body()))
.mount(server.inner())
.await;
let client = server.client();
let state = full_policy_state(client);
let tool = cloud::delete_aa_vpc_peering(state);
let result = call_tool_text(&tool, json!({"subscription_id": 123, "peering_id": 456})).await;
assert!(
!result.contains("Failed"),
"DELETE regions/peerings/456 should have matched, got: {result}"
);
}
#[tokio::test]
async fn test_get_tgw_attachments_request_shape() {
let server = MockCloudServer::start().await;
Mock::given(method("GET"))
.and(path("/subscriptions/123/transitGateways"))
.respond_with(ResponseTemplate::new(200).set_body_json(net_task_body()))
.mount(server.inner())
.await;
let client = server.client();
let state = Arc::new(AppState::with_cloud_client(client));
let tool = cloud::get_tgw_attachments(state);
let result = call_tool_text(&tool, json!({"subscription_id": 123})).await;
assert!(
!result.contains("Failed"),
"GET transitGateways should have matched, got: {result}"
);
}
#[tokio::test]
async fn test_get_tgw_invitations_request_shape() {
let server = MockCloudServer::start().await;
Mock::given(method("GET"))
.and(path("/subscriptions/123/transitGateways/invitations"))
.respond_with(ResponseTemplate::new(200).set_body_json(net_task_body()))
.mount(server.inner())
.await;
let client = server.client();
let state = Arc::new(AppState::with_cloud_client(client));
let tool = cloud::get_tgw_invitations(state);
let result = call_tool_text(&tool, json!({"subscription_id": 123})).await;
assert!(
!result.contains("Failed"),
"GET transitGateways/invitations should have matched, got: {result}"
);
}
#[tokio::test]
async fn test_create_tgw_attachment_request_shape() {
let server = MockCloudServer::start().await;
Mock::given(method("POST"))
.and(path(
"/subscriptions/123/transitGateways/tgw-abc/attachment",
))
.respond_with(ResponseTemplate::new(202).set_body_json(net_task_body()))
.mount(server.inner())
.await;
let client = server.client();
let state = full_policy_state(client);
let tool = cloud::create_tgw_attachment(state);
let result = call_tool_text(&tool, json!({"subscription_id": 123, "tgw_id": "tgw-abc"})).await;
assert!(
!result.contains("Failed"),
"POST transitGateways/tgw-abc/attachment should have matched, got: {result}"
);
}
#[tokio::test]
async fn test_update_tgw_attachment_cidrs_request_shape() {
let server = MockCloudServer::start().await;
Mock::given(method("PUT"))
.and(path(
"/subscriptions/123/transitGateways/attach-1/attachment",
))
.and(body_partial_json(json!({"cidrs": ["10.0.0.0/24"]})))
.respond_with(ResponseTemplate::new(202).set_body_json(net_task_body()))
.mount(server.inner())
.await;
let client = server.client();
let state = full_policy_state(client);
let tool = cloud::update_tgw_attachment_cidrs(state);
let result = call_tool_text(
&tool,
json!({
"subscription_id": 123,
"attachment_id": "attach-1",
"cidrs": ["10.0.0.0/24"]
}),
)
.await;
assert!(
!result.contains("Failed"),
"PUT transitGateways/attach-1/attachment with cidrs should have matched, got: {result}"
);
}
#[tokio::test]
async fn test_delete_tgw_attachment_request_shape() {
let server = MockCloudServer::start().await;
Mock::given(method("DELETE"))
.and(path(
"/subscriptions/123/transitGateways/attach-1/attachment",
))
.respond_with(ResponseTemplate::new(202).set_body_json(net_task_body()))
.mount(server.inner())
.await;
let client = server.client();
let state = full_policy_state(client);
let tool = cloud::delete_tgw_attachment(state);
let result = call_tool_text(
&tool,
json!({"subscription_id": 123, "attachment_id": "attach-1"}),
)
.await;
assert!(
!result.contains("Failed"),
"DELETE transitGateways/attach-1/attachment should have matched, got: {result}"
);
}
#[tokio::test]
async fn test_accept_tgw_invitation_request_shape() {
let server = MockCloudServer::start().await;
Mock::given(method("PUT"))
.and(path(
"/subscriptions/123/transitGateways/invitations/inv-1/accept",
))
.respond_with(ResponseTemplate::new(202).set_body_json(net_task_body()))
.mount(server.inner())
.await;
let client = server.client();
let state = full_policy_state(client);
let tool = cloud::accept_tgw_invitation(state);
let result = call_tool_text(
&tool,
json!({"subscription_id": 123, "invitation_id": "inv-1"}),
)
.await;
assert!(
!result.contains("Failed"),
"PUT transitGateways/invitations/inv-1/accept should have matched, got: {result}"
);
}
#[tokio::test]
async fn test_reject_tgw_invitation_request_shape() {
let server = MockCloudServer::start().await;
Mock::given(method("PUT"))
.and(path(
"/subscriptions/123/transitGateways/invitations/inv-1/reject",
))
.respond_with(ResponseTemplate::new(202).set_body_json(net_task_body()))
.mount(server.inner())
.await;
let client = server.client();
let state = full_policy_state(client);
let tool = cloud::reject_tgw_invitation(state);
let result = call_tool_text(
&tool,
json!({"subscription_id": 123, "invitation_id": "inv-1"}),
)
.await;
assert!(
!result.contains("Failed"),
"PUT transitGateways/invitations/inv-1/reject should have matched, got: {result}"
);
}
#[tokio::test]
async fn test_get_aa_tgw_attachments_request_shape() {
let server = MockCloudServer::start().await;
Mock::given(method("GET"))
.and(path("/subscriptions/123/regions/1/transitGateways"))
.respond_with(ResponseTemplate::new(200).set_body_json(net_task_body()))
.mount(server.inner())
.await;
let client = server.client();
let state = Arc::new(AppState::with_cloud_client(client));
let tool = cloud::get_aa_tgw_attachments(state);
let result = call_tool_text(&tool, json!({"subscription_id": 123, "region_id": 1})).await;
assert!(
!result.contains("Failed"),
"GET regions/1/transitGateways should have matched, got: {result}"
);
}
#[tokio::test]
async fn test_get_aa_tgw_invitations_request_shape() {
let server = MockCloudServer::start().await;
Mock::given(method("GET"))
.and(path(
"/subscriptions/123/regions/1/transitGateways/invitations",
))
.respond_with(ResponseTemplate::new(200).set_body_json(net_task_body()))
.mount(server.inner())
.await;
let client = server.client();
let state = Arc::new(AppState::with_cloud_client(client));
let tool = cloud::get_aa_tgw_invitations(state);
let result = call_tool_text(&tool, json!({"subscription_id": 123, "region_id": 1})).await;
assert!(
!result.contains("Failed"),
"GET regions/1/transitGateways/invitations should have matched, got: {result}"
);
}
#[tokio::test]
async fn test_create_aa_tgw_attachment_request_shape() {
let server = MockCloudServer::start().await;
Mock::given(method("POST"))
.and(path(
"/subscriptions/123/regions/1/transitGateways/tgw-abc/attachment",
))
.respond_with(ResponseTemplate::new(202).set_body_json(net_task_body()))
.mount(server.inner())
.await;
let client = server.client();
let state = full_policy_state(client);
let tool = cloud::create_aa_tgw_attachment(state);
let result = call_tool_text(
&tool,
json!({"subscription_id": 123, "region_id": 1, "tgw_id": "tgw-abc"}),
)
.await;
assert!(
!result.contains("Failed"),
"POST regions/1/transitGateways/tgw-abc/attachment should have matched, got: {result}"
);
}
#[tokio::test]
async fn test_update_aa_tgw_attachment_cidrs_request_shape() {
let server = MockCloudServer::start().await;
Mock::given(method("PUT"))
.and(path(
"/subscriptions/123/regions/1/transitGateways/attach-1/attachment",
))
.and(body_partial_json(json!({"cidrs": ["10.2.0.0/24"]})))
.respond_with(ResponseTemplate::new(202).set_body_json(net_task_body()))
.mount(server.inner())
.await;
let client = server.client();
let state = full_policy_state(client);
let tool = cloud::update_aa_tgw_attachment_cidrs(state);
let result = call_tool_text(
&tool,
json!({
"subscription_id": 123,
"region_id": 1,
"attachment_id": "attach-1",
"cidrs": ["10.2.0.0/24"]
}),
)
.await;
assert!(
!result.contains("Failed"),
"PUT regions/1/transitGateways/attach-1/attachment with cidrs should have matched, got: {result}"
);
}
#[tokio::test]
async fn test_delete_aa_tgw_attachment_request_shape() {
let server = MockCloudServer::start().await;
Mock::given(method("DELETE"))
.and(path(
"/subscriptions/123/regions/1/transitGateways/attach-1/attachment",
))
.respond_with(ResponseTemplate::new(202).set_body_json(net_task_body()))
.mount(server.inner())
.await;
let client = server.client();
let state = full_policy_state(client);
let tool = cloud::delete_aa_tgw_attachment(state);
let result = call_tool_text(
&tool,
json!({"subscription_id": 123, "region_id": 1, "attachment_id": "attach-1"}),
)
.await;
assert!(
!result.contains("Failed"),
"DELETE regions/1/transitGateways/attach-1/attachment should have matched, got: {result}"
);
}
#[tokio::test]
async fn test_accept_aa_tgw_invitation_request_shape() {
let server = MockCloudServer::start().await;
Mock::given(method("PUT"))
.and(path(
"/subscriptions/123/regions/1/transitGateways/invitations/inv-1/accept",
))
.respond_with(ResponseTemplate::new(202).set_body_json(net_task_body()))
.mount(server.inner())
.await;
let client = server.client();
let state = full_policy_state(client);
let tool = cloud::accept_aa_tgw_invitation(state);
let result = call_tool_text(
&tool,
json!({"subscription_id": 123, "region_id": 1, "invitation_id": "inv-1"}),
)
.await;
assert!(
!result.contains("Failed"),
"PUT regions/1/transitGateways/invitations/inv-1/accept should have matched, got: {result}"
);
}
#[tokio::test]
async fn test_reject_aa_tgw_invitation_request_shape() {
let server = MockCloudServer::start().await;
Mock::given(method("PUT"))
.and(path(
"/subscriptions/123/regions/1/transitGateways/invitations/inv-1/reject",
))
.respond_with(ResponseTemplate::new(202).set_body_json(net_task_body()))
.mount(server.inner())
.await;
let client = server.client();
let state = full_policy_state(client);
let tool = cloud::reject_aa_tgw_invitation(state);
let result = call_tool_text(
&tool,
json!({"subscription_id": 123, "region_id": 1, "invitation_id": "inv-1"}),
)
.await;
assert!(
!result.contains("Failed"),
"PUT regions/1/transitGateways/invitations/inv-1/reject should have matched, got: {result}"
);
}
#[tokio::test]
async fn test_get_psc_service_request_shape() {
let server = MockCloudServer::start().await;
Mock::given(method("GET"))
.and(path("/subscriptions/123/private-service-connect"))
.respond_with(ResponseTemplate::new(200).set_body_json(net_task_body()))
.mount(server.inner())
.await;
let client = server.client();
let state = Arc::new(AppState::with_cloud_client(client));
let tool = cloud::get_psc_service(state);
let result = call_tool_text(&tool, json!({"subscription_id": 123})).await;
assert!(
!result.contains("Failed"),
"GET private-service-connect should have matched, got: {result}"
);
}
#[tokio::test]
async fn test_create_psc_service_request_shape() {
let server = MockCloudServer::start().await;
Mock::given(method("POST"))
.and(path("/subscriptions/123/private-service-connect"))
.respond_with(ResponseTemplate::new(202).set_body_json(net_task_body()))
.mount(server.inner())
.await;
let client = server.client();
let state = full_policy_state(client);
let tool = cloud::create_psc_service(state);
let result = call_tool_text(&tool, json!({"subscription_id": 123})).await;
assert!(
!result.contains("Failed"),
"POST private-service-connect should have matched, got: {result}"
);
}
#[tokio::test]
async fn test_delete_psc_service_request_shape() {
let server = MockCloudServer::start().await;
Mock::given(method("DELETE"))
.and(path("/subscriptions/123/private-service-connect"))
.respond_with(ResponseTemplate::new(202).set_body_json(net_task_body()))
.mount(server.inner())
.await;
let client = server.client();
let state = full_policy_state(client);
let tool = cloud::delete_psc_service(state);
let result = call_tool_text(&tool, json!({"subscription_id": 123})).await;
assert!(
!result.contains("Failed"),
"DELETE private-service-connect should have matched, got: {result}"
);
}
#[tokio::test]
async fn test_get_psc_endpoints_request_shape() {
let server = MockCloudServer::start().await;
Mock::given(method("GET"))
.and(path("/subscriptions/123/private-service-connect/10"))
.respond_with(ResponseTemplate::new(200).set_body_json(net_task_body()))
.mount(server.inner())
.await;
let client = server.client();
let state = Arc::new(AppState::with_cloud_client(client));
let tool = cloud::get_psc_endpoints(state);
let result = call_tool_text(&tool, json!({"subscription_id": 123, "psc_service_id": 10})).await;
assert!(
!result.contains("Failed"),
"GET private-service-connect/10 should have matched, got: {result}"
);
}
#[tokio::test]
async fn test_create_psc_endpoint_request_shape() {
let server = MockCloudServer::start().await;
Mock::given(method("POST"))
.and(path("/subscriptions/123/private-service-connect/10"))
.and(body_partial_json(json!({"gcpProjectId": "my-gcp-project"})))
.respond_with(ResponseTemplate::new(202).set_body_json(net_task_body()))
.mount(server.inner())
.await;
let client = server.client();
let state = full_policy_state(client);
let tool = cloud::create_psc_endpoint(state);
let result = call_tool_text(
&tool,
json!({
"subscription_id": 123,
"psc_service_id": 10,
"endpoint_id": 20,
"gcp_project_id": "my-gcp-project"
}),
)
.await;
assert!(
!result.contains("Failed"),
"POST private-service-connect/10 with gcpProjectId should have matched, got: {result}"
);
}
#[tokio::test]
async fn test_update_psc_endpoint_request_shape() {
let server = MockCloudServer::start().await;
Mock::given(method("PUT"))
.and(path(
"/subscriptions/123/private-service-connect/10/endpoints/20",
))
.respond_with(ResponseTemplate::new(202).set_body_json(net_task_body()))
.mount(server.inner())
.await;
let client = server.client();
let state = full_policy_state(client);
let tool = cloud::update_psc_endpoint(state);
let result = call_tool_text(
&tool,
json!({
"subscription_id": 123,
"psc_service_id": 10,
"endpoint_id": 20,
"gcp_project_id": "my-gcp-project"
}),
)
.await;
assert!(
!result.contains("Failed"),
"PUT private-service-connect/10/endpoints/20 should have matched, got: {result}"
);
}
#[tokio::test]
async fn test_delete_psc_endpoint_request_shape() {
let server = MockCloudServer::start().await;
Mock::given(method("DELETE"))
.and(path(
"/subscriptions/123/private-service-connect/10/endpoints/20",
))
.respond_with(ResponseTemplate::new(202).set_body_json(net_task_body()))
.mount(server.inner())
.await;
let client = server.client();
let state = full_policy_state(client);
let tool = cloud::delete_psc_endpoint(state);
let result = call_tool_text(
&tool,
json!({"subscription_id": 123, "psc_service_id": 10, "endpoint_id": 20}),
)
.await;
assert!(
!result.contains("Failed"),
"DELETE private-service-connect/10/endpoints/20 should have matched, got: {result}"
);
}
#[tokio::test]
async fn test_get_psc_creation_script_request_shape() {
let server = MockCloudServer::start().await;
Mock::given(method("GET"))
.and(path(
"/subscriptions/123/private-service-connect/10/endpoints/20/creationScripts",
))
.respond_with(
ResponseTemplate::new(200).set_body_json(json!("#!/bin/bash\necho create-endpoint")),
)
.mount(server.inner())
.await;
let client = server.client();
let state = Arc::new(AppState::with_cloud_client(client));
let tool = cloud::get_psc_creation_script(state);
let result = call_tool_text(
&tool,
json!({"subscription_id": 123, "psc_service_id": 10, "endpoint_id": 20}),
)
.await;
assert!(
!result.contains("Failed") && result.contains("create-endpoint"),
"GET creationScripts should have returned script text, got: {result}"
);
}
#[tokio::test]
async fn test_get_psc_deletion_script_request_shape() {
let server = MockCloudServer::start().await;
Mock::given(method("GET"))
.and(path(
"/subscriptions/123/private-service-connect/10/endpoints/20/deletionScripts",
))
.respond_with(
ResponseTemplate::new(200).set_body_json(json!("#!/bin/bash\necho delete-endpoint")),
)
.mount(server.inner())
.await;
let client = server.client();
let state = Arc::new(AppState::with_cloud_client(client));
let tool = cloud::get_psc_deletion_script(state);
let result = call_tool_text(
&tool,
json!({"subscription_id": 123, "psc_service_id": 10, "endpoint_id": 20}),
)
.await;
assert!(
!result.contains("Failed") && result.contains("delete-endpoint"),
"GET deletionScripts should have returned script text, got: {result}"
);
}
#[tokio::test]
async fn test_get_aa_psc_service_request_shape() {
let server = MockCloudServer::start().await;
Mock::given(method("GET"))
.and(path("/subscriptions/123/regions/1/private-service-connect"))
.respond_with(ResponseTemplate::new(200).set_body_json(net_task_body()))
.mount(server.inner())
.await;
let client = server.client();
let state = Arc::new(AppState::with_cloud_client(client));
let tool = cloud::get_aa_psc_service(state);
let result = call_tool_text(&tool, json!({"subscription_id": 123, "region_id": 1})).await;
assert!(
!result.contains("Failed"),
"GET regions/1/private-service-connect should have matched, got: {result}"
);
}
#[tokio::test]
async fn test_create_aa_psc_service_request_shape() {
let server = MockCloudServer::start().await;
Mock::given(method("POST"))
.and(path("/subscriptions/123/regions/1/private-service-connect"))
.respond_with(ResponseTemplate::new(202).set_body_json(net_task_body()))
.mount(server.inner())
.await;
let client = server.client();
let state = full_policy_state(client);
let tool = cloud::create_aa_psc_service(state);
let result = call_tool_text(&tool, json!({"subscription_id": 123, "region_id": 1})).await;
assert!(
!result.contains("Failed"),
"POST regions/1/private-service-connect should have matched, got: {result}"
);
}
#[tokio::test]
async fn test_delete_aa_psc_service_request_shape() {
let server = MockCloudServer::start().await;
Mock::given(method("DELETE"))
.and(path("/subscriptions/123/regions/1/private-service-connect"))
.respond_with(ResponseTemplate::new(202).set_body_json(net_task_body()))
.mount(server.inner())
.await;
let client = server.client();
let state = full_policy_state(client);
let tool = cloud::delete_aa_psc_service(state);
let result = call_tool_text(&tool, json!({"subscription_id": 123, "region_id": 1})).await;
assert!(
!result.contains("Failed"),
"DELETE regions/1/private-service-connect should have matched, got: {result}"
);
}
#[tokio::test]
async fn test_get_aa_psc_endpoints_request_shape() {
let server = MockCloudServer::start().await;
Mock::given(method("GET"))
.and(path(
"/subscriptions/123/regions/1/private-service-connect/10",
))
.respond_with(ResponseTemplate::new(200).set_body_json(net_task_body()))
.mount(server.inner())
.await;
let client = server.client();
let state = Arc::new(AppState::with_cloud_client(client));
let tool = cloud::get_aa_psc_endpoints(state);
let result = call_tool_text(
&tool,
json!({"subscription_id": 123, "region_id": 1, "psc_service_id": 10}),
)
.await;
assert!(
!result.contains("Failed"),
"GET regions/1/private-service-connect/10 should have matched, got: {result}"
);
}
#[tokio::test]
async fn test_create_aa_psc_endpoint_request_shape() {
let server = MockCloudServer::start().await;
Mock::given(method("POST"))
.and(path(
"/subscriptions/123/regions/1/private-service-connect/10",
))
.and(body_partial_json(json!({"gcpProjectId": "my-gcp-project"})))
.respond_with(ResponseTemplate::new(202).set_body_json(net_task_body()))
.mount(server.inner())
.await;
let client = server.client();
let state = full_policy_state(client);
let tool = cloud::create_aa_psc_endpoint(state);
let result = call_tool_text(
&tool,
json!({
"subscription_id": 123,
"region_id": 1,
"psc_service_id": 10,
"endpoint_id": 20,
"gcp_project_id": "my-gcp-project"
}),
)
.await;
assert!(
!result.contains("Failed"),
"POST regions/1/private-service-connect/10 with gcpProjectId should have matched, got: {result}"
);
}
#[tokio::test]
async fn test_update_aa_psc_endpoint_request_shape() {
let server = MockCloudServer::start().await;
Mock::given(method("PUT"))
.and(path(
"/subscriptions/123/regions/1/private-service-connect/10/endpoints/20",
))
.respond_with(ResponseTemplate::new(202).set_body_json(net_task_body()))
.mount(server.inner())
.await;
let client = server.client();
let state = full_policy_state(client);
let tool = cloud::update_aa_psc_endpoint(state);
let result = call_tool_text(
&tool,
json!({
"subscription_id": 123,
"region_id": 1,
"psc_service_id": 10,
"endpoint_id": 20,
"gcp_project_id": "my-gcp-project"
}),
)
.await;
assert!(
!result.contains("Failed"),
"PUT regions/1/private-service-connect/10/endpoints/20 should have matched, got: {result}"
);
}
#[tokio::test]
async fn test_delete_aa_psc_endpoint_request_shape() {
let server = MockCloudServer::start().await;
Mock::given(method("DELETE"))
.and(path(
"/subscriptions/123/regions/1/private-service-connect/10/endpoints/20",
))
.respond_with(ResponseTemplate::new(202).set_body_json(net_task_body()))
.mount(server.inner())
.await;
let client = server.client();
let state = full_policy_state(client);
let tool = cloud::delete_aa_psc_endpoint(state);
let result = call_tool_text(
&tool,
json!({
"subscription_id": 123,
"region_id": 1,
"psc_service_id": 10,
"endpoint_id": 20
}),
)
.await;
assert!(
!result.contains("Failed"),
"DELETE regions/1/private-service-connect/10/endpoints/20 should have matched, got: {result}"
);
}
#[tokio::test]
async fn test_get_aa_psc_creation_script_request_shape() {
let server = MockCloudServer::start().await;
Mock::given(method("GET"))
.and(path(
"/subscriptions/123/regions/1/private-service-connect/10/endpoints/20/creationScripts",
))
.respond_with(
ResponseTemplate::new(200).set_body_json(json!("#!/bin/bash\necho aa-create")),
)
.mount(server.inner())
.await;
let client = server.client();
let state = Arc::new(AppState::with_cloud_client(client));
let tool = cloud::get_aa_psc_creation_script(state);
let result = call_tool_text(
&tool,
json!({
"subscription_id": 123,
"region_id": 1,
"psc_service_id": 10,
"endpoint_id": 20
}),
)
.await;
assert!(
!result.contains("Failed") && result.contains("aa-create"),
"GET AA creationScripts should have returned script text, got: {result}"
);
}
#[tokio::test]
async fn test_get_aa_psc_deletion_script_request_shape() {
let server = MockCloudServer::start().await;
Mock::given(method("GET"))
.and(path(
"/subscriptions/123/regions/1/private-service-connect/10/endpoints/20/deletionScripts",
))
.respond_with(
ResponseTemplate::new(200).set_body_json(json!("#!/bin/bash\necho aa-delete")),
)
.mount(server.inner())
.await;
let client = server.client();
let state = Arc::new(AppState::with_cloud_client(client));
let tool = cloud::get_aa_psc_deletion_script(state);
let result = call_tool_text(
&tool,
json!({
"subscription_id": 123,
"region_id": 1,
"psc_service_id": 10,
"endpoint_id": 20
}),
)
.await;
assert!(
!result.contains("Failed") && result.contains("aa-delete"),
"GET AA deletionScripts should have returned script text, got: {result}"
);
}
#[tokio::test]
async fn test_get_private_link_request_shape() {
let server = MockCloudServer::start().await;
Mock::given(method("GET"))
.and(path("/subscriptions/123/private-link"))
.respond_with(ResponseTemplate::new(200).set_body_json(net_task_body()))
.mount(server.inner())
.await;
let client = server.client();
let state = Arc::new(AppState::with_cloud_client(client));
let tool = cloud::get_private_link(state);
let result = call_tool_text(&tool, json!({"subscription_id": 123})).await;
assert!(
!result.contains("Failed"),
"GET private-link should have matched, got: {result}"
);
}
#[tokio::test]
async fn test_create_private_link_request_shape() {
let server = MockCloudServer::start().await;
Mock::given(method("POST"))
.and(path("/subscriptions/123/private-link"))
.and(body_partial_json(json!({
"shareName": "my-share",
"principal": "123456789012",
"type": "aws_account"
})))
.respond_with(ResponseTemplate::new(202).set_body_json(net_task_body()))
.mount(server.inner())
.await;
let client = server.client();
let state = full_policy_state(client);
let tool = cloud::create_private_link(state);
let result = call_tool_text(
&tool,
json!({
"subscription_id": 123,
"share_name": "my-share",
"principal": "123456789012",
"principal_type": "aws_account"
}),
)
.await;
assert!(
!result.contains("Failed"),
"POST private-link with shareName/principal/type should have matched, got: {result}"
);
}
#[tokio::test]
async fn test_delete_private_link_request_shape() {
let server = MockCloudServer::start().await;
Mock::given(method("DELETE"))
.and(path("/subscriptions/123/private-link"))
.respond_with(ResponseTemplate::new(202).set_body_json(net_task_body()))
.mount(server.inner())
.await;
let client = server.client();
let state = full_policy_state(client);
let tool = cloud::delete_private_link(state);
let result = call_tool_text(&tool, json!({"subscription_id": 123})).await;
assert!(
!result.contains("Failed"),
"DELETE private-link should have matched, got: {result}"
);
}
#[tokio::test]
async fn test_add_private_link_principals_request_shape() {
let server = MockCloudServer::start().await;
Mock::given(method("POST"))
.and(path("/subscriptions/123/private-link/principals"))
.and(body_partial_json(json!({"principal": "123456789012"})))
.respond_with(ResponseTemplate::new(202).set_body_json(net_task_body()))
.mount(server.inner())
.await;
let client = server.client();
let state = full_policy_state(client);
let tool = cloud::add_private_link_principals(state);
let result = call_tool_text(
&tool,
json!({"subscription_id": 123, "principal": "123456789012"}),
)
.await;
assert!(
!result.contains("Failed"),
"POST private-link/principals should have matched, got: {result}"
);
}
#[tokio::test]
async fn test_remove_private_link_principals_request_shape() {
let server = MockCloudServer::start().await;
Mock::given(method("DELETE"))
.and(path("/subscriptions/123/private-link/principals"))
.and(body_partial_json(json!({"principal": "123456789012"})))
.respond_with(ResponseTemplate::new(202).set_body_json(net_task_body()))
.mount(server.inner())
.await;
let client = server.client();
let state = full_policy_state(client);
let tool = cloud::remove_private_link_principals(state);
let result = call_tool_text(
&tool,
json!({"subscription_id": 123, "principal": "123456789012"}),
)
.await;
assert!(
!result.contains("Failed"),
"DELETE private-link/principals with body should have matched, got: {result}"
);
}
#[tokio::test]
async fn test_get_private_link_endpoint_script_request_shape() {
let server = MockCloudServer::start().await;
Mock::given(method("GET"))
.and(path("/subscriptions/123/private-link/endpoint-script"))
.respond_with(ResponseTemplate::new(200).set_body_json(net_task_body()))
.mount(server.inner())
.await;
let client = server.client();
let state = Arc::new(AppState::with_cloud_client(client));
let tool = cloud::get_private_link_endpoint_script(state);
let result = call_tool_text(&tool, json!({"subscription_id": 123})).await;
assert!(
!result.contains("Failed"),
"GET private-link/endpoint-script should have matched, got: {result}"
);
}
#[tokio::test]
async fn test_get_aa_private_link_request_shape() {
let server = MockCloudServer::start().await;
Mock::given(method("GET"))
.and(path("/subscriptions/123/regions/1/private-link"))
.respond_with(ResponseTemplate::new(200).set_body_json(net_task_body()))
.mount(server.inner())
.await;
let client = server.client();
let state = Arc::new(AppState::with_cloud_client(client));
let tool = cloud::get_aa_private_link(state);
let result = call_tool_text(&tool, json!({"subscription_id": 123, "region_id": 1})).await;
assert!(
!result.contains("Failed"),
"GET regions/1/private-link should have matched, got: {result}"
);
}
#[tokio::test]
async fn test_create_aa_private_link_request_shape() {
let server = MockCloudServer::start().await;
Mock::given(method("POST"))
.and(path("/subscriptions/123/regions/1/private-link"))
.and(body_partial_json(json!({
"shareName": "my-share",
"principal": "123456789012",
"type": "aws_account"
})))
.respond_with(ResponseTemplate::new(202).set_body_json(net_task_body()))
.mount(server.inner())
.await;
let client = server.client();
let state = full_policy_state(client);
let tool = cloud::create_aa_private_link(state);
let result = call_tool_text(
&tool,
json!({
"subscription_id": 123,
"region_id": 1,
"share_name": "my-share",
"principal": "123456789012",
"principal_type": "aws_account"
}),
)
.await;
assert!(
!result.contains("Failed"),
"POST regions/1/private-link should have matched, got: {result}"
);
}
#[tokio::test]
async fn test_add_aa_private_link_principals_request_shape() {
let server = MockCloudServer::start().await;
Mock::given(method("POST"))
.and(path("/subscriptions/123/regions/1/private-link/principals"))
.and(body_partial_json(json!({"principal": "123456789012"})))
.respond_with(ResponseTemplate::new(202).set_body_json(net_task_body()))
.mount(server.inner())
.await;
let client = server.client();
let state = full_policy_state(client);
let tool = cloud::add_aa_private_link_principals(state);
let result = call_tool_text(
&tool,
json!({"subscription_id": 123, "region_id": 1, "principal": "123456789012"}),
)
.await;
assert!(
!result.contains("Failed"),
"POST regions/1/private-link/principals should have matched, got: {result}"
);
}
#[tokio::test]
async fn test_remove_aa_private_link_principals_request_shape() {
let server = MockCloudServer::start().await;
Mock::given(method("DELETE"))
.and(path("/subscriptions/123/regions/1/private-link/principals"))
.and(body_partial_json(json!({"principal": "123456789012"})))
.respond_with(ResponseTemplate::new(202).set_body_json(net_task_body()))
.mount(server.inner())
.await;
let client = server.client();
let state = full_policy_state(client);
let tool = cloud::remove_aa_private_link_principals(state);
let result = call_tool_text(
&tool,
json!({"subscription_id": 123, "region_id": 1, "principal": "123456789012"}),
)
.await;
assert!(
!result.contains("Failed"),
"DELETE regions/1/private-link/principals with body should have matched, got: {result}"
);
}
#[tokio::test]
async fn test_get_aa_private_link_endpoint_script_request_shape() {
let server = MockCloudServer::start().await;
Mock::given(method("GET"))
.and(path(
"/subscriptions/123/regions/1/private-link/endpoint-script",
))
.respond_with(ResponseTemplate::new(200).set_body_json(net_task_body()))
.mount(server.inner())
.await;
let client = server.client();
let state = Arc::new(AppState::with_cloud_client(client));
let tool = cloud::get_aa_private_link_endpoint_script(state);
let result = call_tool_text(&tool, json!({"subscription_id": 123, "region_id": 1})).await;
assert!(
!result.contains("Failed"),
"GET regions/1/private-link/endpoint-script should have matched, got: {result}"
);
}
fn completed_task(task_id: &str, resource_id: Option<i64>) -> serde_json::Value {
let mut body = json!({
"taskId": task_id,
"status": "processing-completed",
});
if let Some(id) = resource_id {
body["response"] = json!({ "resourceId": id });
}
body
}
#[tokio::test]
async fn test_create_subscription_request_shape() {
let server = MockCloudServer::start().await;
Mock::given(method("POST"))
.and(path("/subscriptions"))
.and(body_partial_json(json!({
"name": "demo-sub",
"cloudProviders": [{"provider": "AWS", "regions": [{"region": "us-east-1"}]}],
"databases": [{"name": "demo-db", "memoryLimitInGb": 1.0}]
})))
.respond_with(ResponseTemplate::new(202).set_body_json(json!({
"taskId": "task-create-sub",
"status": "processing-in-progress"
})))
.mount(server.inner())
.await;
Mock::given(method("GET"))
.and(path("/tasks/task-create-sub"))
.respond_with(
ResponseTemplate::new(200).set_body_json(completed_task("task-create-sub", Some(555))),
)
.mount(server.inner())
.await;
let subscription = SubscriptionFixture::new(555, "demo-sub")
.status("active")
.build();
server.mock_subscription_get(555, subscription).await;
let client = server.client();
let state = full_policy_state(client);
let tool = cloud::create_subscription(state);
let result = call_tool_text(
&tool,
json!({
"name": "demo-sub",
"cloud_provider": "AWS",
"region": "us-east-1",
"database_name": "demo-db",
"memory_limit_in_gb": 1.0,
"timeout_seconds": 5
}),
)
.await;
assert!(
!result.contains("Failed") && (result.contains("555") || result.contains("demo-sub")),
"Expected created subscription, got: {result}"
);
}
#[tokio::test]
async fn test_create_database_request_shape() {
let server = MockCloudServer::start().await;
Mock::given(method("POST"))
.and(path("/subscriptions/123/databases"))
.and(body_partial_json(json!({
"name": "demo-db",
"memoryLimitInGb": 1.0
})))
.respond_with(ResponseTemplate::new(202).set_body_json(json!({
"taskId": "task-create-db",
"status": "processing-in-progress"
})))
.mount(server.inner())
.await;
Mock::given(method("GET"))
.and(path("/tasks/task-create-db"))
.respond_with(
ResponseTemplate::new(200).set_body_json(completed_task("task-create-db", Some(1001))),
)
.mount(server.inner())
.await;
let database = DatabaseFixture::new(1001, "demo-db").build();
server.mock_database_get(123, 1001, database).await;
let client = server.client();
let state = full_policy_state(client);
let tool = cloud::create_database(state);
let result = call_tool_text(
&tool,
json!({
"subscription_id": 123,
"name": "demo-db",
"memory_limit_in_gb": 1.0,
"timeout_seconds": 5
}),
)
.await;
assert!(
!result.contains("Failed") && (result.contains("demo-db") || result.contains("1001")),
"Expected created database, got: {result}"
);
}
#[tokio::test]
async fn test_update_database_request_shape() {
let server = MockCloudServer::start().await;
Mock::given(method("PUT"))
.and(path("/subscriptions/123/databases/1001"))
.and(body_partial_json(json!({ "memoryLimitInGb": 2.0 })))
.respond_with(ResponseTemplate::new(202).set_body_json(json!({
"taskId": "task-update-db",
"status": "processing-in-progress"
})))
.mount(server.inner())
.await;
Mock::given(method("GET"))
.and(path("/tasks/task-update-db"))
.respond_with(
ResponseTemplate::new(200).set_body_json(completed_task("task-update-db", Some(1001))),
)
.mount(server.inner())
.await;
let database = DatabaseFixture::new(1001, "demo-db")
.memory_limit_in_gb(2.0)
.build();
server.mock_database_get(123, 1001, database).await;
let client = server.client();
let state = full_policy_state(client);
let tool = cloud::update_database(state);
let result = call_tool_text(
&tool,
json!({
"subscription_id": 123,
"database_id": 1001,
"memory_limit_in_gb": 2.0,
"timeout_seconds": 5
}),
)
.await;
assert!(
!result.contains("Failed"),
"Expected updated database, got: {result}"
);
}
#[tokio::test]
async fn test_backup_database_request_shape() {
let server = MockCloudServer::start().await;
Mock::given(method("POST"))
.and(path("/subscriptions/123/databases/1001/backup"))
.respond_with(ResponseTemplate::new(202).set_body_json(json!({
"taskId": "task-backup-db",
"status": "processing-in-progress"
})))
.mount(server.inner())
.await;
Mock::given(method("GET"))
.and(path("/tasks/task-backup-db"))
.respond_with(
ResponseTemplate::new(200).set_body_json(completed_task("task-backup-db", None)),
)
.mount(server.inner())
.await;
let client = server.client();
let state = full_policy_state(client);
let tool = cloud::backup_database(state);
let result = call_tool_text(
&tool,
json!({
"subscription_id": 123,
"database_id": 1001,
"timeout_seconds": 5
}),
)
.await;
assert!(
!result.contains("Failed"),
"Expected successful backup, got: {result}"
);
}
#[tokio::test]
async fn test_import_database_request_shape() {
let server = MockCloudServer::start().await;
Mock::given(method("POST"))
.and(path("/subscriptions/123/databases/1001/import"))
.and(body_partial_json(json!({
"sourceType": "http",
"importFromUri": ["https://example.com/dump.rdb"]
})))
.respond_with(ResponseTemplate::new(202).set_body_json(json!({
"taskId": "task-import-db",
"status": "processing-in-progress"
})))
.mount(server.inner())
.await;
Mock::given(method("GET"))
.and(path("/tasks/task-import-db"))
.respond_with(
ResponseTemplate::new(200).set_body_json(completed_task("task-import-db", None)),
)
.mount(server.inner())
.await;
let client = server.client();
let state = full_policy_state(client);
let tool = cloud::import_database(state);
let result = call_tool_text(
&tool,
json!({
"subscription_id": 123,
"database_id": 1001,
"source_type": "http",
"import_from_uri": "https://example.com/dump.rdb",
"timeout_seconds": 5
}),
)
.await;
assert!(
!result.contains("Failed"),
"Expected successful import, got: {result}"
);
}
#[tokio::test]
async fn test_update_crdb_local_properties_request_shape() {
let server = MockCloudServer::start().await;
Mock::given(method("PUT"))
.and(path("/subscriptions/123/databases/1001/regions"))
.and(body_partial_json(json!({ "name": "aa-db" })))
.respond_with(ResponseTemplate::new(202).set_body_json(json!({
"taskId": "task-crdb-update",
"status": "processing-in-progress"
})))
.mount(server.inner())
.await;
let client = server.client();
let state = full_policy_state(client);
let tool = cloud::update_crdb_local_properties(state);
let result = call_tool_text(
&tool,
json!({
"subscription_id": 123,
"database_id": 1001,
"name": "aa-db"
}),
)
.await;
assert!(
result.contains("task-crdb-update") || result.contains("taskId"),
"Expected task response for CRDB update, got: {result}"
);
}
#[tokio::test]
async fn test_delete_subscription_request_shape() {
let server = MockCloudServer::start().await;
Mock::given(method("DELETE"))
.and(path("/subscriptions/123"))
.respond_with(ResponseTemplate::new(202).set_body_json(json!({
"taskId": "task-delete-sub",
"status": "processing-in-progress"
})))
.mount(server.inner())
.await;
Mock::given(method("GET"))
.and(path("/tasks/task-delete-sub"))
.respond_with(
ResponseTemplate::new(200).set_body_json(completed_task("task-delete-sub", None)),
)
.mount(server.inner())
.await;
let client = server.client();
let state = full_policy_state(client);
let tool = cloud::delete_subscription(state);
assert!(
tool.annotations
.as_ref()
.is_some_and(|a| a.destructive_hint),
"delete_subscription must carry the destructive annotation"
);
let result = call_tool_text(&tool, json!({"subscription_id": 123, "timeout_seconds": 5})).await;
assert!(
!result.contains("Failed"),
"Expected successful delete_subscription, got: {result}"
);
}
#[tokio::test]
async fn test_delete_database_request_shape() {
let server = MockCloudServer::start().await;
Mock::given(method("DELETE"))
.and(path("/subscriptions/123/databases/1001"))
.respond_with(ResponseTemplate::new(202).set_body_json(json!({
"taskId": "task-delete-db",
"status": "processing-in-progress"
})))
.mount(server.inner())
.await;
Mock::given(method("GET"))
.and(path("/tasks/task-delete-db"))
.respond_with(
ResponseTemplate::new(200).set_body_json(completed_task("task-delete-db", None)),
)
.mount(server.inner())
.await;
let client = server.client();
let state = full_policy_state(client);
let tool = cloud::delete_database(state);
assert!(
tool.annotations
.as_ref()
.is_some_and(|a| a.destructive_hint),
"delete_database must carry the destructive annotation"
);
let result = call_tool_text(
&tool,
json!({"subscription_id": 123, "database_id": 1001, "timeout_seconds": 5}),
)
.await;
assert!(
!result.contains("Failed"),
"Expected successful delete_database, got: {result}"
);
}
#[tokio::test]
async fn test_flush_database_request_shape() {
let server = MockCloudServer::start().await;
Mock::given(method("PUT"))
.and(path("/subscriptions/123/databases/1001/flush"))
.respond_with(ResponseTemplate::new(202).set_body_json(json!({
"taskId": "task-flush-db",
"status": "processing-in-progress"
})))
.mount(server.inner())
.await;
Mock::given(method("GET"))
.and(path("/tasks/task-flush-db"))
.respond_with(
ResponseTemplate::new(200).set_body_json(completed_task("task-flush-db", None)),
)
.mount(server.inner())
.await;
let client = server.client();
let state = full_policy_state(client);
let tool = cloud::flush_database(state);
assert!(
tool.annotations
.as_ref()
.is_some_and(|a| a.destructive_hint),
"flush_database must carry the destructive annotation"
);
let result = call_tool_text(
&tool,
json!({"subscription_id": 123, "database_id": 1001, "timeout_seconds": 5}),
)
.await;
assert!(
!result.contains("Failed"),
"Expected successful flush_database, got: {result}"
);
}
#[tokio::test]
async fn test_flush_crdb_database_request_shape() {
let server = MockCloudServer::start().await;
Mock::given(method("PUT"))
.and(path("/subscriptions/123/databases/1001/flush"))
.respond_with(ResponseTemplate::new(202).set_body_json(json!({
"taskId": "task-flush-crdb",
"status": "processing-in-progress"
})))
.mount(server.inner())
.await;
let client = server.client();
let state = full_policy_state(client);
let tool = cloud::flush_crdb_database(state);
assert!(
tool.annotations
.as_ref()
.is_some_and(|a| a.destructive_hint),
"flush_crdb_database must carry the destructive annotation"
);
let result = call_tool_text(&tool, json!({"subscription_id": 123, "database_id": 1001})).await;
assert!(
result.contains("task-flush-crdb") || result.contains("taskId"),
"Expected task response for flush_crdb_database, got: {result}"
);
}
#[tokio::test]
async fn test_get_backup_status_request_shape() {
let server = MockCloudServer::start().await;
Mock::given(method("GET"))
.and(path("/subscriptions/123/databases/1001/backup"))
.respond_with(ResponseTemplate::new(200).set_body_json(json!({
"taskId": "task-backup-status",
"status": "processing-completed"
})))
.mount(server.inner())
.await;
let client = server.client();
let state = Arc::new(AppState::with_cloud_client(client));
let tool = cloud::get_backup_status(state);
let result = call_tool_text(&tool, json!({"subscription_id": 123, "database_id": 1001})).await;
assert!(
!result.contains("Failed"),
"Expected successful GET backup status, got: {result}"
);
}
#[tokio::test]
async fn test_get_slow_log_request_shape() {
let server = MockCloudServer::start().await;
Mock::given(method("GET"))
.and(path("/subscriptions/123/databases/1001/slow-log"))
.respond_with(ResponseTemplate::new(200).set_body_json(json!({ "entries": [] })))
.mount(server.inner())
.await;
let client = server.client();
let state = Arc::new(AppState::with_cloud_client(client));
let tool = cloud::get_slow_log(state);
let result = call_tool_text(&tool, json!({"subscription_id": 123, "database_id": 1001})).await;
assert!(
!result.contains("Failed"),
"Expected successful GET slow log, got: {result}"
);
}
#[tokio::test]
async fn test_get_database_tags_request_shape() {
let server = MockCloudServer::start().await;
Mock::given(method("GET"))
.and(path("/subscriptions/123/databases/1001/tags"))
.respond_with(ResponseTemplate::new(200).set_body_json(json!({ "tags": [] })))
.mount(server.inner())
.await;
let client = server.client();
let state = Arc::new(AppState::with_cloud_client(client));
let tool = cloud::get_tags(state);
let result = call_tool_text(&tool, json!({"subscription_id": 123, "database_id": 1001})).await;
assert!(
!result.contains("Failed"),
"Expected successful GET database tags, got: {result}"
);
}