nix: drop module-eval rename/removal shim tests
mkRenamedOptionModule/mkRemovedOptionModule are nixpkgs' own tested machinery; proving each call site resolves is redundant with what the library already guarantees. Removes 10 of the ~79 full nixosSystem fixtures (wireguard/forge/matrix/controller/ui/stores/statusPublish/ peers old-path + matrixMovedAppserviceToken/matrixRemovedToken) and their 15 dedicated cases. Fixtures still exercised by genuine behavioral cases (natsOldPath, autheliaOldPath, grafanaOldPath) are kept, minus their now-redundant rename-only case.
This commit is contained in:
parent
15424d3e49
commit
27803e9b97
1 changed files with 0 additions and 338 deletions
|
|
@ -120,73 +120,6 @@ let
|
|||
|
||||
withCi = hive { deploy.forgejo.ci.enable = true; };
|
||||
|
||||
# A host configured against the pre-rename option path. `mkRenamedOptionModule`
|
||||
# is the only thing carrying it, and nothing else in this suite would notice
|
||||
# if it were dropped: the new path evaluates fine on its own, so a missing
|
||||
# shim reads as a clean tree and breaks every existing operator config.
|
||||
wireguardOldPath = hive {
|
||||
swarm.wireguard.enable = true;
|
||||
swarm.wireguard.address = "10.100.0.1/24";
|
||||
swarm.wireguard.privateKeyFile = "/etc/wireguard/hive.key";
|
||||
};
|
||||
|
||||
# Same shape for the forge, which SPLIT rather than moving whole: the six
|
||||
# host-side options are set here through their pre-rename paths while the
|
||||
# rest of `swarm.forge` stays put. All six are defined so that dropping any
|
||||
# single shim entry fails the eval, not just the two the assertion reads.
|
||||
forgeOldPath = hive {
|
||||
swarm.forge.package = pkgs.emptyDirectory;
|
||||
swarm.forge.behindGateway = true;
|
||||
swarm.forge.openFirewall = true;
|
||||
swarm.forge.hostSwarmControllerTokenFile = "/etc/forge/sc.token";
|
||||
swarm.forge.sso.clientSecretFile = "/etc/forge/oidc.secret";
|
||||
swarm.forge.mirrors = [
|
||||
{
|
||||
upstream = "https://example.invalid/tool";
|
||||
dest = "mirrors/tool";
|
||||
}
|
||||
];
|
||||
};
|
||||
|
||||
# The homeserver's turn to split. All seven remaining host-side options are
|
||||
# set through their pre-rename paths — including `gui.enable`, whose value is
|
||||
# deliberately the opposite of its default so the definition has to actually
|
||||
# land, and both packages, stubbed to a derivation neither option defaults to
|
||||
# for the same reason. All seven so that dropping any single shim entry fails
|
||||
# the eval, not just the ones the assertions read.
|
||||
#
|
||||
# `registrationTokenFile` used to be the eighth: both spellings of it are now
|
||||
# `mkRemovedOptionModule`, exercised by `matrixRemovedToken` below instead —
|
||||
# setting it here would make every case that reads this fixture fail on that
|
||||
# one removed option.
|
||||
matrixOldPath = hive {
|
||||
deploy.matrix.enable = true;
|
||||
swarm.matrix.openFirewall = true;
|
||||
swarm.matrix.trustedServers = [ "matrix.example.invalid" ];
|
||||
swarm.matrix.maxRequestSize = 31457280;
|
||||
swarm.matrix.gui.enable = false;
|
||||
swarm.matrix.package = pkgs.emptyDirectory;
|
||||
swarm.matrix.gui.package = pkgs.emptyDirectory;
|
||||
swarm.matrix.sso.clientSecretFile = "/etc/matrix/oidc.secret";
|
||||
};
|
||||
|
||||
# Moving the appservice token, which desyncs hive-c0re's independently
|
||||
# derived path from the homeserver's. Its own fixture because the assertion
|
||||
# it trips would otherwise fire for every case reading `matrixOldPath`.
|
||||
matrixMovedAppserviceToken = hive {
|
||||
deploy.matrix.enable = true;
|
||||
deploy.matrix.appserviceTokenFile = "/etc/matrix/as.token";
|
||||
swarm.matrix.sso.clientSecretFile = "/etc/matrix/oidc.secret";
|
||||
};
|
||||
|
||||
# A config still naming the removed registration token, through the
|
||||
# pre-rename spelling — the one an old deployment is most likely to carry.
|
||||
matrixRemovedToken = hive {
|
||||
deploy.matrix.enable = true;
|
||||
swarm.matrix.registrationTokenFile = "/etc/matrix/register.token";
|
||||
swarm.matrix.sso.clientSecretFile = "/etc/matrix/oidc.secret";
|
||||
};
|
||||
|
||||
# The queue's callout identity, fourth split slice. `autoGenerateCallout` is
|
||||
# left FALSE on purpose: that is what makes the seed paths the thing deciding
|
||||
# `responderConfigured`, so the assertion below is about the seeds rather
|
||||
|
|
@ -233,39 +166,6 @@ let
|
|||
deploy.matrix.maxRequestSize = 99000000;
|
||||
};
|
||||
|
||||
# `enable` is spelled the OLD way like everything else here, so all six
|
||||
# of the controller's shims are exercised rather than five.
|
||||
controllerOldPath = hive {
|
||||
swarm.controller.enable = true;
|
||||
swarm.controller.package = pkgs.emptyDirectory;
|
||||
swarm.controller.swarmctlPackage = pkgs.emptyDirectory;
|
||||
swarm.controller.forgeTokenFile = "/run/secrets/ctrl-forge.token";
|
||||
swarm.controller.authBridgeUrl = "http://127.0.0.1:19097";
|
||||
swarm.controller.queue.clientSecretFile = "/run/secrets/ctrl-queue.secret";
|
||||
};
|
||||
|
||||
# The swarm UI, whose namespace this slice empties: `swarm.ui` had exactly
|
||||
# two options and both have moved, so BOTH old paths are set here and the
|
||||
# whole namespace now lives or dies by its two rename entries.
|
||||
uiOldPath = hive {
|
||||
swarm.ui.enable = true;
|
||||
swarm.ui.package = pkgs.emptyDirectory;
|
||||
};
|
||||
|
||||
# The two stores, which had NO old-path fixture at all until this slice —
|
||||
# their `enable` and `retentionPeriod` shims have been uncovered since they
|
||||
# landed, which is precisely the "a missing shim reads as a clean tree"
|
||||
# failure ./module-eval.nix's wireguard fixture was written to catch. All six
|
||||
# old paths are set, so dropping any single entry fails the eval.
|
||||
storesOldPath = hive {
|
||||
swarm.victoriametrics.enable = true;
|
||||
swarm.victorialogs.enable = true;
|
||||
swarm.victoriametrics.retentionPeriod = "3d";
|
||||
swarm.victorialogs.retentionPeriod = "5d";
|
||||
swarm.victoriametrics.package = pkgs.emptyDirectory;
|
||||
swarm.victorialogs.package = pkgs.emptyDirectory;
|
||||
};
|
||||
|
||||
grafanaOldPath = hive {
|
||||
deploy.grafana.enable = true;
|
||||
swarm.grafana.socketDir = "/run/test-grafana-sock";
|
||||
|
|
@ -342,37 +242,6 @@ let
|
|||
&& lib.hasInfix option a.message
|
||||
) m.assertions;
|
||||
|
||||
# The first slice to leave options on BOTH sides of the split, so the
|
||||
# fixture sets all three of them through the paths an existing config uses:
|
||||
# the two movers via their rename entries, `tokenEndpoint` via the path it
|
||||
# kept. That is also what the all-or-nothing assertion wants, so this hive
|
||||
# is a valid one rather than one that only evaluates because nothing forced
|
||||
# the assertion.
|
||||
statusPublishOldPath = hive {
|
||||
swarm.statusPublish.natsUrl = "nats://10.0.0.9:4222";
|
||||
swarm.statusPublish.tokenEndpoint = "https://auth.example.invalid/api/oidc/token";
|
||||
swarm.statusPublish.clientSecretFile = "/run/secrets/status-client.secret";
|
||||
};
|
||||
|
||||
# The eleventh migration, and the only one of the eleven with no old-path
|
||||
# fixture. It differs from its ten siblings in what a shim DOES: the others
|
||||
# re-route a value, so a broken one shows up as a wrong rendered config. This
|
||||
# one's entire deliverable is a warning, which nothing reads back — so an
|
||||
# unevaluated shim is a warning nobody sees, and it fails silently onto the
|
||||
# one operator who needed it.
|
||||
#
|
||||
# Two peers, only ONE carrying `caCert`, because the module emits a SECOND
|
||||
# warning filtered on exactly that attribute. With a single peer the filtered
|
||||
# list and `attrNames` are the same list, and a `withCaCert` that had
|
||||
# collapsed into `attrNames` would still read green.
|
||||
peersOldPath = hive {
|
||||
swarm.peers.old-one.domain = "old-one.t.local";
|
||||
swarm.peers.old-two = {
|
||||
domain = "old-two.t.local";
|
||||
caCert = "/etc/ssl/old-two-ca.pem";
|
||||
};
|
||||
};
|
||||
|
||||
baoPkcs11 = hive {
|
||||
deploy.bao.enable = true;
|
||||
deploy.bao.seal = "pkcs11";
|
||||
|
|
@ -996,83 +865,6 @@ let
|
|||
name = "the all-local mode turns the swarm controller on";
|
||||
ok = allLocal.services.hyperhive.deploy.swarm-controller.enable;
|
||||
}
|
||||
{
|
||||
# The mesh moved namespace wholesale, so an existing config sets paths
|
||||
# that no longer exist. Reading the *rendered interface* rather than the
|
||||
# option: a rename that resolved but stopped reaching the module would
|
||||
# satisfy an option-level check and still bring up no tunnel.
|
||||
name = "a config written against the pre-rename wireguard path still configures the interface";
|
||||
ok =
|
||||
let
|
||||
wg = wireguardOldPath.networking.wireguard.interfaces.wg-hive;
|
||||
in
|
||||
wg.ips == [ "10.100.0.1/24" ] && wg.privateKeyFile == "/etc/wireguard/hive.key";
|
||||
}
|
||||
{
|
||||
# Same reasoning one namespace over, plus the shape the mesh did not
|
||||
# have: `mirrors` is a single option of a list-of-submodule type, so its
|
||||
# one rename entry has to carry a whole compound value rather than a
|
||||
# scalar. Both arms read a rendered effect — the host firewall and the
|
||||
# env var c0re seeds mirrors from — not the option.
|
||||
name = "a config written against the pre-rename forge paths still opens the firewall and seeds the mirror";
|
||||
ok =
|
||||
let
|
||||
ports = forgeOldPath.networking.firewall.allowedTCPPorts;
|
||||
seeded = builtins.fromJSON forgeOldPath.systemd.services.hive-c0re.environment.HYPERHIVE_FORGE_MIRRORS;
|
||||
in
|
||||
builtins.elem 3000 ports && builtins.any (m: m.dest == "mirrors/tool") seeded;
|
||||
}
|
||||
{
|
||||
# Third split, and the one whose readers were hardest to see: the
|
||||
# appservice token is read only through a `let` alias in another
|
||||
# module, so no full path names it anywhere. Both arms read a rendered
|
||||
# effect — the host firewall and the container's bind-mount table — so a
|
||||
# rename that resolves but stops reaching the module still fails.
|
||||
name = "a config written against the pre-rename matrix paths still opens the port and mounts the appservice registration";
|
||||
ok =
|
||||
let
|
||||
ports = matrixOldPath.networking.firewall.allowedTCPPorts;
|
||||
httpPort = matrixOldPath.services.hyperhive.swarm.matrix.httpPort;
|
||||
in
|
||||
builtins.elem httpPort ports
|
||||
&& matrixOldPath.containers.hive-matrix.bindMounts ? "/var/lib/hyperhive/matrix-appservice";
|
||||
}
|
||||
{
|
||||
# appserviceTokenFile has no override capability (a store-delivered
|
||||
# secret doesn't need one — glue-matrix-bao-token.nix writes into the
|
||||
# fixed path instead of moving it), unlike its siblings in the same
|
||||
# rename. The fixture proves the override still *resolves* (mkDefault,
|
||||
# not a crash); this proves it also gets *rejected*, by a named
|
||||
# assertion rather than nixpkgs' generic conflicting-definition text.
|
||||
# Reads `.assertions` directly (cheap: a list of `{assertion;
|
||||
# message;}`, not `system.build.toplevel`) rather than forcing a real
|
||||
# build just to observe a boolean.
|
||||
name = "moving appserviceTokenFile trips a named assertion, not a silent desync";
|
||||
ok = lib.any (
|
||||
a: !a.assertion && lib.hasInfix "appserviceTokenFile" a.message
|
||||
) matrixMovedAppserviceToken.assertions;
|
||||
}
|
||||
{
|
||||
# The removal has to SAY so. A config carrying the old option through
|
||||
# an upgrade is the common case, and nixpkgs' bare "option does not
|
||||
# exist" would name neither what replaced the token nor where to read
|
||||
# about it.
|
||||
name = "a config still setting registrationTokenFile is refused with a message naming the appservice";
|
||||
ok = lib.any (a: !a.assertion && lib.hasInfix "appservice" a.message) matrixRemovedToken.assertions;
|
||||
}
|
||||
{
|
||||
# Reads the DELIVERY UNIT, not the options: `responderConfigured` gates
|
||||
# whether it exists at all, and the seed path is interpolated into its
|
||||
# script. A rename that resolved but stopped reaching the module would
|
||||
# leave the responder with no credentials and this case would catch it.
|
||||
name = "a config written against the pre-rename nats paths still delivers the responder's seeds";
|
||||
ok =
|
||||
let
|
||||
units = natsOldPath.systemd.services;
|
||||
in
|
||||
units ? swarm-nats-auth-secrets
|
||||
&& lib.hasInfix "/run/secrets/nats-user.seed" units.swarm-nats-auth-secrets.script;
|
||||
}
|
||||
{
|
||||
# Reads the RENDERED settings, not the option: `calloutBlocks {…} // {
|
||||
# … }` is a shallow merge, and a future edit that dropped or shadowed
|
||||
|
|
@ -1121,39 +913,6 @@ let
|
|||
in
|
||||
lib.hasInfix "--agent-publish-subject '$$SWARM.agent-state.{hive}.>'" exec;
|
||||
}
|
||||
{
|
||||
# Reads the host's tmpfiles rules, not the options: the socket directory
|
||||
# nginx and the container share is created there, so a rename that
|
||||
# resolved but stopped reaching the module would leave the gateway
|
||||
# proxying to a path nothing creates. All three old paths are defined
|
||||
# in the fixture, so removing any single shim fails the eval rather
|
||||
# than only the one this assertion reads.
|
||||
# THREE movers (authBridgeUrl, forgeTokenFile, queue.clientSecretFile)
|
||||
# are asserted as rendered effects rather than as option values, so a
|
||||
# rename that resolved but stopped reaching the module is caught
|
||||
# per-option instead of only where one assertion happens to look.
|
||||
# socketPath is no longer one of them: it went readOnly and lost its
|
||||
# shim in the same slice, so it has no pre-rename path left to prove
|
||||
# reaches the unit — the SOCKET line below instead pins the one value
|
||||
# it can ever hold, the default, so the env var vanishing or being
|
||||
# misspelled still fails this arm.
|
||||
name = "a config written against the pre-rename swarm-controller paths still reaches the unit";
|
||||
ok =
|
||||
let
|
||||
u = controllerOldPath.systemd.services.swarm-controller;
|
||||
creds = u.serviceConfig.LoadCredential;
|
||||
in
|
||||
u.environment.SWARM_CONTROLLER_SOCKET == "/run/swarm-controller/controller.sock"
|
||||
&& u.environment.SWARM_CONTROLLER_AUTH_BRIDGE_URL == "http://127.0.0.1:19097"
|
||||
&& lib.any (c: lib.hasInfix "/run/secrets/ctrl-queue.secret" c) creds
|
||||
&& lib.any (c: lib.hasInfix "/run/secrets/ctrl-forge.token" c) creds;
|
||||
}
|
||||
{
|
||||
name = "a config written against the pre-rename grafana paths still creates the socket directory";
|
||||
ok = lib.any (
|
||||
rule: lib.hasInfix "/run/test-grafana-sock" rule
|
||||
) grafanaOldPath.systemd.tmpfiles.rules;
|
||||
}
|
||||
{
|
||||
# This fixture enables grafana and NOT authelia, which is the shape the
|
||||
# login form used to stay enabled in: the toggle read "both services are
|
||||
|
|
@ -1360,25 +1119,6 @@ let
|
|||
&& otelNoIdentity.services.hyperhive.deploy.swarm-otel.clientSecretFile == null
|
||||
&& !(lib.any (a: !a.assertion) otelNoIdentity.assertions);
|
||||
}
|
||||
{
|
||||
# Reads the daemon's rendered unit, not the options: the queue address
|
||||
# arrives as an env var whose whole attrset is guarded on `natsUrl`, and
|
||||
# the secret as a systemd credential. A rename that resolved but stopped
|
||||
# reaching the module leaves hive-c0re coming up perfectly and reporting
|
||||
# to nobody, which is the one failure this option set exists to prevent.
|
||||
# The third arm is the option that did NOT move, read out of the same
|
||||
# attrset: the guard and the value beside it now come from different
|
||||
# namespaces, so a hive that renders one and drops the other is exactly
|
||||
# what a split can silently produce.
|
||||
name = "a config written against the pre-rename statusPublish paths still reaches the daemon";
|
||||
ok =
|
||||
let
|
||||
u = statusPublishOldPath.systemd.services.hive-c0re;
|
||||
in
|
||||
u.environment.HIVE_C0RE_NATS_URL == "nats://10.0.0.9:4222"
|
||||
&& u.environment.HIVE_C0RE_OIDC_TOKEN_ENDPOINT == "https://auth.example.invalid/api/oidc/token"
|
||||
&& lib.any (c: lib.hasInfix "/run/secrets/status-client.secret" c) u.serviceConfig.LoadCredential;
|
||||
}
|
||||
{
|
||||
# The collector's half of the same split, and a different arm from the
|
||||
# authenticator case below: this one reads the PATH the unit loads, so a
|
||||
|
|
@ -2343,43 +2083,6 @@ let
|
|||
# steps live in the same script and one indentation level decides it.
|
||||
&& lib.hasInfix "bao secrets enable -path=secret kv-v2" s;
|
||||
}
|
||||
{
|
||||
# Reads the vhost's rendered `root`, not the option: the UI is served
|
||||
# straight out of a store path, so a shim that resolves but stops
|
||||
# reaching the module would leave nginx pointing at the default build.
|
||||
name = "a config written against the pre-rename swarm-ui paths still serves the operator's build";
|
||||
#
|
||||
# ⚠️ `unsafeDiscardStringContext` on both sides, load-bearing rather than
|
||||
# tidy: interpolating a derivation carries string *context*, and this
|
||||
# suite renders its results into a `buildCommand` that may not reference
|
||||
# store paths. Comparing the paths as plain text is the intent — the case
|
||||
# asks "does nginx point HERE", not "depend on what it points at".
|
||||
ok =
|
||||
let
|
||||
want = builtins.unsafeDiscardStringContext "${pkgs.emptyDirectory}";
|
||||
# `or ""` is NOT enough: nginx's `locations.<l>.root` is `nullOr`, so
|
||||
# a vhost that HAS the attribute set to null skips the default and
|
||||
# reaches the coercion. Filter nulls, then discard context.
|
||||
roots = map builtins.unsafeDiscardStringContext (
|
||||
lib.filter (r: r != null) (
|
||||
map (v: v.locations."/".root or null) (builtins.attrValues uiOldPath.services.nginx.virtualHosts)
|
||||
)
|
||||
);
|
||||
in
|
||||
builtins.elem want roots;
|
||||
}
|
||||
{
|
||||
# Reads the package the CONTAINER renders, not the option: a shim that
|
||||
# resolves but stops reaching the module would leave the store running
|
||||
# nixpkgs' default while the operator's override read back fine.
|
||||
name = "a config written against the pre-rename store paths still picks the operator's package";
|
||||
ok =
|
||||
storesOldPath.containers.swarm-victorialogs.config.services.victorialogs.package
|
||||
== pkgs.emptyDirectory
|
||||
&&
|
||||
storesOldPath.containers.swarm-victoriametrics.config.services.victoriametrics.package
|
||||
== pkgs.emptyDirectory;
|
||||
}
|
||||
{
|
||||
# What makes the granting-unit cases mean something, and the property
|
||||
# the host-side half depends on: no store here, so no bind mount and no
|
||||
|
|
@ -2388,47 +2091,6 @@ let
|
|||
name = "a bootstrap token on a host that runs no store grants nothing";
|
||||
ok = !(baoGrantNoStore.systemd.services ? swarm-bao-bootstrap-dir);
|
||||
}
|
||||
{
|
||||
# Matches the option path, not the surrounding prose: the warning should
|
||||
# stay rewordable, but it must not stop naming what was set or where it
|
||||
# went.
|
||||
name = "a config still setting the removed swarm.peers is told where the entries moved";
|
||||
ok =
|
||||
let
|
||||
hit = lib.filter (
|
||||
w: lib.hasInfix "services.hyperhive.swarm.peers is removed" w
|
||||
) peersOldPath.warnings;
|
||||
in
|
||||
lib.length hit == 1
|
||||
&& lib.hasInfix "services.hyperhive.swarm.hives" (lib.head hit)
|
||||
&& lib.hasInfix "old-one, old-two" (lib.head hit);
|
||||
}
|
||||
{
|
||||
# The caCert half is a separately filtered warning, so it has to name
|
||||
# ONLY the peer that set one. Asserting the peer that did NOT is absent
|
||||
# is what distinguishes the filter from `attrNames`.
|
||||
name = "the removed peers.caCert warns about only the peers that set one";
|
||||
ok =
|
||||
let
|
||||
hit = lib.filter (w: lib.hasInfix "caCert is removed" w) peersOldPath.warnings;
|
||||
in
|
||||
lib.length hit == 1
|
||||
&& lib.hasInfix "Still set on: old-two" (lib.head hit)
|
||||
&& !(lib.hasInfix "old-one" (lib.head hit));
|
||||
}
|
||||
{
|
||||
# Control for both cases above. Without it they pass on any config whose
|
||||
# warning list happens to carry the string, including one where the shim
|
||||
# fires unconditionally — which is the same defect wearing the other sign.
|
||||
name = "a hive that never set swarm.peers gets neither removal warning";
|
||||
ok = !(lib.any (w: lib.hasInfix "swarm.peers" w) bare.warnings);
|
||||
}
|
||||
{
|
||||
name = "a config written against the pre-rename authelia usersFile still reaches the bridge";
|
||||
ok =
|
||||
autheliaOldPath.containers.swarm-authelia.config.systemd.services.swarm-authelia-bridge.environment.SWARM_AUTHELIA_BRIDGE_USERS_FILE
|
||||
== "/var/lib/test-authelia/users.yml";
|
||||
}
|
||||
{
|
||||
# Not a rename test. `hostClientSecretDir` is `readOnly`, so the fixture
|
||||
# cannot define it; what can break is a reader left pointing at the
|
||||
|
|
|
|||
Loading…
Reference in a new issue