aboutsummaryrefslogtreecommitdiffstats
path: root/tests/scenarios.nix
diff options
context:
space:
mode:
Diffstat (limited to 'tests/scenarios.nix')
-rw-r--r--tests/scenarios.nix85
1 files changed, 69 insertions, 16 deletions
diff --git a/tests/scenarios.nix b/tests/scenarios.nix
index ce356b0..d8081d3 100644
--- a/tests/scenarios.nix
+++ b/tests/scenarios.nix
@@ -62,37 +62,57 @@ let
alt = { alt.file = "/fixtures/alt"; };
# Builds a scenario from a name, a target kind, and phases with sources.
- scenario = { name, targetKind, node ? null, prepare ? "", phases }: let
+ # Isolated scenarios start with no target directories and a collected store
+ # on both machines, so no other scenario's store paths or roots stand in for
+ # missing ones.
+ scenario = {
+ name, targetKind, node ? null, isolate ? false,
+ prepare ? "", prepareDeployer ? null, phases,
+ }: let
path = "/var/krops/${name}";
target = targets.${targetKind} path;
vars = "T=${path}\n";
+ # A phase expecting evaluation to fail has a control: sources that
+ # differ only in what makes evaluation fail, or a function returning them
+ # for the phase's sources, and that must evaluate. So the phase doesn't
+ # pass for failing for an unrelated reason, e.g. a misspelt option.
phase = {
sources, prune ? false, deploy ? null,
- expect ? "success", check ? "", mutate ? null,
+ expect ? "success", control ? null,
+ check ? "", mutate ? null, checkDeployer ? null, mutateDeployer ? null,
}: let
- args = {
- source = krops.evalSource [ sources ];
- inherit prune target;
- force = true;
- };
- command =
+ command = sources: let
+ args = {
+ source = krops.evalSource [ sources ];
+ inherit prune target;
+ force = true;
+ };
+ in
if deploy == null then populate args
else writeDeploy "deploy" (args // { useNixOutputMonitor = false; } // deploy);
- evaluated = builtins.tryEval (builtins.seq (toString command) true);
+ evaluates = sources:
+ (builtins.tryEval (builtins.seq (toString (command sources)) true)).success;
+ script = text: if text == null then null else prelude + vars + text;
in
if expect == "eval-error" then
- if evaluated.success then
+ if control == null then
+ throw "scenario ${name}: expecting evaluation to fail needs a control"
+ else if evaluates sources then
throw "scenario ${name}: populate evaluates, but is expected to fail"
+ else if !evaluates (if lib.isFunction control then control sources else control) then
+ throw "scenario ${name}: the control doesn't evaluate either"
else
null
else {
- populate = toString command;
+ populate = toString (command sources);
expect = if expect == "success" then null else expect.fail;
- check = prelude + vars + check;
- mutate = if mutate == null then null else prelude + vars + mutate;
+ check = script check;
+ mutate = script mutate;
+ checkDeployer = script checkDeployer;
+ mutateDeployer = script mutateDeployer;
};
in {
- inherit name;
+ inherit name isolate;
node =
if node != null then node
else if targetKind == "local" then "deployer"
@@ -100,6 +120,8 @@ let
prepare = prelude + vars + ''
rm -fR "$T"
'' + prepare;
+ prepareDeployer =
+ if prepareDeployer == null then null else prelude + vars + prepareDeployer;
phases = lib.filter (x: x != null) (map phase phases);
};
@@ -145,7 +167,19 @@ let
copyingWithSudo = flags: ctx:
ctx.targetKind == "sudo" && !(flags.fetchOnTarget or false);
-in {
+ # The sources with fetchOnTarget set for all flake and flake-input sources,
+ # the control for copying flakes to targets using sudo.
+ withFetchOnTarget = lib.mapAttrs (_: lib.mapAttrs (type: options:
+ if lib.elem type [ "flake" "flake-input" ]
+ then options // { fetchOnTarget = true; }
+ else options));
+
+in
+
+# The remote target must not count as local wherever this gets evaluated.
+assert !(krops.isLocalTarget (targets.remote "/"));
+
+{
derivation = matrix "derivation" {} (_: _: {
phases = twice {
@@ -255,6 +289,7 @@ in {
in
# The flake in a subdirectory has no inputs to draw from sources.
if flags.kind == "subdir" && flags.inputs then null else {
+ isolate = true;
phases = twice ({
sources = {
x.flake = {
@@ -265,6 +300,7 @@ in {
} // optionalAttrs flags.inputs alt;
} // (if copyingWithSudo flags ctx then {
expect = "eval-error";
+ control = withFetchOnTarget;
} else {
check = if flags.kind == "subdir" then checkFlake {
attr = "who"; expected = "sub"; subdir = true;
@@ -282,6 +318,7 @@ in {
}) (flags: ctx: let
inputs = flags.inputs or false;
in {
+ isolate = true;
phases = twice ({
sources = {
x.flake-input = {
@@ -293,6 +330,7 @@ in {
} // optionalAttrs inputs alt;
} // (if copyingWithSudo flags ctx then {
expect = "eval-error";
+ control = withFetchOnTarget;
} else {
check = {
dep = checkFlake {
@@ -472,7 +510,11 @@ in {
# Features spanning several sources.
features = concatMap (targetKind: let
- s = name: args: scenario ({ name = "${name}-${targetKind}"; inherit targetKind; } // args);
+ s = name: args: scenario ({
+ name = "${name}-${targetKind}";
+ inherit targetKind;
+ isolate = true;
+ } // args);
copying = targetKind != "sudo";
fetchOnTarget = !copying;
in [
@@ -602,18 +644,29 @@ in {
b.file = { path = "/fixtures/alt"; inputs.foo.follows = "a"; };
};
expect = "eval-error";
+ control = {
+ a.file = { path = "/fixtures/alt"; inputs.foo.follows = "b"; };
+ b.file = "/fixtures/alt";
+ };
}];
})
(s "self" {
phases = [{
sources.a.file = { path = "/fixtures/alt"; inputs.foo.follows = "a"; };
expect = "eval-error";
+ control = alt // {
+ a.file = { path = "/fixtures/alt"; inputs.foo.follows = "alt"; };
+ };
}];
})
(s "missing" {
phases = [{
sources.a.file = { path = "/fixtures/alt"; inputs.foo.follows = "nope"; };
expect = "eval-error";
+ control = {
+ a.file = { path = "/fixtures/alt"; inputs.foo.follows = "nope"; };
+ nope.file = "/fixtures/alt";
+ };
}];
})
(s "noflake" {