use assert_cmd::Command;
use std::fs;
use std::os::unix::fs::PermissionsExt;
use std::path::{Path, PathBuf};
const FAKE_BUN: &str = r#"#!/bin/sh
printf '%s\n' "$*" >> "$FAKE_LOG"
if [ "$1" = "install" ]; then exit 0; fi
case "$2" in
build)
mkdir -p dist && printf 'export {};\n' > dist/index.js ;;
test-prepare)
out=""
while [ $# -gt 0 ]; do
if [ "$1" = "--out" ]; then out="$2"; fi
shift
done
root="$(pwd -P)"
mkdir -p "$out/tests"
printf 'export {};\n' > "$out/tests/a.test.js"
printf 'export {};\n' > "$out/tests/b.test.js"
printf '{"imports":{}}\n' > "$out/importmap.json"
printf '{"importMap":"%s/%s/importmap.json","tests":[{"source":"%s/tests/a.test.ts","output":"%s/%s/tests/a.test.js"},{"source":"%s/tests/b.test.ts","output":"%s/%s/tests/b.test.js"}]}\n' \
"$root" "$out" "$root" "$root" "$out" "$root" "$root" "$out" > "$out/test-plan.json" ;;
*) exit 64 ;;
esac
"#;
fn write_executable(path: &Path, contents: &str) {
fs::create_dir_all(path.parent().unwrap()).unwrap();
fs::write(path, contents).unwrap();
fs::set_permissions(path, fs::Permissions::from_mode(0o755)).unwrap();
}
struct Fixture {
_tmp: tempfile::TempDir,
project: PathBuf,
bin: PathBuf,
log: PathBuf,
}
fn burger_project() -> Fixture {
let tmp = tempfile::tempdir().unwrap();
let root = tmp.path().canonicalize().unwrap();
let project = root.join("hello-burger");
let files = [
(
"manifest.json",
r#"{"manifest_version":2,"abi":"v1","name":"hello-burger","version":"0.1.0","app_type":"burger","entrypoint":"dist/index.js","description":"Hello","maintainer":"T <t@example.com>","nodeApp":{"blueprint":">=1","minNodeVersion":"7.1.0"}}"#,
),
(
"package.json",
r#"{"name":"hello-burger","version":"0.1.0"}"#,
),
("src/index.ts", "export {};\n"),
("tests/a.test.ts", "export {};\n"),
("tests/b.test.ts", "export {};\n"),
(
"node_modules/@econ-v1/app-sdk/dist/burger-build/cli.js",
"// fake\n",
),
];
for (path, contents) in files {
let full = project.join(path);
fs::create_dir_all(full.parent().unwrap()).unwrap();
fs::write(full, contents).unwrap();
}
let bin = root.join("fake-bin");
write_executable(&bin.join("bun"), FAKE_BUN);
let log = root.join("bun.log");
Fixture {
_tmp: tmp,
project,
bin,
log,
}
}
fn node_app(fixture: &Fixture) -> Command {
let mut cmd = Command::cargo_bin("node-app").unwrap();
cmd.env("PATH", format!("{}:/usr/bin:/bin", fixture.bin.display()))
.env("FAKE_LOG", &fixture.log)
.env_remove("NODE_BURGER_BIN")
.env_remove("NODE_APP_BURGER_DIR")
.env_remove("CARGO_TARGET_DIR");
cmd
}
#[test]
fn burger_app_builds_with_burger_build_and_packages_against_node_app_burger() {
let fixture = burger_project();
node_app(&fixture)
.args(["build", "--path"])
.arg(&fixture.project)
.assert()
.success();
let log = fs::read_to_string(&fixture.log).unwrap();
assert!(log.contains("install --silent"), "{log}");
assert!(
log.contains(
"node_modules/@econ-v1/app-sdk/dist/burger-build/cli.js build src/index.ts --outdir dist"
),
"{log}"
);
let out = fixture.project.join("out");
node_app(&fixture)
.env("NODE_APP_PACKAGE_STAGE_ONLY", "1")
.args(["package", "--path"])
.arg(&fixture.project)
.arg("--out")
.arg(&out)
.assert()
.success();
let control = fs::read_to_string(out.join("staging/DEBIAN/control")).unwrap();
assert!(control.contains("Architecture: all"), "{control}");
assert!(
control.contains("Depends: node-app-burger (>= 0.1.0), node (>= 7.1.0)"),
"{control}"
);
let app_root = out.join("staging/usr/lib/node/apps/hello-burger");
assert!(app_root.join("dist/index.js").is_file());
assert!(!app_root.join("private_modules").exists());
}
#[test]
fn burger_package_rejects_private_runtime_modules() {
let fixture = burger_project();
fs::write(
fixture.project.join("manifest.json"),
r#"{"manifest_version":2,"abi":"v1","name":"hello-burger","version":"0.1.0","app_type":"burger","entrypoint":"dist/index.js","nodeApp":{"minNodeVersion":"7.1.0","privateRuntime":["sharp"]}}"#,
)
.unwrap();
fs::create_dir_all(fixture.project.join("dist")).unwrap();
fs::write(fixture.project.join("dist/index.js"), "export {};\n").unwrap();
let output = node_app(&fixture)
.env("NODE_APP_PACKAGE_STAGE_ONLY", "1")
.args(["package", "--path"])
.arg(&fixture.project)
.arg("--out")
.arg(fixture.project.join("out"))
.output()
.unwrap();
assert!(!output.status.success());
let stderr = String::from_utf8_lossy(&output.stderr);
assert!(
stderr.contains("burger apps cannot ship manifest.nodeApp.privateRuntime (sharp)"),
"{stderr}"
);
}
const FAKE_BURGER: &str = r#"#!/bin/sh
printf '%s\n' "$*" > "$FAKE_BURGER_ARGS"
exit "${FAKE_BURGER_EXIT:-0}"
"#;
#[test]
fn burger_test_prepares_then_runs_the_selected_files_in_node_app_burger() {
let fixture = burger_project();
let burger = fixture.bin.join("node-app-burger");
write_executable(&burger, FAKE_BURGER);
let args_file = fixture.project.join("burger-args.txt");
let caller = fixture.project.parent().unwrap().join("ci");
fs::create_dir_all(&caller).unwrap();
node_app(&fixture)
.current_dir(&caller)
.env("NODE_BURGER_BIN", &burger)
.env("FAKE_BURGER_ARGS", &args_file)
.args(["test", "--path"])
.arg(&fixture.project)
.args(["--junit", "reports/junit.xml", "b.test"])
.assert()
.success();
assert!(
caller.join("reports").is_dir(),
"the --junit directory is created for the runner"
);
let log = fs::read_to_string(&fixture.log).unwrap();
assert!(
log.contains("test-prepare --src src --tests tests --out target/burger-test"),
"{log}"
);
let out = fixture.project.join("target/burger-test");
assert_eq!(
fs::read_to_string(&args_file).unwrap().trim_end(),
format!(
"test --import-map {} --junit {} {}",
out.join("importmap.json").display(),
caller.join("reports/junit.xml").display(),
out.join("tests/b.test.js").display()
)
);
}
#[test]
fn burger_test_fails_when_node_app_burger_fails() {
let fixture = burger_project();
let burger = fixture.bin.join("node-app-burger");
write_executable(&burger, FAKE_BURGER);
node_app(&fixture)
.env("NODE_BURGER_BIN", &burger)
.env("FAKE_BURGER_ARGS", fixture.project.join("burger-args.txt"))
.env("FAKE_BURGER_EXIT", "1")
.args(["test", "--path"])
.arg(&fixture.project)
.assert()
.failure();
}
#[test]
fn burger_test_without_a_runtime_binary_says_where_it_looked() {
let fixture = burger_project();
let output = node_app(&fixture)
.args(["test", "--path"])
.arg(&fixture.project)
.output()
.unwrap();
assert!(!output.status.success());
let stderr = String::from_utf8_lossy(&output.stderr);
assert!(stderr.contains("node-app-burger not found"), "{stderr}");
assert!(
stderr.contains(
&fixture
.project
.join("modules/node-app-burger")
.display()
.to_string()
),
"{stderr}"
);
}