about summary refs log tree commit diff
path: root/nixos/tests
diff options
context:
space:
mode:
Diffstat (limited to 'nixos/tests')
-rw-r--r--nixos/tests/all-tests.nix23
-rw-r--r--nixos/tests/appliance-repart-image.nix116
-rw-r--r--nixos/tests/atuin.nix2
-rw-r--r--nixos/tests/binary-cache.nix10
-rw-r--r--nixos/tests/bpftune.nix20
-rw-r--r--nixos/tests/budgie.nix10
-rw-r--r--nixos/tests/buildkite-agents.nix6
-rw-r--r--nixos/tests/caddy.nix51
-rw-r--r--nixos/tests/cage.nix2
-rw-r--r--nixos/tests/chrony.nix31
-rw-r--r--nixos/tests/common/gpg-keyring.nix21
-rw-r--r--nixos/tests/common/resolver.nix2
-rw-r--r--nixos/tests/cups-pdf.nix2
-rw-r--r--nixos/tests/curl-impersonate.nix157
-rw-r--r--nixos/tests/deepin.nix4
-rw-r--r--nixos/tests/dnscrypt-wrapper/default.nix14
-rw-r--r--nixos/tests/eris-server.nix23
-rw-r--r--nixos/tests/fail2ban.nix18
-rw-r--r--nixos/tests/fontconfig-default-fonts.nix4
-rw-r--r--nixos/tests/freshrss-http-auth.nix20
-rw-r--r--nixos/tests/gitea.nix31
-rw-r--r--nixos/tests/gnome-flashback.nix14
-rw-r--r--nixos/tests/hibernate.nix142
-rw-r--r--nixos/tests/homepage-dashboard.nix14
-rw-r--r--nixos/tests/initrd-network-ssh/default.nix18
-rw-r--r--nixos/tests/installed-tests/default.nix1
-rw-r--r--nixos/tests/installed-tests/upower.nix9
-rw-r--r--nixos/tests/installer-systemd-stage-1.nix2
-rw-r--r--nixos/tests/installer.nix110
-rw-r--r--nixos/tests/installer/flake.nix20
-rw-r--r--nixos/tests/jenkins.nix4
-rw-r--r--nixos/tests/k3s/single-node.nix14
-rw-r--r--nixos/tests/kanidm.nix9
-rw-r--r--nixos/tests/kernel-generic.nix15
-rw-r--r--nixos/tests/keyd.nix2
-rw-r--r--nixos/tests/keymap.nix37
-rw-r--r--nixos/tests/lemmy.nix14
-rw-r--r--nixos/tests/libvirtd.nix7
-rw-r--r--nixos/tests/lxd-ui.nix35
-rw-r--r--nixos/tests/maestral.nix5
-rw-r--r--nixos/tests/miniflux.nix6
-rw-r--r--nixos/tests/miriway.nix2
-rw-r--r--nixos/tests/mumble.nix4
-rw-r--r--nixos/tests/n8n.nix3
-rw-r--r--nixos/tests/netdata.nix5
-rw-r--r--nixos/tests/networking.nix78
-rw-r--r--nixos/tests/nextcloud/basic.nix10
-rw-r--r--nixos/tests/nextcloud/openssl-sse.nix6
-rw-r--r--nixos/tests/nextcloud/with-declarative-redis-and-secrets.nix14
-rw-r--r--nixos/tests/nextcloud/with-mysql-and-memcached.nix2
-rw-r--r--nixos/tests/nextcloud/with-postgresql-and-redis.nix2
-rw-r--r--nixos/tests/nginx-proxyprotocol/default.nix4
-rw-r--r--nixos/tests/nginx-status-page.nix72
-rw-r--r--nixos/tests/nixos-test-driver/busybox.nix16
-rw-r--r--nixos/tests/non-default-filesystems.nix41
-rw-r--r--nixos/tests/noto-fonts-cjk-qt-default-weight.nix2
-rw-r--r--nixos/tests/noto-fonts.nix4
-rw-r--r--nixos/tests/osquery.nix52
-rw-r--r--nixos/tests/paperless.nix14
-rw-r--r--nixos/tests/pgbouncer.nix61
-rw-r--r--nixos/tests/plasma-bigscreen.nix9
-rw-r--r--nixos/tests/plasma5-systemd-start.nix8
-rw-r--r--nixos/tests/plasma5.nix14
-rw-r--r--nixos/tests/prometheus-exporters.nix17
-rw-r--r--nixos/tests/retroarch.nix4
-rw-r--r--nixos/tests/samba-wsdd.nix6
-rw-r--r--nixos/tests/sddm.nix14
-rw-r--r--nixos/tests/sftpgo.nix10
-rw-r--r--nixos/tests/sing-box.nix5
-rw-r--r--nixos/tests/sway.nix2
-rw-r--r--nixos/tests/switch-test.nix47
-rw-r--r--nixos/tests/syncthing-init.nix8
-rw-r--r--nixos/tests/syncthing-no-settings.nix18
-rw-r--r--nixos/tests/systemd-initrd-networkd-ssh.nix17
-rw-r--r--nixos/tests/systemd-initrd-swraid.nix12
-rw-r--r--nixos/tests/systemd-networkd-dhcpserver-static-leases.nix2
-rw-r--r--nixos/tests/systemd-networkd-dhcpserver.nix2
-rw-r--r--nixos/tests/systemd-networkd-ipv6-prefix-delegation.nix6
-rw-r--r--nixos/tests/systemd-nspawn.nix22
-rw-r--r--nixos/tests/systemd-shutdown.nix2
-rw-r--r--nixos/tests/systemd-sysupdate.nix66
-rw-r--r--nixos/tests/terminal-emulators.nix7
-rw-r--r--nixos/tests/twingate.nix14
-rw-r--r--nixos/tests/typesense.nix23
-rw-r--r--nixos/tests/vscodium.nix2
-rw-r--r--nixos/tests/web-apps/peering-manager.nix2
-rw-r--r--nixos/tests/wpa_supplicant.nix228
-rw-r--r--nixos/tests/wrappers.nix21
88 files changed, 1579 insertions, 446 deletions
diff --git a/nixos/tests/all-tests.nix b/nixos/tests/all-tests.nix
index d62a4dd49fbf8..530447b997863 100644
--- a/nixos/tests/all-tests.nix
+++ b/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
@@ -89,6 +89,7 @@ in {
     extra-python-packages = handleTest ./nixos-test-driver/extra-python-packages.nix {};
     lib-extend = handleTestOn [ "x86_64-linux" "aarch64-linux" ] ./nixos-test-driver/lib-extend.nix {};
     node-name = runTest ./nixos-test-driver/node-name.nix;
+    busybox = runTest ./nixos-test-driver/busybox.nix;
   };
 
   # NixOS vm tests and non-vm unit tests
@@ -111,6 +112,7 @@ in {
   anuko-time-tracker = handleTest ./anuko-time-tracker.nix {};
   apcupsd = handleTest ./apcupsd.nix {};
   apfs = runTest ./apfs.nix;
+  appliance-repart-image = runTest ./appliance-repart-image.nix;
   apparmor = handleTest ./apparmor.nix {};
   atd = handleTest ./atd.nix {};
   atop = handleTest ./atop.nix {};
@@ -138,6 +140,7 @@ in {
   borgbackup = handleTest ./borgbackup.nix {};
   botamusique = handleTest ./botamusique.nix {};
   bpf = handleTestOn ["x86_64-linux" "aarch64-linux"] ./bpf.nix {};
+  bpftune = handleTest ./bpftune.nix {};
   breitbandmessung = handleTest ./breitbandmessung.nix {};
   brscan5 = handleTest ./brscan5.nix {};
   btrbk = handleTest ./btrbk.nix {};
@@ -164,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 {};
@@ -202,6 +206,7 @@ in {
   couchdb = handleTest ./couchdb.nix {};
   cri-o = handleTestOn ["aarch64-linux" "x86_64-linux"] ./cri-o.nix {};
   cups-pdf = handleTest ./cups-pdf.nix {};
+  curl-impersonate = handleTest ./curl-impersonate.nix {};
   custom-ca = handleTest ./custom-ca.nix {};
   croc = handleTest ./croc.nix {};
   darling = handleTest ./darling.nix {};
@@ -213,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 {};
@@ -248,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 { };
@@ -256,6 +262,7 @@ in {
   etebase-server = handleTest ./etebase-server.nix {};
   etesync-dav = handleTest ./etesync-dav.nix {};
   evcc = handleTest ./evcc.nix {};
+  fail2ban = handleTest ./fail2ban.nix { };
   fakeroute = handleTest ./fakeroute.nix {};
   fancontrol = handleTest ./fancontrol.nix {};
   fcitx5 = handleTest ./fcitx5 {};
@@ -266,6 +273,7 @@ in {
   firefox-devedition = handleTest ./firefox.nix { firefoxPackage = pkgs.firefox-devedition; };
   firefox-esr    = handleTest ./firefox.nix { firefoxPackage = pkgs.firefox-esr; }; # used in `tested` job
   firefox-esr-102 = handleTest ./firefox.nix { firefoxPackage = pkgs.firefox-esr-102; };
+  firefox-esr-115 = handleTest ./firefox.nix { firefoxPackage = pkgs.firefox-esr-115; };
   firejail = handleTest ./firejail.nix {};
   firewall = handleTest ./firewall.nix { nftables = false; };
   firewall-nftables = handleTest ./firewall.nix { nftables = true; };
@@ -335,6 +343,7 @@ in {
   hbase3 = handleTest ./hbase.nix { package=pkgs.hbase3; };
   hedgedoc = handleTest ./hedgedoc.nix {};
   herbstluftwm = handleTest ./herbstluftwm.nix {};
+  homepage-dashboard = handleTest ./homepage-dashboard.nix {};
   installed-tests = pkgs.recurseIntoAttrs (handleTest ./installed-tests {});
   invidious = handleTest ./invidious.nix {};
   oci-containers = handleTestOn ["aarch64-linux" "x86_64-linux"] ./oci-containers.nix {};
@@ -434,6 +443,7 @@ in {
   lxd = handleTest ./lxd.nix {};
   lxd-nftables = handleTest ./lxd-nftables.nix {};
   lxd-image-server = handleTest ./lxd-image-server.nix {};
+  lxd-ui = handleTest ./lxd-ui.nix {};
   #logstash = handleTest ./logstash.nix {};
   lorri = handleTest ./lorri/default.nix {};
   maddy = discoverTests (import ./maddy { inherit handleTest; });
@@ -527,11 +537,12 @@ in {
   nginx-http3 = handleTest ./nginx-http3.nix {};
   nginx-modsecurity = handleTest ./nginx-modsecurity.nix {};
   nginx-njs = handleTest ./nginx-njs.nix {};
+  nginx-proxyprotocol = handleTest ./nginx-proxyprotocol {};
   nginx-pubhtml = handleTest ./nginx-pubhtml.nix {};
   nginx-sandbox = handleTestOn ["x86_64-linux"] ./nginx-sandbox.nix {};
   nginx-sso = handleTest ./nginx-sso.nix {};
+  nginx-status-page = handleTest ./nginx-status-page.nix {};
   nginx-variants = handleTest ./nginx-variants.nix {};
-  nginx-proxyprotocol = handleTest ./nginx-proxyprotocol {};
   nifi = handleTestOn ["x86_64-linux"] ./web-apps/nifi.nix {};
   nitter = handleTest ./nitter.nix {};
   nix-ld = handleTest ./nix-ld.nix {};
@@ -572,6 +583,7 @@ in {
   openvscode-server = handleTest ./openvscode-server.nix {};
   orangefs = handleTest ./orangefs.nix {};
   os-prober = handleTestOn ["x86_64-linux"] ./os-prober.nix {};
+  osquery = handleTestOn ["x86_64-linux"] ./osquery.nix {};
   osrm-backend = handleTest ./osrm-backend.nix {};
   overlayfs = handleTest ./overlayfs.nix {};
   pacemaker = handleTest ./pacemaker.nix {};
@@ -593,6 +605,7 @@ in {
   peertube = handleTestOn ["x86_64-linux"] ./web-apps/peertube.nix {};
   peroxide = handleTest ./peroxide.nix {};
   pgadmin4 = handleTest ./pgadmin4.nix {};
+  pgbouncer = handleTest ./pgbouncer.nix {};
   pgjwt = handleTest ./pgjwt.nix {};
   pgmanage = handleTest ./pgmanage.nix {};
   phosh = handleTest ./phosh.nix {};
@@ -722,6 +735,7 @@ in {
   switchTest = handleTest ./switch-test.nix {};
   sympa = handleTest ./sympa.nix {};
   syncthing = handleTest ./syncthing.nix {};
+  syncthing-no-settings = handleTest ./syncthing-no-settings.nix {};
   syncthing-init = handleTest ./syncthing-init.nix {};
   syncthing-relay = handleTest ./syncthing-relay.nix {};
   systemd = handleTest ./systemd.nix {};
@@ -762,6 +776,7 @@ in {
   systemd-portabled = handleTest ./systemd-portabled.nix {};
   systemd-repart = handleTest ./systemd-repart.nix {};
   systemd-shutdown = handleTest ./systemd-shutdown.nix {};
+  systemd-sysupdate = runTest ./systemd-sysupdate.nix;
   systemd-timesyncd = handleTest ./systemd-timesyncd.nix {};
   systemd-user-tmpfiles-rules = handleTest ./systemd-user-tmpfiles-rules.nix {};
   systemd-misc = handleTest ./systemd-misc.nix {};
@@ -799,6 +814,8 @@ in {
   tuptime = handleTest ./tuptime.nix {};
   turbovnc-headless-server = handleTest ./turbovnc-headless-server.nix {};
   tuxguitar = handleTest ./tuxguitar.nix {};
+  twingate = runTest ./twingate.nix;
+  typesense = handleTest ./typesense.nix {};
   ucarp = handleTest ./ucarp.nix {};
   udisks2 = handleTest ./udisks2.nix {};
   ulogd = handleTest ./ulogd.nix {};
diff --git a/nixos/tests/appliance-repart-image.nix b/nixos/tests/appliance-repart-image.nix
new file mode 100644
index 0000000000000..3f256db846214
--- /dev/null
+++ b/nixos/tests/appliance-repart-image.nix
@@ -0,0 +1,116 @@
+# Tests building and running a GUID Partition Table (GPT) appliance image.
+# "Appliance" here means that the image does not contain the normal NixOS
+# infrastructure of a system profile and cannot be re-built via
+# `nixos-rebuild`.
+
+{ lib, ... }:
+
+let
+  rootPartitionLabel = "root";
+
+  bootLoaderConfigPath = "/loader/entries/nixos.conf";
+  kernelPath = "/EFI/nixos/kernel.efi";
+  initrdPath = "/EFI/nixos/initrd.efi";
+in
+{
+  name = "appliance-gpt-image";
+
+  meta.maintainers = with lib.maintainers; [ nikstur ];
+
+  nodes.machine = { config, lib, pkgs, ... }: {
+
+    imports = [ ../modules/image/repart.nix ];
+
+    virtualisation.directBoot.enable = false;
+    virtualisation.mountHostNixStore = false;
+    virtualisation.useEFIBoot = true;
+
+    # Disable boot loaders because we install one "manually".
+    # TODO(raitobezarius): revisit this when #244907 lands
+    boot.loader.grub.enable = false;
+
+    virtualisation.fileSystems = lib.mkForce {
+      "/" = {
+        device = "/dev/disk/by-partlabel/${rootPartitionLabel}";
+        fsType = "ext4";
+      };
+    };
+
+    image.repart = {
+      name = "appliance-gpt-image";
+      partitions = {
+        "esp" = {
+          contents =
+            let
+              efiArch = config.nixpkgs.hostPlatform.efiArch;
+            in
+            {
+              "/EFI/BOOT/BOOT${lib.toUpper efiArch}.EFI".source =
+                "${pkgs.systemd}/lib/systemd/boot/efi/systemd-boot${efiArch}.efi";
+
+              # TODO: create an abstraction for Boot Loader Specification (BLS) entries.
+              "${bootLoaderConfigPath}".source = pkgs.writeText "nixos.conf" ''
+                title NixOS
+                linux ${kernelPath}
+                initrd ${initrdPath}
+                options init=${config.system.build.toplevel}/init ${toString config.boot.kernelParams}
+              '';
+
+              "${kernelPath}".source =
+                "${config.boot.kernelPackages.kernel}/${config.system.boot.loader.kernelFile}";
+
+              "${initrdPath}".source =
+                "${config.system.build.initialRamdisk}/${config.system.boot.loader.initrdFile}";
+            };
+          repartConfig = {
+            Type = "esp";
+            Format = "vfat";
+            # Minimize = "guess" seems to not work very vell for vfat
+            # partitons. It's better to set a sensible default instead. The
+            # aarch64 kernel seems to generally be a little bigger than the
+            # x86_64 kernel. To stay on the safe side, leave some more slack
+            # for every platform other than x86_64.
+            SizeMinBytes = if config.nixpkgs.hostPlatform.isx86_64 then "64M" else "96M";
+          };
+        };
+        "root" = {
+          storePaths = [ config.system.build.toplevel ];
+          repartConfig = {
+            Type = "root";
+            Format = config.fileSystems."/".fsType;
+            Label = rootPartitionLabel;
+            Minimize = "guess";
+          };
+        };
+      };
+    };
+  };
+
+  testScript = { nodes, ... }: ''
+    import os
+    import subprocess
+    import tempfile
+
+    tmp_disk_image = tempfile.NamedTemporaryFile()
+
+    subprocess.run([
+      "${nodes.machine.virtualisation.qemu.package}/bin/qemu-img",
+      "create",
+      "-f",
+      "qcow2",
+      "-b",
+      "${nodes.machine.system.build.image}/image.raw",
+      "-F",
+      "raw",
+      tmp_disk_image.name,
+    ])
+
+    # Set NIX_DISK_IMAGE so that the qemu script finds the right disk image.
+    os.environ['NIX_DISK_IMAGE'] = tmp_disk_image.name
+
+    bootctl_status = machine.succeed("bootctl status")
+    assert "${bootLoaderConfigPath}" in bootctl_status
+    assert "${kernelPath}" in bootctl_status
+    assert "${initrdPath}" in bootctl_status
+  '';
+}
diff --git a/nixos/tests/atuin.nix b/nixos/tests/atuin.nix
index 9f23a3cf6713f..3164c83c683dc 100644
--- a/nixos/tests/atuin.nix
+++ b/nixos/tests/atuin.nix
@@ -14,6 +14,8 @@ in
     server =
       { ... }:
       {
+        services.postgresql.enable = true;
+
         services.atuin = {
           enable = true;
           port = testPort;
diff --git a/nixos/tests/binary-cache.nix b/nixos/tests/binary-cache.nix
index 0809e59e5a115..bc1c6fb9a2671 100644
--- a/nixos/tests/binary-cache.nix
+++ b/nixos/tests/binary-cache.nix
@@ -1,16 +1,14 @@
-import ./make-test-python.nix ({ lib, ... }:
-
-with lib;
+import ./make-test-python.nix ({ lib, pkgs, ... }:
 
 {
   name = "binary-cache";
-  meta.maintainers = with maintainers; [ thomasjm ];
+  meta.maintainers = with lib.maintainers; [ thomasjm ];
 
   nodes.machine =
     { pkgs, ... }: {
       imports = [ ../modules/installer/cd-dvd/channel.nix ];
-      environment.systemPackages = with pkgs; [python3];
-      system.extraDependencies = with pkgs; [hello.inputDerivation];
+      environment.systemPackages = [ pkgs.python3 ];
+      system.extraDependencies = [ pkgs.hello.inputDerivation ];
       nix.extraOptions = ''
         experimental-features = nix-command
       '';
diff --git a/nixos/tests/bpftune.nix b/nixos/tests/bpftune.nix
new file mode 100644
index 0000000000000..c17bbcd110920
--- /dev/null
+++ b/nixos/tests/bpftune.nix
@@ -0,0 +1,20 @@
+import ./make-test-python.nix ({ lib, pkgs, ... }: {
+
+  name = "bpftune";
+
+  meta = {
+    maintainers = with lib.maintainers; [ nickcao ];
+  };
+
+  nodes = {
+    machine = { pkgs, ... }: {
+      services.bpftune.enable = true;
+    };
+  };
+
+  testScript = ''
+    machine.wait_for_unit("bpftune.service")
+    machine.wait_for_console_text("bpftune works")
+  '';
+
+})
diff --git a/nixos/tests/budgie.nix b/nixos/tests/budgie.nix
index b1b0e618c884c..34ee1e303de9f 100644
--- a/nixos/tests/budgie.nix
+++ b/nixos/tests/budgie.nix
@@ -32,7 +32,15 @@ import ./make-test-python.nix ({ pkgs, lib, ... }: {
     in
     ''
       with subtest("Wait for login"):
-          machine.wait_for_x()
+          # wait_for_x() checks graphical-session.target, which is expected to be
+          # inactive on Budgie before #228946 (i.e. systemd managed gnome-session) is
+          # done on upstream.
+          # https://github.com/BuddiesOfBudgie/budgie-desktop/blob/v10.7.2/src/session/budgie-desktop.in#L16
+          #
+          # Previously this was unconditionally touched by xsessionWrapper but was
+          # changed in #233981 (we have Budgie:GNOME in XDG_CURRENT_DESKTOP).
+          # machine.wait_for_x()
+          machine.wait_until_succeeds('journalctl -t gnome-session-binary --grep "Entering running state"')
           machine.wait_for_file("${user.home}/.Xauthority")
           machine.succeed("xauth merge ${user.home}/.Xauthority")
 
diff --git a/nixos/tests/buildkite-agents.nix b/nixos/tests/buildkite-agents.nix
index 2c5593323e873..a5abfdb5e2e5d 100644
--- a/nixos/tests/buildkite-agents.nix
+++ b/nixos/tests/buildkite-agents.nix
@@ -1,10 +1,8 @@
-import ./make-test-python.nix ({ pkgs, ... }:
+import ./make-test-python.nix ({ lib, pkgs, ... }:
 
 {
   name = "buildkite-agent";
-  meta = with pkgs.lib.maintainers; {
-    maintainers = [ flokli ];
-  };
+  meta.maintainers = with lib.maintainers; [ flokli ];
 
   nodes.machine = { pkgs, ... }: {
     services.buildkite-agents = {
diff --git a/nixos/tests/caddy.nix b/nixos/tests/caddy.nix
index c4abd33d0395d..238091ec606f5 100644
--- a/nixos/tests/caddy.nix
+++ b/nixos/tests/caddy.nix
@@ -20,22 +20,7 @@ import ./make-test-python.nix ({ pkgs, ... }: {
           }
         }
       '';
-
-      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"
-              ''
-            }
-          }
-        '';
-      };
+      services.caddy.enableReload = true;
 
       specialisation.config-reload.configuration = {
         services.caddy.extraConfig = ''
@@ -54,9 +39,8 @@ import ./make-test-python.nix ({ pkgs, ... }: {
 
   testScript = { nodes, ... }:
     let
-      etagSystem = "${nodes.webserver.config.system.build.toplevel}/specialisation/etag";
-      justReloadSystem = "${nodes.webserver.config.system.build.toplevel}/specialisation/config-reload";
-      multipleConfigs = "${nodes.webserver.config.system.build.toplevel}/specialisation/multiple-configs";
+      justReloadSystem = "${nodes.webserver.system.build.toplevel}/specialisation/config-reload";
+      multipleConfigs = "${nodes.webserver.system.build.toplevel}/specialisation/multiple-configs";
     in
     ''
       url = "http://localhost/example.html"
@@ -64,38 +48,13 @@ 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"
           )
           webserver.wait_for_open_port(8080)
+          webserver.fail("journalctl -u caddy | grep -q -i stopped")
+          webserver.succeed("journalctl -u caddy | grep -q -i reloaded")
 
       with subtest("multiple configs are correctly merged"):
           webserver.succeed(
diff --git a/nixos/tests/cage.nix b/nixos/tests/cage.nix
index db1b7854673df..3b49185124f3e 100644
--- a/nixos/tests/cage.nix
+++ b/nixos/tests/cage.nix
@@ -11,7 +11,7 @@ import ./make-test-python.nix ({ pkgs, ...} :
   {
     imports = [ ./common/user-account.nix ];
 
-    fonts.fonts = with pkgs; [ dejavu_fonts ];
+    fonts.packages = with pkgs; [ dejavu_fonts ];
 
     services.cage = {
       enable = true;
diff --git a/nixos/tests/chrony.nix b/nixos/tests/chrony.nix
new file mode 100644
index 0000000000000..578b1e32d50c9
--- /dev/null
+++ b/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/nixos/tests/common/gpg-keyring.nix b/nixos/tests/common/gpg-keyring.nix
new file mode 100644
index 0000000000000..fb8d07b1183e0
--- /dev/null
+++ b/nixos/tests/common/gpg-keyring.nix
@@ -0,0 +1,21 @@
+{ pkgs, ... }:
+
+pkgs.runCommand "gpg-keyring" { nativeBuildInputs = [ pkgs.gnupg ]; } ''
+  mkdir -p $out
+  export GNUPGHOME=$out
+  cat > foo <<EOF
+    %echo Generating a basic OpenPGP key
+    %no-protection
+    Key-Type: EdDSA
+    Key-Curve: ed25519
+    Name-Real: Bob Foobar
+    Name-Email: bob@foo.bar
+    Expire-Date: 0
+    # Do a commit here, so that we can later print "done"
+    %commit
+    %echo done
+  EOF
+  gpg --batch --generate-key foo
+  rm $out/S.gpg-agent $out/S.gpg-agent.*
+  gpg --export bob@foo.bar -a > $out/pubkey.gpg
+''
diff --git a/nixos/tests/common/resolver.nix b/nixos/tests/common/resolver.nix
index 3ddf730668c4f..609058a7374a5 100644
--- a/nixos/tests/common/resolver.nix
+++ b/nixos/tests/common/resolver.nix
@@ -63,7 +63,7 @@
             matched = builtins.match "[ \t]+(${reHost})(.*)" str;
             continue = lib.singleton (lib.head matched)
                     ++ matchAliases (lib.last matched);
-          in if matched == null then [] else continue;
+          in lib.optional (matched != null) continue;
 
           matchLine = str: let
             result = builtins.match "[ \t]*(${reIp})[ \t]+(${reHost})(.*)" str;
diff --git a/nixos/tests/cups-pdf.nix b/nixos/tests/cups-pdf.nix
index 957b0296a755b..5602193b0408a 100644
--- a/nixos/tests/cups-pdf.nix
+++ b/nixos/tests/cups-pdf.nix
@@ -4,7 +4,7 @@ import ./make-test-python.nix ({ lib, pkgs, ... }: {
   nodes.machine = { pkgs, ... }: {
     imports = [ ./common/user-account.nix ];
     environment.systemPackages = [ pkgs.poppler_utils ];
-    fonts.fonts = [ pkgs.dejavu_fonts ];  # yields more OCR-able pdf
+    fonts.packages = [ pkgs.dejavu_fonts ];  # yields more OCR-able pdf
     services.printing.cups-pdf.enable = true;
     services.printing.cups-pdf.instances = {
       opt = {};
diff --git a/nixos/tests/curl-impersonate.nix b/nixos/tests/curl-impersonate.nix
new file mode 100644
index 0000000000000..7954e9e5584c4
--- /dev/null
+++ b/nixos/tests/curl-impersonate.nix
@@ -0,0 +1,157 @@
+/*
+  Test suite for curl-impersonate
+
+  Abstract:
+    Uses the test suite from the curl-impersonate source repo which:
+
+    1. Performs requests with libcurl and captures the TLS client-hello
+       packets with tcpdump to compare against known-good signatures
+    2. Spins up an nghttpd2 server to test client HTTP/2 headers against
+       known-good headers
+
+    See https://github.com/lwthiker/curl-impersonate/tree/main/tests/signatures
+    for details.
+
+  Notes:
+    - We need to have our own web server running because the tests expect to be able
+      to hit domains like wikipedia.org and the sandbox has no internet
+    - We need to be able to do (verifying) TLS handshakes without internet access.
+      We do that by creating a trusted CA and issuing a cert that includes
+      all of the test domains as subject-alternative names and then spoofs the
+      hostnames in /etc/hosts.
+*/
+
+import ./make-test-python.nix ({ pkgs, lib, ... }: let
+  # Update with domains in TestImpersonate.TEST_URLS if needed from:
+  # https://github.com/lwthiker/curl-impersonate/blob/main/tests/test_impersonate.py
+  domains = [
+    "www.wikimedia.org"
+    "www.wikipedia.org"
+    "www.mozilla.org"
+    "www.apache.org"
+    "www.kernel.org"
+    "git-scm.com"
+  ];
+
+  tls-certs = let
+    # Configure CA with X.509 v3 extensions that would be trusted by curl
+    ca-cert-conf = pkgs.writeText "curl-impersonate-ca.cnf" ''
+      basicConstraints = critical, CA:TRUE
+      subjectKeyIdentifier = hash
+      authorityKeyIdentifier = keyid:always, issuer:always
+      keyUsage = critical, cRLSign, digitalSignature, keyCertSign
+    '';
+
+    # Configure leaf certificate with X.509 v3 extensions that would be trusted
+    # by curl and set subject-alternative names for test domains
+    tls-cert-conf = pkgs.writeText "curl-impersonate-tls.cnf" ''
+      basicConstraints = critical, CA:FALSE
+      subjectKeyIdentifier = hash
+      authorityKeyIdentifier = keyid:always, issuer:always
+      keyUsage = critical, nonRepudiation, digitalSignature, keyEncipherment, keyAgreement
+      extendedKeyUsage = critical, serverAuth
+      subjectAltName = @alt_names
+
+      [alt_names]
+      ${lib.concatStringsSep "\n" (lib.imap0 (idx: domain: "DNS.${toString idx} = ${domain}") domains)}
+    '';
+  in pkgs.runCommand "curl-impersonate-test-certs" {
+    nativeBuildInputs = [ pkgs.openssl ];
+  } ''
+    # create CA certificate and key
+    openssl req -newkey rsa:4096 -keyout ca-key.pem -out ca-csr.pem -nodes -subj '/CN=curl-impersonate-ca.nixos.test'
+    openssl x509 -req -sha512 -in ca-csr.pem -key ca-key.pem -out ca.pem -extfile ${ca-cert-conf} -days 36500
+    openssl x509 -in ca.pem -text
+
+    # create server certificate and key
+    openssl req -newkey rsa:4096 -keyout key.pem -out csr.pem -nodes -subj '/CN=curl-impersonate.nixos.test'
+    openssl x509 -req -sha512 -in csr.pem -CA ca.pem -CAkey ca-key.pem -CAcreateserial -out cert.pem -extfile ${tls-cert-conf} -days 36500
+    openssl x509 -in cert.pem -text
+
+    # output CA cert and server cert and key
+    mkdir -p $out
+    cp key.pem cert.pem ca.pem $out
+  '';
+
+  # Test script
+  curl-impersonate-test = let
+    # Build miniature libcurl client used by test driver
+    minicurl = pkgs.runCommandCC "minicurl" {
+      buildInputs = [ pkgs.curl ];
+    } ''
+      mkdir -p $out/bin
+      $CC -Wall -Werror -o $out/bin/minicurl ${pkgs.curl-impersonate.src}/tests/minicurl.c `curl-config --libs`
+    '';
+  in pkgs.writeShellScript "curl-impersonate-test" ''
+    set -euxo pipefail
+
+    # Test driver requirements
+    export PATH="${with pkgs; lib.makeBinPath [
+      bash
+      coreutils
+      python3Packages.pytest
+      nghttp2
+      tcpdump
+    ]}"
+    export PYTHONPATH="${with pkgs.python3Packages; makePythonPath [
+      pyyaml
+      pytest-asyncio
+      dpkt
+    ]}"
+
+    # Prepare test root prefix
+    mkdir -p usr/{bin,lib}
+    cp -rs ${pkgs.curl-impersonate}/* ${minicurl}/* usr/
+
+    cp -r ${pkgs.curl-impersonate.src}/tests ./
+
+    # Run tests
+    cd tests
+    pytest . --install-dir ../usr --capture-interface eth1
+  '';
+in {
+  name = "curl-impersonate";
+
+  meta = with lib.maintainers; {
+    maintainers = [ lilyinstarlight ];
+  };
+
+  nodes = {
+    web = { nodes, pkgs, lib, config, ... }: {
+      networking.firewall.allowedTCPPorts = [ 80 443 ];
+
+      services = {
+        nginx = {
+          enable = true;
+          virtualHosts."curl-impersonate.nixos.test" = {
+            default = true;
+            addSSL = true;
+            sslCertificate = "${tls-certs}/cert.pem";
+            sslCertificateKey = "${tls-certs}/key.pem";
+          };
+        };
+      };
+    };
+
+    curl = { nodes, pkgs, lib, config, ... }: {
+      networking.extraHosts = lib.concatStringsSep "\n" (map (domain: "${nodes.web.networking.primaryIPAddress}  ${domain}") domains);
+
+      security.pki.certificateFiles = [ "${tls-certs}/ca.pem" ];
+    };
+  };
+
+  testScript = { nodes, ... }: ''
+    start_all()
+
+    with subtest("Wait for network"):
+        web.wait_for_unit("network-online.target")
+        curl.wait_for_unit("network-online.target")
+
+    with subtest("Wait for web server"):
+        web.wait_for_unit("nginx.service")
+        web.wait_for_open_port(443)
+
+    with subtest("Run curl-impersonate tests"):
+        curl.succeed("${curl-impersonate-test}")
+  '';
+})
diff --git a/nixos/tests/deepin.nix b/nixos/tests/deepin.nix
index acec27ca1c42a..7b2e2430f31c7 100644
--- a/nixos/tests/deepin.nix
+++ b/nixos/tests/deepin.nix
@@ -1,9 +1,7 @@
 import ./make-test-python.nix ({ pkgs, lib, ... }: {
   name = "deepin";
 
-  meta = with lib; {
-    maintainers = teams.deepin.members;
-  };
+  meta.maintainers = lib.teams.deepin.members;
 
   nodes.machine = { ... }: {
     imports = [
diff --git a/nixos/tests/dnscrypt-wrapper/default.nix b/nixos/tests/dnscrypt-wrapper/default.nix
index 1bdd064e1130c..1c05376e097b3 100644
--- a/nixos/tests/dnscrypt-wrapper/default.nix
+++ b/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/nixos/tests/eris-server.nix b/nixos/tests/eris-server.nix
new file mode 100644
index 0000000000000..a50db3afebf5f
--- /dev/null
+++ b/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/nixos/tests/fail2ban.nix b/nixos/tests/fail2ban.nix
new file mode 100644
index 0000000000000..c3708575b702b
--- /dev/null
+++ b/nixos/tests/fail2ban.nix
@@ -0,0 +1,18 @@
+import ./make-test-python.nix ({ pkgs, ... }: {
+  name = "fail2ban";
+
+  nodes.machine = _: {
+    services.fail2ban = {
+      enable = true;
+      bantime-increment.enable = true;
+    };
+
+    services.openssh.enable = true;
+  };
+
+  testScript = ''
+    machine.wait_for_unit("multi-user.target")
+
+    machine.wait_for_unit("fail2ban")
+  '';
+})
diff --git a/nixos/tests/fontconfig-default-fonts.nix b/nixos/tests/fontconfig-default-fonts.nix
index 664afc9bf44c4..d8c6ea0f721b5 100644
--- a/nixos/tests/fontconfig-default-fonts.nix
+++ b/nixos/tests/fontconfig-default-fonts.nix
@@ -7,8 +7,8 @@ import ./make-test-python.nix ({ lib, ... }:
   ];
 
   nodes.machine = { config, pkgs, ... }: {
-    fonts.enableDefaultFonts = true; # Background fonts
-    fonts.fonts = with pkgs; [
+    fonts.enableDefaultPackages = true; # Background fonts
+    fonts.packages = with pkgs; [
       noto-fonts-emoji
       cantarell-fonts
       twitter-color-emoji
diff --git a/nixos/tests/freshrss-http-auth.nix b/nixos/tests/freshrss-http-auth.nix
new file mode 100644
index 0000000000000..d0ec3da316896
--- /dev/null
+++ b/nixos/tests/freshrss-http-auth.nix
@@ -0,0 +1,20 @@
+import ./make-test-python.nix ({ lib, pkgs, ... }: {
+  name = "freshrss";
+  meta.maintainers = with lib.maintainers; [ mattchrist ];
+
+  nodes.machine = { pkgs, ... }: {
+    services.freshrss = {
+      enable = true;
+      baseUrl = "http://localhost";
+      dataDir = "/srv/freshrss";
+      authType = "http_auth";
+    };
+  };
+
+  testScript = ''
+    machine.wait_for_unit("multi-user.target")
+    machine.wait_for_open_port(80)
+    response = machine.succeed("curl -vvv -s -H 'Host: freshrss' -H 'Remote-User: testuser' http://127.0.0.1:80/i/")
+    assert 'Account: testuser' in response, "http_auth method didn't work."
+  '';
+})
diff --git a/nixos/tests/gitea.nix b/nixos/tests/gitea.nix
index 2285bb5a42529..b747659de8298 100644
--- a/nixos/tests/gitea.nix
+++ b/nixos/tests/gitea.nix
@@ -37,9 +37,25 @@ let
           package = giteaPackage;
           settings.service.DISABLE_REGISTRATION = true;
           settings."repository.signing".SIGNING_KEY = signingPrivateKeyId;
+          settings.actions.ENABLED = true;
         };
         environment.systemPackages = [ giteaPackage pkgs.gnupg pkgs.jq ];
         services.openssh.enable = true;
+
+        specialisation.runner = {
+          inheritParentConfig = true;
+
+          configuration.services.gitea-actions-runner.instances."test" = {
+            enable = true;
+            name = "ci";
+            url = "http://localhost:3000";
+            labels = [
+              # don't require docker/podman
+              "native:host"
+            ];
+            tokenFile = "/var/lib/gitea/runner_token";
+          };
+        };
       };
       client1 = { config, pkgs, ... }: {
         environment.systemPackages = [ pkgs.git ];
@@ -49,8 +65,9 @@ let
       };
     };
 
-    testScript = let
+    testScript = { nodes, ... }: let
       inherit (import ./ssh-keys.nix pkgs) snakeOilPrivateKey snakeOilPublicKey;
+      serverSystem = nodes.server.system.build.toplevel;
     in ''
       GIT_SSH_COMMAND = "ssh -i $HOME/.ssh/privk -o StrictHostKeyChecking=no"
       REPO = "gitea@server:test/repo"
@@ -121,14 +138,18 @@ let
       client2.succeed(f"GIT_SSH_COMMAND='{GIT_SSH_COMMAND}' git clone {REPO}")
       client2.succeed('test "$(cat repo/testfile | xargs echo -n)" = "hello world"')
 
-      server.succeed(
+      server.wait_until_succeeds(
           'test "$(curl http://localhost:3000/api/v1/repos/test/repo/commits '
           + '-H "Accept: application/json" | jq length)" = "1"'
       )
 
-      client1.shutdown()
-      client2.shutdown()
-      server.shutdown()
+      with subtest("Testing runner registration"):
+          server.succeed(
+              "su -l gitea -c 'GITEA_WORK_DIR=/var/lib/gitea gitea actions generate-runner-token' | sed 's/^/TOKEN=/' | tee /var/lib/gitea/runner_token"
+          )
+          server.succeed("${serverSystem}/specialisation/runner/bin/switch-to-configuration test")
+          server.wait_for_unit("gitea-runner-test.service")
+          server.succeed("journalctl -o cat -u gitea-runner-test.service | grep -q 'Runner registered successfully'")
     '';
   });
 in
diff --git a/nixos/tests/gnome-flashback.nix b/nixos/tests/gnome-flashback.nix
index 70569db797ec9..876d36477c1a9 100644
--- a/nixos/tests/gnome-flashback.nix
+++ b/nixos/tests/gnome-flashback.nix
@@ -4,7 +4,7 @@ import ./make-test-python.nix ({ pkgs, lib, ...} : {
 
   nodes.machine = { nodes, ... }:
     let
-      user = nodes.machine.config.users.users.alice;
+      user = nodes.machine.users.users.alice;
     in
 
     { imports = [ ./common/user-account.nix ];
@@ -27,12 +27,20 @@ import ./make-test-python.nix ({ pkgs, lib, ...} : {
     };
 
   testScript = { nodes, ... }: let
-    user = nodes.machine.config.users.users.alice;
+    user = nodes.machine.users.users.alice;
     uid = toString user.uid;
     xauthority = "/run/user/${uid}/gdm/Xauthority";
   in ''
       with subtest("Login to GNOME Flashback with GDM"):
-          machine.wait_for_x()
+          # wait_for_x() checks graphical-session.target, which is expected to be
+          # inactive on gnome-flashback before #228946 (i.e. systemd managed
+          # gnome-session) is done.
+          # https://github.com/NixOS/nixpkgs/pull/208060
+          #
+          # Previously this was unconditionally touched by xsessionWrapper but was
+          # changed in #233981 (we have GNOME-Flashback:GNOME in XDG_CURRENT_DESKTOP).
+          # machine.wait_for_x()
+          machine.wait_until_succeeds('journalctl -t gnome-session-binary --grep "Entering running state"')
           # Wait for alice to be logged in"
           machine.wait_for_unit("default.target", "${user.name}")
           machine.wait_for_file("${xauthority}")
diff --git a/nixos/tests/hibernate.nix b/nixos/tests/hibernate.nix
index cd8c436ee2b37..296aa9ba68b92 100644
--- a/nixos/tests/hibernate.nix
+++ b/nixos/tests/hibernate.nix
@@ -8,128 +8,48 @@
 
 with import ../lib/testing-python.nix { inherit system pkgs; };
 
-let
-  # System configuration of the installed system, which is used for the actual
-  # hibernate testing.
-  installedConfig = with pkgs.lib; {
-    imports = [
-      ../modules/testing/test-instrumentation.nix
-      ../modules/profiles/qemu-guest.nix
-      ../modules/profiles/minimal.nix
-    ];
-
-    hardware.enableAllFirmware = mkForce false;
-    documentation.nixos.enable = false;
-    boot.loader.grub.device = "/dev/vda";
-
-    systemd.services.backdoor.conflicts = [ "sleep.target" ];
-
-    powerManagement.resumeCommands = "systemctl --no-block restart backdoor.service";
-
-    fileSystems."/" = {
-      device = "/dev/vda2";
-      fsType = "ext3";
-    };
-    swapDevices = mkOverride 0 [ { device = "/dev/vda1"; } ];
-    boot.resumeDevice = mkIf systemdStage1 "/dev/vda1";
-    boot.initrd.systemd = mkIf systemdStage1 {
-      enable = true;
-      emergencyAccess = true;
-    };
-  };
-  installedSystem = (import ../lib/eval-config.nix {
-    inherit system;
-    modules = [ installedConfig ];
-  }).config.system.build.toplevel;
-in makeTest {
+makeTest {
   name = "hibernate";
 
   nodes = {
-    # System configuration used for installing the installedConfig from above.
     machine = { config, lib, pkgs, ... }: {
       imports = [
-        ../modules/profiles/installation-device.nix
-        ../modules/profiles/base.nix
         ./common/auto-format-root-device.nix
       ];
 
-      nix.settings = {
-        substituters = lib.mkForce [];
-        hashed-mirrors = null;
-        connect-timeout = 1;
-      };
+      systemd.services.backdoor.conflicts = [ "sleep.target" ];
+      powerManagement.resumeCommands = "systemctl --no-block restart backdoor.service";
 
-      virtualisation.diskSize = 8 * 1024;
-      virtualisation.emptyDiskImages = [
-        # Small root disk for installer
-        512
-      ];
-      virtualisation.rootDevice = "/dev/vdb";
+      virtualisation.emptyDiskImages = [ (2 * config.virtualisation.memorySize) ];
+      virtualisation.useNixStoreImage = true;
+
+      swapDevices = lib.mkOverride 0 [ { device = "/dev/vdc"; options = [ "x-systemd.makefs" ]; } ];
+      boot.resumeDevice = "/dev/vdc";
+      boot.initrd.systemd.enable = systemdStage1;
     };
   };
 
-  # 9P doesn't support reconnection to virtio transport after a hibernation.
-  # Therefore, machine just hangs on any Nix store access.
-  # To avoid this, we install NixOS onto a temporary disk with everything we need
-  # included into the store.
-
-  testScript =
-    ''
-      def create_named_machine(name):
-          machine = create_machine(
-              {
-                  "qemuFlags": "-cpu max ${
-                    if system == "x86_64-linux" then "-m 1024"
-                    else "-m 768 -enable-kvm -machine virt,gic-version=host"}",
-                  "hdaInterface": "virtio",
-                  "hda": "vm-state-machine/machine.qcow2",
-                  "name": name,
-              }
-          )
-          driver.machines.append(machine)
-          return machine
-
-
-      # Install NixOS
-      machine.start()
-      machine.succeed(
-          # Partition /dev/vda
-          "flock /dev/vda parted --script /dev/vda -- mklabel msdos"
-          + " mkpart primary linux-swap 1M 1024M"
-          + " mkpart primary ext2 1024M -1s",
-          "udevadm settle",
-          "mkfs.ext3 -L nixos /dev/vda2",
-          "mount LABEL=nixos /mnt",
-          "mkswap /dev/vda1 -L swap",
-          # Install onto /mnt
-          "nix-store --load-db < ${pkgs.closureInfo {rootPaths = [installedSystem];}}/registration",
-          "nixos-install --root /mnt --system ${installedSystem} --no-root-passwd --no-channel-copy >&2",
-      )
-      machine.shutdown()
-
-      # Start up
-      hibernate = create_named_machine("hibernate")
-
-      # Drop in file that checks if we un-hibernated properly (and not booted fresh)
-      hibernate.succeed(
-          "mkdir /run/test",
-          "mount -t ramfs -o size=1m ramfs /run/test",
-          "echo not persisted to disk > /run/test/suspended",
-      )
-
-      # Hibernate machine
-      hibernate.execute("systemctl hibernate >&2 &", check_return=False)
-      hibernate.wait_for_shutdown()
-
-      # Restore machine from hibernation, validate our ramfs file is there.
-      resume = create_named_machine("resume")
-      resume.start()
-      resume.succeed("grep 'not persisted to disk' /run/test/suspended")
-
-      # Ensure we don't restore from hibernation when booting again
-      resume.crash()
-      resume.wait_for_unit("default.target")
-      resume.fail("grep 'not persisted to disk' /run/test/suspended")
-    '';
+  testScript = ''
+    # Drop in file that checks if we un-hibernated properly (and not booted fresh)
+    machine.wait_for_unit("default.target")
+    machine.succeed(
+        "mkdir /run/test",
+        "mount -t ramfs -o size=1m ramfs /run/test",
+        "echo not persisted to disk > /run/test/suspended",
+    )
+
+    # Hibernate machine
+    machine.execute("systemctl hibernate >&2 &", check_return=False)
+    machine.wait_for_shutdown()
+
+    # Restore machine from hibernation, validate our ramfs file is there.
+    machine.start()
+    machine.succeed("grep 'not persisted to disk' /run/test/suspended")
+
+    # Ensure we don't restore from hibernation when booting again
+    machine.crash()
+    machine.wait_for_unit("default.target")
+    machine.fail("grep 'not persisted to disk' /run/test/suspended")
+  '';
 
 }
diff --git a/nixos/tests/homepage-dashboard.nix b/nixos/tests/homepage-dashboard.nix
new file mode 100644
index 0000000000000..56e077f5ff6de
--- /dev/null
+++ b/nixos/tests/homepage-dashboard.nix
@@ -0,0 +1,14 @@
+import ./make-test-python.nix ({ lib, ... }: {
+  name = "homepage-dashboard";
+  meta.maintainers = with lib.maintainers; [ jnsgruk ];
+
+  nodes.machine = { pkgs, ... }: {
+    services.homepage-dashboard.enable = true;
+  };
+
+  testScript = ''
+    machine.wait_for_unit("homepage-dashboard.service")
+    machine.wait_for_open_port(8082)
+    machine.succeed("curl --fail http://localhost:8082/")
+  '';
+})
diff --git a/nixos/tests/initrd-network-ssh/default.nix b/nixos/tests/initrd-network-ssh/default.nix
index 017de6882081d..17b6c21ff1e99 100644
--- a/nixos/tests/initrd-network-ssh/default.nix
+++ b/nixos/tests/initrd-network-ssh/default.nix
@@ -1,12 +1,10 @@
-import ../make-test-python.nix ({ lib, ... }:
+import ../make-test-python.nix ({ lib, pkgs, ... }:
 
 {
   name = "initrd-network-ssh";
-  meta = with lib.maintainers; {
-    maintainers = [ willibutz emily ];
-  };
+  meta.maintainers = with lib.maintainers; [ willibutz emily ];
 
-  nodes = with lib; {
+  nodes = {
     server =
       { config, ... }:
       {
@@ -17,7 +15,7 @@ import ../make-test-python.nix ({ lib, ... }:
           enable = true;
           ssh = {
             enable = true;
-            authorizedKeys = [ (readFile ./id_ed25519.pub) ];
+            authorizedKeys = [ (lib.readFile ./id_ed25519.pub) ];
             port = 22;
             hostKeys = [ ./ssh_host_ed25519_key ];
           };
@@ -37,12 +35,12 @@ import ../make-test-python.nix ({ lib, ... }:
       {
         environment.etc = {
           knownHosts = {
-            text = concatStrings [
+            text = lib.concatStrings [
               "server,"
-              "${toString (head (splitString " " (
-                toString (elemAt (splitString "\n" config.networking.extraHosts) 2)
+              "${toString (lib.head (lib.splitString " " (
+                toString (lib.elemAt (lib.splitString "\n" config.networking.extraHosts) 2)
               )))} "
-              "${readFile ./ssh_host_ed25519_key.pub}"
+              "${lib.readFile ./ssh_host_ed25519_key.pub}"
             ];
           };
           sshKey = {
diff --git a/nixos/tests/installed-tests/default.nix b/nixos/tests/installed-tests/default.nix
index 78a6325a245ea..e87edb2007e93 100644
--- a/nixos/tests/installed-tests/default.nix
+++ b/nixos/tests/installed-tests/default.nix
@@ -107,5 +107,6 @@ in
   malcontent = callInstalledTest ./malcontent.nix {};
   ostree = callInstalledTest ./ostree.nix {};
   pipewire = callInstalledTest ./pipewire.nix {};
+  upower = callInstalledTest ./upower.nix {};
   xdg-desktop-portal = callInstalledTest ./xdg-desktop-portal.nix {};
 }
diff --git a/nixos/tests/installed-tests/upower.nix b/nixos/tests/installed-tests/upower.nix
new file mode 100644
index 0000000000000..a8e777a555279
--- /dev/null
+++ b/nixos/tests/installed-tests/upower.nix
@@ -0,0 +1,9 @@
+{ pkgs, makeInstalledTest, ... }:
+
+makeInstalledTest {
+  tested = pkgs.upower;
+
+  testConfig = {
+    services.upower.enable = true;
+  };
+}
diff --git a/nixos/tests/installer-systemd-stage-1.nix b/nixos/tests/installer-systemd-stage-1.nix
index 05fb2b2ae89c7..85155a6c682b3 100644
--- a/nixos/tests/installer-systemd-stage-1.nix
+++ b/nixos/tests/installer-systemd-stage-1.nix
@@ -28,7 +28,7 @@
     simpleUefiGrubSpecialisation
     simpleUefiSystemdBoot
     stratisRoot
-    # swraid
+    swraid
     zfsroot
     ;
 
diff --git a/nixos/tests/installer.nix b/nixos/tests/installer.nix
index 4f4b91518845e..56ba85b76e6f5 100644
--- a/nixos/tests/installer.nix
+++ b/nixos/tests/installer.nix
@@ -11,16 +11,20 @@ let
 
   # The configuration to install.
   makeConfig = { bootLoader, grubDevice, grubIdentifier, grubUseEfi
-               , extraConfig, forceGrubReinstallCount ? 0
+               , extraConfig, forceGrubReinstallCount ? 0, flake ? false
                }:
     pkgs.writeText "configuration.nix" ''
       { config, lib, pkgs, modulesPath, ... }:
 
       { imports =
           [ ./hardware-configuration.nix
-            <nixpkgs/nixos/modules/testing/test-instrumentation.nix>
+            ${if flake
+              then "" # Still included, but via installer/flake.nix
+              else "<nixpkgs/nixos/modules/testing/test-instrumentation.nix>"}
           ];
 
+        networking.hostName = "thatworked";
+
         documentation.enable = false;
 
         # To ensure that we can rebuild the grub configuration on the nixos-rebuild
@@ -67,7 +71,7 @@ let
   # partitions and filesystems.
   testScriptFun = { bootLoader, createPartitions, grubDevice, grubUseEfi
                   , grubIdentifier, preBootCommands, postBootCommands, extraConfig
-                  , testSpecialisationConfig
+                  , testSpecialisationConfig, testFlakeSwitch
                   }:
     let iface = "virtio";
         isEfi = bootLoader == "systemd-boot" || (bootLoader == "grub" && grubUseEfi);
@@ -86,9 +90,14 @@ let
 
       qemu_flags = {"qemuFlags": assemble_qemu_flags()}
 
+      import os
+
+      image_dir = machine.state_dir
+      disk_image = os.path.join(image_dir, "machine.qcow2")
+
       hd_flags = {
           "hdaInterface": "${iface}",
-          "hda": "vm-state-machine/machine.qcow2",
+          "hda": disk_image,
       }
       ${optionalString isEfi ''
         hd_flags.update(
@@ -232,6 +241,11 @@ let
       machine = create_machine_named("boot-after-rebuild-switch")
       ${preBootCommands}
       machine.wait_for_unit("network.target")
+
+      # Sanity check, is it the configuration.nix we generated?
+      hostname = machine.succeed("hostname").strip()
+      assert hostname == "thatworked"
+
       ${postBootCommands}
       machine.shutdown()
 
@@ -272,6 +286,84 @@ let
 
       ${postBootCommands}
       machine.shutdown()
+    ''
+    + optionalString testFlakeSwitch ''
+      ${preBootCommands}
+      machine.start()
+
+      with subtest("Configure system with flake"):
+        # TODO: evaluate as user?
+        machine.succeed("""
+          mkdir /root/my-config
+          mv /etc/nixos/hardware-configuration.nix /root/my-config/
+          mv /etc/nixos/secret /root/my-config/
+          rm /etc/nixos/configuration.nix
+        """)
+        machine.copy_from_host_via_shell(
+          "${makeConfig {
+               inherit bootLoader grubDevice grubIdentifier grubUseEfi extraConfig;
+               forceGrubReinstallCount = 1;
+               flake = true;
+            }}",
+          "/root/my-config/configuration.nix",
+        )
+        machine.copy_from_host_via_shell(
+          "${./installer/flake.nix}",
+          "/root/my-config/flake.nix",
+        )
+        machine.succeed("""
+          # for some reason the image does not have `pkgs.path`, so
+          # we use readlink to find a Nixpkgs source.
+          pkgs=$(readlink -f /nix/var/nix/profiles/per-user/root/channels)/nixos
+          if ! [[ -e $pkgs/pkgs/top-level/default.nix ]]; then
+            echo 1>&2 "$pkgs does not seem to be a nixpkgs source. Please fix the test so that pkgs points to a nixpkgs source.";
+            exit 1;
+          fi
+          sed -e s^@nixpkgs@^$pkgs^ -i /root/my-config/flake.nix
+        """)
+
+      with subtest("Switch to flake based config"):
+        machine.succeed("nixos-rebuild switch --flake /root/my-config#xyz")
+
+      ${postBootCommands}
+      machine.shutdown()
+
+      ${preBootCommands}
+      machine.start()
+
+      machine.wait_for_unit("multi-user.target")
+
+      with subtest("nix-channel command is not available anymore"):
+        machine.succeed("! which nix-channel")
+
+      # Note that the channel profile is still present on disk, but configured
+      # not to be used.
+      with subtest("builtins.nixPath is now empty"):
+        machine.succeed("""
+          [[ "[ ]" == "$(nix-instantiate builtins.nixPath --eval --expr)" ]]
+        """)
+
+      with subtest("<nixpkgs> does not resolve"):
+        machine.succeed("""
+          ! nix-instantiate '<nixpkgs>' --eval --expr
+        """)
+
+      with subtest("Evaluate flake config in fresh env without nix-channel"):
+        machine.succeed("nixos-rebuild switch --flake /root/my-config#xyz")
+
+      with subtest("Evaluate flake config in fresh env without channel profiles"):
+        machine.succeed("""
+          (
+            exec 1>&2
+            rm -v /root/.nix-channels
+            rm -vrf ~/.nix-defexpr
+            rm -vrf /nix/var/nix/profiles/per-user/root/channels*
+          )
+        """)
+        machine.succeed("nixos-rebuild switch --flake /root/my-config#xyz")
+
+      ${postBootCommands}
+      machine.shutdown()
     '';
 
 
@@ -282,6 +374,7 @@ let
     , grubDevice ? "/dev/vda", grubIdentifier ? "uuid", grubUseEfi ? false
     , enableOCR ? false, meta ? {}
     , testSpecialisationConfig ? false
+    , testFlakeSwitch ? false
     }:
     makeTest {
       inherit enableOCR;
@@ -334,6 +427,7 @@ let
           # The test cannot access the network, so any packages we
           # need must be included in the VM.
           system.extraDependencies = with pkgs; [
+            bintools
             brotli
             brotli.dev
             brotli.lib
@@ -387,7 +481,7 @@ let
       testScript = testScriptFun {
         inherit bootLoader createPartitions preBootCommands postBootCommands
                 grubDevice grubIdentifier grubUseEfi extraConfig
-                testSpecialisationConfig;
+                testSpecialisationConfig testFlakeSwitch;
       };
     };
 
@@ -439,6 +533,10 @@ let
     '';
   };
 
+  simple-test-config-flake = simple-test-config // {
+    testFlakeSwitch = true;
+  };
+
   simple-uefi-grub-config = {
     createPartitions = ''
       machine.succeed(
@@ -493,6 +591,8 @@ in {
   # one big filesystem partition.
   simple = makeInstallerTest "simple" simple-test-config;
 
+  switchToFlake = makeInstallerTest "switch-to-flake" simple-test-config-flake;
+
   # Test cloned configurations with the simple grub configuration
   simpleSpecialised = makeInstallerTest "simpleSpecialised" (simple-test-config // specialisation-test-extraconfig);
 
diff --git a/nixos/tests/installer/flake.nix b/nixos/tests/installer/flake.nix
new file mode 100644
index 0000000000000..4bbef44e34fc9
--- /dev/null
+++ b/nixos/tests/installer/flake.nix
@@ -0,0 +1,20 @@
+# This file gets copied into the installation
+
+{
+  # To keep things simple, we'll use an absolute path dependency here.
+  inputs.nixpkgs.url = "@nixpkgs@";
+
+  outputs = { nixpkgs, ... }: {
+
+    nixosConfigurations.xyz = nixpkgs.lib.nixosSystem {
+      modules = [
+        ./configuration.nix
+        ( nixpkgs + "/nixos/modules/testing/test-instrumentation.nix" )
+        {
+          # We don't need nix-channel anymore
+          nix.channel.enable = false;
+        }
+      ];
+    };
+  };
+}
diff --git a/nixos/tests/jenkins.nix b/nixos/tests/jenkins.nix
index a1ede6dc917bf..a8f6210006547 100644
--- a/nixos/tests/jenkins.nix
+++ b/nixos/tests/jenkins.nix
@@ -73,8 +73,8 @@ import ./make-test-python.nix ({ pkgs, ...} : {
 
   testScript = { nodes, ... }:
     let
-      configWithoutJobs = "${nodes.master.config.system.build.toplevel}/specialisation/noJenkinsJobs";
-      jenkinsPort = nodes.master.config.services.jenkins.port;
+      configWithoutJobs = "${nodes.master.system.build.toplevel}/specialisation/noJenkinsJobs";
+      jenkinsPort = nodes.master.services.jenkins.port;
       jenkinsUrl = "http://localhost:${toString jenkinsPort}";
     in ''
     start_all()
diff --git a/nixos/tests/k3s/single-node.nix b/nixos/tests/k3s/single-node.nix
index d61595d889e2a..e059603b9c9d7 100644
--- a/nixos/tests/k3s/single-node.nix
+++ b/nixos/tests/k3s/single-node.nix
@@ -62,20 +62,20 @@ import ../make-test-python.nix ({ pkgs, lib, k3s, ... }:
       start_all()
 
       machine.wait_for_unit("k3s")
-      machine.succeed("k3s kubectl cluster-info")
-      machine.fail("sudo -u noprivs k3s kubectl cluster-info")
+      machine.succeed("kubectl cluster-info")
+      machine.fail("sudo -u noprivs kubectl cluster-info")
       '' # Fix-Me: Tests fail for 'aarch64-linux' as: "CONFIG_CGROUP_FREEZER: missing (fail)"
       + lib.optionalString (!pkgs.stdenv.isAarch64) ''machine.succeed("k3s check-config")'' + ''
 
       machine.succeed(
-          "${pauseImage} | k3s ctr image import -"
+          "${pauseImage} | ctr image import -"
       )
 
       # Also wait for our service account to show up; it takes a sec
-      machine.wait_until_succeeds("k3s kubectl get serviceaccount default")
-      machine.succeed("k3s kubectl apply -f ${testPodYaml}")
-      machine.succeed("k3s kubectl wait --for 'condition=Ready' pod/test")
-      machine.succeed("k3s kubectl delete -f ${testPodYaml}")
+      machine.wait_until_succeeds("kubectl get serviceaccount default")
+      machine.succeed("kubectl apply -f ${testPodYaml}")
+      machine.succeed("kubectl wait --for 'condition=Ready' pod/test")
+      machine.succeed("kubectl delete -f ${testPodYaml}")
 
       # regression test for #176445
       machine.fail("journalctl -o cat -u k3s.service | grep 'ipset utility not found'")
diff --git a/nixos/tests/kanidm.nix b/nixos/tests/kanidm.nix
index 673a65174dfee..3f5bca397740e 100644
--- a/nixos/tests/kanidm.nix
+++ b/nixos/tests/kanidm.nix
@@ -67,9 +67,10 @@ import ./make-test-python.nix ({ pkgs, ... }:
       ''
         start_all()
         server.wait_for_unit("kanidm.service")
+        client.wait_for_unit("network-online.target")
 
         with subtest("Test HTTP interface"):
-            server.wait_until_succeeds("curl -sf https://${serverDomain} | grep Kanidm")
+            server.wait_until_succeeds("curl -Lsf https://${serverDomain} | grep Kanidm")
 
         with subtest("Test LDAP interface"):
             server.succeed("ldapsearch -H ldaps://${serverDomain}:636 -b '${ldapBaseDN}' -x '(name=test)'")
@@ -80,15 +81,11 @@ import ./make-test-python.nix ({ pkgs, ... }:
             client.succeed("kanidm logout")
 
         with subtest("Recover idm_admin account"):
-            # Must stop the server for account recovery or else kanidmd fails with
-            # "unable to lock kanidm exclusive lock at /var/lib/kanidm/kanidm.db.klock".
-            server.succeed("systemctl stop kanidm")
             idm_admin_password = server.succeed("su - kanidm -c 'kanidmd recover-account -c ${serverConfigFile} idm_admin 2>&1 | rg -o \'[A-Za-z0-9]{48}\' '").strip().removeprefix("'").removesuffix("'")
-            server.succeed("systemctl start kanidm")
 
         with subtest("Test unixd connection"):
             client.wait_for_unit("kanidm-unixd.service")
-            # TODO: client.wait_for_file("/run/kanidm-unixd/sock")
+            client.wait_for_file("/run/kanidm-unixd/sock")
             client.wait_until_succeeds("kanidm-unix status | grep working!")
 
         with subtest("Test user creation"):
diff --git a/nixos/tests/kernel-generic.nix b/nixos/tests/kernel-generic.nix
index 82d9118c6fb10..e69dd550289c1 100644
--- a/nixos/tests/kernel-generic.nix
+++ b/nixos/tests/kernel-generic.nix
@@ -9,7 +9,7 @@ let
   testsForLinuxPackages = linuxPackages: (import ./make-test-python.nix ({ pkgs, ... }: {
     name = "kernel-${linuxPackages.kernel.version}";
     meta = with pkgs.lib.maintainers; {
-      maintainers = [ nequissimus atemu ];
+      maintainers = [ nequissimus atemu ma27 ];
     };
 
     nodes.machine = { ... }:
@@ -31,13 +31,20 @@ let
       linux_5_10_hardened
       linux_5_15_hardened
       linux_6_1_hardened
-      linux_6_3_hardened
+      linux_6_4_hardened
+      linux_rt_5_4
+      linux_rt_5_10
+      linux_rt_5_15
+      linux_rt_6_1
+      linux_libre
 
       linux_testing;
   };
 
 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/nixos/tests/keyd.nix b/nixos/tests/keyd.nix
index d492cc194895c..1ee08b4101f72 100644
--- a/nixos/tests/keyd.nix
+++ b/nixos/tests/keyd.nix
@@ -32,7 +32,7 @@ let
     nodes.machine = {
       services.keyd = {
         enable = true;
-        inherit settings;
+        keyboards.default = { inherit settings; };
       };
     };
 
diff --git a/nixos/tests/keymap.nix b/nixos/tests/keymap.nix
index 0bde21093b0a2..cc45824667edb 100644
--- a/nixos/tests/keymap.nix
+++ b/nixos/tests/keymap.nix
@@ -29,10 +29,10 @@ let
   mkKeyboardTest = layout: { extraConfig ? {}, tests }: with pkgs.lib; makeTest {
     name = "keymap-${layout}";
 
-    machine.console.keyMap = mkOverride 900 layout;
-    machine.services.xserver.desktopManager.xterm.enable = false;
-    machine.services.xserver.layout = mkOverride 900 layout;
-    machine.imports = [ ./common/x11.nix extraConfig ];
+    nodes.machine.console.keyMap = mkOverride 900 layout;
+    nodes.machine.services.xserver.desktopManager.xterm.enable = false;
+    nodes.machine.services.xserver.layout = mkOverride 900 layout;
+    nodes.machine.imports = [ ./common/x11.nix extraConfig ];
 
     testScript = ''
       import json
@@ -201,4 +201,33 @@ in pkgs.lib.mapAttrs mkKeyboardTest {
     extraConfig.console.keyMap = "de";
     extraConfig.services.xserver.layout = "de";
   };
+
+  custom = {
+    tests = {
+      us.qwerty = [ "a" "b" "g" "d" "z" "shift-2" "shift-3" ];
+      us.expect = [ "a" "b" "g" "d" "z" "@" "#" ];
+      greek.qwerty = map (x: "alt_r-${x}")
+                     [ "a" "b" "g" "d" "z" ];
+      greek.expect = [ "α" "β" "γ" "δ" "ζ" ];
+    };
+
+    extraConfig.console.useXkbConfig = true;
+    extraConfig.services.xserver.layout = "us-greek";
+    extraConfig.services.xserver.extraLayouts.us-greek =
+      { description = "US layout with alt-gr greek";
+        languages   = [ "eng" ];
+        symbolsFile = pkgs.writeText "us-greek" ''
+          xkb_symbols "us-greek"
+          {
+            include "us(basic)"
+            include "level3(ralt_switch)"
+            key <LatA> { [ a, A, Greek_alpha ] };
+            key <LatB> { [ b, B, Greek_beta  ] };
+            key <LatG> { [ g, G, Greek_gamma ] };
+            key <LatD> { [ d, D, Greek_delta ] };
+            key <LatZ> { [ z, Z, Greek_zeta  ] };
+          };
+        '';
+      };
+  };
 }
diff --git a/nixos/tests/lemmy.nix b/nixos/tests/lemmy.nix
index b1a39827b0723..de2c4938fe231 100644
--- a/nixos/tests/lemmy.nix
+++ b/nixos/tests/lemmy.nix
@@ -22,16 +22,16 @@ in
           # Without setup, the /feeds/* and /nodeinfo/* API endpoints won't return 200
           setup = {
             admin_username = "mightyiam";
-            admin_password = "ThisIsWhatIUseEverywhereTryIt";
             site_name = "Lemmy FTW";
             admin_email = "mightyiam@example.com";
           };
-          # https://github.com/LemmyNet/lemmy/blob/50efb1d519c63a7007a07f11cc8a11487703c70d/crates/utils/src/settings/mod.rs#L52
-          database.uri = "postgres:///lemmy?host=/run/postgresql&user=lemmy";
         };
+        adminPasswordFile = /etc/lemmy-admin-password.txt;
         caddy.enable = true;
       };
 
+      environment.etc."lemmy-admin-password.txt".text = "ThisIsWhatIUseEverywhereTryIt";
+
       networking.firewall.allowedTCPPorts = [ 80 ];
 
       # pict-rs seems to need more than 1025114112 bytes
@@ -42,8 +42,14 @@ in
   testScript = ''
     server = ${lemmyNodeName}
 
-    with subtest("the backend starts and responds"):
+    with subtest("the merged config is secure"):
         server.wait_for_unit("lemmy.service")
+        config_permissions = server.succeed("stat --format %A /run/lemmy/config.hjson").rstrip()
+        assert config_permissions == "-rw-------", f"merged config permissions {config_permissions} are insecure"
+        directory_permissions = server.succeed("stat --format %A /run/lemmy").rstrip()
+        assert directory_permissions[5] == directory_permissions[8] == "-", "merged config can be replaced"
+
+    with subtest("the backend starts and responds"):
         server.wait_for_open_port(${toString backendPort})
         server.succeed("curl --fail localhost:${toString backendPort}/api/v3/site")
 
diff --git a/nixos/tests/libvirtd.nix b/nixos/tests/libvirtd.nix
index b6e6040759976..41d06cc9643fe 100644
--- a/nixos/tests/libvirtd.nix
+++ b/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/nixos/tests/lxd-ui.nix b/nixos/tests/lxd-ui.nix
new file mode 100644
index 0000000000000..19eaa226c0bfe
--- /dev/null
+++ b/nixos/tests/lxd-ui.nix
@@ -0,0 +1,35 @@
+import ./make-test-python.nix ({ pkgs, lib, ... }: {
+  name = "lxd-ui";
+
+  meta = with pkgs.lib.maintainers; {
+    maintainers = [ jnsgruk ];
+  };
+
+  nodes.machine = { lib, ... }: {
+    virtualisation = {
+      lxd.enable = true;
+      lxd.ui.enable = true;
+    };
+
+    environment.systemPackages = [ pkgs.curl ];
+  };
+
+  testScript = ''
+    machine.wait_for_unit("sockets.target")
+    machine.wait_for_unit("lxd.service")
+    machine.wait_for_file("/var/lib/lxd/unix.socket")
+
+    # Wait for lxd to settle
+    machine.succeed("lxd waitready")
+
+    # Configure LXC listen address
+    machine.succeed("lxc config set core.https_address :8443")
+    machine.succeed("systemctl restart lxd")
+
+    # Check that the LXD_UI environment variable is populated in the systemd unit
+    machine.succeed("cat /etc/systemd/system/lxd.service | grep 'LXD_UI'")
+
+    # Ensure the endpoint returns an HTML page with 'LXD UI' in the title
+    machine.succeed("curl -kLs https://localhost:8443/ui | grep '<title>LXD UI</title>'")
+  '';
+})
diff --git a/nixos/tests/maestral.nix b/nixos/tests/maestral.nix
index ba2e0b2f3baab..67a265926187d 100644
--- a/nixos/tests/maestral.nix
+++ b/nixos/tests/maestral.nix
@@ -52,7 +52,7 @@ import ./make-test-python.nix ({ pkgs, ... }: {
 
   testScript = { nodes, ... }:
     let
-      user = nodes.cli.config.users.users.alice;
+      user = nodes.cli.users.users.alice;
     in
     ''
       start_all()
@@ -65,7 +65,8 @@ import ./make-test-python.nix ({ pkgs, ... }: {
 
       with subtest("GUI"):
         gui.wait_for_x()
-        gui.succeed("xauth merge ${user.home}/.Xauthority")
+        gui.wait_for_file("/tmp/xauth_*")
+        gui.succeed("xauth merge /tmp/xauth_*")
         gui.wait_for_window("^Desktop ")
         gui.wait_for_unit("maestral.service", "${user.name}")
     '';
diff --git a/nixos/tests/miniflux.nix b/nixos/tests/miniflux.nix
index be3e7abb6abd4..a3af53db0e7a1 100644
--- a/nixos/tests/miniflux.nix
+++ b/nixos/tests/miniflux.nix
@@ -25,6 +25,7 @@ in
     default =
       { ... }:
       {
+        security.apparmor.enable = true;
         services.miniflux = {
           enable = true;
           inherit adminCredentialsFile;
@@ -34,6 +35,7 @@ in
     withoutSudo =
       { ... }:
       {
+        security.apparmor.enable = true;
         services.miniflux = {
           enable = true;
           inherit adminCredentialsFile;
@@ -44,6 +46,7 @@ in
     customized =
       { ... }:
       {
+        security.apparmor.enable = true;
         services.miniflux = {
           enable = true;
           config = {
@@ -63,6 +66,7 @@ in
     default.succeed(
         "curl 'http://localhost:${toString defaultPort}/v1/me' -u '${defaultUsername}:${defaultPassword}' -H Content-Type:application/json | grep '\"is_admin\":true'"
     )
+    default.fail('journalctl -b --no-pager --grep "^audit: .*apparmor=\\"DENIED\\""')
 
     withoutSudo.wait_for_unit("miniflux.service")
     withoutSudo.wait_for_open_port(${toString defaultPort})
@@ -70,6 +74,7 @@ in
     withoutSudo.succeed(
         "curl 'http://localhost:${toString defaultPort}/v1/me' -u '${defaultUsername}:${defaultPassword}' -H Content-Type:application/json | grep '\"is_admin\":true'"
     )
+    withoutSudo.fail('journalctl -b --no-pager --grep "^audit: .*apparmor=\\"DENIED\\""')
 
     customized.wait_for_unit("miniflux.service")
     customized.wait_for_open_port(${toString port})
@@ -77,5 +82,6 @@ in
     customized.succeed(
         "curl 'http://localhost:${toString port}/v1/me' -u '${username}:${password}' -H Content-Type:application/json | grep '\"is_admin\":true'"
     )
+    customized.fail('journalctl -b --no-pager --grep "^audit: .*apparmor=\\"DENIED\\""')
   '';
 })
diff --git a/nixos/tests/miriway.nix b/nixos/tests/miriway.nix
index 9000e92781978..f12c4d5ecc41e 100644
--- a/nixos/tests/miriway.nix
+++ b/nixos/tests/miriway.nix
@@ -83,7 +83,7 @@ import ./make-test-python.nix ({ pkgs, lib, ... }: {
       };
     };
 
-    fonts.fonts = [ pkgs.inconsolata ];
+    fonts.packages = [ pkgs.inconsolata ];
   };
 
   enableOCR = true;
diff --git a/nixos/tests/mumble.nix b/nixos/tests/mumble.nix
index 2b5cc20163bcb..8eee454721a13 100644
--- a/nixos/tests/mumble.nix
+++ b/nixos/tests/mumble.nix
@@ -20,6 +20,7 @@ in
 
   nodes = {
     server = { config, ... }: {
+      security.apparmor.enable = true;
       services.murmur.enable = true;
       services.murmur.registerName = "NixOS tests";
       services.murmur.password = "$MURMURD_PASSWORD";
@@ -81,5 +82,8 @@ in
     server.sleep(5)  # wait to get screenshot
     client1.screenshot("screen1")
     client2.screenshot("screen2")
+
+    # check if apparmor denied anything
+    server.fail('journalctl -b --no-pager --grep "^audit: .*apparmor=\\"DENIED\\""')
   '';
 })
diff --git a/nixos/tests/n8n.nix b/nixos/tests/n8n.nix
index 771296bf37a5a..0a12192d5c712 100644
--- a/nixos/tests/n8n.nix
+++ b/nixos/tests/n8n.nix
@@ -1,6 +1,7 @@
 import ./make-test-python.nix ({ lib, ... }:
 let
   port = 5678;
+  webhookUrl = "http://example.com";
 in
 {
   name = "n8n";
@@ -11,6 +12,7 @@ in
     {
       services.n8n = {
         enable = true;
+        webhookUrl = webhookUrl;
       };
     };
 
@@ -18,5 +20,6 @@ in
     machine.wait_for_unit("n8n.service")
     machine.wait_for_console_text("Editor is now accessible via")
     machine.succeed("curl --fail -vvv http://localhost:${toString port}/")
+    machine.succeed("grep -qF ${webhookUrl} /etc/systemd/system/n8n.service")
   '';
 })
diff --git a/nixos/tests/netdata.nix b/nixos/tests/netdata.nix
index aea67c29d0d4b..c5f7294f79abc 100644
--- a/nixos/tests/netdata.nix
+++ b/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/nixos/tests/networking.nix b/nixos/tests/networking.nix
index 05fed0f4b4732..46fc715d0891d 100644
--- a/nixos/tests/networking.nix
+++ b/nixos/tests/networking.nix
@@ -29,23 +29,49 @@ let
             ipv6.addresses = [ { address = "fd00:1234:5678:${toString n}::1"; prefixLength = 64; } ];
           })));
       };
-      services.dhcpd4 = {
-        enable = true;
-        interfaces = map (n: "eth${toString n}") vlanIfs;
-        extraConfig = flip concatMapStrings vlanIfs (n: ''
-          subnet 192.168.${toString n}.0 netmask 255.255.255.0 {
-            option routers 192.168.${toString n}.1;
-            range 192.168.${toString n}.3 192.168.${toString n}.254;
-          }
-        '')
-        ;
-        machines = flip map vlanIfs (vlan:
-          {
-            hostName = "client${toString vlan}";
-            ethernetAddress = qemu-common.qemuNicMac vlan 1;
-            ipAddress = "192.168.${toString vlan}.2";
-          }
-        );
+      services.kea = {
+        dhcp4 = {
+          enable = true;
+          settings = {
+            interfaces-config = {
+              interfaces = map (n: "eth${toString n}") vlanIfs;
+              dhcp-socket-type = "raw";
+              service-sockets-require-all = true;
+              service-sockets-max-retries = 5;
+              service-sockets-retry-wait-time = 2500;
+            };
+            subnet4 = map (n: {
+              id = n;
+              subnet = "192.168.${toString n}.0/24";
+              pools = [{ pool = "192.168.${toString n}.3 - 192.168.${toString n}.254"; }];
+              option-data = [{ name = "routers"; data = "192.168.${toString n}.1"; }];
+
+              reservations = [{
+                hw-address = qemu-common.qemuNicMac n 1;
+                hostname = "client${toString n}";
+                ip-address = "192.168.${toString n}.2";
+              }];
+            }) vlanIfs;
+          };
+        };
+        dhcp6 = {
+          enable = true;
+          settings = {
+            interfaces-config = {
+              interfaces = map (n: "eth${toString n}") vlanIfs;
+              service-sockets-require-all = true;
+              service-sockets-max-retries = 5;
+              service-sockets-retry-wait-time = 2500;
+            };
+
+            subnet6 = map (n: {
+              id = n;
+              subnet = "fd00:1234:5678:${toString n}::/64";
+              interface = "eth${toString n}";
+              pools = [{ pool = "fd00:1234:5678:${toString n}::2-fd00:1234:5678:${toString n}::2"; }];
+            }) vlanIfs;
+          };
+        };
       };
       services.radvd = {
         enable = true;
@@ -61,17 +87,6 @@ let
           };
         '');
       };
-      services.dhcpd6 = {
-        enable = true;
-        interfaces = map (n: "eth${toString n}") vlanIfs;
-        extraConfig = ''
-          authoritative;
-        '' + flip concatMapStrings vlanIfs (n: ''
-          subnet6 fd00:1234:5678:${toString n}::/64 {
-            range6 fd00:1234:5678:${toString n}::2 fd00:1234:5678:${toString n}::2;
-          }
-        '');
-      };
     };
 
   testCases = {
@@ -117,8 +132,9 @@ let
           client.wait_for_unit("network.target")
           router.wait_for_unit("network-online.target")
 
-          with subtest("Make sure dhcpcd is not started"):
-              client.fail("systemctl status dhcpcd.service")
+          with subtest("Make sure DHCP server is not started"):
+              client.fail("systemctl status kea-dhcp4-server.service")
+              client.fail("systemctl status kea-dhcp6-server.service")
 
           with subtest("Test vlan 1"):
               client.wait_until_succeeds("ping -c 1 192.168.1.1")
@@ -1035,7 +1051,7 @@ let
       testScript = ''
         machine.succeed("udevadm settle")
         print(machine.succeed("ip link show dev enCustom"))
-        machine.wait_until_succeeds("ip link show dev enCustom | grep -q '52:54:00:12:0b:01")
+        machine.wait_until_succeeds("ip link show dev enCustom | grep -q 52:54:00:12:0b:01")
       '';
     };
   };
diff --git a/nixos/tests/nextcloud/basic.nix b/nixos/tests/nextcloud/basic.nix
index e17f701c54b7d..b7af6d6d73647 100644
--- a/nixos/tests/nextcloud/basic.nix
+++ b/nixos/tests/nextcloud/basic.nix
@@ -14,12 +14,12 @@ in {
     client = { ... }: {
       services.davfs2.enable = true;
       system.activationScripts.davfs2-secrets = ''
-        echo "http://nextcloud/remote.php/webdav/ ${adminuser} ${adminpass}" > /tmp/davfs2-secrets
+        echo "http://nextcloud/remote.php/dav/files/${adminuser} ${adminuser} ${adminpass}" > /tmp/davfs2-secrets
         chmod 600 /tmp/davfs2-secrets
       '';
       virtualisation.fileSystems = {
         "/mnt/dav" = {
-          device = "http://nextcloud/remote.php/webdav/";
+          device = "http://nextcloud/remote.php/dav/files/${adminuser}";
           fsType = "davfs";
           options = let
             davfs2Conf = (pkgs.writeText "davfs2.conf" "secrets /tmp/davfs2-secrets");
@@ -70,7 +70,7 @@ in {
     withRcloneEnv = pkgs.writeScript "with-rclone-env" ''
       #!${pkgs.runtimeShell}
       export RCLONE_CONFIG_NEXTCLOUD_TYPE=webdav
-      export RCLONE_CONFIG_NEXTCLOUD_URL="http://nextcloud/remote.php/webdav/"
+      export RCLONE_CONFIG_NEXTCLOUD_URL="http://nextcloud/remote.php/dav/files/${adminuser}"
       export RCLONE_CONFIG_NEXTCLOUD_VENDOR="nextcloud"
       export RCLONE_CONFIG_NEXTCLOUD_USER="${adminuser}"
       export RCLONE_CONFIG_NEXTCLOUD_PASS="$(${pkgs.rclone}/bin/rclone obscure ${adminpass})"
@@ -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/nixos/tests/nextcloud/openssl-sse.nix b/nixos/tests/nextcloud/openssl-sse.nix
index 659a4311cdddd..d6ea39c6155a5 100644
--- a/nixos/tests/nextcloud/openssl-sse.nix
+++ b/nixos/tests/nextcloud/openssl-sse.nix
@@ -33,7 +33,7 @@ in {
     withRcloneEnv = host: pkgs.writeScript "with-rclone-env" ''
       #!${pkgs.runtimeShell}
       export RCLONE_CONFIG_NEXTCLOUD_TYPE=webdav
-      export RCLONE_CONFIG_NEXTCLOUD_URL="http://${host}/remote.php/webdav/"
+      export RCLONE_CONFIG_NEXTCLOUD_URL="http://${host}/remote.php/dav/files/${adminuser}"
       export RCLONE_CONFIG_NEXTCLOUD_VENDOR="nextcloud"
       export RCLONE_CONFIG_NEXTCLOUD_USER="${adminuser}"
       export RCLONE_CONFIG_NEXTCLOUD_PASS="$(${pkgs.rclone}/bin/rclone obscure ${adminpass})"
@@ -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/nixos/tests/nextcloud/with-declarative-redis-and-secrets.nix b/nixos/tests/nextcloud/with-declarative-redis-and-secrets.nix
index 915fa58ab1458..e638f2e5b861f 100644
--- a/nixos/tests/nextcloud/with-declarative-redis-and-secrets.nix
+++ b/nixos/tests/nextcloud/with-declarative-redis-and-secrets.nix
@@ -1,6 +1,6 @@
 args@{ nextcloudVersion ? 27, ... }:
 (import ../make-test-python.nix ({ pkgs, ...}: let
-  username = "custom_admin_username";
+  adminuser = "custom_admin_username";
   # This will be used both for redis and postgresql
   pass = "hunter2";
   # Don't do this at home, use a file outside of the nix store instead
@@ -34,9 +34,9 @@ in {
         config = {
           dbtype = "pgsql";
           dbname = "nextcloud";
-          dbuser = username;
+          dbuser = adminuser;
           dbpassFile = passFile;
-          adminuser = username;
+          adminuser = adminuser;
           adminpassFile = passFile;
         };
         secretFile = "/etc/nextcloud-secrets.json";
@@ -66,9 +66,9 @@ in {
       systemd.services.postgresql.postStart = pkgs.lib.mkAfter ''
         password=$(cat ${passFile})
         ${config.services.postgresql.package}/bin/psql <<EOF
-          CREATE ROLE ${username} WITH LOGIN PASSWORD '$password' CREATEDB;
+          CREATE ROLE ${adminuser} WITH LOGIN PASSWORD '$password' CREATEDB;
           CREATE DATABASE nextcloud;
-          GRANT ALL PRIVILEGES ON DATABASE nextcloud TO ${username};
+          GRANT ALL PRIVILEGES ON DATABASE nextcloud TO ${adminuser};
         EOF
       '';
 
@@ -89,9 +89,9 @@ in {
     withRcloneEnv = pkgs.writeScript "with-rclone-env" ''
       #!${pkgs.runtimeShell}
       export RCLONE_CONFIG_NEXTCLOUD_TYPE=webdav
-      export RCLONE_CONFIG_NEXTCLOUD_URL="http://nextcloud/remote.php/webdav/"
+      export RCLONE_CONFIG_NEXTCLOUD_URL="http://nextcloud/remote.php/dav/files/${adminuser}"
       export RCLONE_CONFIG_NEXTCLOUD_VENDOR="nextcloud"
-      export RCLONE_CONFIG_NEXTCLOUD_USER="${username}"
+      export RCLONE_CONFIG_NEXTCLOUD_USER="${adminuser}"
       export RCLONE_CONFIG_NEXTCLOUD_PASS="$(${pkgs.rclone}/bin/rclone obscure ${pass})"
       "''${@}"
     '';
diff --git a/nixos/tests/nextcloud/with-mysql-and-memcached.nix b/nixos/tests/nextcloud/with-mysql-and-memcached.nix
index e57aabfaf86b5..035a7fdcb0c80 100644
--- a/nixos/tests/nextcloud/with-mysql-and-memcached.nix
+++ b/nixos/tests/nextcloud/with-mysql-and-memcached.nix
@@ -49,7 +49,7 @@ in {
     withRcloneEnv = pkgs.writeScript "with-rclone-env" ''
       #!${pkgs.runtimeShell}
       export RCLONE_CONFIG_NEXTCLOUD_TYPE=webdav
-      export RCLONE_CONFIG_NEXTCLOUD_URL="http://nextcloud/remote.php/webdav/"
+      export RCLONE_CONFIG_NEXTCLOUD_URL="http://nextcloud/remote.php/dav/files/${adminuser}"
       export RCLONE_CONFIG_NEXTCLOUD_VENDOR="nextcloud"
       export RCLONE_CONFIG_NEXTCLOUD_USER="${adminuser}"
       export RCLONE_CONFIG_NEXTCLOUD_PASS="$(${pkgs.rclone}/bin/rclone obscure ${adminpass})"
diff --git a/nixos/tests/nextcloud/with-postgresql-and-redis.nix b/nixos/tests/nextcloud/with-postgresql-and-redis.nix
index a91208e6cdc4e..586bf50fd939c 100644
--- a/nixos/tests/nextcloud/with-postgresql-and-redis.nix
+++ b/nixos/tests/nextcloud/with-postgresql-and-redis.nix
@@ -60,7 +60,7 @@ in {
     withRcloneEnv = pkgs.writeScript "with-rclone-env" ''
       #!${pkgs.runtimeShell}
       export RCLONE_CONFIG_NEXTCLOUD_TYPE=webdav
-      export RCLONE_CONFIG_NEXTCLOUD_URL="http://nextcloud/remote.php/webdav/"
+      export RCLONE_CONFIG_NEXTCLOUD_URL="http://nextcloud/remote.php/dav/files/${adminuser}"
       export RCLONE_CONFIG_NEXTCLOUD_VENDOR="nextcloud"
       export RCLONE_CONFIG_NEXTCLOUD_USER="${adminuser}"
       export RCLONE_CONFIG_NEXTCLOUD_PASS="$(${pkgs.rclone}/bin/rclone obscure ${adminpass})"
diff --git a/nixos/tests/nginx-proxyprotocol/default.nix b/nixos/tests/nginx-proxyprotocol/default.nix
index 9ef5d01cd65b0..2ff7debfcbe25 100644
--- a/nixos/tests/nginx-proxyprotocol/default.nix
+++ b/nixos/tests/nginx-proxyprotocol/default.nix
@@ -4,6 +4,10 @@ in
 import ../make-test-python.nix ({ pkgs, ... }: {
   name = "nginx-proxyprotocol";
 
+  meta = {
+    maintainers = with pkgs.lib.maintainers; [ raitobezarius ];
+  };
+
   nodes = {
     webserver = { pkgs, lib, ... }: {
       environment.systemPackages = [ pkgs.netcat ];
diff --git a/nixos/tests/nginx-status-page.nix b/nixos/tests/nginx-status-page.nix
new file mode 100644
index 0000000000000..ff2c0940379c7
--- /dev/null
+++ b/nixos/tests/nginx-status-page.nix
@@ -0,0 +1,72 @@
+import ./make-test-python.nix ({ pkgs, ... }: {
+  name = "nginx-status-page";
+  meta = with pkgs.lib.maintainers; {
+    maintainers = [ h7x4 ];
+  };
+
+  nodes = {
+    webserver = { ... }: {
+      virtualisation.vlans = [ 1 ];
+
+      networking = {
+        useNetworkd = true;
+        useDHCP = false;
+        firewall.enable = false;
+      };
+
+      systemd.network.networks."01-eth1" = {
+        name = "eth1";
+        networkConfig.Address = "10.0.0.1/24";
+      };
+
+      services.nginx = {
+        enable = true;
+        statusPage = true;
+        virtualHosts."localhost".locations."/index.html".return = "200 'hello world\n'";
+      };
+
+      environment.systemPackages = with pkgs; [ curl ];
+    };
+
+    client = { ... }: {
+      virtualisation.vlans = [ 1 ];
+
+      networking = {
+        useNetworkd = true;
+        useDHCP = false;
+        firewall.enable = false;
+      };
+
+      systemd.network.networks."01-eth1" = {
+        name = "eth1";
+        networkConfig.Address = "10.0.0.2/24";
+      };
+
+      environment.systemPackages = with pkgs; [ curl ];
+    };
+  };
+
+  testScript = { nodes, ... }: ''
+    start_all()
+
+    webserver.wait_for_unit("nginx")
+    webserver.wait_for_open_port(80)
+
+    def expect_http_code(node, code, url):
+        http_code = node.succeed(f"curl -w '%{{http_code}}' '{url}'")
+        assert http_code.split("\n")[-1].strip() == code, \
+          f"expected {code} but got following response:\n{http_code}"
+
+    with subtest("localhost can access status page"):
+        expect_http_code(webserver, "200", "http://localhost/nginx_status")
+
+    with subtest("localhost can access other page"):
+        expect_http_code(webserver, "200", "http://localhost/index.html")
+
+    with subtest("client can not access status page"):
+        expect_http_code(client, "403", "http://10.0.0.1/nginx_status")
+
+    with subtest("client can access other page"):
+        expect_http_code(client, "200", "http://10.0.0.1/index.html")
+  '';
+})
diff --git a/nixos/tests/nixos-test-driver/busybox.nix b/nixos/tests/nixos-test-driver/busybox.nix
new file mode 100644
index 0000000000000..426f4494436e2
--- /dev/null
+++ b/nixos/tests/nixos-test-driver/busybox.nix
@@ -0,0 +1,16 @@
+{
+  name = "Test that basic tests work when busybox is installed";
+
+  nodes = {
+    machine = ({ pkgs, ... }: {
+      environment.systemPackages = [
+        pkgs.busybox
+      ];
+    });
+  };
+
+  testScript = ''
+    start_all()
+    machine.wait_for_unit("multi-user.target")
+  '';
+}
diff --git a/nixos/tests/non-default-filesystems.nix b/nixos/tests/non-default-filesystems.nix
index 6233e8d265d0f..08a17107dd2fd 100644
--- a/nixos/tests/non-default-filesystems.nix
+++ b/nixos/tests/non-default-filesystems.nix
@@ -94,6 +94,8 @@ with pkgs.lib;
     makeTest {
       name = "non-default-filesystems-erofs";
 
+      meta.maintainers = with maintainers; [ nikstur ];
+
       nodes.machine = _: {
         virtualisation.qemu.drives = [{
           name = "non-default-filesystem";
@@ -128,4 +130,43 @@ with pkgs.lib;
         assert "erofs" in file_contents
       '';
     };
+
+  squashfs =
+    let
+      fsImage = "/tmp/non-default-filesystem.img";
+    in
+    makeTest {
+      name = "non-default-filesystems-squashfs";
+
+      meta.maintainers = with maintainers; [ nikstur ];
+
+      nodes.machine = {
+        virtualisation.qemu.drives = [{
+          name = "non-default-filesystem";
+          file = fsImage;
+          deviceExtraOpts.serial = "non-default";
+        }];
+
+        virtualisation.fileSystems."/non-default" = {
+          device = "/dev/disk/by-id/virtio-non-default";
+          fsType = "squashfs";
+          neededForBoot = true;
+        };
+      };
+
+      testScript = ''
+        import subprocess
+
+        with open("filesystem", "w") as f:
+          f.write("squashfs")
+
+        subprocess.run([
+          "${pkgs.squashfsTools}/bin/mksquashfs",
+          "filesystem",
+          "${fsImage}",
+        ])
+
+        assert "squashfs" in machine.succeed("cat /non-default/filesystem")
+      '';
+    };
 }
diff --git a/nixos/tests/noto-fonts-cjk-qt-default-weight.nix b/nixos/tests/noto-fonts-cjk-qt-default-weight.nix
index 678013cf3ab90..c2e0cb3adaebd 100644
--- a/nixos/tests/noto-fonts-cjk-qt-default-weight.nix
+++ b/nixos/tests/noto-fonts-cjk-qt-default-weight.nix
@@ -5,7 +5,7 @@ import ./make-test-python.nix ({ pkgs, lib, ... }: {
   nodes.machine = {
     imports = [ ./common/x11.nix ];
     fonts = {
-      enableDefaultFonts = false;
+      enableDefaultPackages = false;
       fonts = [ pkgs.noto-fonts-cjk-sans ];
     };
   };
diff --git a/nixos/tests/noto-fonts.nix b/nixos/tests/noto-fonts.nix
index 0515f16d101cd..edbb0db4cb7aa 100644
--- a/nixos/tests/noto-fonts.nix
+++ b/nixos/tests/noto-fonts.nix
@@ -4,9 +4,9 @@ import ./make-test-python.nix ({ pkgs, lib, ... }: {
 
   nodes.machine = {
     imports = [ ./common/x11.nix ];
-    environment.systemPackages = [ pkgs.gnome.gedit ];
+    environment.systemPackages = [ pkgs.gedit ];
     fonts = {
-      enableDefaultFonts = false;
+      enableDefaultPackages = false;
       fonts = with pkgs;[
         noto-fonts
         noto-fonts-cjk-sans
diff --git a/nixos/tests/osquery.nix b/nixos/tests/osquery.nix
new file mode 100644
index 0000000000000..9aa9820e50c52
--- /dev/null
+++ b/nixos/tests/osquery.nix
@@ -0,0 +1,52 @@
+import ./make-test-python.nix ({ lib, pkgs, ... }:
+
+let
+  config_refresh = "10";
+  nullvalue = "NULL";
+  utc = false;
+in
+{
+  name = "osquery";
+  meta.maintainers = with lib.maintainers; [ znewman01 lewo ];
+
+  nodes.machine = { config, pkgs, ... }: {
+    services.osquery = {
+      enable = true;
+
+      settings.options = { inherit nullvalue utc; };
+      flags = {
+        inherit config_refresh;
+        nullvalue = "IGNORED";
+      };
+    };
+  };
+
+  testScript = { nodes, ... }:
+    let
+      cfg = nodes.machine.services.osquery;
+    in
+    ''
+      machine.start()
+      machine.wait_for_unit("osqueryd.service")
+
+      # Stop the osqueryd service so that we can use osqueryi to check information stored in the database.
+      machine.wait_until_succeeds("systemctl stop osqueryd.service")
+
+      # osqueryd was able to query information about the host.
+      machine.succeed("echo 'SELECT address FROM etc_hosts LIMIT 1;' | osqueryi | tee /dev/console | grep -q '127.0.0.1'")
+
+      # osquery binaries respect configuration from the Nix config option.
+      machine.succeed("echo 'SELECT value FROM osquery_flags WHERE name = \"utc\";' | osqueryi | tee /dev/console | grep -q ${boolToString utc}")
+
+      # osquery binaries respect configuration from the Nix flags option.
+      machine.succeed("echo 'SELECT value FROM osquery_flags WHERE name = \"config_refresh\";' | osqueryi | tee /dev/console | grep -q ${config_refresh}")
+
+      # Demonstrate that osquery binaries prefer configuration plugin options over CLI flags.
+      # https://osquery.readthedocs.io/en/latest/deployment/configuration/#options.
+      machine.succeed("echo 'SELECT value FROM osquery_flags WHERE name = \"nullvalue\";' | osqueryi | tee /dev/console | grep -q ${nullvalue}")
+
+      # Module creates directories for default database_path and pidfile flag values.
+      machine.succeed("test -d $(dirname ${cfg.flags.database_path})")
+      machine.succeed("test -d $(dirname ${cfg.flags.pidfile})")
+    '';
+})
diff --git a/nixos/tests/paperless.nix b/nixos/tests/paperless.nix
index 7f36de4c29b71..ce6a4d8128dfd 100644
--- a/nixos/tests/paperless.nix
+++ b/nixos/tests/paperless.nix
@@ -30,20 +30,27 @@ import ./make-test-python.nix ({ lib, ... }: {
     with subtest("Task-queue gets ready"):
         machine.wait_for_unit("paperless-task-queue.service")
 
-    with subtest("Add a document via the web interface"):
+    with subtest("Add a png document via the web interface"):
         machine.succeed(
             "convert -size 400x40 xc:white -font 'DejaVu-Sans' -pointsize 20 -fill black "
             "-annotate +5+20 'hello web 16-10-2005' /tmp/webdoc.png"
         )
         machine.wait_until_succeeds("curl -u admin:admin -F document=@/tmp/webdoc.png -fs localhost:28981/api/documents/post_document/")
 
+    with subtest("Add a txt document via the web interface"):
+        machine.succeed(
+            "echo 'hello web 16-10-2005' > /tmp/webdoc.txt"
+        )
+        machine.wait_until_succeeds("curl -u admin:admin -F document=@/tmp/webdoc.txt -fs localhost:28981/api/documents/post_document/")
+
     with subtest("Documents are consumed"):
         machine.wait_until_succeeds(
-            "(($(curl -u admin:admin -fs localhost:28981/api/documents/ | jq .count) == 2))"
+            "(($(curl -u admin:admin -fs localhost:28981/api/documents/ | jq .count) == 3))"
         )
         docs = json.loads(machine.succeed("curl -u admin:admin -fs localhost:28981/api/documents/"))['results']
         assert "2005-10-16" in docs[0]['created']
         assert "2005-10-16" in docs[1]['created']
+        assert "2005-10-16" in docs[2]['created']
 
     # Detects gunicorn issues, see PR #190888
     with subtest("Document metadata can be accessed"):
@@ -52,5 +59,8 @@ import ./make-test-python.nix ({ lib, ... }: {
 
         metadata = json.loads(machine.succeed("curl -u admin:admin -fs localhost:28981/api/documents/2/metadata/"))
         assert "original_checksum" in metadata
+
+        metadata = json.loads(machine.succeed("curl -u admin:admin -fs localhost:28981/api/documents/3/metadata/"))
+        assert "original_checksum" in metadata
   '';
 })
diff --git a/nixos/tests/pgbouncer.nix b/nixos/tests/pgbouncer.nix
new file mode 100644
index 0000000000000..1e72327d42005
--- /dev/null
+++ b/nixos/tests/pgbouncer.nix
@@ -0,0 +1,61 @@
+import ./make-test-python.nix ({ pkgs, ... } :
+let
+  testAuthFile = pkgs.writeTextFile {
+    name = "authFile";
+    text = ''
+      "testuser" "testpass"
+    '';
+  };
+in
+{
+  name = "pgbouncer";
+  meta = with pkgs.lib.maintainers; {
+    maintainers = [ _1000101 ];
+  };
+  nodes = {
+    one = { config, pkgs, ... }: {
+
+      systemd.services.postgresql = {
+        postStart = ''
+            ${pkgs.postgresql}/bin/psql -U postgres -c "ALTER ROLE testuser WITH LOGIN PASSWORD 'testpass'";
+        '';
+      };
+
+      services = {
+        postgresql = {
+          enable = true;
+          ensureDatabases = [ "testdb" ];
+          ensureUsers = [
+          {
+            name = "testuser";
+            ensurePermissions = {
+              "DATABASE testdb" = "ALL PRIVILEGES";
+            };
+          }];
+          authentication = ''
+            local testdb testuser scram-sha-256
+          '';
+        };
+
+        pgbouncer = {
+          enable = true;
+          listenAddress = "localhost";
+          databases = { testdb = "host=/run/postgresql/ port=5432 auth_user=testuser dbname=testdb"; };
+          authType = "scram-sha-256";
+          authFile = testAuthFile;
+        };
+      };
+    };
+  };
+
+  testScript = ''
+    start_all()
+    one.wait_for_unit("default.target")
+    one.require_unit_state("pgbouncer.service", "active")
+
+    # Test if we can make a query through PgBouncer
+    one.wait_until_succeeds(
+        "psql 'postgres://testuser:testpass@localhost:6432/testdb' -c 'SELECT 1;'"
+    )
+  '';
+})
diff --git a/nixos/tests/plasma-bigscreen.nix b/nixos/tests/plasma-bigscreen.nix
index 1c61cafcbff33..2fe90fa9b539c 100644
--- a/nixos/tests/plasma-bigscreen.nix
+++ b/nixos/tests/plasma-bigscreen.nix
@@ -22,14 +22,11 @@ import ./make-test-python.nix ({ pkgs, ...} :
     users.users.alice.extraGroups = ["uinput"];
   };
 
-  testScript = { nodes, ... }: let
-    user = nodes.machine.users.users.alice;
-    xdo = "${pkgs.xdotool}/bin/xdotool";
-  in ''
+  testScript = { nodes, ... }: ''
     with subtest("Wait for login"):
         start_all()
-        machine.wait_for_file("${user.home}/.Xauthority")
-        machine.succeed("xauth merge ${user.home}/.Xauthority")
+        machine.wait_for_file("/tmp/xauth_*")
+        machine.succeed("xauth merge /tmp/xauth_*")
 
     with subtest("Check plasmashell started"):
         machine.wait_until_succeeds("pgrep plasmashell")
diff --git a/nixos/tests/plasma5-systemd-start.nix b/nixos/tests/plasma5-systemd-start.nix
index f584c1ec137aa..31a313af308b4 100644
--- a/nixos/tests/plasma5-systemd-start.nix
+++ b/nixos/tests/plasma5-systemd-start.nix
@@ -23,13 +23,11 @@ import ./make-test-python.nix ({ pkgs, ...} :
     };
   };
 
-  testScript = { nodes, ... }: let
-    user = nodes.machine.config.users.users.alice;
-  in ''
+  testScript = { nodes, ... }: ''
     with subtest("Wait for login"):
         start_all()
-        machine.wait_for_file("${user.home}/.Xauthority")
-        machine.succeed("xauth merge ${user.home}/.Xauthority")
+        machine.wait_for_file("/tmp/xauth_*")
+        machine.succeed("xauth merge /tmp/xauth_*")
 
     with subtest("Check plasmashell started"):
         machine.wait_until_succeeds("pgrep plasmashell")
diff --git a/nixos/tests/plasma5.nix b/nixos/tests/plasma5.nix
index b3836cf641d4b..fb8a5b73832ea 100644
--- a/nixos/tests/plasma5.nix
+++ b/nixos/tests/plasma5.nix
@@ -13,10 +13,8 @@ import ./make-test-python.nix ({ pkgs, ...} :
     services.xserver.enable = true;
     services.xserver.displayManager.sddm.enable = true;
     services.xserver.displayManager.defaultSession = "plasma";
-    services.xserver.desktopManager.plasma5 = {
-      enable = true;
-      excludePackages = [ pkgs.plasma5Packages.elisa ];
-    };
+    services.xserver.desktopManager.plasma5.enable = true;
+    environment.plasma5.excludePackages = [ pkgs.plasma5Packages.elisa ];
     services.xserver.displayManager.autoLogin = {
       enable = true;
       user = "alice";
@@ -25,13 +23,13 @@ import ./make-test-python.nix ({ pkgs, ...} :
   };
 
   testScript = { nodes, ... }: let
-    user = nodes.machine.config.users.users.alice;
+    user = nodes.machine.users.users.alice;
     xdo = "${pkgs.xdotool}/bin/xdotool";
   in ''
     with subtest("Wait for login"):
         start_all()
-        machine.wait_for_file("${user.home}/.Xauthority")
-        machine.succeed("xauth merge ${user.home}/.Xauthority")
+        machine.wait_for_file("/tmp/xauth_*")
+        machine.succeed("xauth merge /tmp/xauth_*")
 
     with subtest("Check plasmashell started"):
         machine.wait_until_succeeds("pgrep plasmashell")
@@ -46,6 +44,8 @@ import ./make-test-python.nix ({ pkgs, ...} :
     with subtest("Ensure Elisa is not installed"):
         machine.fail("which elisa")
 
+    machine.succeed("su - ${user.name} -c 'xauth merge /tmp/xauth_*'")
+
     with subtest("Run Dolphin"):
         machine.execute("su - ${user.name} -c 'DISPLAY=:0.0 dolphin >&2 &'")
         machine.wait_for_window(" Dolphin")
diff --git a/nixos/tests/prometheus-exporters.nix b/nixos/tests/prometheus-exporters.nix
index 23740dd98e3d1..64e2811beb060 100644
--- a/nixos/tests/prometheus-exporters.nix
+++ b/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;
diff --git a/nixos/tests/retroarch.nix b/nixos/tests/retroarch.nix
index f4bf232ea725e..0e5f60aa8be28 100644
--- a/nixos/tests/retroarch.nix
+++ b/nixos/tests/retroarch.nix
@@ -30,8 +30,8 @@ import ./make-test-python.nix ({ pkgs, ... }:
       in ''
         with subtest("Wait for login"):
             start_all()
-            machine.wait_for_file("${user.home}/.Xauthority")
-            machine.succeed("xauth merge ${user.home}/.Xauthority")
+            machine.wait_for_file("/tmp/xauth_*")
+            machine.succeed("xauth merge /tmp/xauth_*")
 
         with subtest("Check RetroArch started"):
             machine.wait_until_succeeds("pgrep retroarch")
diff --git a/nixos/tests/samba-wsdd.nix b/nixos/tests/samba-wsdd.nix
index 0e3185b0c6849..666a626d1b4a5 100644
--- a/nixos/tests/samba-wsdd.nix
+++ b/nixos/tests/samba-wsdd.nix
@@ -8,25 +8,23 @@ import ./make-test-python.nix ({ pkgs, ... }:
     client_wsdd = { pkgs, ... }: {
       services.samba-wsdd = {
         enable = true;
+        openFirewall = true;
         interface = "eth1";
         workgroup = "WORKGROUP";
         hostname = "CLIENT-WSDD";
         discovery = true;
         extraOptions = [ "--no-host" ];
       };
-      networking.firewall.allowedTCPPorts = [ 5357 ];
-      networking.firewall.allowedUDPPorts = [ 3702 ];
     };
 
     server_wsdd = { ... }: {
       services.samba-wsdd = {
         enable = true;
+        openFirewall = true;
         interface = "eth1";
         workgroup = "WORKGROUP";
         hostname = "SERVER-WSDD";
       };
-      networking.firewall.allowedTCPPorts = [ 5357 ];
-      networking.firewall.allowedUDPPorts = [ 3702 ];
     };
   };
 
diff --git a/nixos/tests/sddm.nix b/nixos/tests/sddm.nix
index c76a9683e66d0..b6c05deac05e4 100644
--- a/nixos/tests/sddm.nix
+++ b/nixos/tests/sddm.nix
@@ -23,14 +23,14 @@ let
       enableOCR = true;
 
       testScript = { nodes, ... }: let
-        user = nodes.machine.config.users.users.alice;
+        user = nodes.machine.users.users.alice;
       in ''
         start_all()
         machine.wait_for_text("(?i)select your user")
         machine.screenshot("sddm")
         machine.send_chars("${user.password}\n")
-        machine.wait_for_file("${user.home}/.Xauthority")
-        machine.succeed("xauth merge ${user.home}/.Xauthority")
+        machine.wait_for_file("/tmp/xauth_*")
+        machine.succeed("xauth merge /tmp/xauth_*")
         machine.wait_for_window("^IceWM ")
       '';
     };
@@ -55,12 +55,10 @@ let
         services.xserver.windowManager.icewm.enable = true;
       };
 
-      testScript = { nodes, ... }: let
-        user = nodes.machine.config.users.users.alice;
-      in ''
+      testScript = { nodes, ... }: ''
         start_all()
-        machine.wait_for_file("${user.home}/.Xauthority")
-        machine.succeed("xauth merge ${user.home}/.Xauthority")
+        machine.wait_for_file("/tmp/xauth_*")
+        machine.succeed("xauth merge /tmp/xauth_*")
         machine.wait_for_window("^IceWM ")
       '';
     };
diff --git a/nixos/tests/sftpgo.nix b/nixos/tests/sftpgo.nix
index ca55b9c962a07..8cd5675c1d4d4 100644
--- a/nixos/tests/sftpgo.nix
+++ b/nixos/tests/sftpgo.nix
@@ -12,8 +12,6 @@
 # would be a nice to have for the future.
 { pkgs, lib, ...  }:
 
-with lib;
-
 let
   inherit (import ./ssh-keys.nix pkgs) snakeOilPrivateKey snakeOilPublicKey;
 
@@ -54,7 +52,7 @@ let
       # inside the dataprovider they will be automatically created.
       # You have to create the folder on the filesystem yourself
       virtual_folders =
-        optional (isMemberOf config sharedFolderName user) {
+        lib.optional (lib.isMemberOf config sharedFolderName user) {
           name = sharedFolderName;
           mapped_path = "${config.services.sftpgo.dataDir}/${sharedFolderName}";
           virtual_path = "/${sharedFolderName}";
@@ -62,10 +60,10 @@ let
 
       # Defines the ACL on the virtual filesystem
       permissions =
-        recursiveUpdate {
+        lib.recursiveUpdate {
           "/" = [ "list" ];     # read-only top level directory
           "/private" = [ "*" ]; # private subdirectory, not shared with others
-        } (optionalAttrs (isMemberOf config "shared" user) {
+        } (lib.optionalAttrs (lib.isMemberOf config "shared" user) {
           "/shared" = [ "*" ];
         });
 
@@ -91,7 +89,7 @@ let
   # of users and folders to import to SFTPGo.
   loadDataJson = config: pkgs.writeText "users-and-folders.json" (builtins.toJSON {
     users =
-      mapAttrsToList (name: user: generateUserAttrSet config user) (normalUsers config);
+      lib.mapAttrsToList (name: user: lib.generateUserAttrSet config user) (normalUsers config);
 
     folders = [
       {
diff --git a/nixos/tests/sing-box.nix b/nixos/tests/sing-box.nix
index 43be89317642c..582d594be3fdb 100644
--- a/nixos/tests/sing-box.nix
+++ b/nixos/tests/sing-box.nix
@@ -8,7 +8,10 @@ import ./make-test-python.nix ({ lib, pkgs, ... }: {
 
   nodes.machine = { pkgs, ... }: {
     environment.systemPackages = [ pkgs.curl ];
-    services.nginx.enable = true;
+    services.nginx = {
+      enable = true;
+      statusPage = true;
+    };
     services.sing-box = {
       enable = true;
       settings = {
diff --git a/nixos/tests/sway.nix b/nixos/tests/sway.nix
index d95334c10e494..695d4a7708104 100644
--- a/nixos/tests/sway.nix
+++ b/nixos/tests/sway.nix
@@ -51,7 +51,7 @@ import ./make-test-python.nix ({ pkgs, lib, ... }: {
       '';
     };
 
-    fonts.fonts = [ pkgs.inconsolata ];
+    fonts.packages = [ pkgs.inconsolata ];
 
     # Automatically configure and start Sway when logging in on tty1:
     programs.bash.loginShellInit = ''
diff --git a/nixos/tests/switch-test.nix b/nixos/tests/switch-test.nix
index f891a2cb2f4c2..f44dede7fef45 100644
--- a/nixos/tests/switch-test.nix
+++ b/nixos/tests/switch-test.nix
@@ -70,6 +70,19 @@ in {
           };
         };
 
+        simpleServiceSeparateActivationScript.configuration = {
+          system.activatable = false;
+          systemd.services.test = {
+            wantedBy = [ "multi-user.target" ];
+            serviceConfig = {
+              Type = "oneshot";
+              RemainAfterExit = true;
+              ExecStart = "${pkgs.coreutils}/bin/true";
+              ExecReload = "${pkgs.coreutils}/bin/true";
+            };
+          };
+        };
+
         simpleServiceDifferentDescription.configuration = {
           imports = [ simpleService.configuration ];
           systemd.services.test.description = "Test unit";
@@ -482,9 +495,9 @@ in {
   };
 
   testScript = { nodes, ... }: let
-    originalSystem = nodes.machine.config.system.build.toplevel;
-    otherSystem = nodes.other.config.system.build.toplevel;
-    machine = nodes.machine.config.system.build.toplevel;
+    originalSystem = nodes.machine.system.build.toplevel;
+    otherSystem = nodes.other.system.build.toplevel;
+    machine = nodes.machine.system.build.toplevel;
 
     # Ensures failures pass through using pipefail, otherwise failing to
     # switch-to-configuration is hidden by the success of `tee`.
@@ -497,11 +510,15 @@ in {
   in /* python */ ''
     def switch_to_specialisation(system, name, action="test", fail=False):
         if name == "":
-            stc = f"{system}/bin/switch-to-configuration"
+            switcher = f"{system}/bin/switch-to-configuration"
         else:
-            stc = f"{system}/specialisation/{name}/bin/switch-to-configuration"
-        out = machine.fail(f"{stc} {action} 2>&1") if fail \
-            else machine.succeed(f"{stc} {action} 2>&1")
+            switcher = f"{system}/specialisation/{name}/bin/switch-to-configuration"
+        return run_switch(switcher, action, fail)
+
+    # like above but stc = switcher
+    def run_switch(switcher, action="test", fail=False):
+        out = machine.fail(f"{switcher} {action} 2>&1") if fail \
+            else machine.succeed(f"{switcher} {action} 2>&1")
         assert_lacks(out, "switch-to-configuration line")  # Perl warnings
         return out
 
@@ -639,6 +656,22 @@ in {
         assert_lacks(out, "the following new units were started:")
         assert_contains(out, "would start the following units: test.service\n")
 
+        out = switch_to_specialisation("${machine}", "", action="test")
+
+        # Ensure the service can be started when the activation script isn't in toplevel
+        # This is a lot like "Start a simple service", except activation-only deps could be gc-ed
+        out = run_switch("${nodes.machine.specialisation.simpleServiceSeparateActivationScript.configuration.system.build.separateActivationScript}/bin/switch-to-configuration");
+        assert_lacks(out, "installing dummy bootloader")  # test does not install a bootloader
+        assert_lacks(out, "stopping the following units:")
+        assert_lacks(out, "NOT restarting the following changed units:")
+        assert_contains(out, "reloading the following units: dbus.service\n")  # huh
+        assert_lacks(out, "\nrestarting the following units:")
+        assert_lacks(out, "\nstarting the following units:")
+        assert_contains(out, "the following new units were started: test.service\n")
+        machine.succeed("! test -e /run/current-system/activate")
+        machine.succeed("! test -e /run/current-system/dry-activate")
+        machine.succeed("! test -e /run/current-system/bin/switch-to-configuration")
+
         # Ensure \ works in unit names
         out = switch_to_specialisation("${machine}", "unitWithBackslash")
         assert_contains(out, "stopping the following units: test.service\n")
diff --git a/nixos/tests/syncthing-init.nix b/nixos/tests/syncthing-init.nix
index 5102c01278320..195c157ffb6e8 100644
--- a/nixos/tests/syncthing-init.nix
+++ b/nixos/tests/syncthing-init.nix
@@ -10,14 +10,14 @@ in {
   nodes.machine = {
     services.syncthing = {
       enable = true;
-      devices.${testName} = {
+      settings.devices.testDevice = {
         id = testId;
       };
-      folders.testFolder = {
+      settings.folders.testFolder = {
         path = "/tmp/test";
-        devices = [ testName ];
+        devices = [ "testDevice" ];
       };
-      extraOptions.gui.user = "guiUser";
+      settings.gui.user = "guiUser";
     };
   };
 
diff --git a/nixos/tests/syncthing-no-settings.nix b/nixos/tests/syncthing-no-settings.nix
new file mode 100644
index 0000000000000..fee122b5e35c0
--- /dev/null
+++ b/nixos/tests/syncthing-no-settings.nix
@@ -0,0 +1,18 @@
+import ./make-test-python.nix ({ lib, pkgs, ... }: {
+  name = "syncthing";
+  meta.maintainers = with pkgs.lib.maintainers; [ chkno ];
+
+  nodes = {
+    a = {
+      environment.systemPackages = with pkgs; [ curl libxml2 syncthing ];
+      services.syncthing = {
+        enable = true;
+      };
+    };
+  };
+  # Test that indeed a syncthing-init.service systemd service is not created.
+  #
+  testScript = /* python */ ''
+    a.succeed("systemctl list-unit-files | awk '$1 == \"syncthing-init.service\" {exit 1;}'")
+  '';
+})
diff --git a/nixos/tests/systemd-initrd-networkd-ssh.nix b/nixos/tests/systemd-initrd-networkd-ssh.nix
index 30bd1950de242..6aaa6c828f7bd 100644
--- a/nixos/tests/systemd-initrd-networkd-ssh.nix
+++ b/nixos/tests/systemd-initrd-networkd-ssh.nix
@@ -2,15 +2,16 @@ import ./make-test-python.nix ({ lib, ... }: {
   name = "systemd-initrd-network-ssh";
   meta.maintainers = [ lib.maintainers.elvishjerricco ];
 
-  nodes = with lib; {
+  nodes = {
     server = { config, pkgs, ... }: {
-      environment.systemPackages = [pkgs.cryptsetup];
+      environment.systemPackages = [ pkgs.cryptsetup ];
       boot.loader.systemd-boot.enable = true;
       boot.loader.timeout = 0;
       virtualisation = {
         emptyDiskImages = [ 4096 ];
         useBootLoader = true;
-        # Booting off the encrypted disk requires an available init script from the Nix store
+        # Booting off the encrypted disk requires an available init script from
+        # the Nix store
         mountHostNixStore = true;
         useEFIBoot = true;
       };
@@ -26,7 +27,7 @@ import ./make-test-python.nix ({ lib, ... }: {
           enable = true;
           ssh = {
             enable = true;
-            authorizedKeys = [ (readFile ./initrd-network-ssh/id_ed25519.pub) ];
+            authorizedKeys = [ (lib.readFile ./initrd-network-ssh/id_ed25519.pub) ];
             port = 22;
             # Terrible hack so it works with useBootLoader
             hostKeys = [ { outPath = "${./initrd-network-ssh/ssh_host_ed25519_key}"; } ];
@@ -38,13 +39,13 @@ import ./make-test-python.nix ({ lib, ... }: {
     client = { config, ... }: {
       environment.etc = {
         knownHosts = {
-          text = concatStrings [
+          text = lib.concatStrings [
             "server,"
             "${
-              toString (head (splitString " " (toString
-                (elemAt (splitString "\n" config.networking.extraHosts) 2))))
+              toString (lib.head (lib.splitString " " (toString
+                (lib.elemAt (lib.splitString "\n" config.networking.extraHosts) 2))))
             } "
-            "${readFile ./initrd-network-ssh/ssh_host_ed25519_key.pub}"
+            "${lib.readFile ./initrd-network-ssh/ssh_host_ed25519_key.pub}"
           ];
         };
         sshKey = {
diff --git a/nixos/tests/systemd-initrd-swraid.nix b/nixos/tests/systemd-initrd-swraid.nix
index 0d5a1c6354d05..d87170c925742 100644
--- a/nixos/tests/systemd-initrd-swraid.nix
+++ b/nixos/tests/systemd-initrd-swraid.nix
@@ -14,17 +14,17 @@ import ./make-test-python.nix ({ lib, pkgs, ... }: {
     boot.loader.efi.canTouchEfiVariables = true;
 
     environment.systemPackages = with pkgs; [ mdadm e2fsprogs ]; # for mdadm and mkfs.ext4
+    boot.swraid = {
+      enable = true;
+      mdadmConf = ''
+        ARRAY /dev/md0 devices=/dev/vdb,/dev/vdc
+      '';
+    };
     boot.initrd = {
       systemd = {
         enable = true;
         emergencyAccess = true;
       };
-      services.swraid = {
-        enable = true;
-        mdadmConf = ''
-          ARRAY /dev/md0 devices=/dev/vdb,/dev/vdc
-        '';
-      };
       kernelModules = [ "raid0" ];
     };
 
diff --git a/nixos/tests/systemd-networkd-dhcpserver-static-leases.nix b/nixos/tests/systemd-networkd-dhcpserver-static-leases.nix
index a8254a158016b..f6d5411aa5cad 100644
--- a/nixos/tests/systemd-networkd-dhcpserver-static-leases.nix
+++ b/nixos/tests/systemd-networkd-dhcpserver-static-leases.nix
@@ -3,7 +3,7 @@
 import ./make-test-python.nix ({ lib, ... }: {
   name = "systemd-networkd-dhcpserver-static-leases";
   meta = with lib.maintainers; {
-    maintainers = [ veehaitch tomfitzhenry ];
+    maintainers = [ veehaitch ];
   };
   nodes = {
     router = {
diff --git a/nixos/tests/systemd-networkd-dhcpserver.nix b/nixos/tests/systemd-networkd-dhcpserver.nix
index a016f059456e1..cf0ccb7442118 100644
--- a/nixos/tests/systemd-networkd-dhcpserver.nix
+++ b/nixos/tests/systemd-networkd-dhcpserver.nix
@@ -10,7 +10,7 @@
 import ./make-test-python.nix ({pkgs, ...}: {
   name = "systemd-networkd-dhcpserver";
   meta = with pkgs.lib.maintainers; {
-    maintainers = [ tomfitzhenry ];
+    maintainers = [ ];
   };
   nodes = {
     router = { config, pkgs, ... }: {
diff --git a/nixos/tests/systemd-networkd-ipv6-prefix-delegation.nix b/nixos/tests/systemd-networkd-ipv6-prefix-delegation.nix
index e6bed6b9218ff..54f371e6c070f 100644
--- a/nixos/tests/systemd-networkd-ipv6-prefix-delegation.nix
+++ b/nixos/tests/systemd-networkd-ipv6-prefix-delegation.nix
@@ -67,14 +67,14 @@ import ./make-test-python.nix ({ pkgs, lib, ... }: {
             interfaces-config.interfaces = [ "eth1" ];
             subnet6 = [ {
               interface = "eth1";
-              subnet = "2001:DB8:F::/36";
+              subnet = "2001:DB8::/32";
               pd-pools = [ {
-                prefix = "2001:DB8:F::";
+                prefix = "2001:DB8:1000::";
                 prefix-len = 36;
                 delegated-len = 48;
               } ];
               pools = [ {
-                pool = "2001:DB8:0000:0000:FFFF::-2001:DB8:0000:0000:FFFF::FFFF";
+                pool = "2001:DB8:0000:0000::-2001:DB8:0FFF:FFFF::FFFF";
               } ];
             } ];
 
diff --git a/nixos/tests/systemd-nspawn.nix b/nixos/tests/systemd-nspawn.nix
index bc77ee2a4d158..1a4251ef069e8 100644
--- a/nixos/tests/systemd-nspawn.nix
+++ b/nixos/tests/systemd-nspawn.nix
@@ -1,26 +1,6 @@
 import ./make-test-python.nix ({pkgs, lib, ...}:
 let
-  gpgKeyring = (pkgs.runCommand "gpg-keyring" { buildInputs = [ pkgs.gnupg ]; } ''
-    mkdir -p $out
-    export GNUPGHOME=$out
-    cat > foo <<EOF
-      %echo Generating a basic OpenPGP key
-      %no-protection
-      Key-Type: DSA
-      Key-Length: 1024
-      Subkey-Type: ELG-E
-      Subkey-Length: 1024
-      Name-Real: Bob Foobar
-      Name-Email: bob@foo.bar
-      Expire-Date: 0
-      # Do a commit here, so that we can later print "done"
-      %commit
-      %echo done
-    EOF
-    gpg --batch --generate-key foo
-    rm $out/S.gpg-agent $out/S.gpg-agent.*
-    gpg --export bob@foo.bar -a > $out/pubkey.gpg
-  '');
+  gpgKeyring = import ./common/gpg-keyring.nix { inherit pkgs; };
 
   nspawnImages = (pkgs.runCommand "localhost" { buildInputs = [ pkgs.coreutils pkgs.gnupg ]; } ''
     mkdir -p $out
diff --git a/nixos/tests/systemd-shutdown.nix b/nixos/tests/systemd-shutdown.nix
index dad8167f198fc..ca6754046f57a 100644
--- a/nixos/tests/systemd-shutdown.nix
+++ b/nixos/tests/systemd-shutdown.nix
@@ -22,6 +22,6 @@ in {
     machine.wait_for_console_text("Unmounting '/oldroot'")
     machine.wait_for_console_text("${msg}")
     # Don't try to sync filesystems
-    machine.booted = False
+    machine.wait_for_shutdown()
   '';
 })
diff --git a/nixos/tests/systemd-sysupdate.nix b/nixos/tests/systemd-sysupdate.nix
new file mode 100644
index 0000000000000..37811605dbb2b
--- /dev/null
+++ b/nixos/tests/systemd-sysupdate.nix
@@ -0,0 +1,66 @@
+# Tests downloading a signed update aritfact from a server to a target machine.
+# This test does not rely on the `systemd.timer` units provided by the
+# `systemd-sysupdate` module but triggers the `systemd-sysupdate` service
+# manually to make the test more robust.
+
+{ lib, pkgs, ... }:
+
+let
+  gpgKeyring = import ./common/gpg-keyring.nix { inherit pkgs; };
+in
+{
+  name = "systemd-sysupdate";
+
+  meta.maintainers = with lib.maintainers; [ nikstur ];
+
+  nodes = {
+    server = { pkgs, ... }: {
+      networking.firewall.enable = false;
+      services.nginx = {
+        enable = true;
+        virtualHosts."server" = {
+          root = pkgs.runCommand "sysupdate-artifacts" { buildInputs = [ pkgs.gnupg ]; } ''
+            mkdir -p $out
+            cd $out
+
+            echo "nixos" > nixos_1.efi
+            sha256sum nixos_1.efi > SHA256SUMS
+
+            export GNUPGHOME="$(mktemp -d)"
+            cp -R ${gpgKeyring}/* $GNUPGHOME
+
+            gpg --batch --sign --detach-sign --output SHA256SUMS.gpg SHA256SUMS
+          '';
+        };
+      };
+    };
+
+    target = {
+      systemd.sysupdate = {
+        enable = true;
+        transfers = {
+          "uki" = {
+            Source = {
+              Type = "url-file";
+              Path = "http://server/";
+              MatchPattern = "nixos_@v.efi";
+            };
+            Target = {
+              Path = "/boot/EFI/Linux";
+              MatchPattern = "nixos_@v.efi";
+            };
+          };
+        };
+      };
+
+      environment.etc."systemd/import-pubring.gpg".source = "${gpgKeyring}/pubkey.gpg";
+    };
+  };
+
+  testScript = ''
+    server.wait_for_unit("nginx.service")
+
+    target.succeed("systemctl start systemd-sysupdate")
+    assert "nixos" in target.wait_until_succeeds("cat /boot/EFI/Linux/nixos_1.efi", timeout=5)
+  '';
+}
diff --git a/nixos/tests/terminal-emulators.nix b/nixos/tests/terminal-emulators.nix
index 4269d05056d8c..6d76cc8e57412 100644
--- a/nixos/tests/terminal-emulators.nix
+++ b/nixos/tests/terminal-emulators.nix
@@ -35,6 +35,8 @@ let tests = {
 
       darktile.pkg = p: p.darktile;
 
+      deepin-terminal.pkg = p: p.deepin.deepin-terminal;
+
       eterm.pkg = p: p.eterm;
       eterm.executable = "Eterm";
       eterm.pinkValue = "#D40055";
@@ -72,6 +74,9 @@ let tests = {
       qterminal.pkg = p: p.lxqt.qterminal;
       qterminal.kill = true;
 
+      rio.pkg = p: p.rio;
+      rio.cmd = "rio -e $command";
+
       roxterm.pkg = p: p.roxterm;
       roxterm.cmd = "roxterm -e $command";
 
@@ -118,7 +123,7 @@ in mapAttrs (name: { pkg, executable ? name, cmd ? "SHELL=$command ${executable}
     maintainers = [ jjjollyjim ];
   };
 
-  machine = { pkgsInner, ... }:
+  nodes.machine = { pkgsInner, ... }:
 
   {
     imports = [ ./common/x11.nix ./common/user-account.nix ];
diff --git a/nixos/tests/twingate.nix b/nixos/tests/twingate.nix
new file mode 100644
index 0000000000000..f8bede09d9f26
--- /dev/null
+++ b/nixos/tests/twingate.nix
@@ -0,0 +1,14 @@
+{
+  name = "twingate";
+
+  nodes.machine.services.twingate.enable = true;
+
+  testScript = { nodes, ... }: ''
+    machine.wait_for_unit("twingate.service")
+    machine.succeed("twingate --version | grep '${nodes.machine.services.twingate.package.version}' >&2")
+    machine.succeed("twingate config log-level 'debug'")
+    machine.systemctl("restart twingate.service")
+    machine.succeed("grep 'debug' /etc/twingate/log_level.conf >&2")
+    machine.succeed("twingate config log-level | grep 'debug' >&2")
+  '';
+}
diff --git a/nixos/tests/typesense.nix b/nixos/tests/typesense.nix
new file mode 100644
index 0000000000000..4f07a2e194be8
--- /dev/null
+++ b/nixos/tests/typesense.nix
@@ -0,0 +1,23 @@
+import ./make-test-python.nix ({ pkgs, ... }: let
+  testPort = 8108;
+in {
+  name = "typesense";
+  meta.maintainers = with pkgs.lib.maintainers; [ oddlama ];
+
+  nodes.machine = { ... }: {
+    services.typesense = {
+      enable = true;
+      apiKeyFile = pkgs.writeText "typesense-api-key" "dummy";
+      settings.server = {
+        api-port = testPort;
+        api-address = "0.0.0.0";
+      };
+    };
+  };
+
+  testScript = ''
+    machine.wait_for_unit("typesense.service")
+    machine.wait_for_open_port(${toString testPort})
+    assert machine.succeed("curl --fail http://localhost:${toString testPort}/health") == '{"ok":true}'
+  '';
+})
diff --git a/nixos/tests/vscodium.nix b/nixos/tests/vscodium.nix
index 3eda8b6cfb206..d817ce927ff8d 100644
--- a/nixos/tests/vscodium.nix
+++ b/nixos/tests/vscodium.nix
@@ -8,7 +8,7 @@ let
       environment.variables.NIXOS_OZONE_WL = "1";
       environment.variables.DISPLAY = "do not use";
 
-      fonts.fonts = with pkgs; [ dejavu_fonts ];
+      fonts.packages = with pkgs; [ dejavu_fonts ];
     };
     xorg = { pkgs, ... }: {
       imports = [ ./common/user-account.nix ./common/x11.nix ];
diff --git a/nixos/tests/web-apps/peering-manager.nix b/nixos/tests/web-apps/peering-manager.nix
index 56b7eebfadffd..3f0acd560d132 100644
--- a/nixos/tests/web-apps/peering-manager.nix
+++ b/nixos/tests/web-apps/peering-manager.nix
@@ -34,7 +34,7 @@ import ../make-test-python.nix ({ lib, pkgs, ... }: {
             "sudo -u postgres psql -c 'ALTER USER \"peering-manager\" WITH SUPERUSER;'"
         )
         machine.succeed(
-            "cd ${nodes.machine.config.system.build.peeringManagerPkg}/opt/peering-manager ; peering-manager-manage test --no-input"
+            "cd ${nodes.machine.system.build.peeringManagerPkg}/opt/peering-manager ; peering-manager-manage test --no-input"
         )
   '';
 })
diff --git a/nixos/tests/wpa_supplicant.nix b/nixos/tests/wpa_supplicant.nix
index a05a79e8367d9..8c701ca7d5f71 100644
--- a/nixos/tests/wpa_supplicant.nix
+++ b/nixos/tests/wpa_supplicant.nix
@@ -2,63 +2,160 @@ import ./make-test-python.nix ({ pkgs, lib, ...}:
 {
   name = "wpa_supplicant";
   meta = with lib.maintainers; {
-    maintainers = [ rnhmjoj ];
+    maintainers = [ oddlama rnhmjoj ];
   };
 
-  nodes.machine = { ... }: {
-    imports = [ ../modules/profiles/minimal.nix ];
+  nodes = let
+    machineWithHostapd = extraConfigModule: { ... }: {
+      imports = [
+        ../modules/profiles/minimal.nix
+        extraConfigModule
+      ];
+
+      # add a virtual wlan interface
+      boot.kernelModules = [ "mac80211_hwsim" ];
+
+      # wireless access point
+      services.hostapd = {
+        enable = true;
+        radios.wlan0 = {
+          band = "2g";
+          countryCode = "US";
+          networks = {
+            wlan0 = {
+              ssid = "nixos-test-sae";
+              authentication = {
+                mode = "wpa3-sae";
+                saePasswords = [ { password = "reproducibility"; } ];
+              };
+              bssid = "02:00:00:00:00:00";
+            };
+            wlan0-1 = {
+              ssid = "nixos-test-mixed";
+              authentication = {
+                mode = "wpa3-sae-transition";
+                saeAddToMacAllow = true;
+                saePasswordsFile = pkgs.writeText "password" "reproducibility";
+                wpaPasswordFile = pkgs.writeText "password" "reproducibility";
+              };
+              bssid = "02:00:00:00:00:01";
+            };
+            wlan0-2 = {
+              ssid = "nixos-test-wpa2";
+              authentication = {
+                mode = "wpa2-sha256";
+                wpaPassword = "reproducibility";
+              };
+              bssid = "02:00:00:00:00:02";
+            };
+          };
+        };
+      };
 
-    # add a virtual wlan interface
-    boot.kernelModules = [ "mac80211_hwsim" ];
+      # wireless client
+      networking.wireless = {
+        # the override is needed because the wifi is
+        # disabled with mkVMOverride in qemu-vm.nix.
+        enable = lib.mkOverride 0 true;
+        userControlled.enable = true;
+        interfaces = [ "wlan1" ];
+        fallbackToWPA2 = lib.mkDefault true;
+
+        # networks will be added on-demand below for the specific
+        # network that should be tested
+
+        # secrets
+        environmentFile = pkgs.writeText "wpa-secrets" ''
+          PSK_NIXOS_TEST="reproducibility"
+        '';
+      };
+    };
+  in {
+    basic = { ... }: {
+      imports = [ ../modules/profiles/minimal.nix ];
+
+      # add a virtual wlan interface
+      boot.kernelModules = [ "mac80211_hwsim" ];
+
+      # wireless client
+      networking.wireless = {
+        # the override is needed because the wifi is
+        # disabled with mkVMOverride in qemu-vm.nix.
+        enable = lib.mkOverride 0 true;
+        userControlled.enable = true;
+        interfaces = [ "wlan1" ];
+        fallbackToWPA2 = true;
+
+        networks = {
+          # test WPA2 fallback
+          mixed-wpa = {
+            psk = "password";
+            authProtocols = [ "WPA-PSK" "SAE" ];
+          };
+          sae-only = {
+            psk = "password";
+            authProtocols = [ "SAE" ];
+          };
+
+          # secrets substitution test cases
+          test1.psk = "@PSK_VALID@";              # should be replaced
+          test2.psk = "@PSK_SPECIAL@";            # should be replaced
+          test3.psk = "@PSK_MISSING@";            # should not be replaced
+          test4.psk = "P@ssowrdWithSome@tSymbol"; # should not be replaced
+        };
 
-    # wireless access point
-    services.hostapd = {
-      enable = true;
-      wpa = true;
-      interface = "wlan0";
-      ssid = "nixos-test";
-      wpaPassphrase = "reproducibility";
+        # secrets
+        environmentFile = pkgs.writeText "wpa-secrets" ''
+          PSK_VALID="S0m3BadP4ssw0rd";
+          # taken from https://github.com/minimaxir/big-list-of-naughty-strings
+          PSK_SPECIAL=",./;'[]\-= <>?:\"{}|_+ !@#$%^\&*()`~";
+        '';
+      };
     };
 
-    # wireless client
-    networking.wireless = {
-      # the override is needed because the wifi is
-      # disabled with mkVMOverride in qemu-vm.nix.
-      enable = lib.mkOverride 0 true;
-      userControlled.enable = true;
-      interfaces = [ "wlan1" ];
-      fallbackToWPA2 = true;
-
-      networks = {
-        # test WPA2 fallback
-        mixed-wpa = {
-          psk = "password";
-          authProtocols = [ "WPA-PSK" "SAE" ];
-        };
-        sae-only = {
-          psk = "password";
+    # Test connecting to the SAE-only hotspot using SAE
+    machineSae = machineWithHostapd {
+      networking.wireless = {
+        fallbackToWPA2 = false;
+        networks.nixos-test-sae = {
+          psk = "@PSK_NIXOS_TEST@";
           authProtocols = [ "SAE" ];
         };
+      };
+    };
 
-        # test network
-        nixos-test.psk = "@PSK_NIXOS_TEST@";
-
-        # secrets substitution test cases
-        test1.psk = "@PSK_VALID@";              # should be replaced
-        test2.psk = "@PSK_SPECIAL@";            # should be replaced
-        test3.psk = "@PSK_MISSING@";            # should not be replaced
-        test4.psk = "P@ssowrdWithSome@tSymbol"; # should not be replaced
+    # Test connecting to the SAE and WPA2 mixed hotspot using SAE
+    machineMixedUsingSae = machineWithHostapd {
+      networking.wireless = {
+        fallbackToWPA2 = false;
+        networks.nixos-test-mixed = {
+          psk = "@PSK_NIXOS_TEST@";
+          authProtocols = [ "SAE" ];
+        };
       };
+    };
 
-      # secrets
-      environmentFile = pkgs.writeText "wpa-secrets" ''
-        PSK_NIXOS_TEST="reproducibility"
-        PSK_VALID="S0m3BadP4ssw0rd";
-        # taken from https://github.com/minimaxir/big-list-of-naughty-strings
-        PSK_SPECIAL=",./;'[]\-= <>?:\"{}|_+ !@#$%^\&*()`~";
-      '';
+    # Test connecting to the SAE and WPA2 mixed hotspot using WPA2
+    machineMixedUsingWpa2 = machineWithHostapd {
+      networking.wireless = {
+        fallbackToWPA2 = true;
+        networks.nixos-test-mixed = {
+          psk = "@PSK_NIXOS_TEST@";
+          authProtocols = [ "WPA-PSK-SHA256" ];
+        };
+      };
     };
 
+    # Test connecting to the WPA2 legacy hotspot using WPA2
+    machineWpa2 = machineWithHostapd {
+      networking.wireless = {
+        fallbackToWPA2 = true;
+        networks.nixos-test-wpa2 = {
+          psk = "@PSK_NIXOS_TEST@";
+          authProtocols = [ "WPA-PSK-SHA256" ];
+        };
+      };
+    };
   };
 
   testScript =
@@ -66,30 +163,47 @@ import ./make-test-python.nix ({ pkgs, lib, ...}:
       config_file = "/run/wpa_supplicant/wpa_supplicant.conf"
 
       with subtest("Configuration file is inaccessible to other users"):
-          machine.wait_for_file(config_file)
-          machine.fail(f"sudo -u nobody ls {config_file}")
+          basic.wait_for_file(config_file)
+          basic.fail(f"sudo -u nobody ls {config_file}")
 
       with subtest("Secrets variables have been substituted"):
-          machine.fail(f"grep -q @PSK_VALID@ {config_file}")
-          machine.fail(f"grep -q @PSK_SPECIAL@ {config_file}")
-          machine.succeed(f"grep -q @PSK_MISSING@ {config_file}")
-          machine.succeed(f"grep -q P@ssowrdWithSome@tSymbol {config_file}")
+          basic.fail(f"grep -q @PSK_VALID@ {config_file}")
+          basic.fail(f"grep -q @PSK_SPECIAL@ {config_file}")
+          basic.succeed(f"grep -q @PSK_MISSING@ {config_file}")
+          basic.succeed(f"grep -q P@ssowrdWithSome@tSymbol {config_file}")
 
       with subtest("WPA2 fallbacks have been generated"):
-          assert int(machine.succeed(f"grep -c sae-only {config_file}")) == 1
-          assert int(machine.succeed(f"grep -c mixed-wpa {config_file}")) == 2
+          assert int(basic.succeed(f"grep -c sae-only {config_file}")) == 1
+          assert int(basic.succeed(f"grep -c mixed-wpa {config_file}")) == 2
 
       # save file for manual inspection
-      machine.copy_from_vm(config_file)
+      basic.copy_from_vm(config_file)
 
       with subtest("Daemon is running and accepting connections"):
-          machine.wait_for_unit("wpa_supplicant-wlan1.service")
-          status = machine.succeed("wpa_cli -i wlan1 status")
+          basic.wait_for_unit("wpa_supplicant-wlan1.service")
+          status = basic.succeed("wpa_cli -i wlan1 status")
           assert "Failed to connect" not in status, \
                  "Failed to connect to the daemon"
 
-      with subtest("Daemon can connect to the access point"):
-          machine.wait_until_succeeds(
+      machineSae.wait_for_unit("hostapd.service")
+      machineSae.copy_from_vm("/run/hostapd/wlan0.hostapd.conf")
+      with subtest("Daemon can connect to the SAE access point using SAE"):
+          machineSae.wait_until_succeeds(
+            "wpa_cli -i wlan1 status | grep -q wpa_state=COMPLETED"
+          )
+
+      with subtest("Daemon can connect to the SAE and WPA2 mixed access point using SAE"):
+          machineMixedUsingSae.wait_until_succeeds(
+            "wpa_cli -i wlan1 status | grep -q wpa_state=COMPLETED"
+          )
+
+      with subtest("Daemon can connect to the SAE and WPA2 mixed access point using WPA2"):
+          machineMixedUsingWpa2.wait_until_succeeds(
+            "wpa_cli -i wlan1 status | grep -q wpa_state=COMPLETED"
+          )
+
+      with subtest("Daemon can connect to the WPA2 access point using WPA2"):
+          machineWpa2.wait_until_succeeds(
             "wpa_cli -i wlan1 status | grep -q wpa_state=COMPLETED"
           )
     '';
diff --git a/nixos/tests/wrappers.nix b/nixos/tests/wrappers.nix
index 08c1ad0b6b999..391e9b42b45bd 100644
--- a/nixos/tests/wrappers.nix
+++ b/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"))
     '';
 })