about summary refs log tree commit diff
path: root/nixpkgs/nixos/tests
diff options
context:
space:
mode:
Diffstat (limited to 'nixpkgs/nixos/tests')
-rw-r--r--nixpkgs/nixos/tests/all-tests.nix10
-rw-r--r--nixpkgs/nixos/tests/budgie.nix6
-rw-r--r--nixpkgs/nixos/tests/caddy.nix44
-rw-r--r--nixpkgs/nixos/tests/chrony.nix31
-rw-r--r--nixpkgs/nixos/tests/dnscrypt-wrapper/default.nix14
-rw-r--r--nixpkgs/nixos/tests/eris-server.nix23
-rw-r--r--nixpkgs/nixos/tests/fcitx5/config11
-rw-r--r--nixpkgs/nixos/tests/fcitx5/default.nix47
-rw-r--r--nixpkgs/nixos/tests/fcitx5/profile27
-rw-r--r--nixpkgs/nixos/tests/influxdb2.nix36
-rw-r--r--nixpkgs/nixos/tests/jool.nix250
-rw-r--r--nixpkgs/nixos/tests/kernel-generic.nix6
-rw-r--r--nixpkgs/nixos/tests/libvirtd.nix7
-rw-r--r--nixpkgs/nixos/tests/matrix/synapse.nix15
-rw-r--r--nixpkgs/nixos/tests/mediamtx.nix57
-rw-r--r--nixpkgs/nixos/tests/netdata.nix5
-rw-r--r--nixpkgs/nixos/tests/nextcloud/basic.nix4
-rw-r--r--nixpkgs/nixos/tests/nextcloud/openssl-sse.nix4
-rw-r--r--nixpkgs/nixos/tests/nixops/default.nix9
-rw-r--r--nixpkgs/nixos/tests/opensnitch.nix62
-rw-r--r--nixpkgs/nixos/tests/pantheon.nix3
-rw-r--r--nixpkgs/nixos/tests/prometheus-exporters.nix19
-rw-r--r--nixpkgs/nixos/tests/switch-test.nix162
-rw-r--r--nixpkgs/nixos/tests/systemd-boot.nix10
-rw-r--r--nixpkgs/nixos/tests/web-apps/mastodon/remote-postgresql.nix2
-rw-r--r--nixpkgs/nixos/tests/web-servers/agate.nix2
-rw-r--r--nixpkgs/nixos/tests/wrappers.nix21
27 files changed, 755 insertions, 132 deletions
diff --git a/nixpkgs/nixos/tests/all-tests.nix b/nixpkgs/nixos/tests/all-tests.nix
index 3b4a39f5ff96..6f17bd2cdd3b 100644
--- a/nixpkgs/nixos/tests/all-tests.nix
+++ b/nixpkgs/nixos/tests/all-tests.nix
@@ -21,7 +21,7 @@ let
     if isAttrs val
     then
       if hasAttr "test" val then callTest val
-      else mapAttrs (n: s: discoverTests s) val
+      else mapAttrs (n: s: if n == "passthru" then s else discoverTests s) val
     else if isFunction val
       then
         # Tests based on make-test-python.nix will return the second lambda
@@ -167,6 +167,7 @@ in {
   cgit = handleTest ./cgit.nix {};
   charliecloud = handleTest ./charliecloud.nix {};
   chromium = (handleTestOn ["aarch64-linux" "x86_64-linux"] ./chromium.nix {}).stable or {};
+  chrony = handleTestOn ["aarch64-linux" "x86_64-linux"] ./chrony.nix {};
   chrony-ptp = handleTestOn ["aarch64-linux" "x86_64-linux"] ./chrony-ptp.nix {};
   cinnamon = handleTest ./cinnamon.nix {};
   cjdns = handleTest ./cjdns.nix {};
@@ -217,7 +218,7 @@ in {
   disable-installer-tools = handleTest ./disable-installer-tools.nix {};
   discourse = handleTest ./discourse.nix {};
   dnscrypt-proxy2 = handleTestOn ["x86_64-linux"] ./dnscrypt-proxy2.nix {};
-  dnscrypt-wrapper = handleTestOn ["x86_64-linux"] ./dnscrypt-wrapper {};
+  dnscrypt-wrapper = runTestOn ["x86_64-linux"] ./dnscrypt-wrapper;
   dnsdist = handleTest ./dnsdist.nix {};
   doas = handleTest ./doas.nix {};
   docker = handleTestOn ["aarch64-linux" "x86_64-linux"] ./docker.nix {};
@@ -252,6 +253,7 @@ in {
   envoy = handleTest ./envoy.nix {};
   ergo = handleTest ./ergo.nix {};
   ergochat = handleTest ./ergochat.nix {};
+  eris-server = handleTest ./eris-server.nix {};
   esphome = handleTest ./esphome.nix {};
   etc = pkgs.callPackage ../modules/system/etc/test.nix { inherit evalMinimalConfig; };
   activation = pkgs.callPackage ../modules/system/activation/test.nix { };
@@ -365,6 +367,7 @@ in {
   iftop = handleTest ./iftop.nix {};
   incron = handleTest ./incron.nix {};
   influxdb = handleTest ./influxdb.nix {};
+  influxdb2 = handleTest ./influxdb2.nix {};
   initrd-network-openvpn = handleTest ./initrd-network-openvpn {};
   initrd-network-ssh = handleTest ./initrd-network-ssh {};
   initrd-luks-empty-passphrase = handleTest ./initrd-luks-empty-passphrase.nix {};
@@ -388,6 +391,7 @@ in {
   jibri = handleTest ./jibri.nix {};
   jirafeau = handleTest ./jirafeau.nix {};
   jitsi-meet = handleTest ./jitsi-meet.nix {};
+  jool = handleTest ./jool.nix {};
   k3s = handleTest ./k3s {};
   kafka = handleTest ./kafka.nix {};
   kanidm = handleTest ./kanidm.nix {};
@@ -461,6 +465,7 @@ in {
   matrix-conduit = handleTest ./matrix/conduit.nix {};
   matrix-synapse = handleTest ./matrix/synapse.nix {};
   mattermost = handleTest ./mattermost.nix {};
+  mediamtx = handleTest ./mediamtx.nix {};
   mediatomb = handleTest ./mediatomb.nix {};
   mediawiki = handleTest ./mediawiki.nix {};
   meilisearch = handleTest ./meilisearch.nix {};
@@ -569,6 +574,7 @@ in {
   openresty-lua = handleTest ./openresty-lua.nix {};
   opensmtpd = handleTest ./opensmtpd.nix {};
   opensmtpd-rspamd = handleTest ./opensmtpd-rspamd.nix {};
+  opensnitch = handleTest ./opensnitch.nix {};
   openssh = handleTest ./openssh.nix {};
   octoprint = handleTest ./octoprint.nix {};
   openstack-image-metadata = (handleTestOn ["x86_64-linux"] ./openstack-image.nix {}).metadata or {};
diff --git a/nixpkgs/nixos/tests/budgie.nix b/nixpkgs/nixos/tests/budgie.nix
index 34ee1e303de9..19d9b2bd0bed 100644
--- a/nixpkgs/nixos/tests/budgie.nix
+++ b/nixpkgs/nixos/tests/budgie.nix
@@ -52,10 +52,16 @@ import ./make-test-python.nix ({ pkgs, lib, ... }: {
           machine.wait_for_window("budgie-daemon")
           machine.wait_until_succeeds("pgrep budgie-panel")
           machine.wait_for_window("budgie-panel")
+          # We don't check xwininfo for this one.
+          # See https://github.com/NixOS/nixpkgs/pull/216737#discussion_r1155312754
+          machine.wait_until_succeeds("pgrep budgie-wm")
 
       with subtest("Open MATE terminal"):
           machine.succeed("su - ${user.name} -c 'DISPLAY=:0 mate-terminal >&2 &'")
           machine.wait_for_window("Terminal")
+
+      with subtest("Check if budgie-wm has ever coredumped"):
+          machine.fail("coredumpctl --json=short | grep budgie-wm")
           machine.sleep(20)
           machine.screenshot("screen")
     '';
diff --git a/nixpkgs/nixos/tests/caddy.nix b/nixpkgs/nixos/tests/caddy.nix
index ed88f08739e8..238091ec606f 100644
--- a/nixpkgs/nixos/tests/caddy.nix
+++ b/nixpkgs/nixos/tests/caddy.nix
@@ -22,22 +22,6 @@ import ./make-test-python.nix ({ pkgs, ... }: {
       '';
       services.caddy.enableReload = true;
 
-      specialisation.etag.configuration = {
-        services.caddy.extraConfig = lib.mkForce ''
-          http://localhost {
-            encode gzip
-
-            file_server
-            root * ${
-              pkgs.runCommand "testdir2" {} ''
-                mkdir "$out"
-                echo changed > "$out/example.html"
-              ''
-            }
-          }
-        '';
-      };
-
       specialisation.config-reload.configuration = {
         services.caddy.extraConfig = ''
           http://localhost:8080 {
@@ -55,7 +39,6 @@ import ./make-test-python.nix ({ pkgs, ... }: {
 
   testScript = { nodes, ... }:
     let
-      etagSystem = "${nodes.webserver.system.build.toplevel}/specialisation/etag";
       justReloadSystem = "${nodes.webserver.system.build.toplevel}/specialisation/config-reload";
       multipleConfigs = "${nodes.webserver.system.build.toplevel}/specialisation/multiple-configs";
     in
@@ -65,33 +48,6 @@ import ./make-test-python.nix ({ pkgs, ... }: {
       webserver.wait_for_open_port(80)
 
 
-      def check_etag(url):
-          etag = webserver.succeed(
-              "curl --fail -v '{}' 2>&1 | sed -n -e \"s/^< [Ee][Tt][Aa][Gg]: *//p\"".format(
-                  url
-              )
-          )
-          etag = etag.replace("\r\n", " ")
-          http_code = webserver.succeed(
-              "curl --fail --silent --show-error -o /dev/null -w \"%{{http_code}}\" --head -H 'If-None-Match: {}' {}".format(
-                  etag, url
-              )
-          )
-          assert int(http_code) == 304, "HTTP code is {}, expected 304".format(http_code)
-          return etag
-
-
-      with subtest("check ETag if serving Nix store paths"):
-          old_etag = check_etag(url)
-          webserver.succeed(
-              "${etagSystem}/bin/switch-to-configuration test >&2"
-          )
-          webserver.sleep(1)
-          new_etag = check_etag(url)
-          assert old_etag != new_etag, "Old ETag {} is the same as {}".format(
-              old_etag, new_etag
-          )
-
       with subtest("config is reloaded on nixos-rebuild switch"):
           webserver.succeed(
               "${justReloadSystem}/bin/switch-to-configuration test >&2"
diff --git a/nixpkgs/nixos/tests/chrony.nix b/nixpkgs/nixos/tests/chrony.nix
new file mode 100644
index 000000000000..578b1e32d50c
--- /dev/null
+++ b/nixpkgs/nixos/tests/chrony.nix
@@ -0,0 +1,31 @@
+import ./make-test-python.nix ({ lib, ... }:
+{
+  name = "chrony";
+
+  meta = {
+    maintainers = with lib.maintainers; [ fpletz ];
+  };
+
+  nodes = {
+    default = {
+      services.chrony.enable = true;
+    };
+    graphene-hardened = {
+      services.chrony.enable = true;
+      services.chrony.enableMemoryLocking = true;
+      environment.memoryAllocator.provider = "graphene-hardened";
+      # dhcpcd privsep is incompatible with graphene-hardened
+      networking.useNetworkd = true;
+    };
+  };
+
+  testScript = {nodes, ...} : let
+    graphene-hardened = nodes.graphene-hardened.system.build.toplevel;
+  in ''
+    default.start()
+    default.wait_for_unit('multi-user.target')
+    default.succeed('systemctl is-active chronyd.service')
+    default.succeed('${graphene-hardened}/bin/switch-to-configuration test')
+    default.succeed('systemctl is-active chronyd.service')
+  '';
+})
diff --git a/nixpkgs/nixos/tests/dnscrypt-wrapper/default.nix b/nixpkgs/nixos/tests/dnscrypt-wrapper/default.nix
index 1bdd064e1130..1c05376e097b 100644
--- a/nixpkgs/nixos/tests/dnscrypt-wrapper/default.nix
+++ b/nixpkgs/nixos/tests/dnscrypt-wrapper/default.nix
@@ -1,4 +1,6 @@
-import ../make-test-python.nix ({ pkgs, ... }: {
+{ lib, pkgs, ... }:
+
+{
   name = "dnscrypt-wrapper";
   meta = with pkgs.lib.maintainers; {
     maintainers = [ rnhmjoj ];
@@ -50,23 +52,23 @@ import ../make-test-python.nix ({ pkgs, ... }: {
         server.wait_for_unit("dnscrypt-wrapper")
         server.wait_for_file("/var/lib/dnscrypt-wrapper/2.dnscrypt-cert.server.key")
         server.wait_for_file("/var/lib/dnscrypt-wrapper/2.dnscrypt-cert.server.crt")
+        almost_expiration = server.succeed("date --date '4days 23 hours 56min'").strip()
 
     with subtest("The client can connect to the server"):
         server.wait_for_unit("tinydns")
         client.wait_for_unit("dnscrypt-proxy2")
-        assert "1.2.3.4" in client.succeed(
+        assert "1.2.3.4" in client.wait_until_succeeds(
             "host it.works"
         ), "The IP address of 'it.works' does not match 1.2.3.4"
 
     with subtest("The server rotates the ephemeral keys"):
         # advance time by a little less than 5 days
-        server.succeed("date -s \"$(date --date '4 days 6 hours')\"")
-        client.succeed("date -s \"$(date --date '4 days 6 hours')\"")
+        server.succeed(f"date -s '{almost_expiration}'")
+        client.succeed(f"date -s '{almost_expiration}'")
         server.wait_for_file("/var/lib/dnscrypt-wrapper/oldkeys")
 
     with subtest("The client can still connect to the server"):
         server.wait_for_unit("dnscrypt-wrapper")
         client.succeed("host it.works")
   '';
-})
-
+}
diff --git a/nixpkgs/nixos/tests/eris-server.nix b/nixpkgs/nixos/tests/eris-server.nix
new file mode 100644
index 000000000000..a50db3afebf5
--- /dev/null
+++ b/nixpkgs/nixos/tests/eris-server.nix
@@ -0,0 +1,23 @@
+import ./make-test-python.nix ({ pkgs, lib, ... }: {
+  name = "eris-server";
+  meta.maintainers = with lib.maintainers; [ ehmry ];
+
+  nodes.server = {
+    environment.systemPackages = [ pkgs.eris-go pkgs.nim.pkgs.eris ];
+    services.eris-server = {
+      enable = true;
+      decode = true;
+      listenHttp = "[::1]:80";
+      backends = [ "badger+file:///var/cache/eris.badger?get&put" ];
+      mountpoint = "/eris";
+    };
+  };
+
+  testScript = ''
+    start_all()
+    server.wait_for_unit("eris-server.service")
+    server.wait_for_open_port(5683)
+    server.wait_for_open_port(80)
+    server.succeed("eriscmd get http://[::1] $(echo 'Hail ERIS!' | eriscmd put coap+tcp://[::1]:5683)")
+  '';
+})
diff --git a/nixpkgs/nixos/tests/fcitx5/config b/nixpkgs/nixos/tests/fcitx5/config
deleted file mode 100644
index cf4334639f1c..000000000000
--- a/nixpkgs/nixos/tests/fcitx5/config
+++ /dev/null
@@ -1,11 +0,0 @@
-[Hotkey]
-EnumerateSkipFirst=False
-
-[Hotkey/TriggerKeys]
-0=Control+space
-
-[Hotkey/EnumerateForwardKeys]
-0=Alt+Shift_L
-
-[Hotkey/EnumerateBackwardKeys]
-0=Alt+Shift_R
diff --git a/nixpkgs/nixos/tests/fcitx5/default.nix b/nixpkgs/nixos/tests/fcitx5/default.nix
index 9b000da48eaf..c113f2e2c052 100644
--- a/nixpkgs/nixos/tests/fcitx5/default.nix
+++ b/nixpkgs/nixos/tests/fcitx5/default.nix
@@ -36,6 +36,43 @@ rec {
         pkgs.fcitx5-m17n
         pkgs.fcitx5-mozc
       ];
+      fcitx5.settings = {
+        globalOptions = {
+          "Hotkey"."EnumerateSkipFirst" = "False";
+          "Hotkey/TriggerKeys"."0" = "Control+space";
+          "Hotkey/EnumerateForwardKeys"."0" = "Alt+Shift_L";
+          "Hotkey/EnumerateBackwardKeys"."0" = "Alt+Shift_R";
+        };
+        inputMethod = {
+          "GroupOrder" = {
+            "0" = "NixOS_test";
+          };
+          "Groups/0" = {
+            "Default Layout" = "us";
+            "DefaultIM" = "wbx";
+            "Name" = "NixOS_test";
+          };
+          "Groups/0/Items/0" = {
+            "Name" = "keyboard-us";
+          };
+          "Groups/0/Items/1" = {
+            "Layout" = "us";
+            "Name" = "wbx";
+          };
+          "Groups/0/Items/2" = {
+            "Layout" = "us";
+            "Name" = "hangul";
+          };
+          "Groups/0/Items/3" = {
+            "Layout" = "us";
+            "Name" = "m17n_sa_harvard-kyoto";
+          };
+          "Groups/0/Items/4" = {
+            "Layout" = "us";
+            "Name" = "mozc";
+          };
+        };
+      };
     };
   };
 
@@ -43,7 +80,6 @@ rec {
     let
       user = nodes.machine.users.users.alice;
       xauth         = "${user.home}/.Xauthority";
-      fcitx_confdir = "${user.home}/.config/fcitx5";
     in
       ''
             start_all()
@@ -56,15 +92,6 @@ rec {
             machine.succeed("su - ${user.name} -c 'kill $(pgrep fcitx5)'")
             machine.sleep(1)
 
-            machine.copy_from_host(
-                "${./profile}",
-                "${fcitx_confdir}/profile",
-            )
-            machine.copy_from_host(
-                "${./config}",
-                "${fcitx_confdir}/config",
-            )
-
             machine.succeed("su - ${user.name} -c 'alacritty >&2 &'")
             machine.succeed("su - ${user.name} -c 'fcitx5 >&2 &'")
             machine.sleep(10)
diff --git a/nixpkgs/nixos/tests/fcitx5/profile b/nixpkgs/nixos/tests/fcitx5/profile
deleted file mode 100644
index 1b48c634e0eb..000000000000
--- a/nixpkgs/nixos/tests/fcitx5/profile
+++ /dev/null
@@ -1,27 +0,0 @@
-[Groups/0]
-Name=NixOS_test
-Default Layout=us
-DefaultIM=wbx
-
-[Groups/0/Items/0]
-Name=keyboard-us
-Layout=
-
-[Groups/0/Items/1]
-Name=wbx
-Layout=us
-
-[Groups/0/Items/2]
-Name=hangul
-Layout=us
-
-[Groups/0/Items/3]
-Name=m17n_sa_harvard-kyoto
-Layout=us
-
-[Groups/0/Items/4]
-Name=mozc
-Layout=us
-
-[GroupOrder]
-0=NixOS_test
diff --git a/nixpkgs/nixos/tests/influxdb2.nix b/nixpkgs/nixos/tests/influxdb2.nix
new file mode 100644
index 000000000000..c9c54b788cc0
--- /dev/null
+++ b/nixpkgs/nixos/tests/influxdb2.nix
@@ -0,0 +1,36 @@
+import ./make-test-python.nix ({ pkgs, ...} : {
+  name = "influxdb2";
+  meta = with pkgs.lib.maintainers; {
+    maintainers = [ offline ];
+  };
+
+  nodes.machine = { lib, ... }: {
+    environment.systemPackages = [ pkgs.influxdb2-cli ];
+    services.influxdb2.enable = true;
+    services.influxdb2.provision = {
+      enable = true;
+      initialSetup = {
+        organization = "default";
+        bucket = "default";
+        passwordFile = pkgs.writeText "admin-pw" "ExAmPl3PA55W0rD";
+        tokenFile = pkgs.writeText "admin-token" "verysecureadmintoken";
+      };
+    };
+  };
+
+  testScript = { nodes, ... }:
+    let
+      tokenArg = "--token verysecureadmintoken";
+    in ''
+      machine.wait_for_unit("influxdb2.service")
+
+      machine.fail("curl --fail -X POST 'http://localhost:8086/api/v2/signin' -u admin:wrongpassword")
+      machine.succeed("curl --fail -X POST 'http://localhost:8086/api/v2/signin' -u admin:ExAmPl3PA55W0rD")
+
+      out = machine.succeed("influx org list ${tokenArg}")
+      assert "default" in out
+
+      out = machine.succeed("influx bucket list ${tokenArg} --org default")
+      assert "default" in out
+    '';
+})
diff --git a/nixpkgs/nixos/tests/jool.nix b/nixpkgs/nixos/tests/jool.nix
new file mode 100644
index 000000000000..6d5ded9b18e0
--- /dev/null
+++ b/nixpkgs/nixos/tests/jool.nix
@@ -0,0 +1,250 @@
+{ system ? builtins.currentSystem,
+  config ? {},
+  pkgs ? import ../.. { inherit system config; }
+}:
+
+with import ../lib/testing-python.nix { inherit system pkgs; };
+
+let
+  inherit (pkgs) lib;
+
+  ipv6Only = {
+    networking.useDHCP = false;
+    networking.interfaces.eth1.ipv4.addresses = lib.mkVMOverride [ ];
+  };
+
+  ipv4Only = {
+    networking.useDHCP = false;
+    networking.interfaces.eth1.ipv6.addresses = lib.mkVMOverride [ ];
+  };
+
+  webserver = ip: msg: {
+    systemd.services.webserver = {
+      description = "Mock webserver";
+      wants = [ "network-online.target" ];
+      wantedBy = [ "multi-user.target" ];
+      serviceConfig.Restart = "always";
+      script = ''
+        while true; do
+        {
+          printf 'HTTP/1.0 200 OK\n'
+          printf 'Content-Length: ${toString (1 + builtins.stringLength msg)}\n'
+          printf '\n${msg}\n\n'
+        } | ${pkgs.libressl.nc}/bin/nc -${toString ip}nvl 80
+        done
+      '';
+    };
+    networking.firewall.allowedTCPPorts = [ 80 ];
+  };
+
+in
+
+{
+  siit = makeTest {
+    # This test simulates the setup described in [1] with two IPv6 and
+    # IPv4-only devices on different subnets communicating through a border
+    # relay running Jool in SIIT mode.
+    # [1]: https://nicmx.github.io/Jool/en/run-vanilla.html
+    name = "jool-siit";
+    meta.maintainers = with lib.maintainers; [ rnhmjoj ];
+
+    # Border relay
+    nodes.relay = { ... }: {
+      imports = [ ../modules/profiles/minimal.nix ];
+      virtualisation.vlans = [ 1 2 ];
+
+      # Enable packet routing
+      boot.kernel.sysctl = {
+        "net.ipv6.conf.all.forwarding" = 1;
+        "net.ipv4.conf.all.forwarding" = 1;
+      };
+
+      networking.useDHCP = false;
+      networking.interfaces = lib.mkVMOverride {
+        eth1.ipv6.addresses = [ { address = "fd::198.51.100.1"; prefixLength = 120; } ];
+        eth2.ipv4.addresses = [ { address = "192.0.2.1";  prefixLength = 24; } ];
+      };
+
+      networking.jool = {
+        enable = true;
+        siit.enable = true;
+        siit.config.global.pool6 = "fd::/96";
+      };
+    };
+
+    # IPv6 only node
+    nodes.alice = { ... }: {
+      imports = [
+        ../modules/profiles/minimal.nix
+        ipv6Only
+        (webserver 6 "Hello, Bob!")
+      ];
+
+      virtualisation.vlans = [ 1 ];
+      networking.interfaces.eth1.ipv6 = {
+        addresses = [ { address = "fd::198.51.100.8"; prefixLength = 120; } ];
+        routes    = [ { address = "fd::192.0.2.0"; prefixLength = 120;
+                        via = "fd::198.51.100.1"; } ];
+      };
+    };
+
+    # IPv4 only node
+    nodes.bob = { ... }: {
+      imports = [
+        ../modules/profiles/minimal.nix
+        ipv4Only
+        (webserver 4 "Hello, Alice!")
+      ];
+
+      virtualisation.vlans = [ 2 ];
+      networking.interfaces.eth1.ipv4 = {
+        addresses = [ { address = "192.0.2.16"; prefixLength = 24; } ];
+        routes    = [ { address = "198.51.100.0"; prefixLength = 24;
+                        via = "192.0.2.1"; } ];
+      };
+    };
+
+    testScript = ''
+      start_all()
+
+      relay.wait_for_unit("jool-siit.service")
+      alice.wait_for_unit("network-addresses-eth1.service")
+      bob.wait_for_unit("network-addresses-eth1.service")
+
+      with subtest("Alice and Bob can't ping each other"):
+        relay.systemctl("stop jool-siit.service")
+        alice.fail("ping -c1 fd::192.0.2.16")
+        bob.fail("ping -c1 198.51.100.8")
+
+      with subtest("Alice and Bob can ping using the relay"):
+        relay.systemctl("start jool-siit.service")
+        alice.wait_until_succeeds("ping -c1 fd::192.0.2.16")
+        bob.wait_until_succeeds("ping -c1 198.51.100.8")
+
+      with subtest("Alice can connect to Bob's webserver"):
+        bob.wait_for_open_port(80)
+        alice.succeed("curl -vvv http://[fd::192.0.2.16] >&2")
+        alice.succeed("curl --fail -s http://[fd::192.0.2.16] | grep -q Alice")
+
+      with subtest("Bob can connect to Alices's webserver"):
+        alice.wait_for_open_port(80)
+        bob.succeed("curl --fail -s http://198.51.100.8 | grep -q Bob")
+    '';
+  };
+
+  nat64 = makeTest {
+    # This test simulates the setup described in [1] with two IPv6-only nodes
+    # (a client and a homeserver) on the LAN subnet and an IPv4 node on the WAN.
+    # The router runs Jool in stateful NAT64 mode, masquarading the LAN and
+    # forwarding ports using static BIB entries.
+    # [1]: https://nicmx.github.io/Jool/en/run-nat64.html
+    name = "jool-nat64";
+    meta.maintainers = with lib.maintainers; [ rnhmjoj ];
+
+    # Router
+    nodes.router = { ... }: {
+      imports = [ ../modules/profiles/minimal.nix ];
+      virtualisation.vlans = [ 1 2 ];
+
+      # Enable packet routing
+      boot.kernel.sysctl = {
+        "net.ipv6.conf.all.forwarding" = 1;
+        "net.ipv4.conf.all.forwarding" = 1;
+      };
+
+      networking.useDHCP = false;
+      networking.interfaces = lib.mkVMOverride {
+        eth1.ipv6.addresses = [ { address = "2001:db8::1"; prefixLength = 96; } ];
+        eth2.ipv4.addresses = [ { address = "203.0.113.1"; prefixLength = 24; } ];
+      };
+
+      networking.jool = {
+        enable = true;
+        nat64.enable = true;
+        nat64.config = {
+          bib = [
+            { # forward HTTP 203.0.113.1 (router) → 2001:db8::9 (homeserver)
+              "protocol"     = "TCP";
+              "ipv4 address" = "203.0.113.1#80";
+              "ipv6 address" = "2001:db8::9#80";
+            }
+          ];
+          pool4 = [
+            # Ports for dynamic translation
+            { protocol =  "TCP";  prefix = "203.0.113.1/32"; "port range" = "40001-65535"; }
+            { protocol =  "UDP";  prefix = "203.0.113.1/32"; "port range" = "40001-65535"; }
+            { protocol = "ICMP";  prefix = "203.0.113.1/32"; "port range" = "40001-65535"; }
+            # Ports for static BIB entries
+            { protocol =  "TCP";  prefix = "203.0.113.1/32"; "port range" = "80"; }
+          ];
+        };
+      };
+    };
+
+    # LAN client (IPv6 only)
+    nodes.client = { ... }: {
+      imports = [ ../modules/profiles/minimal.nix ipv6Only ];
+      virtualisation.vlans = [ 1 ];
+
+      networking.interfaces.eth1.ipv6 = {
+        addresses = [ { address = "2001:db8::8"; prefixLength = 96; } ];
+        routes    = [ { address = "64:ff9b::";   prefixLength = 96;
+                        via = "2001:db8::1"; } ];
+      };
+    };
+
+    # LAN server (IPv6 only)
+    nodes.homeserver = { ... }: {
+      imports = [
+        ../modules/profiles/minimal.nix
+        ipv6Only
+        (webserver 6 "Hello from IPv6!")
+      ];
+
+      virtualisation.vlans = [ 1 ];
+      networking.interfaces.eth1.ipv6 = {
+        addresses = [ { address = "2001:db8::9"; prefixLength = 96; } ];
+        routes    = [ { address = "64:ff9b::";   prefixLength = 96;
+                        via = "2001:db8::1"; } ];
+      };
+    };
+
+    # WAN server (IPv4 only)
+    nodes.server = { ... }: {
+      imports = [
+        ../modules/profiles/minimal.nix
+        ipv4Only
+        (webserver 4 "Hello from IPv4!")
+      ];
+
+      virtualisation.vlans = [ 2 ];
+      networking.interfaces.eth1.ipv4.addresses =
+        [ { address = "203.0.113.16"; prefixLength = 24; } ];
+    };
+
+    testScript = ''
+      start_all()
+
+      for node in [client, homeserver, server]:
+        node.wait_for_unit("network-addresses-eth1.service")
+
+      with subtest("Client can ping the WAN server"):
+        router.wait_for_unit("jool-nat64.service")
+        client.succeed("ping -c1 64:ff9b::203.0.113.16")
+
+      with subtest("Client can connect to the WAN webserver"):
+        server.wait_for_open_port(80)
+        client.succeed("curl --fail -s http://[64:ff9b::203.0.113.16] | grep -q IPv4!")
+
+      with subtest("Router BIB entries are correctly populated"):
+        router.succeed("jool bib display | grep -q 'Dynamic TCP.*2001:db8::8'")
+        router.succeed("jool bib display | grep -q 'Static TCP.*2001:db8::9'")
+
+      with subtest("WAN server can reach the LAN server"):
+        homeserver.wait_for_open_port(80)
+        server.succeed("curl --fail -s http://203.0.113.1 | grep -q IPv6!")
+    '';
+
+  };
+
+}
diff --git a/nixpkgs/nixos/tests/kernel-generic.nix b/nixpkgs/nixos/tests/kernel-generic.nix
index e4a8e06df1ef..e69dd550289c 100644
--- a/nixpkgs/nixos/tests/kernel-generic.nix
+++ b/nixpkgs/nixos/tests/kernel-generic.nix
@@ -42,7 +42,9 @@ let
   };
 
 in mapAttrs (_: lP: testsForLinuxPackages lP) kernels // {
-  inherit testsForLinuxPackages;
+  passthru = {
+    inherit testsForLinuxPackages;
 
-  testsForKernel = kernel: testsForLinuxPackages (pkgs.linuxPackagesFor kernel);
+    testsForKernel = kernel: testsForLinuxPackages (pkgs.linuxPackagesFor kernel);
+  };
 }
diff --git a/nixpkgs/nixos/tests/libvirtd.nix b/nixpkgs/nixos/tests/libvirtd.nix
index b6e604075997..41d06cc9643f 100644
--- a/nixpkgs/nixos/tests/libvirtd.nix
+++ b/nixpkgs/nixos/tests/libvirtd.nix
@@ -11,6 +11,9 @@ import ./make-test-python.nix ({ pkgs, ... }: {
           memorySize = 2048;
 
           libvirtd.enable = true;
+          libvirtd.hooks.qemu.is_working = "${pkgs.writeShellScript "testHook.sh" ''
+            touch /tmp/qemu_hook_is_working
+          ''}";
         };
         boot.supportedFilesystems = [ "zfs" ];
         networking.hostId = "deadbeef"; # needed for zfs
@@ -57,5 +60,9 @@ import ./make-test-python.nix ({ pkgs, ... }: {
       virthost.shutdown()
       virthost.wait_for_unit("multi-user.target")
       virthost.wait_until_succeeds("ping -c 1 nixos")
+
+    with subtest("test if hooks are linked and run"):
+      virthost.succeed("ls /var/lib/libvirt/hooks/qemu.d/is_working")
+      virthost.succeed("ls /tmp/qemu_hook_is_working")
   '';
 })
diff --git a/nixpkgs/nixos/tests/matrix/synapse.nix b/nixpkgs/nixos/tests/matrix/synapse.nix
index 698d67c793e3..98b077469192 100644
--- a/nixpkgs/nixos/tests/matrix/synapse.nix
+++ b/nixpkgs/nixos/tests/matrix/synapse.nix
@@ -65,7 +65,7 @@ in {
 
   nodes = {
     # Since 0.33.0, matrix-synapse doesn't allow underscores in server names
-    serverpostgres = { pkgs, nodes, ... }: let
+    serverpostgres = { pkgs, nodes, config, ... }: let
       mailserverIP = nodes.mailserver.config.networking.primaryIPAddress;
     in
     {
@@ -77,6 +77,11 @@ in {
             name = "psycopg2";
             args.password = "synapse";
           };
+          redis = {
+            enabled = true;
+            host = "localhost";
+            port = config.services.redis.servers.matrix-synapse.port;
+          };
           tls_certificate_path = "${cert}";
           tls_private_key_path = "${key}";
           registration_shared_secret = registrationSharedSecret;
@@ -107,6 +112,11 @@ in {
         '';
       };
 
+      services.redis.servers.matrix-synapse = {
+        enable = true;
+        port = 6380;
+      };
+
       networking.extraHosts = ''
         ${mailserverIP} ${mailerDomain}
       '';
@@ -208,6 +218,9 @@ in {
     serverpostgres.wait_until_succeeds(
         "curl --fail -L --cacert ${ca_pem} https://localhost:8448/"
     )
+    serverpostgres.wait_until_succeeds(
+        "journalctl -u matrix-synapse.service | grep -q 'Connected to redis'"
+    )
     serverpostgres.require_unit_state("postgresql.service")
     serverpostgres.succeed("register_new_matrix_user -u ${testUser} -p ${testPassword} -a -k ${registrationSharedSecret} https://localhost:8448/")
     serverpostgres.succeed("obtain-token-and-register-email")
diff --git a/nixpkgs/nixos/tests/mediamtx.nix b/nixpkgs/nixos/tests/mediamtx.nix
new file mode 100644
index 000000000000..8cacd02631d9
--- /dev/null
+++ b/nixpkgs/nixos/tests/mediamtx.nix
@@ -0,0 +1,57 @@
+import ./make-test-python.nix ({ pkgs, lib, ...} :
+
+{
+  name = "mediamtx";
+  meta.maintainers = with lib.maintainers; [ fpletz ];
+
+  nodes = {
+    machine = { config, ... }: {
+      services.mediamtx = {
+        enable = true;
+        settings = {
+          metrics = true;
+          paths.all.source = "publisher";
+        };
+      };
+
+      systemd.services.rtmp-publish = {
+        description = "Publish an RTMP stream to mediamtx";
+        after = [ "mediamtx.service" ];
+        bindsTo = [ "mediamtx.service" ];
+        wantedBy = [ "multi-user.target" ];
+        serviceConfig = {
+          DynamicUser = true;
+          Restart = "on-failure";
+          RestartSec = "1s";
+          TimeoutStartSec = "10s";
+          ExecStart = "${lib.getBin pkgs.ffmpeg-headless}/bin/ffmpeg -re -f lavfi -i smptebars=size=800x600:rate=10 -c libx264 -f flv rtmp://localhost:1935/test";
+        };
+      };
+
+      systemd.services.rtmp-receive = {
+        description = "Receive an RTMP stream from mediamtx";
+        after = [ "rtmp-publish.service" ];
+        bindsTo = [ "rtmp-publish.service" ];
+        wantedBy = [ "multi-user.target" ];
+        serviceConfig = {
+          DynamicUser = true;
+          Restart = "on-failure";
+          RestartSec = "1s";
+          TimeoutStartSec = "10s";
+          ExecStart = "${lib.getBin pkgs.ffmpeg-headless}/bin/ffmpeg -y -re -i rtmp://localhost:1935/test -f flv /dev/null";
+        };
+      };
+    };
+  };
+
+  testScript = ''
+    start_all()
+
+    machine.wait_for_unit("mediamtx.service")
+    machine.wait_for_unit("rtmp-publish.service")
+    machine.wait_for_unit("rtmp-receive.service")
+    machine.wait_for_open_port(9998)
+    machine.succeed("curl http://localhost:9998/metrics | grep '^rtmp_conns.*state=\"publish\".*1$'")
+    machine.succeed("curl http://localhost:9998/metrics | grep '^rtmp_conns.*state=\"read\".*1$'")
+  '';
+})
diff --git a/nixpkgs/nixos/tests/netdata.nix b/nixpkgs/nixos/tests/netdata.nix
index aea67c29d0d4..c5f7294f79ab 100644
--- a/nixpkgs/nixos/tests/netdata.nix
+++ b/nixpkgs/nixos/tests/netdata.nix
@@ -10,7 +10,7 @@ import ./make-test-python.nix ({ pkgs, ...} : {
     netdata =
       { pkgs, ... }:
         {
-          environment.systemPackages = with pkgs; [ curl jq ];
+          environment.systemPackages = with pkgs; [ curl jq netdata ];
           services.netdata.enable = true;
         };
     };
@@ -34,5 +34,8 @@ import ./make-test-python.nix ({ pkgs, ...} : {
     filter = '[.data[range(10)][.labels | indices("root")[0]]] | add | . > 0'
     cmd = f"curl -s {url} | jq -e '{filter}'"
     netdata.wait_until_succeeds(cmd)
+
+    # check if the control socket is available
+    netdata.succeed("sudo netdatacli ping")
   '';
 })
diff --git a/nixpkgs/nixos/tests/nextcloud/basic.nix b/nixpkgs/nixos/tests/nextcloud/basic.nix
index db5cee9f6584..b7af6d6d7364 100644
--- a/nixpkgs/nixos/tests/nextcloud/basic.nix
+++ b/nixpkgs/nixos/tests/nextcloud/basic.nix
@@ -90,8 +90,8 @@ in {
       test -e graph
       grep "$what" graph >$out || true
     '';
-    nextcloudUsesImagick = findInClosure "imagick" nodes.nextcloud.config.system.build.vm;
-    nextcloudWithoutDoesntUseIt = findInClosure "imagick" nodes.nextcloudWithoutMagick.config.system.build.vm;
+    nextcloudUsesImagick = findInClosure "imagick" nodes.nextcloud.system.build.vm;
+    nextcloudWithoutDoesntUseIt = findInClosure "imagick" nodes.nextcloudWithoutMagick.system.build.vm;
   in ''
     assert open("${nextcloudUsesImagick}").read() != ""
     assert open("${nextcloudWithoutDoesntUseIt}").read() == ""
diff --git a/nixpkgs/nixos/tests/nextcloud/openssl-sse.nix b/nixpkgs/nixos/tests/nextcloud/openssl-sse.nix
index 92beb869eb03..d6ea39c6155a 100644
--- a/nixpkgs/nixos/tests/nextcloud/openssl-sse.nix
+++ b/nixpkgs/nixos/tests/nextcloud/openssl-sse.nix
@@ -49,8 +49,8 @@ in {
       #!${pkgs.runtimeShell}
       echo 'bye' | ${withRcloneEnv3} ${pkgs.rclone}/bin/rclone rcat nextcloud:test-shared-file2
     '';
-    openssl1-node = nodes.nextcloudwithopenssl1.config.system.build.toplevel;
-    openssl3-node = nodes.nextcloudwithopenssl3.config.system.build.toplevel;
+    openssl1-node = nodes.nextcloudwithopenssl1.system.build.toplevel;
+    openssl3-node = nodes.nextcloudwithopenssl3.system.build.toplevel;
   in ''
     nextcloudwithopenssl1.start()
     nextcloudwithopenssl1.wait_for_unit("multi-user.target")
diff --git a/nixpkgs/nixos/tests/nixops/default.nix b/nixpkgs/nixos/tests/nixops/default.nix
index bd00e6143639..b8f747b2a19f 100644
--- a/nixpkgs/nixos/tests/nixops/default.nix
+++ b/nixpkgs/nixos/tests/nixops/default.nix
@@ -14,11 +14,12 @@ let
     # inherit testsForPackage;
   };
 
-  testsForPackage = lib.makeOverridable (args: lib.recurseIntoAttrs {
+  testsForPackage = args: lib.recurseIntoAttrs {
     legacyNetwork = testLegacyNetwork args;
-  });
+    passthru.override = args': testsForPackage (args // args');
+  };
 
-  testLegacyNetwork = { nixopsPkg }: pkgs.nixosTest ({
+  testLegacyNetwork = { nixopsPkg, ... }: pkgs.nixosTest ({
     name = "nixops-legacy-network";
     nodes = {
       deployer = { config, lib, nodes, pkgs, ... }: {
@@ -52,7 +53,7 @@ let
           chmod 0400 ~/.ssh/id_ed25519
         '';
         serverNetworkJSON = pkgs.writeText "server-network.json"
-          (builtins.toJSON nodes.server.config.system.build.networkConfig);
+          (builtins.toJSON nodes.server.system.build.networkConfig);
       in
       ''
         import shlex
diff --git a/nixpkgs/nixos/tests/opensnitch.nix b/nixpkgs/nixos/tests/opensnitch.nix
new file mode 100644
index 000000000000..d84e4e0a935b
--- /dev/null
+++ b/nixpkgs/nixos/tests/opensnitch.nix
@@ -0,0 +1,62 @@
+import ./make-test-python.nix ({ pkgs, ... }: {
+  name = "opensnitch";
+
+  meta = with pkgs.lib.maintainers; {
+    maintainers = [ onny ];
+  };
+
+  nodes = {
+    server =
+      { ... }: {
+        networking.firewall.allowedTCPPorts = [ 80 ];
+        services.caddy = {
+          enable = true;
+          virtualHosts."localhost".extraConfig = ''
+            respond "Hello, world!"
+          '';
+        };
+      };
+
+    clientBlocked =
+      { ... }: {
+        services.opensnitch = {
+          enable = true;
+          settings.DefaultAction = "deny";
+        };
+      };
+
+    clientAllowed =
+      { ... }: {
+        services.opensnitch = {
+          enable = true;
+          settings.DefaultAction = "deny";
+          rules = {
+            opensnitch = {
+              name = "curl";
+              enabled = true;
+              action = "allow";
+              duration = "always";
+              operator = {
+                type ="simple";
+                sensitive = false;
+                operand = "process.path";
+                data = "${pkgs.curl}/bin/curl";
+              };
+            };
+          };
+        };
+      };
+  };
+
+  testScript = ''
+    start_all()
+    server.wait_for_unit("caddy.service")
+    server.wait_for_open_port(80)
+
+    clientBlocked.wait_for_unit("opensnitchd.service")
+    clientBlocked.fail("curl http://server")
+
+    clientAllowed.wait_for_unit("opensnitchd.service")
+    clientAllowed.succeed("curl http://server")
+  '';
+})
diff --git a/nixpkgs/nixos/tests/pantheon.nix b/nixpkgs/nixos/tests/pantheon.nix
index 653fcc6edad1..dee6964644c5 100644
--- a/nixpkgs/nixos/tests/pantheon.nix
+++ b/nixpkgs/nixos/tests/pantheon.nix
@@ -60,6 +60,9 @@ import ./make-test-python.nix ({ pkgs, lib, ...} :
     with subtest("Open elementary terminal"):
         machine.execute("su - ${user.name} -c 'DISPLAY=:0 io.elementary.terminal >&2 &'")
         machine.wait_for_window("io.elementary.terminal")
+
+    with subtest("Check if gala has ever coredumped"):
+        machine.fail("coredumpctl --json=short | grep gala")
         machine.sleep(20)
         machine.screenshot("screen")
   '';
diff --git a/nixpkgs/nixos/tests/prometheus-exporters.nix b/nixpkgs/nixos/tests/prometheus-exporters.nix
index 23740dd98e3d..d86f8ac634e8 100644
--- a/nixpkgs/nixos/tests/prometheus-exporters.nix
+++ b/nixpkgs/nixos/tests/prometheus-exporters.nix
@@ -307,6 +307,23 @@ let
       '';
     };
 
+    idrac = {
+      exporterConfig = {
+        enable = true;
+        port = 9348;
+        configuration = {
+          hosts = {
+            default = { username = "username"; password = "password"; };
+          };
+        };
+      };
+      exporterTest = ''
+        wait_for_unit("prometheus-idrac-exporter.service")
+        wait_for_open_port(9348)
+        wait_until_succeeds("curl localhost:9348")
+      '';
+    };
+
     influxdb = {
       exporterConfig = {
         enable = true;
@@ -1183,7 +1200,7 @@ let
       };
       exporterTest = ''
         wait_until_succeeds(
-            'journalctl -eu prometheus-smartctl-exporter.service -o cat | grep "Device unavailable"'
+            'journalctl -eu prometheus-smartctl-exporter.service -o cat | grep "Unable to detect device type"'
         )
       '';
     };
diff --git a/nixpkgs/nixos/tests/switch-test.nix b/nixpkgs/nixos/tests/switch-test.nix
index f44dede7fef4..53595ae7d3e2 100644
--- a/nixpkgs/nixos/tests/switch-test.nix
+++ b/nixpkgs/nixos/tests/switch-test.nix
@@ -1,6 +1,6 @@
 # Test configuration switching.
 
-import ./make-test-python.nix ({ pkgs, ...} : let
+import ./make-test-python.nix ({ lib, pkgs, ...} : let
 
   # Simple service that can either be socket-activated or that will
   # listen on port 1234 if not socket-activated.
@@ -279,6 +279,28 @@ in {
           systemd.services.test-service.unitConfig.RefuseManualStart = true;
         };
 
+        unitWithTemplate.configuration = {
+          systemd.services."instantiated@".serviceConfig = {
+            Type = "oneshot";
+            RemainAfterExit = true;
+            ExecStart = "${pkgs.coreutils}/bin/true";
+            ExecReload = "${pkgs.coreutils}/bin/true";
+          };
+          systemd.services."instantiated@one" = {
+            wantedBy = [ "multi-user.target" ];
+            overrideStrategy = "asDropin";
+          };
+          systemd.services."instantiated@two" = {
+            wantedBy = [ "multi-user.target" ];
+            overrideStrategy = "asDropin";
+          };
+        };
+
+        unitWithTemplateModified.configuration = {
+          imports = [ unitWithTemplate.configuration ];
+          systemd.services."instantiated@".serviceConfig.X-Test = "test";
+        };
+
         restart-and-reload-by-activation-script.configuration = {
           systemd.services = rec {
             simple-service = {
@@ -290,29 +312,50 @@ in {
                 ExecReload = "${pkgs.coreutils}/bin/true";
               };
             };
+            "templated-simple-service@" = simple-service;
+            "templated-simple-service@instance".overrideStrategy = "asDropin";
 
             simple-restart-service = simple-service // {
               stopIfChanged = false;
             };
+            "templated-simple-restart-service@" = simple-restart-service;
+            "templated-simple-restart-service@instance".overrideStrategy = "asDropin";
 
             simple-reload-service = simple-service // {
               reloadIfChanged = true;
             };
+            "templated-simple-reload-service@" = simple-reload-service;
+            "templated-simple-reload-service@instance".overrideStrategy = "asDropin";
 
             no-restart-service = simple-service // {
               restartIfChanged = false;
             };
+            "templated-no-restart-service@" = no-restart-service;
+            "templated-no-restart-service@instance".overrideStrategy = "asDropin";
 
             reload-triggers = simple-service // {
               wantedBy = [ "multi-user.target" ];
             };
+            "templated-reload-triggers@" = simple-service;
+            "templated-reload-triggers@instance" = {
+              overrideStrategy = "asDropin";
+              wantedBy = [ "multi-user.target" ];
+            };
 
             reload-triggers-and-restart-by-as = simple-service;
+            "templated-reload-triggers-and-restart-by-as@" = reload-triggers-and-restart-by-as;
+            "templated-reload-triggers-and-restart-by-as@instance".overrideStrategy = "asDropin";
 
             reload-triggers-and-restart = simple-service // {
               stopIfChanged = false; # easier to check for this
               wantedBy = [ "multi-user.target" ];
             };
+            "templated-reload-triggers-and-restart@" = simple-service;
+            "templated-reload-triggers-and-restart@instance" = {
+              overrideStrategy = "asDropin";
+              stopIfChanged = false; # easier to check for this
+              wantedBy = [ "multi-user.target" ];
+            };
           };
 
           system.activationScripts.restart-and-reload-test = {
@@ -332,12 +375,20 @@ in {
               simple-reload-service.service
               no-restart-service.service
               reload-triggers-and-restart-by-as.service
+              templated-simple-service@instance.service
+              templated-simple-restart-service@instance.service
+              templated-simple-reload-service@instance.service
+              templated-no-restart-service@instance.service
+              templated-reload-triggers-and-restart-by-as@instance.service
               EOF
 
               cat <<EOF >> "$g"
               reload-triggers.service
               reload-triggers-and-restart-by-as.service
               reload-triggers-and-restart.service
+              templated-reload-triggers@instance.service
+              templated-reload-triggers-and-restart-by-as@instance.service
+              templated-reload-triggers-and-restart@instance.service
               EOF
             '';
           };
@@ -346,6 +397,10 @@ in {
         restart-and-reload-by-activation-script-modified.configuration = {
           imports = [ restart-and-reload-by-activation-script.configuration ];
           systemd.services.reload-triggers-and-restart.serviceConfig.X-Modified = "test";
+          systemd.services."templated-reload-triggers-and-restart@instance" = {
+            overrideStrategy = "asDropin";
+            serviceConfig.X-Modified = "test";
+          };
         };
 
         simple-socket.configuration = {
@@ -507,6 +562,10 @@ in {
       set -o pipefail
       exec env -i "$@" | tee /dev/stderr
     '';
+
+    # Returns a comma separated representation of the given list in sorted
+    # order, that matches the output format of switch-to-configuration.pl
+    sortedUnits = xs: lib.concatStringsSep ", " (builtins.sort builtins.lessThan xs);
   in /* python */ ''
     def switch_to_specialisation(system, name, action="test", fail=False):
         if name == "":
@@ -733,6 +792,16 @@ in {
         assert_contains(out, "\nstarting the following units: required-service.service\n")
         assert_lacks(out, "the following new units were started:")
 
+        # Ensure templated units are restarted when the base unit changes
+        switch_to_specialisation("${machine}", "unitWithTemplate")
+        out = switch_to_specialisation("${machine}", "unitWithTemplateModified")
+        assert_contains(out, "stopping the following units: instantiated@one.service, instantiated@two.service\n")
+        assert_lacks(out, "NOT restarting the following changed units:")
+        assert_lacks(out, "reloading the following units:")
+        assert_lacks(out, "\nrestarting the following units:")
+        assert_contains(out, "\nstarting the following units: instantiated@one.service, instantiated@two.service\n")
+        assert_lacks(out, "the following new units were started:")
+
     with subtest("failing units"):
         # Let the simple service fail
         switch_to_specialisation("${machine}", "simpleServiceModified")
@@ -896,15 +965,62 @@ in {
         assert_lacks(out, "NOT restarting the following changed units:")
         assert_lacks(out, "reloading the following units:")
         assert_lacks(out, "restarting the following units:")
-        assert_contains(out, "\nstarting the following units: no-restart-service.service, reload-triggers-and-restart-by-as.service, simple-reload-service.service, simple-restart-service.service, simple-service.service\n")
-        assert_contains(out, "the following new units were started: no-restart-service.service, reload-triggers-and-restart-by-as.service, reload-triggers-and-restart.service, reload-triggers.service, simple-reload-service.service, simple-restart-service.service, simple-service.service\n")
+        assert_contains(out, "\nstarting the following units: ${sortedUnits [
+          "no-restart-service.service"
+          "reload-triggers-and-restart-by-as.service"
+          "simple-reload-service.service"
+          "simple-restart-service.service"
+          "simple-service.service"
+          "templated-no-restart-service@instance.service"
+          "templated-reload-triggers-and-restart-by-as@instance.service"
+          "templated-simple-reload-service@instance.service"
+          "templated-simple-restart-service@instance.service"
+          "templated-simple-service@instance.service"
+        ]}\n")
+        assert_contains(out, "the following new units were started: ${sortedUnits [
+          "no-restart-service.service"
+          "reload-triggers-and-restart-by-as.service"
+          "reload-triggers-and-restart.service"
+          "reload-triggers.service"
+          "simple-reload-service.service"
+          "simple-restart-service.service"
+          "simple-service.service"
+          "system-templated\\\\x2dno\\\\x2drestart\\\\x2dservice.slice"
+          "system-templated\\\\x2dreload\\\\x2dtriggers.slice"
+          "system-templated\\\\x2dreload\\\\x2dtriggers\\\\x2dand\\\\x2drestart.slice"
+          "system-templated\\\\x2dreload\\\\x2dtriggers\\\\x2dand\\\\x2drestart\\\\x2dby\\\\x2das.slice"
+          "system-templated\\\\x2dsimple\\\\x2dreload\\\\x2dservice.slice"
+          "system-templated\\\\x2dsimple\\\\x2drestart\\\\x2dservice.slice"
+          "system-templated\\\\x2dsimple\\\\x2dservice.slice"
+          "templated-no-restart-service@instance.service"
+          "templated-reload-triggers-and-restart-by-as@instance.service"
+          "templated-reload-triggers-and-restart@instance.service"
+          "templated-reload-triggers@instance.service"
+          "templated-simple-reload-service@instance.service"
+          "templated-simple-restart-service@instance.service"
+          "templated-simple-service@instance.service"
+        ]}\n")
         # Switch to the same system where the example services get restarted
         # and reloaded by the activation script
         out = switch_to_specialisation("${machine}", "restart-and-reload-by-activation-script")
         assert_lacks(out, "stopping the following units:")
         assert_lacks(out, "NOT restarting the following changed units:")
-        assert_contains(out, "reloading the following units: reload-triggers-and-restart.service, reload-triggers.service, simple-reload-service.service\n")
-        assert_contains(out, "restarting the following units: reload-triggers-and-restart-by-as.service, simple-restart-service.service, simple-service.service\n")
+        assert_contains(out, "reloading the following units: ${sortedUnits [
+          "reload-triggers-and-restart.service"
+          "reload-triggers.service"
+          "simple-reload-service.service"
+          "templated-reload-triggers-and-restart@instance.service"
+          "templated-reload-triggers@instance.service"
+          "templated-simple-reload-service@instance.service"
+        ]}\n")
+        assert_contains(out, "restarting the following units: ${sortedUnits [
+          "reload-triggers-and-restart-by-as.service"
+          "simple-restart-service.service"
+          "simple-service.service"
+          "templated-reload-triggers-and-restart-by-as@instance.service"
+          "templated-simple-restart-service@instance.service"
+          "templated-simple-service@instance.service"
+        ]}\n")
         assert_lacks(out, "\nstarting the following units:")
         assert_lacks(out, "the following new units were started:")
         # Switch to the same system and see if the service gets restarted when it's modified
@@ -912,16 +1028,44 @@ in {
         out = switch_to_specialisation("${machine}", "restart-and-reload-by-activation-script-modified")
         assert_lacks(out, "stopping the following units:")
         assert_lacks(out, "NOT restarting the following changed units:")
-        assert_contains(out, "reloading the following units: reload-triggers.service, simple-reload-service.service\n")
-        assert_contains(out, "restarting the following units: reload-triggers-and-restart-by-as.service, reload-triggers-and-restart.service, simple-restart-service.service, simple-service.service\n")
+        assert_contains(out, "reloading the following units: ${sortedUnits [
+          "reload-triggers.service"
+          "simple-reload-service.service"
+          "templated-reload-triggers@instance.service"
+          "templated-simple-reload-service@instance.service"
+        ]}\n")
+        assert_contains(out, "restarting the following units: ${sortedUnits [
+          "reload-triggers-and-restart-by-as.service"
+          "reload-triggers-and-restart.service"
+          "simple-restart-service.service"
+          "simple-service.service"
+          "templated-reload-triggers-and-restart-by-as@instance.service"
+          "templated-reload-triggers-and-restart@instance.service"
+          "templated-simple-restart-service@instance.service"
+          "templated-simple-service@instance.service"
+        ]}\n")
         assert_lacks(out, "\nstarting the following units:")
         assert_lacks(out, "the following new units were started:")
         # The same, but in dry mode
         out = switch_to_specialisation("${machine}", "restart-and-reload-by-activation-script", action="dry-activate")
         assert_lacks(out, "would stop the following units:")
         assert_lacks(out, "would NOT stop the following changed units:")
-        assert_contains(out, "would reload the following units: reload-triggers.service, simple-reload-service.service\n")
-        assert_contains(out, "would restart the following units: reload-triggers-and-restart-by-as.service, reload-triggers-and-restart.service, simple-restart-service.service, simple-service.service\n")
+        assert_contains(out, "would reload the following units: ${sortedUnits [
+          "reload-triggers.service"
+          "simple-reload-service.service"
+          "templated-reload-triggers@instance.service"
+          "templated-simple-reload-service@instance.service"
+        ]}\n")
+        assert_contains(out, "would restart the following units: ${sortedUnits [
+          "reload-triggers-and-restart-by-as.service"
+          "reload-triggers-and-restart.service"
+          "simple-restart-service.service"
+          "simple-service.service"
+          "templated-reload-triggers-and-restart-by-as@instance.service"
+          "templated-reload-triggers-and-restart@instance.service"
+          "templated-simple-restart-service@instance.service"
+          "templated-simple-service@instance.service"
+        ]}\n")
         assert_lacks(out, "\nwould start the following units:")
 
     with subtest("socket-activated services"):
diff --git a/nixpkgs/nixos/tests/systemd-boot.nix b/nixpkgs/nixos/tests/systemd-boot.nix
index 84a4da5aa6ec..c1f8637989e3 100644
--- a/nixpkgs/nixos/tests/systemd-boot.nix
+++ b/nixpkgs/nixos/tests/systemd-boot.nix
@@ -118,14 +118,11 @@ in
     nodes.machine = { pkgs, lib, ... }: {
       imports = [ common ];
       boot.loader.systemd-boot.memtest86.enable = true;
-      nixpkgs.config.allowUnfreePredicate = pkg: builtins.elem (lib.getName pkg) [
-        "memtest86-efi"
-      ];
     };
 
     testScript = ''
       machine.succeed("test -e /boot/loader/entries/memtest86.conf")
-      machine.succeed("test -e /boot/efi/memtest86/BOOTX64.efi")
+      machine.succeed("test -e /boot/efi/memtest86/memtest.efi")
     '';
   };
 
@@ -152,15 +149,12 @@ in
       imports = [ common ];
       boot.loader.systemd-boot.memtest86.enable = true;
       boot.loader.systemd-boot.memtest86.entryFilename = "apple.conf";
-      nixpkgs.config.allowUnfreePredicate = pkg: builtins.elem (lib.getName pkg) [
-        "memtest86-efi"
-      ];
     };
 
     testScript = ''
       machine.fail("test -e /boot/loader/entries/memtest86.conf")
       machine.succeed("test -e /boot/loader/entries/apple.conf")
-      machine.succeed("test -e /boot/efi/memtest86/BOOTX64.efi")
+      machine.succeed("test -e /boot/efi/memtest86/memtest.efi")
     '';
   };
 
diff --git a/nixpkgs/nixos/tests/web-apps/mastodon/remote-postgresql.nix b/nixpkgs/nixos/tests/web-apps/mastodon/remote-postgresql.nix
index 2fd3983e13ec..715477191bfb 100644
--- a/nixpkgs/nixos/tests/web-apps/mastodon/remote-postgresql.nix
+++ b/nixpkgs/nixos/tests/web-apps/mastodon/remote-postgresql.nix
@@ -13,7 +13,7 @@ let
 in
 {
   name = "mastodon-remote-postgresql";
-  meta.maintainers = with pkgs.lib.maintainers; [ erictapen izorkin turion ];
+  meta.maintainers = with pkgs.lib.maintainers; [ erictapen izorkin ];
 
   nodes = {
     database = {
diff --git a/nixpkgs/nixos/tests/web-servers/agate.nix b/nixpkgs/nixos/tests/web-servers/agate.nix
index e8d789a9ca44..0de27b6f7d8d 100644
--- a/nixpkgs/nixos/tests/web-servers/agate.nix
+++ b/nixpkgs/nixos/tests/web-servers/agate.nix
@@ -20,7 +20,7 @@
     geminiserver.wait_for_open_port(1965)
 
     with subtest("check is serving over gemini"):
-      response = geminiserver.succeed("${pkgs.gmni}/bin/gmni -j once -i -N gemini://localhost:1965")
+      response = geminiserver.succeed("${pkgs.gemget}/bin/gemget --header -o - gemini://localhost:1965")
       print(response)
       assert "Hello NixOS!" in response
   '';
diff --git a/nixpkgs/nixos/tests/wrappers.nix b/nixpkgs/nixos/tests/wrappers.nix
index 08c1ad0b6b99..391e9b42b45b 100644
--- a/nixpkgs/nixos/tests/wrappers.nix
+++ b/nixpkgs/nixos/tests/wrappers.nix
@@ -55,6 +55,10 @@ in
         out = machine.succeed(cmd_as_regular(cmd)).strip()
         assert out == expected, "Expected {0} to output {1}, but got {2}".format(cmd, expected, out)
 
+      def test_as_regular_in_userns_mapped_as_root(cmd, expected):
+        out = machine.succeed(f"su -l regular -c '${pkgs.util-linux}/bin/unshare -rm {cmd}'").strip()
+        assert out == expected, "Expected {0} to output {1}, but got {2}".format(cmd, expected, out)
+
       test_as_regular('${busybox pkgs}/bin/busybox id -u', '${toString userUid}')
       test_as_regular('${busybox pkgs}/bin/busybox id -ru', '${toString userUid}')
       test_as_regular('${busybox pkgs}/bin/busybox id -g', '${toString usersGid}')
@@ -70,10 +74,27 @@ in
       test_as_regular('/run/wrappers/bin/sgid_root_busybox id -g', '0')
       test_as_regular('/run/wrappers/bin/sgid_root_busybox id -rg', '${toString usersGid}')
 
+      test_as_regular_in_userns_mapped_as_root('/run/wrappers/bin/suid_root_busybox id -u', '0')
+      test_as_regular_in_userns_mapped_as_root('/run/wrappers/bin/suid_root_busybox id -ru', '0')
+      test_as_regular_in_userns_mapped_as_root('/run/wrappers/bin/suid_root_busybox id -g', '0')
+      test_as_regular_in_userns_mapped_as_root('/run/wrappers/bin/suid_root_busybox id -rg', '0')
+
+      test_as_regular_in_userns_mapped_as_root('/run/wrappers/bin/sgid_root_busybox id -u', '0')
+      test_as_regular_in_userns_mapped_as_root('/run/wrappers/bin/sgid_root_busybox id -ru', '0')
+      test_as_regular_in_userns_mapped_as_root('/run/wrappers/bin/sgid_root_busybox id -g', '0')
+      test_as_regular_in_userns_mapped_as_root('/run/wrappers/bin/sgid_root_busybox id -rg', '0')
+
       # We are only testing the permitted set, because it's easiest to look at with capsh.
       machine.fail(cmd_as_regular('${pkgs.libcap}/bin/capsh --has-p=CAP_CHOWN'))
       machine.fail(cmd_as_regular('${pkgs.libcap}/bin/capsh --has-p=CAP_SYS_ADMIN'))
       machine.succeed(cmd_as_regular('/run/wrappers/bin/capsh_with_chown --has-p=CAP_CHOWN'))
       machine.fail(cmd_as_regular('/run/wrappers/bin/capsh_with_chown --has-p=CAP_SYS_ADMIN'))
+
+      # test a few "attacks" against which the wrapper protects itself
+      machine.succeed("cp /run/wrappers/bin/suid_root_busybox{,.real} /tmp/")
+      machine.fail(cmd_as_regular("/tmp/suid_root_busybox id -u"))
+
+      machine.succeed("chmod u+s,a+w /run/wrappers/bin/suid_root_busybox")
+      machine.fail(cmd_as_regular("/run/wrappers/bin/suid_root_busybox id -u"))
     '';
 })