test(scoped-includes): add tests for variable isolation

Tests verify:
- Legacy mode: vars merged globally (A sees B's VAR, can access UNIQUE_B)
- Scoped mode: vars isolated (A sees own VAR, cannot access UNIQUE_B)
- Inheritance: includes can still access root vars (ROOT_VAR)

Test structure:
- testdata/scoped_includes/ with main Taskfile and two includes
- inc_a and inc_b both define VAR with different values
- Cross-include test shows A trying to access B's UNIQUE_B
This commit is contained in:
Valentin Maerten
2025-12-26 21:19:13 +01:00
parent 04b8b75525
commit efaea39503
10 changed files with 137 additions and 0 deletions

View File

@@ -1180,3 +1180,69 @@ func TestIf(t *testing.T) {
NewExecutorTest(t, opts...)
}
}
func TestScopedIncludes(t *testing.T) {
t.Parallel()
// Legacy tests (without experiment) - vars should be merged globally
t.Run("legacy", func(t *testing.T) {
// Test with scoped includes disabled (legacy) - vars should be merged globally
NewExecutorTest(t,
WithName("default"),
WithExecutorOptions(
task.WithDir("testdata/scoped_includes"),
task.WithSilent(true),
),
)
// In legacy mode, UNIQUE_B should be accessible (merged globally)
NewExecutorTest(t,
WithName("cross-include"),
WithExecutorOptions(
task.WithDir("testdata/scoped_includes"),
task.WithSilent(true),
),
WithTask("a:try-access-b"),
)
})
// Scoped tests (with experiment enabled) - vars should be isolated
t.Run("scoped", func(t *testing.T) {
enableExperimentForTest(t, &experiments.ScopedIncludes, 1)
// Test with scoped includes enabled - vars should be isolated
NewExecutorTest(t,
WithName("default"),
WithExecutorOptions(
task.WithDir("testdata/scoped_includes"),
task.WithSilent(true),
),
)
// Test inheritance: include can access root vars
NewExecutorTest(t,
WithName("inheritance-a"),
WithExecutorOptions(
task.WithDir("testdata/scoped_includes"),
task.WithSilent(true),
),
WithTask("a:print"),
)
// Test isolation: each include sees its own vars
NewExecutorTest(t,
WithName("isolation-b"),
WithExecutorOptions(
task.WithDir("testdata/scoped_includes"),
task.WithSilent(true),
),
WithTask("b:print"),
)
// In scoped mode, UNIQUE_B should be empty (isolated)
NewExecutorTest(t,
WithName("cross-include"),
WithExecutorOptions(
task.WithDir("testdata/scoped_includes"),
task.WithSilent(true),
),
WithTask("a:try-access-b"),
)
})
}

20
testdata/scoped_includes/Taskfile.yml vendored Normal file
View File

@@ -0,0 +1,20 @@
version: "3"
vars:
ROOT_VAR: from_root
includes:
a: ./inc_a
b: ./inc_b
tasks:
default:
desc: Test scoped includes - vars should be isolated
cmds:
- task: a:print
- task: b:print
print-root-var:
desc: Print ROOT_VAR from root
cmds:
- echo "ROOT_VAR={{.ROOT_VAR}}"

View File

@@ -0,0 +1,18 @@
version: "3"
vars:
VAR: value_from_a
UNIQUE_A: only_in_a
tasks:
print:
desc: Print vars from include A
cmds:
- echo "A:VAR={{.VAR}}"
- echo "A:UNIQUE_A={{.UNIQUE_A}}"
- echo "A:ROOT_VAR={{.ROOT_VAR}}"
try-access-b:
desc: Try to access B's unique var (should fail in scoped mode)
cmds:
- echo "A:UNIQUE_B={{.UNIQUE_B}}"

View File

@@ -0,0 +1,13 @@
version: "3"
vars:
VAR: value_from_b
UNIQUE_B: only_in_b
tasks:
print:
desc: Print vars from include B
cmds:
- echo "B:VAR={{.VAR}}"
- echo "B:UNIQUE_B={{.UNIQUE_B}}"
- echo "B:ROOT_VAR={{.ROOT_VAR}}"

View File

@@ -0,0 +1 @@
A:UNIQUE_B=only_in_b

View File

@@ -0,0 +1,6 @@
A:VAR=value_from_b
A:UNIQUE_A=only_in_a
A:ROOT_VAR=from_root
B:VAR=value_from_b
B:UNIQUE_B=only_in_b
B:ROOT_VAR=from_root

View File

@@ -0,0 +1 @@
A:UNIQUE_B=

View File

@@ -0,0 +1,6 @@
A:VAR=value_from_a
A:UNIQUE_A=only_in_a
A:ROOT_VAR=from_root
B:VAR=value_from_b
B:UNIQUE_B=only_in_b
B:ROOT_VAR=from_root

View File

@@ -0,0 +1,3 @@
A:VAR=value_from_a
A:UNIQUE_A=only_in_a
A:ROOT_VAR=from_root

View File

@@ -0,0 +1,3 @@
B:VAR=value_from_b
B:UNIQUE_B=only_in_b
B:ROOT_VAR=from_root