#![cfg(feature = "itest-e2e")]
use std::path::{Path, PathBuf};
use std::process::Command;
fn fixture_root() -> PathBuf {
Path::new(env!("CARGO_MANIFEST_DIR")).join("../camel-integration-test/tests/fixtures/outbound")
}
#[cfg(feature = "integration-http")]
#[test]
fn cli_runs_full_boot_scenario() {
let root = fixture_root();
let doc = root.join("bridge.test.yaml");
assert!(
doc.is_file(),
"fixture document must exist: {}",
doc.display()
);
let output = Command::new(env!("CARGO_BIN_EXE_camel"))
.arg("test")
.arg(&doc)
.current_dir(&root)
.output()
.expect("camel test must spawn"); let stdout = String::from_utf8_lossy(&output.stdout).into_owned();
let stderr = String::from_utf8_lossy(&output.stderr).into_owned();
assert!(
output.status.success(),
"a full-boot pass must exit 0\nstdout:\n{stdout}\nstderr:\n{stderr}"
);
assert!(
stdout.contains("[full]"),
"stdout:\n{stdout}\nstderr:\n{stderr}"
);
for row in [
"scenario[0] send",
"scenario[1] receive",
"scenario[6] validate",
] {
assert!(
stdout.contains(&format!("#{row}")),
"missing PASS row {row}\nstdout:\n{stdout}\nstderr:\n{stderr}"
);
assert!(
!stdout.contains(&format!("FAIL #{row}")),
"row {row} must not fail\nstdout:\n{stdout}\nstderr:\n{stderr}"
);
}
assert!(
stdout.contains("7 passed, 0 failed"),
"every action must pass\nstdout:\n{stdout}\nstderr:\n{stderr}"
);
}
#[cfg(feature = "integration-sql")]
#[test]
fn sql_only_doc_boots_full() {
let dir = tempfile::tempdir().expect("temp dir");
std::fs::write(
dir.path().join("Camel.toml"),
r#"
[datasources.appdb]
db_url = "sqlite::memory:?cache=shared"
max_connections = 1
"#,
)
.expect("write Camel.toml");
std::fs::write(
dir.path().join("routes.yaml"),
r#"
routes:
- id: boot-route
from: direct:start
steps:
- to: log:info
"#,
)
.expect("write route file");
let doc = dir.path().join("seed.test.yaml");
std::fs::write(
&doc,
r#"
routeFiles: [routes.yaml]
scenario:
- sql:
datasource: appdb
prepare:
- CREATE TABLE seeded (v TEXT)
- INSERT INTO seeded VALUES ('e2e')
"#,
)
.expect("write document");
let output = Command::new(env!("CARGO_BIN_EXE_camel"))
.arg("test")
.arg(&doc)
.current_dir(dir.path())
.output()
.expect("camel test must spawn"); let stdout = String::from_utf8_lossy(&output.stdout).into_owned();
let stderr = String::from_utf8_lossy(&output.stderr).into_owned();
assert!(
output.status.success(),
"an sql-only full-boot pass must exit 0\nstdout:\n{stdout}\nstderr:\n{stderr}"
);
}
const SQL_VALIDATE_CAMEL_TOML: &str = r#"
[datasources.appdb]
db_url = "sqlite::memory:?cache=shared"
max_connections = 1
"#;
const SQL_VALIDATE_ROUTES_YAML: &str = r#"
routes:
- id: boot-route
from: direct:start
steps:
- to: log:info
"#;
fn write_sql_validate_doc(dir: &Path, expected_rows: &str) -> std::io::Result<std::path::PathBuf> {
std::fs::write(dir.join("Camel.toml"), SQL_VALIDATE_CAMEL_TOML)?;
std::fs::write(dir.join("routes.yaml"), SQL_VALIDATE_ROUTES_YAML)?;
let doc = dir.join("validate.test.yaml");
std::fs::write(
&doc,
format!(
r#"
routeFiles: [routes.yaml]
scenario:
- sql:
datasource: appdb
prepare:
- CREATE TABLE seeded (id INTEGER, name TEXT)
- INSERT INTO seeded VALUES (1, 'alice')
- INSERT INTO seeded VALUES (2, 'bob')
- validate:
deadline: 300ms
target:
sql:
datasource: appdb
query: SELECT id, name FROM seeded ORDER BY id
expectation:
rows: {expected_rows}
"#
),
)?;
Ok(doc)
}
#[cfg(feature = "integration-sql")]
#[test]
fn sql_validate_e2e_pass() {
let dir = tempfile::tempdir().expect("temp dir");
let doc = write_sql_validate_doc(dir.path(), "[[1, \"alice\"], [2, \"bob\"]]")
.expect("write validate document");
let output = Command::new(env!("CARGO_BIN_EXE_camel"))
.arg("test")
.arg(&doc)
.current_dir(dir.path())
.output()
.expect("camel test must spawn"); let stdout = String::from_utf8_lossy(&output.stdout).into_owned();
let stderr = String::from_utf8_lossy(&output.stderr).into_owned();
assert!(
output.status.success(),
"a matching sql validate must exit 0\nstdout:\n{stdout}\nstderr:\n{stderr}"
);
for row in ["scenario[0] action", "scenario[1] validate"] {
assert!(
stdout.contains(&format!("#{row}")),
"missing PASS row {row}\nstdout:\n{stdout}\nstderr:\n{stderr}"
);
}
assert!(
stdout.contains("2 passed, 0 failed"),
"every action must pass\nstdout:\n{stdout}\nstderr:\n{stderr}"
);
}
#[cfg(feature = "integration-sql")]
#[test]
fn sql_validate_e2e_fail_redacts() {
let dir = tempfile::tempdir().expect("temp dir");
let doc = write_sql_validate_doc(dir.path(), "[[1, \"alice\"], [2, \"charlie\"]]")
.expect("write validate document");
let output = Command::new(env!("CARGO_BIN_EXE_camel"))
.arg("test")
.arg(&doc)
.current_dir(dir.path())
.output()
.expect("camel test must spawn"); let stdout = String::from_utf8_lossy(&output.stdout).into_owned();
let stderr = String::from_utf8_lossy(&output.stderr).into_owned();
assert!(
!output.status.success(),
"a mismatching sql validate must exit nonzero\nstdout:\n{stdout}\nstderr:\n{stderr}"
);
let combined = format!("{stdout}{stderr}");
let report = combined
.lines()
.find(|line| line.contains("validation-mismatch"))
.expect("a validation-mismatch report must render")
.to_string();
assert!(
report.contains("sql appdb"),
"mismatch must name the datasource\nreport: {report}"
);
for leaked in ["sqlite::memory:"] {
assert!(
!combined.contains(leaked),
"output must not leak {leaked:?}\nstdout:\n{stdout}\nstderr:\n{stderr}"
);
}
for leaked in ["alice", "bob", "charlie", "INSERT INTO", "SELECT id, name"] {
assert!(
!report.contains(leaked),
"mismatch report must not leak {leaked:?}\nreport: {report}"
);
}
}