mod common;
use assert_cmd::Command;
use common::write_config;
use serde_json::json;
use std::fs;
use wiremock::matchers::{method, path};
use wiremock::{Mock, MockServer, ResponseTemplate};
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn export_catalog_schemas_writes_files_and_exits_zero() {
let server = MockServer::start().await;
Mock::given(method("GET"))
.and(path("/catalogs"))
.respond_with(ResponseTemplate::new(200).set_body_json(json!({
"catalogs": [
{
"name": "cardiology",
"description": "Cardiology catalog",
"fields": [
{"name": "id", "type": "string"},
{"name": "score", "type": "number"}
]
},
{
"name": "dermatology",
"fields": [
{"name": "id", "type": "string"}
]
}
]
})))
.mount(&server)
.await;
let tmp = tempfile::tempdir().unwrap();
let config_path = write_config(tmp.path(), &server.uri());
let tmp_path = tmp.path().to_path_buf();
tokio::task::spawn_blocking(move || {
Command::cargo_bin("braze-sync")
.unwrap()
.env("BRAZE_API_KEY", "test-key")
.args(["--config", config_path.to_str().unwrap()])
.args(["export", "--resource", "catalog_schema"])
.assert()
.success();
})
.await
.unwrap();
let cardiology = tmp_path.join("catalogs/cardiology/schema.yaml");
let dermatology = tmp_path.join("catalogs/dermatology/schema.yaml");
assert!(cardiology.exists(), "cardiology schema should exist");
assert!(dermatology.exists(), "dermatology schema should exist");
let content = fs::read_to_string(&cardiology).unwrap();
assert!(content.contains("name: cardiology"));
assert!(content.contains("- name: id"));
assert!(content.contains("- name: score"));
assert!(content.starts_with("# Generated by braze-sync."));
}
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn export_with_name_filter_uses_get_endpoint() {
let server = MockServer::start().await;
Mock::given(method("GET"))
.and(path("/catalogs/cardiology"))
.respond_with(ResponseTemplate::new(200).set_body_json(json!({
"catalogs": [
{"name": "cardiology", "fields": [{"name": "id", "type": "string"}]}
]
})))
.mount(&server)
.await;
let tmp = tempfile::tempdir().unwrap();
let config_path = write_config(tmp.path(), &server.uri());
let tmp_path = tmp.path().to_path_buf();
tokio::task::spawn_blocking(move || {
Command::cargo_bin("braze-sync")
.unwrap()
.env("BRAZE_API_KEY", "test-key")
.args(["--config", config_path.to_str().unwrap()])
.args([
"export",
"--resource",
"catalog_schema",
"--name",
"cardiology",
])
.assert()
.success();
})
.await
.unwrap();
assert!(
tmp_path.join("catalogs/cardiology/schema.yaml").exists(),
"cardiology schema should exist after --name export"
);
}
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn unauthorized_braze_response_yields_exit_code_4() {
let server = MockServer::start().await;
Mock::given(method("GET"))
.and(path("/catalogs"))
.respond_with(ResponseTemplate::new(401).set_body_string("invalid api key"))
.mount(&server)
.await;
let tmp = tempfile::tempdir().unwrap();
let config_path = write_config(tmp.path(), &server.uri());
tokio::task::spawn_blocking(move || {
Command::cargo_bin("braze-sync")
.unwrap()
.env("BRAZE_API_KEY", "wrong-key")
.args(["--config", config_path.to_str().unwrap()])
.args(["export", "--resource", "catalog_schema"])
.assert()
.failure()
.code(4);
})
.await
.unwrap();
}
#[test]
fn missing_config_file_yields_exit_code_3() {
Command::cargo_bin("braze-sync")
.unwrap()
.env("BRAZE_API_KEY", "anything")
.args(["--config", "/nonexistent/braze-sync.config.yaml"])
.args(["export"])
.assert()
.failure()
.code(3);
}
#[test]
fn invalid_args_name_without_resource_yields_exit_code_3() {
Command::cargo_bin("braze-sync")
.unwrap()
.args(["export", "--name", "x"])
.assert()
.failure()
.code(3);
}
#[test]
fn help_flag_exits_zero() {
Command::cargo_bin("braze-sync")
.unwrap()
.arg("--help")
.assert()
.success();
}