#[cfg(feature = "cloud-pull-aws")]
mod inner {
use assert_cmd::Command;
use mockito::Server;
use predicates::prelude::*;
use tempfile::tempdir;
fn tsafe() -> Command {
Command::cargo_bin("tsafe").unwrap()
}
fn init_vault(dir: &std::path::Path) {
tsafe()
.args(["--profile", "default", "init"])
.env("TSAFE_VAULT_DIR", dir)
.env("TSAFE_PASSWORD", "test-pw")
.assert()
.success();
}
fn set_secret(dir: &std::path::Path, key: &str, value: &str) {
tsafe()
.args(["--profile", "default", "set", key, value])
.env("TSAFE_VAULT_DIR", dir)
.env("TSAFE_PASSWORD", "test-pw")
.assert()
.success();
}
fn aws_push_cmd(server_url: &str, dir: &std::path::Path) -> Command {
let mut cmd = tsafe();
cmd.args(["--profile", "default"])
.env("TSAFE_VAULT_DIR", dir)
.env("TSAFE_PASSWORD", "test-pw")
.env("AWS_DEFAULT_REGION", "us-east-1")
.env("AWS_ACCESS_KEY_ID", "AKID-TEST")
.env("AWS_SECRET_ACCESS_KEY", "secret-test")
.env_remove("AWS_SESSION_TOKEN")
.env("TSAFE_AWS_SM_TEST_ENDPOINT", server_url);
cmd
}
fn mock_empty_list(server: &mut Server) {
server
.mock("POST", "/")
.match_header("X-Amz-Target", "secretsmanager.ListSecrets")
.with_status(200)
.with_header("Content-Type", "application/x-amz-json-1.1")
.with_body(r#"{"SecretList":[]}"#)
.create();
}
fn mock_remote_secret(server: &mut Server, name: &str, value: &str) {
server
.mock("POST", "/")
.match_header("X-Amz-Target", "secretsmanager.ListSecrets")
.with_status(200)
.with_header("Content-Type", "application/x-amz-json-1.1")
.with_body(format!(r#"{{"SecretList":[{{"Name":"{name}","ARN":"arn:aws:secretsmanager:us-east-1:123:secret:{name}"}}]}}"#))
.create();
server
.mock("POST", "/")
.match_header("X-Amz-Target", "secretsmanager.GetSecretValue")
.with_status(200)
.with_header("Content-Type", "application/x-amz-json-1.1")
.with_body(format!(r#"{{"Name":"{name}","SecretString":"{value}"}}"#))
.create();
}
#[test]
fn aws_push_dry_run_shows_diff_without_writing() {
let dir = tempdir().unwrap();
init_vault(dir.path());
set_secret(dir.path(), "MY_SECRET", "hello");
let mut server = Server::new();
mock_empty_list(&mut server);
let no_write = server
.mock("POST", "/")
.match_header(
"X-Amz-Target",
mockito::Matcher::Regex("(CreateSecret|PutSecretValue)".to_string()),
)
.expect(0)
.create();
aws_push_cmd(&server.url(), dir.path())
.args(["aws-push", "--dry-run"])
.assert()
.success()
.stdout(predicate::str::contains("create").or(predicate::str::contains("update")))
.stdout(predicate::str::contains("Dry-run complete"));
no_write.assert();
}
#[test]
fn aws_push_aborts_without_yes_in_non_interactive() {
let dir = tempdir().unwrap();
init_vault(dir.path());
set_secret(dir.path(), "SOME_KEY", "value123");
let mut server = Server::new();
mock_empty_list(&mut server);
let no_write = server
.mock("POST", "/")
.match_header(
"X-Amz-Target",
mockito::Matcher::Regex("(CreateSecret|PutSecretValue)".to_string()),
)
.expect(0)
.create();
aws_push_cmd(&server.url(), dir.path())
.args(["aws-push"]) .assert()
.failure()
.stderr(predicate::str::contains("--yes"));
no_write.assert();
}
#[test]
fn aws_push_collision_detection_aborts_before_write() {
let dir = tempdir().unwrap();
init_vault(dir.path());
set_secret(dir.path(), "MY_KEY", "value-a");
set_secret(dir.path(), "my_key", "value-b");
let mut server = Server::new();
mock_empty_list(&mut server);
let no_write = server
.mock("POST", "/")
.match_header(
"X-Amz-Target",
mockito::Matcher::Regex("(CreateSecret|PutSecretValue)".to_string()),
)
.expect(0)
.create();
aws_push_cmd(&server.url(), dir.path())
.args(["aws-push", "--yes"])
.assert()
.failure()
.stderr(predicate::str::contains("collision").or(predicate::str::contains("my-key")));
no_write.assert();
}
#[test]
fn aws_push_unchanged_secrets_produce_no_write_calls() {
let dir = tempdir().unwrap();
init_vault(dir.path());
set_secret(dir.path(), "API_TOKEN", "identical-value");
let mut server = Server::new();
mock_remote_secret(&mut server, "api-token", "identical-value");
let no_write = server
.mock("POST", "/")
.match_header(
"X-Amz-Target",
mockito::Matcher::Regex("(CreateSecret|PutSecretValue)".to_string()),
)
.expect(0)
.create();
aws_push_cmd(&server.url(), dir.path())
.args(["aws-push", "--yes"])
.assert()
.success()
.stdout(
predicate::str::contains("unchanged").or(predicate::str::contains("up to date")),
);
no_write.assert();
}
}