use std::{error::Error, io};
use cloudcover_aws::{AwsError, AwsProvider};
use cloudcover_core::{
ApiMethod, CloudProvider, GoMethodReference, JavaScriptMethodReference, Language,
MethodReference, PythonMethodReference, ResolvedSdk, Sdk, SdkMethodMapping, SdkModule,
SdkModuleReplacement, TerraformMethodReference,
};
use serde_json::json;
#[test]
fn lists_known_aws_methods() {
let methods = AwsProvider::new().list_api_methods();
assert!(methods.contains(&ApiMethod::new("s3", "GetObject")));
assert!(methods.contains(&ApiMethod::new("ec2", "DescribeInstances")));
let mut sorted = methods.clone();
sorted.sort();
assert_eq!(methods, sorted);
assert!(methods.windows(2).all(|window| window[0] != window[1]));
}
#[test]
fn lists_supported_aws_sdks() {
let sdks = AwsProvider::new().list_sdks();
assert_eq!(sdks.first(), Some(&Sdk::new("aws-sdk-go-v2", Language::Go)));
assert_eq!(sdks[1], Sdk::new("aws-sdk-go-v1", Language::Go));
assert_eq!(sdks[2], Sdk::new("boto3", Language::Python));
assert_eq!(sdks[3], Sdk::new("aws-sdk-js-v3", Language::JavaScript));
assert_eq!(sdks[4], Sdk::new("aws-sdk-js-v3", Language::TypeScript));
let terraform_sdks = &sdks[5..];
assert_eq!(
terraform_sdks.len(),
cloudcover_terraform_provider_aws::provider_versions().len()
);
}
#[test]
fn maps_runtime_sdk_clients_to_shared_iam_services() -> Result<(), Box<dyn Error>> {
let provider = AwsProvider::new();
for (module, version, operation, canonical, permission) in [
(
"bedrockruntime",
"v1.0.0",
"InvokeModel",
"bedrock",
"bedrock:InvokeModel",
),
(
"sagemakerruntime",
"v0.27.0",
"InvokeEndpoint",
"sagemaker",
"sagemaker:InvokeEndpoint",
),
(
"directoryservicedata",
"v1.0.0",
"CreateUser",
"ds-data",
"ds-data:CreateUser",
),
(
"opensearch",
"v1.0.0",
"CreateDomain",
"opensearch",
"es:CreateDomain",
),
] {
let package = format!("github.com/aws/aws-sdk-go-v2/service/{module}");
let sdk = ResolvedSdk::new(Sdk::new("aws-sdk-go-v2", Language::Go))
.with_modules([SdkModule::new(&package, version)]);
let mappings = provider.sdk_method_mappings(&sdk)?;
let reference = MethodReference::Go(GoMethodReference::new(
&package,
Some("Client".to_owned()),
operation,
));
let mapping = mappings
.iter()
.find(|mapping| mapping.method() == &reference)
.ok_or_else(|| format!("missing {module} {operation}"))?;
assert_eq!(
mapping.api_methods(),
&[ApiMethod::new(canonical, operation)]
);
assert!(
provider
.iam_permissions(&mapping.api_methods()[0])?
.contains(&permission)
);
}
Ok(())
}
#[test]
#[allow(clippy::too_many_lines)]
fn maps_sdk_methods_to_api_methods() -> Result<(), Box<dyn Error>> {
let provider = AwsProvider::new();
let python_sdk = Sdk::new("boto3", Language::Python);
let go_sdk = Sdk::new("aws-sdk-go-v2", Language::Go);
let go_v1_sdk = Sdk::new("aws-sdk-go-v1", Language::Go);
let javascript_sdk = Sdk::new("aws-sdk-js-v3", Language::JavaScript);
let terraform_sdk =
Sdk::new("terraform-provider-aws", Language::Terraform).with_version("6.64.0");
let python_mappings = provider.sdk_method_mappings(&ResolvedSdk::new(python_sdk.clone()))?;
let go_mappings =
provider.sdk_method_mappings(&ResolvedSdk::new(go_sdk.clone()).with_modules([
SdkModule::new("github.com/aws/aws-sdk-go-v2/service/s3", "v1.104.0"),
]))?;
let go_v1_mappings = provider.sdk_method_mappings(
&ResolvedSdk::new(go_v1_sdk.clone())
.with_modules([SdkModule::new("github.com/aws/aws-sdk-go", "v1.55.8")]),
)?;
let go_paginator_mappings =
provider.sdk_method_mappings(&ResolvedSdk::new(go_sdk.clone()).with_modules([
SdkModule::new("github.com/aws/aws-sdk-go-v2/service/ec2", "v1.335.0"),
]))?;
let javascript_mappings = provider.sdk_method_mappings(
&ResolvedSdk::new(javascript_sdk.clone())
.with_modules([SdkModule::new("@aws-sdk/client-s3", "3.1137.0")]),
)?;
let terraform_mappings =
provider.sdk_method_mappings(&ResolvedSdk::new(terraform_sdk.clone()))?;
assert!(python_mappings.contains(&SdkMethodMapping::new(
python_sdk.clone(),
MethodReference::Python(PythonMethodReference::new(
"boto3",
Some("s3".to_owned()),
"get_object",
)),
vec![ApiMethod::new("s3", "GetObject")],
)));
assert!(python_mappings.contains(&SdkMethodMapping::new(
python_sdk,
MethodReference::Python(PythonMethodReference::new(
"boto3",
Some("ec2".to_owned()),
"describe_instances",
)),
vec![ApiMethod::new("ec2", "DescribeInstances")],
)));
assert!(go_mappings.contains(&SdkMethodMapping::new(
go_sdk.clone(),
MethodReference::Go(GoMethodReference::new(
"github.com/aws/aws-sdk-go-v2/service/s3",
Some("Client".to_owned()),
"GetObject",
)),
vec![ApiMethod::new("s3", "GetObject")],
)));
assert!(go_paginator_mappings.contains(&SdkMethodMapping::new(
go_sdk,
MethodReference::Go(GoMethodReference::new(
"github.com/aws/aws-sdk-go-v2/service/ec2",
Some("DescribeInstancesPaginator".to_owned()),
"NextPage",
)),
vec![ApiMethod::new("ec2", "DescribeInstances")],
)));
assert!(go_v1_mappings.contains(&SdkMethodMapping::new(
go_v1_sdk,
MethodReference::Go(GoMethodReference::new(
"github.com/aws/aws-sdk-go/service/s3",
Some("S3".to_owned()),
"GetObject",
)),
vec![ApiMethod::new("s3", "GetObject")],
)));
assert!(javascript_mappings.contains(&SdkMethodMapping::new(
javascript_sdk.clone(),
MethodReference::JavaScript(JavaScriptMethodReference::new(
"@aws-sdk/client-s3",
Some("S3".to_owned()),
"getObject",
)),
vec![ApiMethod::new("s3", "GetObject")],
)));
assert!(javascript_mappings.contains(&SdkMethodMapping::new(
javascript_sdk,
MethodReference::JavaScript(JavaScriptMethodReference::new(
"@aws-sdk/client-s3",
None,
"GetObjectCommand",
)),
vec![ApiMethod::new("s3", "GetObject")],
)));
let terraform_bucket_create = terraform_mappings
.iter()
.find(|mapping| {
mapping.method()
== &MethodReference::Terraform(TerraformMethodReference::new(
"resource",
"aws_s3_bucket",
"create",
))
})
.ok_or_else(|| {
io::Error::other("missing terraform-provider-aws aws_s3_bucket create mapping")
})?;
assert_eq!(terraform_bucket_create.sdk(), &terraform_sdk);
assert!(
terraform_bucket_create
.api_methods()
.contains(&ApiMethod::new("s3", "CreateBucket"))
);
let api_methods = provider.list_api_methods();
for mapping in go_mappings
.iter()
.chain(go_v1_mappings.iter())
.chain(go_paginator_mappings.iter())
.chain(terraform_mappings.iter())
.chain(javascript_mappings.iter())
{
for api_method in mapping.api_methods() {
assert!(api_methods.contains(api_method));
}
}
for mappings in [
&python_mappings,
&go_mappings,
&go_v1_mappings,
&go_paginator_mappings,
&terraform_mappings,
&javascript_mappings,
] {
let mut sorted = mappings.clone();
sorted.sort();
assert_eq!(*mappings, sorted);
assert!(mappings.windows(2).all(|window| window[0] != window[1]));
}
Ok(())
}
#[test]
fn uses_canonical_service_names_from_generated_mappings() -> Result<(), Box<dyn Error>> {
let provider = AwsProvider::new();
let go_v2_sdk = Sdk::new("aws-sdk-go-v2", Language::Go);
let go_v2_mappings =
provider.sdk_method_mappings(&ResolvedSdk::new(go_v2_sdk).with_modules([
SdkModule::new(
"github.com/aws/aws-sdk-go-v2/service/cloudwatchlogs",
"v1.88.0",
),
SdkModule::new("github.com/aws/aws-sdk-go-v2/service/sfn", "v1.51.0"),
]))?;
let go_v1_mappings = provider.sdk_method_mappings(
&ResolvedSdk::new(Sdk::new("aws-sdk-go-v1", Language::Go))
.with_modules([SdkModule::new("github.com/aws/aws-sdk-go", "v1.55.8")]),
)?;
let terraform_mappings = provider.sdk_method_mappings(&ResolvedSdk::new(
Sdk::new("terraform-provider-aws", Language::Terraform).with_version("6.64.0"),
))?;
assert!(mapping_contains(
&go_v2_mappings,
&MethodReference::Go(GoMethodReference::new(
"github.com/aws/aws-sdk-go-v2/service/cloudwatchlogs",
Some("Client".to_owned()),
"CreateLogGroup",
)),
&ApiMethod::new("logs", "CreateLogGroup"),
));
assert!(mapping_contains(
&go_v2_mappings,
&MethodReference::Go(GoMethodReference::new(
"github.com/aws/aws-sdk-go-v2/service/sfn",
Some("Client".to_owned()),
"CreateStateMachine",
)),
&ApiMethod::new("states", "CreateStateMachine"),
));
assert!(mapping_contains(
&go_v1_mappings,
&MethodReference::Go(GoMethodReference::new(
"github.com/aws/aws-sdk-go/service/cloudwatchlogs",
Some("CloudWatchLogs".to_owned()),
"CreateLogGroup",
)),
&ApiMethod::new("logs", "CreateLogGroup"),
));
assert!(mapping_contains(
&go_v1_mappings,
&MethodReference::Go(GoMethodReference::new(
"github.com/aws/aws-sdk-go/service/sfn",
Some("SFN".to_owned()),
"CreateStateMachine",
)),
&ApiMethod::new("states", "CreateStateMachine"),
));
assert!(mapping_contains(
&terraform_mappings,
&MethodReference::Terraform(TerraformMethodReference::new(
"resource",
"aws_cloudwatch_log_group",
"create",
)),
&ApiMethod::new("logs", "CreateLogGroup"),
));
assert!(mapping_contains(
&terraform_mappings,
&MethodReference::Terraform(TerraformMethodReference::new(
"resource",
"aws_sfn_state_machine",
"create",
)),
&ApiMethod::new("states", "CreateStateMachine"),
));
Ok(())
}
#[test]
fn maps_terraform_service_names_to_catalog_services() -> Result<(), Box<dyn Error>> {
let mappings = AwsProvider::new().sdk_method_mappings(&ResolvedSdk::new(
Sdk::new("terraform-provider-aws", Language::Terraform).with_version("6.64.0"),
))?;
for (resource, operation, service) in [
(
"aws_bedrockagentcore_gateway",
"CreateGateway",
"bedrock-agentcore",
),
("aws_cognito_user_pool", "CreateUserPool", "cognito-idp"),
(
"aws_cognito_identity_pool",
"CreateIdentityPool",
"cognito-identity",
),
("aws_apigatewayv2_api", "CreateApi", "apigateway"),
("aws_cloudwatch_event_rule", "PutRule", "events"),
("aws_s3_bucket", "CreateBucket", "s3"),
("aws_mwaa_environment", "CreateEnvironment", "airflow"),
("aws_lb", "CreateLoadBalancer", "elasticloadbalancing"),
("aws_alb", "CreateLoadBalancer", "elasticloadbalancing"),
] {
assert!(
mapping_contains(
&mappings,
&MethodReference::Terraform(TerraformMethodReference::new(
"resource", resource, "create",
)),
&ApiMethod::new(service, operation),
),
"missing {service}:{operation} mapping for {resource}",
);
}
Ok(())
}
#[test]
fn maps_legacy_terraform_service_names_to_catalog_services() -> Result<(), Box<dyn Error>> {
let mappings = AwsProvider::new().sdk_method_mappings(&ResolvedSdk::new(
Sdk::new("terraform-provider-aws", Language::Terraform).with_version("5.63.1"),
))?;
assert!(mapping_contains(
&mappings,
&MethodReference::Terraform(TerraformMethodReference::new(
"resource",
"aws_simpledb_domain",
"create",
)),
&ApiMethod::new("sdb", "CreateDomain"),
));
Ok(())
}
fn mapping_contains(
mappings: &[SdkMethodMapping],
method: &MethodReference,
api_method: &ApiMethod,
) -> bool {
mappings
.iter()
.any(|mapping| mapping.method() == method && mapping.api_methods().contains(api_method))
}
#[test]
fn rejects_unsupported_sdk_mappings() {
let provider = AwsProvider::new();
let result = provider.sdk_method_mappings(&ResolvedSdk::new(Sdk::new(
"terraform-provider-aws",
Language::Terraform,
)));
assert!(matches!(
result,
Err(AwsError::MissingSdkVersion { name }) if name == "terraform-provider-aws"
));
for version in ["7.0.0", "6.64", "v6.64.0", "6.64.0+local"] {
let result = provider.sdk_method_mappings(&ResolvedSdk::new(
Sdk::new("terraform-provider-aws", Language::Terraform).with_version(version),
));
assert!(matches!(
result,
Err(AwsError::UnsupportedSdkVersion { name, version: actual })
if name == "terraform-provider-aws" && actual == version
));
}
for version in ["1.48.0", "1.42", "v1.42.0", "1.42.0+local"] {
let result = provider.sdk_method_mappings(&ResolvedSdk::new(
Sdk::new("aws-sdk-go-v2", Language::Go).with_version(version),
));
assert!(matches!(
result,
Err(AwsError::UnsupportedSdkVersion { name, version: actual })
if name == "aws-sdk-go-v2" && actual == version
));
}
let service_path = "github.com/aws/aws-sdk-go-v2/service/s3";
let pseudo_version = "v1.104.1-0.20260617000000-deadbeefdead";
let result = provider.sdk_method_mappings(
&ResolvedSdk::new(Sdk::new("aws-sdk-go-v2", Language::Go))
.with_modules([SdkModule::new(service_path, pseudo_version)]),
);
assert!(matches!(
result,
Err(AwsError::UnsupportedSdkModuleVersion { path, version })
if path == service_path && version == pseudo_version
));
let result =
provider.sdk_method_mappings(
&ResolvedSdk::new(Sdk::new("aws-sdk-go-v2", Language::Go))
.with_modules([SdkModule::new(service_path, "v1.104.0")
.with_replacement(SdkModuleReplacement::new("../local-s3", None))]),
);
assert!(matches!(
result,
Err(AwsError::UnsupportedSdkModuleReplacement {
path,
replacement_path,
replacement_version: None,
}) if path == service_path && replacement_path == "../local-s3"
));
let result = provider.sdk_method_mappings(&ResolvedSdk::new(Sdk::new(
"other-terraform-provider",
Language::Terraform,
)));
assert!(matches!(
result,
Err(AwsError::UnsupportedSdk { name, language })
if name == "other-terraform-provider" && language == Language::Terraform
));
}
#[test]
fn distinguishes_unsupported_terraform_provider_aws_versions_from_unknown_versions() {
let provider = AwsProvider::new();
let legacy_result = provider.sdk_method_mappings(&ResolvedSdk::new(
Sdk::new("terraform-provider-aws", Language::Terraform).with_version("1.56.0"),
));
assert!(matches!(
legacy_result,
Err(AwsError::UnsupportedTerraformProviderAwsVersion { version, reason })
if version == "1.56.0" && reason.as_str() == "aws_sdk_go_v1"
));
let supported_result = provider.sdk_method_mappings(&ResolvedSdk::new(
Sdk::new("terraform-provider-aws", Language::Terraform).with_version("1.57.0"),
));
assert!(matches!(supported_result, Ok(mappings) if !mappings.is_empty()));
for version in ["1.56", "v1.56.0", "1.56.0+local"] {
let result = provider.sdk_method_mappings(&ResolvedSdk::new(
Sdk::new("terraform-provider-aws", Language::Terraform).with_version(version),
));
assert!(matches!(
result,
Err(AwsError::UnsupportedSdkVersion { name, version: actual })
if name == "terraform-provider-aws" && actual == version
));
}
}
#[test]
fn builds_deterministic_iam_allow_policy() -> Result<(), Box<dyn Error>> {
let policy = AwsProvider::new().permissions_policy(&[
ApiMethod::new("s3", "PutObject"),
ApiMethod::new("s3", "GetObject"),
ApiMethod::new("ec2", "DescribeInstances"),
ApiMethod::new("s3", "GetObject"),
])?;
assert_eq!(
policy,
json!({
"Version":"2012-10-17",
"Statement":[
{
"Effect":"Allow",
"Action":["ec2:DescribeInstances"],
"Resource":"*"
},
{
"Effect":"Allow",
"Action":["kms:GenerateDataKey"],
"Resource":"arn:${Partition}:kms:${Region}:${Account}:key/${DestinationKeyId}",
"Condition":{"StringEquals":{"kms:ViaService":"s3.${Region}.amazonaws.com"}}
},
{
"Effect":"Allow",
"Action":["kms:Decrypt","kms:GenerateDataKey"],
"Resource":"arn:${Partition}:kms:${Region}:${Account}:key/${DirectoryKeyId}",
"Condition":{"StringEquals":{"kms:ViaService":"s3.${Region}.amazonaws.com"}}
},
{
"Effect":"Allow",
"Action":["kms:Decrypt"],
"Resource":"arn:${Partition}:kms:${Region}:${Account}:key/${ObjectKeyId}",
"Condition":{"StringEquals":{"kms:ViaService":"s3.${Region}.amazonaws.com"}}
},
{
"Effect":"Allow",
"Action":[
"s3:GetObject",
"s3:GetObjectLegalHold",
"s3:GetObjectRetention",
"s3:GetObjectTagging",
"s3:GetObjectVersion",
"s3:PutObject",
"s3:PutObjectAcl",
"s3:PutObjectLegalHold",
"s3:PutObjectRetention",
"s3:PutObjectTagging"
],
"Resource":[
"arn:${Partition}:s3:${Region}:${Account}:accesspoint/${AccessPointName}/object/${ObjectName}",
"arn:${Partition}:s3:::${BucketName}/${ObjectName}"
]
},
{
"Effect":"Allow",
"Action":["s3-object-lambda:GetObject","s3-object-lambda:PutObject"],
"Resource":"arn:${Partition}:s3-object-lambda:${Region}:${Account}:accesspoint/${AccessPointName}"
}
]
})
);
Ok(())
}
#[test]
fn builds_terraform_iam_policy_document() -> Result<(), Box<dyn Error>> {
let hcl = AwsProvider::new().permissions_policy_hcl(&[
ApiMethod::new("s3", "GetObject"),
ApiMethod::new("ec2", "DescribeInstances"),
])?;
let _: hcl::Body = hcl::from_str(&hcl)?;
assert!(hcl.contains(r#"data "aws_iam_policy_document" "cloudcover""#));
assert!(hcl.contains(r#"effect = "Allow""#));
assert!(hcl.contains(r#""ec2:DescribeInstances""#));
assert!(hcl.contains(r#""s3:GetObject""#));
assert!(hcl.contains("resources = ["));
assert!(hcl.contains(r#""*""#));
assert!(hcl.contains(r#""arn:$${Partition}:s3:::$${BucketName}/$${ObjectName}""#));
Ok(())
}
#[test]
fn excludes_get_caller_identity_from_mixed_policy() -> Result<(), Box<dyn Error>> {
let policy = AwsProvider::new().permissions_policy(&[
ApiMethod::new("sts", "GetCallerIdentity"),
ApiMethod::new("ec2", "DescribeInstances"),
])?;
assert_eq!(
policy,
json!({
"Version":"2012-10-17",
"Statement":[
{
"Effect":"Allow",
"Action":["ec2:DescribeInstances"],
"Resource":"*"
}
]
})
);
Ok(())
}
#[test]
fn excludes_get_caller_identity_from_sole_permission_policy() -> Result<(), Box<dyn Error>> {
let policy =
AwsProvider::new().permissions_policy(&[ApiMethod::new("sts", "GetCallerIdentity")])?;
assert_eq!(policy, json!({"Version":"2012-10-17","Statement":[]}));
Ok(())
}
#[test]
fn splits_actions_by_service_and_resource_scope() -> Result<(), Box<dyn Error>> {
let policy = AwsProvider::new().permissions_policy(&[
ApiMethod::new("iam", "AttachUserPolicy"),
ApiMethod::new("iam", "CreateAccountAlias"),
ApiMethod::new("iam", "AttachRolePolicy"),
])?;
assert_eq!(
policy,
json!({
"Version":"2012-10-17",
"Statement":[
{
"Effect":"Allow",
"Action":["iam:CreateAccountAlias"],
"Resource":"*"
},
{
"Effect":"Allow",
"Action":["iam:AttachRolePolicy"],
"Resource":"arn:${Partition}:iam::${Account}:role/${RoleNameWithPath}"
},
{
"Effect":"Allow",
"Action":["iam:AttachUserPolicy"],
"Resource":"arn:${Partition}:iam::${Account}:user/${UserNameWithPath}"
}
]
})
);
Ok(())
}
#[test]
fn operation_permissions_use_authorized_actions() -> Result<(), Box<dyn Error>> {
let policy = AwsProvider::new().permissions_policy(&[ApiMethod::new("s3", "CopyObject")])?;
let statements = policy
.get("Statement")
.and_then(serde_json::Value::as_array)
.ok_or_else(|| io::Error::other("missing policy statements"))?;
let mut actions = statements
.iter()
.filter_map(|statement| statement.get("Action"))
.flat_map(|action| action.as_array().into_iter().flatten())
.filter_map(serde_json::Value::as_str)
.map(str::to_owned)
.collect::<Vec<_>>();
actions.sort();
actions.dedup();
assert_eq!(
actions,
vec![
"kms:Decrypt".to_owned(),
"kms:GenerateDataKey".to_owned(),
"s3-object-lambda:PutObject".to_owned(),
"s3:GetObject".to_owned(),
"s3:GetObjectVersion".to_owned(),
"s3:PutObject".to_owned(),
"s3:PutObjectAcl".to_owned(),
"s3:PutObjectLegalHold".to_owned(),
"s3:PutObjectRetention".to_owned(),
"s3:PutObjectTagging".to_owned(),
]
);
Ok(())
}
#[test]
fn lists_iam_permissions_for_api_operations() -> Result<(), Box<dyn Error>> {
let permissions = AwsProvider::new().iam_permissions(&ApiMethod::new("s3", "CopyObject"))?;
assert_eq!(
permissions,
vec![
"kms:Decrypt",
"kms:GenerateDataKey",
"s3-object-lambda:PutObject",
"s3:GetObject",
"s3:GetObjectVersion",
"s3:PutObject",
"s3:PutObjectAcl",
"s3:PutObjectLegalHold",
"s3:PutObjectRetention",
"s3:PutObjectTagging",
]
);
Ok(())
}
#[test]
fn supplements_authorized_actionless_operation_with_fas_permissions() -> Result<(), Box<dyn Error>>
{
assert_eq!(
AwsProvider::new().iam_permissions(&ApiMethod::new("s3", "CreateSession"))?,
vec!["kms:Decrypt", "kms:GenerateDataKey"]
);
Ok(())
}
#[test]
fn rejects_unknown_api_method_for_iam_permissions() {
assert!(matches!(
AwsProvider::new().iam_permissions(&ApiMethod::new("not-a-service", "Nope")),
Err(AwsError::UnknownApiMethod { service, name })
if service == "not-a-service" && name == "Nope"
));
}
#[test]
fn operation_with_no_authorized_actions_builds_fas_policy_statement() -> Result<(), Box<dyn Error>>
{
let policy = AwsProvider::new().permissions_policy(&[ApiMethod::new("s3", "CreateSession")])?;
assert_eq!(
policy,
json!({"Version":"2012-10-17","Statement":[{
"Effect":"Allow", "Action":["kms:Decrypt", "kms:GenerateDataKey"],
"Resource":"arn:${Partition}:kms:${Region}:${Account}:key/${DirectoryKeyId}",
"Condition":{"StringEquals":{"kms:ViaService":"s3.${Region}.amazonaws.com"}}
}]})
);
Ok(())
}
#[test]
fn empty_method_set_builds_empty_policy() -> Result<(), Box<dyn Error>> {
let policy = AwsProvider::new().permissions_policy(&[])?;
assert_eq!(policy, json!({"Version":"2012-10-17","Statement":[]}));
Ok(())
}
#[test]
fn rejects_unknown_api_method() -> Result<(), Box<dyn Error>> {
let result = AwsProvider::new().permissions_policy(&[ApiMethod::new("not-a-service", "Nope")]);
let Err(AwsError::UnknownApiMethod { service, name }) = result else {
return Err(io::Error::other("expected unknown API method error").into());
};
assert_eq!(service, "not-a-service");
assert_eq!(name, "Nope");
Ok(())
}