1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
//! `export NAME=VALUE` inside a function must publish the value to the shared
//! (global) scope, not the function's frame — matching bash and kaish's
//! documented "functions execute in shared scope" rule (docs/LANGUAGE.md).
//!
//! Regression: `export` wrote the value via `set` (innermost frame), so the
//! function's pushed frame was popped on return and the value vanished — even
//! though the export *marking* (a flat set) survived, leaving an exported name
//! with no value.
// Test-fixture code: unwrap/expect on known-good setup is the idiom here.
#![allow(clippy::unwrap_used, clippy::expect_used)]
use std::sync::Arc;
use kaish_kernel::{Kernel, KernelConfig};
async fn setup() -> Arc<Kernel> {
Kernel::new(KernelConfig::isolated().with_skip_validation(true))
.expect("kernel")
.into_arc()
}
#[tokio::test]
async fn export_assignment_in_function_persists_globally() {
let k = setup().await;
let r = k
.execute("greet() { export FOO=bar; }\ngreet\necho \"[$FOO]\"")
.await
.expect("ok");
assert_eq!(
r.text_out().trim(),
"[bar]",
"export FOO=bar in a function should publish FOO to the shared scope"
);
}
#[tokio::test]
async fn export_assignment_in_function_stays_exported() {
let k = setup().await;
// After the function returns, FOO is both set and marked exported, so it
// shows up in `export -p` with its value.
let r = k
.execute("greet() { export FOO=bar; }\ngreet\nexport -p")
.await
.expect("ok");
assert!(
r.text_out().contains("declare -x FOO=\"bar\""),
"FOO should be listed as exported with its value; got: {}",
r.text_out()
);
}
#[tokio::test]
async fn plain_assignment_in_function_already_persists() {
// Control: a plain assignment already uses shared-scope semantics. This
// pins that the export fix matches the existing plain-assignment behavior.
let k = setup().await;
let r = k
.execute("greet() { BAZ=qux; }\ngreet\necho \"[$BAZ]\"")
.await
.expect("ok");
assert_eq!(r.text_out().trim(), "[qux]");
}