#![cfg(feature = "typescript")]
use bashkit::Bash;
fn bash_ts() -> Bash {
Bash::builder().typescript().build()
}
#[tokio::test]
async fn bash_writes_file_ts_reads() {
let mut bash = bash_ts();
bash.exec("echo 'hello from bash' > /tmp/shared.txt")
.await
.unwrap();
let r = bash
.exec("ts -c \"await readFile('/tmp/shared.txt')\"")
.await
.unwrap();
assert_eq!(r.exit_code, 0);
assert!(r.stdout.contains("hello from bash"));
}
#[tokio::test]
async fn ts_writes_file_bash_reads() {
let mut bash = bash_ts();
bash.exec("ts -c \"await writeFile('/tmp/tsfile.txt', 'hello from ts\\n')\"")
.await
.unwrap();
let r = bash.exec("cat /tmp/tsfile.txt").await.unwrap();
assert_eq!(r.exit_code, 0);
assert_eq!(r.stdout, "hello from ts\n");
}
#[tokio::test]
async fn roundtrip_bash_ts_bash() {
let mut bash = bash_ts();
bash.exec("echo 'original' > /tmp/roundtrip.txt")
.await
.unwrap();
bash.exec("ts -c \"await writeFile('/tmp/roundtrip.txt', 'modified by ts')\"")
.await
.unwrap();
let r = bash.exec("cat /tmp/roundtrip.txt").await.unwrap();
assert_eq!(r.exit_code, 0);
assert_eq!(r.stdout, "modified by ts");
}
#[tokio::test]
async fn ts_mkdir_bash_writes_ts_reads() {
let mut bash = bash_ts();
bash.exec("ts -c \"await mkdir('/data')\"").await.unwrap();
bash.exec("echo 'file1' > /data/a.txt && echo 'file2' > /data/b.txt")
.await
.unwrap();
let r = bash
.exec("ts -c \"await readFile('/data/a.txt')\"")
.await
.unwrap();
assert_eq!(r.exit_code, 0);
assert!(r.stdout.contains("file1"));
}
#[tokio::test]
async fn bash_generates_csv_ts_sums() {
let mut bash = bash_ts();
bash.exec("printf '10\\n20\\n30\\n' > /tmp/nums.txt")
.await
.unwrap();
let r = bash
.exec("ts -c \"await readFile('/tmp/nums.txt')\"")
.await
.unwrap();
assert_eq!(r.exit_code, 0);
assert!(r.stdout.contains("10"));
assert!(r.stdout.contains("30"));
}
#[tokio::test]
async fn ts_writes_json_bash_reads() {
let mut bash = bash_ts();
bash.exec("ts -c \"await writeFile('/tmp/result.json', '{\\\"count\\\":42}')\"")
.await
.unwrap();
let r = bash.exec("cat /tmp/result.json").await.unwrap();
assert_eq!(r.exit_code, 0);
assert_eq!(r.stdout, "{\"count\":42}");
}
#[tokio::test]
async fn ts_command_substitution() {
let mut bash = bash_ts();
let r = bash
.exec("result=$(ts -c \"console.log(6 * 7)\") && echo \"answer: $result\"")
.await
.unwrap();
assert_eq!(r.exit_code, 0);
assert_eq!(r.stdout.trim(), "answer: 42");
}
#[tokio::test]
async fn ts_in_pipeline() {
let mut bash = bash_ts();
let r = bash
.exec("ts -c \"for (let i = 0; i < 5; i++) { console.log('item-' + i); }\" | grep 'item-3'")
.await
.unwrap();
assert_eq!(r.exit_code, 0);
assert_eq!(r.stdout.trim(), "item-3");
}
#[tokio::test]
async fn ts_conditional_in_bash() {
let mut bash = bash_ts();
let r = bash
.exec("if ts -c \"console.log('ok')\"; then echo 'passed'; else echo 'failed'; fi")
.await
.unwrap();
assert_eq!(r.exit_code, 0);
assert!(r.stdout.contains("passed"));
let r = bash
.exec(
"if ts -c \"throw new Error()\" 2>/dev/null; then echo 'passed'; else echo 'failed'; fi",
)
.await
.unwrap();
assert_eq!(r.exit_code, 0);
assert!(r.stdout.contains("failed"));
}
#[tokio::test]
async fn run_ts_file_from_vfs() {
let mut bash = bash_ts();
bash.exec("echo 'console.log(\"from ts file\")' > /tmp/script.ts")
.await
.unwrap();
let r = bash.exec("ts /tmp/script.ts").await.unwrap();
assert_eq!(r.exit_code, 0);
assert_eq!(r.stdout.trim(), "from ts file");
}
#[tokio::test]
async fn run_js_file_via_node_alias() {
let mut bash = bash_ts();
bash.exec("echo 'console.log(\"from js file\")' > /tmp/script.js")
.await
.unwrap();
let r = bash.exec("node /tmp/script.js").await.unwrap();
assert_eq!(r.exit_code, 0);
assert_eq!(r.stdout.trim(), "from js file");
}
#[tokio::test]
async fn ts_file_with_vfs_access() {
let mut bash = bash_ts();
bash.exec("echo 'hello world' > /tmp/input.txt")
.await
.unwrap();
bash.exec(
"cat > /tmp/reader.ts << 'EOF'\n'Read: ' + (await readFile('/tmp/input.txt')).trim()\nEOF",
)
.await
.unwrap();
let r = bash.exec("ts /tmp/reader.ts").await.unwrap();
assert_eq!(r.exit_code, 0);
assert_eq!(r.stdout.trim(), "Read: hello world");
}
#[tokio::test]
async fn all_aliases_execute_files() {
let mut bash = bash_ts();
bash.exec("echo 'console.log(\"works\")' > /tmp/test.ts")
.await
.unwrap();
for cmd in &["ts", "typescript", "node", "deno", "bun"] {
let r = bash.exec(&format!("{cmd} /tmp/test.ts")).await.unwrap();
assert_eq!(r.exit_code, 0, "{cmd} should execute .ts files");
assert_eq!(r.stdout.trim(), "works", "{cmd} output mismatch");
}
}
#[tokio::test]
async fn vfs_state_persists_across_ts_invocations() {
let mut bash = bash_ts();
bash.exec("ts -c \"await writeFile('/tmp/counter.txt', '1')\"")
.await
.unwrap();
let r = bash
.exec("ts -c \"await readFile('/tmp/counter.txt')\"")
.await
.unwrap();
assert_eq!(r.exit_code, 0);
assert_eq!(r.stdout.trim(), "1");
let r = bash
.exec("n=$(ts -c \"await readFile('/tmp/counter.txt')\") && ts -c \"await writeFile('/tmp/counter.txt', '2')\"")
.await
.unwrap();
assert_eq!(r.exit_code, 0);
let r = bash.exec("cat /tmp/counter.txt").await.unwrap();
assert_eq!(r.stdout, "2");
}