|
1 | 1 | use assert_cmd::{Command, cargo}; |
| 2 | +use cb_cli::docker_init::{CB_COMPOSE_FILE, CB_ENV_FILE}; |
| 3 | + |
| 4 | +// --------------------------------------------------------------------------- |
| 5 | +// Fixtures |
| 6 | +// --------------------------------------------------------------------------- |
| 7 | + |
| 8 | +const MINIMAL_PBS_TOML: &str = r#" |
| 9 | +chain = "Holesky" |
| 10 | +[pbs] |
| 11 | +docker_image = "ghcr.io/commit-boost/pbs:latest" |
| 12 | +"#; |
| 13 | + |
| 14 | +const MINIMAL_WITH_MODULE_TOML: &str = r#" |
| 15 | +chain = "Holesky" |
| 16 | +[pbs] |
| 17 | +docker_image = "ghcr.io/commit-boost/pbs:latest" |
| 18 | +
|
| 19 | +[signer.local.loader] |
| 20 | +key_path = "/keys/keys.json" |
| 21 | +
|
| 22 | +[[modules]] |
| 23 | +id = "DA_COMMIT" |
| 24 | +type = "commit" |
| 25 | +docker_image = "test_da_commit" |
| 26 | +"#; |
| 27 | + |
| 28 | +// --------------------------------------------------------------------------- |
| 29 | +// Helpers |
| 30 | +// --------------------------------------------------------------------------- |
| 31 | + |
| 32 | +/// Returns a `Command` pointed at the `commit-boost` binary under test. |
| 33 | +fn cmd() -> Command { |
| 34 | + Command::new(cargo::cargo_bin!()) |
| 35 | +} |
| 36 | + |
| 37 | +/// Writes `contents` to `cb.toml` inside `dir` and returns the path. |
| 38 | +fn write_config(dir: &tempfile::TempDir, contents: &str) -> std::path::PathBuf { |
| 39 | + let path = dir.path().join("cb.toml"); |
| 40 | + std::fs::write(&path, contents).expect("write test config"); |
| 41 | + path |
| 42 | +} |
| 43 | + |
| 44 | +/// Returns a `commit-boost init` command configured with the given config and |
| 45 | +/// output directory. |
| 46 | +fn init_cmd(config: &std::path::Path, output_dir: &std::path::Path) -> Command { |
| 47 | + let mut c = cmd(); |
| 48 | + c.args([ |
| 49 | + "init", |
| 50 | + "--config", |
| 51 | + config.to_str().expect("valid config path"), |
| 52 | + "--output", |
| 53 | + output_dir.to_str().expect("valid output dir"), |
| 54 | + ]); |
| 55 | + c |
| 56 | +} |
| 57 | + |
| 58 | +// --------------------------------------------------------------------------- |
| 59 | +// Binary smoke tests |
| 60 | +// --------------------------------------------------------------------------- |
2 | 61 |
|
3 | 62 | /// Tests that the binary can be run and returns a version string |
4 | 63 | #[test] |
5 | 64 | fn test_load_example_config() { |
6 | | - let mut cmd = Command::new(cargo::cargo_bin!()); |
7 | 65 | let expected_version = format!("Commit-Boost {}\n", commit_boost::VERSION); |
8 | | - cmd.arg("--version").assert().success().stdout(expected_version); |
| 66 | + cmd().arg("--version").assert().success().stdout(expected_version); |
9 | 67 | } |
10 | 68 |
|
11 | 69 | /// Tests that the init command can be run and complains about not having |
12 | 70 | /// --config set |
13 | 71 | #[test] |
14 | 72 | fn test_run_init() { |
15 | | - let mut cmd = Command::new(cargo::cargo_bin!()); |
16 | | - cmd.args(["init"]).assert().failure().stderr(predicates::str::contains( |
| 73 | + cmd().args(["init"]).assert().failure().stderr(predicates::str::contains( |
17 | 74 | "error: the following required arguments were not provided:\n --config <CONFIG_PATH>", |
18 | 75 | )); |
19 | 76 | } |
20 | 77 |
|
21 | 78 | /// Tests that PBS runs without CB_CONFIG being set and complains normally |
22 | 79 | #[test] |
23 | 80 | fn test_run_pbs_no_config() { |
24 | | - let mut cmd = Command::new(cargo::cargo_bin!()); |
25 | | - cmd.args(["pbs"]).assert().failure().stderr(predicates::str::contains("CB_CONFIG is not set")); |
| 81 | + cmd() |
| 82 | + .args(["pbs"]) |
| 83 | + .assert() |
| 84 | + .failure() |
| 85 | + .stderr(predicates::str::contains("CB_CONFIG is not set")); |
26 | 86 | } |
27 | 87 |
|
28 | 88 | /// Tests that Signer runs without CB_CONFIG being set and complains normally |
29 | 89 | #[test] |
30 | 90 | fn test_run_signer_no_config() { |
31 | | - let mut cmd = Command::new(cargo::cargo_bin!()); |
32 | | - cmd.args(["signer"]) |
| 91 | + cmd() |
| 92 | + .args(["signer"]) |
33 | 93 | .assert() |
34 | 94 | .failure() |
35 | 95 | .stderr(predicates::str::contains("CB_CONFIG is not set")); |
36 | 96 | } |
| 97 | + |
| 98 | +// --------------------------------------------------------------------------- |
| 99 | +// handle_docker_init (via `commit-boost init`) integration tests |
| 100 | +// --------------------------------------------------------------------------- |
| 101 | + |
| 102 | +/// Minimal PBS-only config produces a compose file and no .env file. |
| 103 | +#[test] |
| 104 | +fn test_init_pbs_only_creates_compose_file() { |
| 105 | + let dir = tempfile::tempdir().expect("tempdir"); |
| 106 | + let config = write_config(&dir, MINIMAL_PBS_TOML); |
| 107 | + |
| 108 | + init_cmd(&config, dir.path()).assert().success(); |
| 109 | + |
| 110 | + assert!(dir.path().join(CB_COMPOSE_FILE).exists(), "compose file should be created"); |
| 111 | + assert!(!dir.path().join(CB_ENV_FILE).exists(), "no .env file for PBS-only config"); |
| 112 | +} |
| 113 | + |
| 114 | +/// PBS-only compose file has the expected service structure. |
| 115 | +#[test] |
| 116 | +fn test_init_compose_file_pbs_service_structure() { |
| 117 | + let dir = tempfile::tempdir().expect("tempdir"); |
| 118 | + let config = write_config(&dir, MINIMAL_PBS_TOML); |
| 119 | + |
| 120 | + init_cmd(&config, dir.path()).assert().success(); |
| 121 | + |
| 122 | + let contents = |
| 123 | + std::fs::read_to_string(dir.path().join(CB_COMPOSE_FILE)).expect("read compose file"); |
| 124 | + let compose: serde_yaml::Value = |
| 125 | + serde_yaml::from_str(&contents).expect("compose file is valid YAML"); |
| 126 | + |
| 127 | + let pbs = &compose["services"]["cb_pbs"]; |
| 128 | + assert!(!pbs.is_null(), "cb_pbs service must exist"); |
| 129 | + assert_eq!(pbs["image"].as_str(), Some("ghcr.io/commit-boost/pbs:latest"), "image"); |
| 130 | + assert_eq!(pbs["container_name"].as_str(), Some("cb_pbs"), "container_name"); |
| 131 | + |
| 132 | + // Config file must be mounted inside the container. |
| 133 | + let volumes = pbs["volumes"].as_sequence().expect("volumes is a list"); |
| 134 | + assert!( |
| 135 | + volumes.iter().any(|v| v.as_str().map_or(false, |s| s.ends_with(":/cb-config.toml:ro"))), |
| 136 | + "config must be mounted at /cb-config.toml" |
| 137 | + ); |
| 138 | + |
| 139 | + // Required environment variables must be present. |
| 140 | + let env = &pbs["environment"]; |
| 141 | + assert!(!env["CB_CONFIG"].is_null(), "CB_CONFIG env var must be set"); |
| 142 | + assert!(!env["CB_PBS_ENDPOINT"].is_null(), "CB_PBS_ENDPOINT env var must be set"); |
| 143 | + |
| 144 | + // Port 18550 must be exposed. |
| 145 | + let ports = pbs["ports"].as_sequence().expect("ports is a list"); |
| 146 | + assert!( |
| 147 | + ports.iter().any(|p| p.as_str().map_or(false, |s| s.contains("18550"))), |
| 148 | + "port 18550 must be exposed" |
| 149 | + ); |
| 150 | + |
| 151 | + // No signer service and no extra network in a PBS-only config. |
| 152 | + assert!(compose["services"]["cb_signer"].is_null(), "cb_signer must not exist"); |
| 153 | + assert!(compose["networks"].is_null(), "no networks for PBS-only config"); |
| 154 | +} |
| 155 | + |
| 156 | +/// Config with a commit module produces both a compose file and a .env file. |
| 157 | +#[test] |
| 158 | +fn test_init_with_module_creates_env_file() { |
| 159 | + let dir = tempfile::tempdir().expect("tempdir"); |
| 160 | + let config = write_config(&dir, MINIMAL_WITH_MODULE_TOML); |
| 161 | + |
| 162 | + init_cmd(&config, dir.path()).assert().success(); |
| 163 | + |
| 164 | + assert!(dir.path().join(CB_COMPOSE_FILE).exists(), "compose file should be created"); |
| 165 | + assert!(dir.path().join(CB_ENV_FILE).exists(), ".env file should be created for modules"); |
| 166 | +} |
| 167 | + |
| 168 | +/// .env file contains a JWT entry for the module. |
| 169 | +#[test] |
| 170 | +fn test_init_env_file_contains_module_jwt() { |
| 171 | + let dir = tempfile::tempdir().expect("tempdir"); |
| 172 | + let config = write_config(&dir, MINIMAL_WITH_MODULE_TOML); |
| 173 | + |
| 174 | + init_cmd(&config, dir.path()).assert().success(); |
| 175 | + |
| 176 | + let env_contents = |
| 177 | + std::fs::read_to_string(dir.path().join(CB_ENV_FILE)).expect("read .env file"); |
| 178 | + assert!(env_contents.contains("CB_JWT_DA_COMMIT="), ".env must contain module JWT"); |
| 179 | +} |
| 180 | + |
| 181 | +/// Missing --config argument produces a clear error message. |
| 182 | +#[test] |
| 183 | +fn test_init_missing_config_flag_fails_with_message() { |
| 184 | + cmd().args(["init"]).assert().failure().stderr(predicates::str::contains("--config")); |
| 185 | +} |
| 186 | + |
| 187 | +/// Non-existent config file produces an error. |
| 188 | +#[test] |
| 189 | +fn test_init_nonexistent_config_file_fails() { |
| 190 | + let dir = tempfile::tempdir().expect("tempdir"); |
| 191 | + cmd() |
| 192 | + .args([ |
| 193 | + "init", |
| 194 | + "--config", |
| 195 | + "/nonexistent/path/cb.toml", |
| 196 | + "--output", |
| 197 | + dir.path().to_str().expect("valid dir"), |
| 198 | + ]) |
| 199 | + .assert() |
| 200 | + .failure(); |
| 201 | +} |
0 commit comments