summary refs log tree commit diff
path: root/nixos/tests
diff options
context:
space:
mode:
authorJoão Figueira <jmc.figueira@campus.fct.unl.pt>2022-01-24 01:33:13 +0000
committerGitHub <noreply@github.com>2022-01-24 01:33:13 +0000
commitae10fad86b2d7a357825c19a563dd5dac60a67da (patch)
treea2bde77769ba6055989c27aaf1ac7287ad56d640 /nixos/tests
parent0ac894e7d1d91e32fdcdc901229f16f27f321374 (diff)
parent8dbc4efd40169a8e3e57375b08d78205e5f83ff0 (diff)
downloadnixpkgs-ae10fad86b2d7a357825c19a563dd5dac60a67da.tar
nixpkgs-ae10fad86b2d7a357825c19a563dd5dac60a67da.tar.gz
nixpkgs-ae10fad86b2d7a357825c19a563dd5dac60a67da.tar.bz2
nixpkgs-ae10fad86b2d7a357825c19a563dd5dac60a67da.tar.lz
nixpkgs-ae10fad86b2d7a357825c19a563dd5dac60a67da.tar.xz
nixpkgs-ae10fad86b2d7a357825c19a563dd5dac60a67da.tar.zst
nixpkgs-ae10fad86b2d7a357825c19a563dd5dac60a67da.zip
Merge branch 'master' into wine-wayland
Diffstat (limited to 'nixos/tests')
-rw-r--r--nixos/tests/acme.nix12
-rw-r--r--nixos/tests/adguardhome.nix57
-rw-r--r--nixos/tests/all-tests.nix23
-rw-r--r--nixos/tests/boot-stage1.nix2
-rw-r--r--nixos/tests/borgbackup.nix2
-rw-r--r--nixos/tests/btrbk.nix2
-rw-r--r--nixos/tests/dnsdist.nix48
-rw-r--r--nixos/tests/ergochat.nix97
-rw-r--r--nixos/tests/frr.nix104
-rw-r--r--nixos/tests/google-oslogin/server.nix2
-rw-r--r--nixos/tests/handbrake.nix33
-rw-r--r--nixos/tests/installer.nix10
-rw-r--r--nixos/tests/invoiceplane.nix82
-rw-r--r--nixos/tests/jibri.nix2
-rw-r--r--nixos/tests/k3s-single-node-docker.nix84
-rw-r--r--nixos/tests/k3s-single-node.nix82
-rw-r--r--nixos/tests/k3s.nix78
-rw-r--r--nixos/tests/kubernetes/base.nix7
-rw-r--r--nixos/tests/libreswan.nix4
-rw-r--r--nixos/tests/lorri/default.nix2
-rw-r--r--nixos/tests/mysql/common.nix10
-rw-r--r--nixos/tests/mysql/mariadb-galera-mariabackup.nix233
-rw-r--r--nixos/tests/mysql/mariadb-galera-rsync.nix226
-rw-r--r--nixos/tests/mysql/mariadb-galera.nix250
-rw-r--r--nixos/tests/mysql/mysql-autobackup.nix81
-rw-r--r--nixos/tests/mysql/mysql-backup.nix104
-rw-r--r--nixos/tests/mysql/mysql-replication.nix158
-rw-r--r--nixos/tests/mysql/mysql.nix340
-rw-r--r--nixos/tests/networking.nix10
-rw-r--r--nixos/tests/nextcloud/with-mysql-and-memcached.nix15
-rw-r--r--nixos/tests/rstudio-server.nix30
-rw-r--r--nixos/tests/starship.nix42
-rw-r--r--nixos/tests/sway.nix47
-rw-r--r--nixos/tests/switch-test.nix70
-rw-r--r--nixos/tests/systemd-boot.nix141
-rw-r--r--nixos/tests/systemd-networkd-vrf.nix2
-rw-r--r--nixos/tests/teleport.nix99
-rw-r--r--nixos/tests/thelounge.nix29
-rw-r--r--nixos/tests/tinywl.nix56
-rw-r--r--nixos/tests/tsm-client-gui.nix57
-rw-r--r--nixos/tests/vscodium.nix50
-rw-r--r--nixos/tests/wordpress.nix9
-rw-r--r--nixos/tests/xmonad.nix99
-rw-r--r--nixos/tests/xmpp/prosody-mysql.nix92
44 files changed, 1865 insertions, 1118 deletions
diff --git a/nixos/tests/acme.nix b/nixos/tests/acme.nix
index 0dd7743c52b..2dd06a50f40 100644
--- a/nixos/tests/acme.nix
+++ b/nixos/tests/acme.nix
@@ -54,15 +54,15 @@ import ./make-test-python.nix ({ pkgs, lib, ... }: let
     baseConfig = { nodes, config, specialConfig ? {} }: lib.mkMerge [
       {
         security.acme = {
-          defaults = (dnsConfig nodes) // {
-            inherit group;
-          };
+          defaults = (dnsConfig nodes);
           # One manual wildcard cert
           certs."example.test" = {
             domain = "*.example.test";
           };
         };
 
+        users.users."${config.services."${server}".user}".extraGroups = ["acme"];
+
         services."${server}" = {
           enable = true;
           virtualHosts = {
@@ -252,15 +252,15 @@ in {
       } // (let
         baseCaddyConfig = { nodes, config, ... }: {
           security.acme = {
-            defaults = (dnsConfig nodes) // {
-              group = config.services.caddy.group;
-            };
+            defaults = (dnsConfig nodes);
             # One manual wildcard cert
             certs."example.test" = {
               domain = "*.example.test";
             };
           };
 
+          users.users."${config.services.caddy.user}".extraGroups = ["acme"];
+
           services.caddy = {
             enable = true;
             virtualHosts."a.exmaple.test" = {
diff --git a/nixos/tests/adguardhome.nix b/nixos/tests/adguardhome.nix
new file mode 100644
index 00000000000..ddbe8ff9c11
--- /dev/null
+++ b/nixos/tests/adguardhome.nix
@@ -0,0 +1,57 @@
+import ./make-test-python.nix {
+  name = "adguardhome";
+
+  nodes = {
+    minimalConf = { ... }: {
+      services.adguardhome = { enable = true; };
+    };
+
+    declarativeConf = { ... }: {
+      services.adguardhome = {
+        enable = true;
+
+        mutableSettings = false;
+        settings = {
+          dns = {
+            bind_host = "0.0.0.0";
+            bootstrap_dns = "127.0.0.1";
+          };
+        };
+      };
+    };
+
+    mixedConf = { ... }: {
+      services.adguardhome = {
+        enable = true;
+
+        mutableSettings = true;
+        settings = {
+          dns = {
+            bind_host = "0.0.0.0";
+            bootstrap_dns = "127.0.0.1";
+          };
+        };
+      };
+    };
+  };
+
+  testScript = ''
+    with subtest("Minimal config test"):
+        minimalConf.wait_for_unit("adguardhome.service")
+        minimalConf.wait_for_open_port(3000)
+
+    with subtest("Declarative config test, DNS will be reachable"):
+        declarativeConf.wait_for_unit("adguardhome.service")
+        declarativeConf.wait_for_open_port(53)
+        declarativeConf.wait_for_open_port(3000)
+
+    with subtest("Mixed config test, check whether merging works"):
+        mixedConf.wait_for_unit("adguardhome.service")
+        mixedConf.wait_for_open_port(53)
+        mixedConf.wait_for_open_port(3000)
+        # Test whether merging works properly, even if nothing is changed
+        mixedConf.systemctl("restart adguardhome.service")
+        mixedConf.wait_for_unit("adguardhome.service")
+        mixedConf.wait_for_open_port(3000)
+  '';
+}
diff --git a/nixos/tests/all-tests.nix b/nixos/tests/all-tests.nix
index 4f62980e8e9..7959fe5bb18 100644
--- a/nixos/tests/all-tests.nix
+++ b/nixos/tests/all-tests.nix
@@ -19,10 +19,18 @@ let
   handleTestOn = systems: path: args:
     if elem system systems then handleTest path args
     else {};
+
+  nixosLib = import ../lib {
+    # Experimental features need testing too, but there's no point in warning
+    # about it, so we enable the feature flag.
+    featureFlags.minimalModules = {};
+  };
+  evalMinimalConfig = module: nixosLib.evalModules { modules = [ module ]; };
 in
 {
   _3proxy = handleTest ./3proxy.nix {};
   acme = handleTest ./acme.nix {};
+  adguardhome = handleTest ./adguardhome.nix {};
   aesmd = handleTest ./aesmd.nix {};
   agda = handleTest ./agda.nix {};
   airsonic = handleTest ./airsonic.nix {};
@@ -103,6 +111,7 @@ in
   discourse = handleTest ./discourse.nix {};
   dnscrypt-proxy2 = handleTestOn ["x86_64-linux"] ./dnscrypt-proxy2.nix {};
   dnscrypt-wrapper = handleTestOn ["x86_64-linux"] ./dnscrypt-wrapper {};
+  dnsdist = handleTest ./dnsdist.nix {};
   doas = handleTest ./doas.nix {};
   docker = handleTestOn ["x86_64-linux"] ./docker.nix {};
   docker-rootless = handleTestOn ["x86_64-linux"] ./docker-rootless.nix {};
@@ -125,6 +134,7 @@ in
   enlightenment = handleTest ./enlightenment.nix {};
   env = handleTest ./env.nix {};
   ergo = handleTest ./ergo.nix {};
+  ergochat = handleTest ./ergochat.nix {};
   etcd = handleTestOn ["x86_64-linux"] ./etcd.nix {};
   etcd-cluster = handleTestOn ["x86_64-linux"] ./etcd-cluster.nix {};
   etebase-server = handleTest ./etebase-server.nix {};
@@ -143,6 +153,7 @@ in
   fluidd = handleTest ./fluidd.nix {};
   fontconfig-default-fonts = handleTest ./fontconfig-default-fonts.nix {};
   freeswitch = handleTest ./freeswitch.nix {};
+  frr = handleTest ./frr.nix {};
   fsck = handleTest ./fsck.nix {};
   ft2-clone = handleTest ./ft2-clone.nix {};
   gerrit = handleTest ./gerrit.nix {};
@@ -170,7 +181,6 @@ in
   hadoop.all = handleTestOn [ "x86_64-linux" ] ./hadoop/hadoop.nix {};
   hadoop.hdfs = handleTestOn [ "x86_64-linux" ] ./hadoop/hdfs.nix {};
   hadoop.yarn = handleTestOn [ "x86_64-linux" ] ./hadoop/yarn.nix {};
-  handbrake = handleTestOn ["x86_64-linux"] ./handbrake.nix {};
   haproxy = handleTest ./haproxy.nix {};
   hardened = handleTest ./hardened.nix {};
   hedgedoc = handleTest ./hedgedoc.nix {};
@@ -204,6 +214,7 @@ in
   initrd-secrets = handleTest ./initrd-secrets.nix {};
   inspircd = handleTest ./inspircd.nix {};
   installer = handleTest ./installer.nix {};
+  invoiceplane = handleTest ./invoiceplane.nix {};
   iodine = handleTest ./iodine.nix {};
   ipfs = handleTest ./ipfs.nix {};
   ipv6 = handleTest ./ipv6.nix {};
@@ -256,8 +267,7 @@ in
   mailcatcher = handleTest ./mailcatcher.nix {};
   mailhog = handleTest ./mailhog.nix {};
   man = handleTest ./man.nix {};
-  mariadb-galera-mariabackup = handleTest ./mysql/mariadb-galera-mariabackup.nix {};
-  mariadb-galera-rsync = handleTest ./mysql/mariadb-galera-rsync.nix {};
+  mariadb-galera = handleTest ./mysql/mariadb-galera.nix {};
   matomo = handleTest ./matomo.nix {};
   matrix-appservice-irc = handleTest ./matrix-appservice-irc.nix {};
   matrix-conduit = handleTest ./matrix-conduit.nix {};
@@ -327,6 +337,7 @@ in
   nix-serve-ssh = handleTest ./nix-serve-ssh.nix {};
   nixops = handleTest ./nixops/default.nix {};
   nixos-generate-config = handleTest ./nixos-generate-config.nix {};
+  nixpkgs = pkgs.callPackage ../modules/misc/nixpkgs/test.nix { inherit evalMinimalConfig; };
   node-red = handleTest ./node-red.nix {};
   nomad = handleTest ./nomad.nix {};
   novacomd = handleTestOn ["x86_64-linux"] ./novacomd.nix {};
@@ -396,7 +407,6 @@ in
   prometheus = handleTest ./prometheus.nix {};
   prometheus-exporters = handleTest ./prometheus-exporters.nix {};
   prosody = handleTest ./xmpp/prosody.nix {};
-  prosodyMysql = handleTest ./xmpp/prosody-mysql.nix {};
   proxy = handleTest ./proxy.nix {};
   prowlarr = handleTest ./prowlarr.nix {};
   pt2-clone = handleTest ./pt2-clone.nix {};
@@ -445,6 +455,7 @@ in
   sslh = handleTest ./sslh.nix {};
   sssd = handleTestOn ["x86_64-linux"] ./sssd.nix {};
   sssd-ldap = handleTestOn ["x86_64-linux"] ./sssd-ldap.nix {};
+  starship = handleTest ./starship.nix {};
   step-ca = handleTestOn ["x86_64-linux"] ./step-ca.nix {};
   strongswan-swanctl = handleTest ./strongswan-swanctl.nix {};
   sudo = handleTest ./sudo.nix {};
@@ -471,11 +482,14 @@ in
   systemd-unit-path = handleTest ./systemd-unit-path.nix {};
   taskserver = handleTest ./taskserver.nix {};
   telegraf = handleTest ./telegraf.nix {};
+  teleport = handleTest ./teleport.nix {};
+  thelounge = handleTest ./thelounge.nix {};
   tiddlywiki = handleTest ./tiddlywiki.nix {};
   tigervnc = handleTest ./tigervnc.nix {};
   timezone = handleTest ./timezone.nix {};
   tinc = handleTest ./tinc {};
   tinydns = handleTest ./tinydns.nix {};
+  tinywl = handleTest ./tinywl.nix {};
   tor = handleTest ./tor.nix {};
   # traefik test relies on docker-containers
   traefik = handleTestOn ["x86_64-linux"] ./traefik.nix {};
@@ -484,6 +498,7 @@ in
   trezord = handleTest ./trezord.nix {};
   trickster = handleTest ./trickster.nix {};
   trilium-server = handleTestOn ["x86_64-linux"] ./trilium-server.nix {};
+  tsm-client-gui = handleTest ./tsm-client-gui.nix {};
   txredisapi = handleTest ./txredisapi.nix {};
   tuptime = handleTest ./tuptime.nix {};
   turbovnc-headless-server = handleTest ./turbovnc-headless-server.nix {};
diff --git a/nixos/tests/boot-stage1.nix b/nixos/tests/boot-stage1.nix
index ce86fc5f494..756decd2039 100644
--- a/nixos/tests/boot-stage1.nix
+++ b/nixos/tests/boot-stage1.nix
@@ -33,6 +33,8 @@ import ./make-test-python.nix ({ pkgs, ... }: {
         #include <linux/sched/signal.h>
         #endif
 
+        MODULE_LICENSE("GPL");
+
         struct task_struct *canaryTask;
 
         static int kcanary(void *nothing)
diff --git a/nixos/tests/borgbackup.nix b/nixos/tests/borgbackup.nix
index cbb28689209..d3cd6c66bfe 100644
--- a/nixos/tests/borgbackup.nix
+++ b/nixos/tests/borgbackup.nix
@@ -106,7 +106,7 @@ in {
       services.openssh = {
         enable = true;
         passwordAuthentication = false;
-        challengeResponseAuthentication = false;
+        kbdInteractiveAuthentication = false;
       };
 
       services.borgbackup.repos.repo1 = {
diff --git a/nixos/tests/btrbk.nix b/nixos/tests/btrbk.nix
index 2689bb66c63..9f34f7dfbe3 100644
--- a/nixos/tests/btrbk.nix
+++ b/nixos/tests/btrbk.nix
@@ -53,7 +53,7 @@ import ./make-test-python.nix ({ pkgs, ... }:
         services.openssh = {
           enable = true;
           passwordAuthentication = false;
-          challengeResponseAuthentication = false;
+          kbdInteractiveAuthentication = false;
         };
         services.btrbk = {
           extraPackages = [ pkgs.lz4 ];
diff --git a/nixos/tests/dnsdist.nix b/nixos/tests/dnsdist.nix
new file mode 100644
index 00000000000..cfc41c13864
--- /dev/null
+++ b/nixos/tests/dnsdist.nix
@@ -0,0 +1,48 @@
+import ./make-test-python.nix (
+  { pkgs, ... }: {
+    name = "dnsdist";
+    meta = with pkgs.lib; {
+      maintainers = with maintainers; [ jojosch ];
+    };
+
+    machine = { pkgs, lib, ... }: {
+      services.bind = {
+        enable = true;
+        extraOptions = "empty-zones-enable no;";
+        zones = lib.singleton {
+          name = ".";
+          master = true;
+          file = pkgs.writeText "root.zone" ''
+            $TTL 3600
+            . IN SOA ns.example.org. admin.example.org. ( 1 3h 1h 1w 1d )
+            . IN NS ns.example.org.
+
+            ns.example.org. IN A    192.168.0.1
+            ns.example.org. IN AAAA abcd::1
+
+            1.0.168.192.in-addr.arpa IN PTR ns.example.org.
+          '';
+        };
+      };
+      services.dnsdist = {
+        enable = true;
+        listenPort = 5353;
+        extraConfig = ''
+          newServer({address="127.0.0.1:53", name="local-bind"})
+        '';
+      };
+
+      environment.systemPackages = with pkgs; [ dig ];
+    };
+
+    testScript = ''
+      machine.wait_for_unit("bind.service")
+      machine.wait_for_open_port(53)
+      machine.succeed("dig @127.0.0.1 +short -x 192.168.0.1 | grep -qF ns.example.org")
+
+      machine.wait_for_unit("dnsdist.service")
+      machine.wait_for_open_port(5353)
+      machine.succeed("dig @127.0.0.1 -p 5353 +short -x 192.168.0.1 | grep -qF ns.example.org")
+    '';
+  }
+)
diff --git a/nixos/tests/ergochat.nix b/nixos/tests/ergochat.nix
new file mode 100644
index 00000000000..2e9dc55e648
--- /dev/null
+++ b/nixos/tests/ergochat.nix
@@ -0,0 +1,97 @@
+let
+  clients = [
+    "ircclient1"
+    "ircclient2"
+  ];
+  server = "ergochat";
+  ircPort = 6667;
+  channel = "nixos-cat";
+  iiDir = "/tmp/irc";
+in
+
+import ./make-test-python.nix ({ pkgs, lib, ... }: {
+  name = "ergochat";
+  nodes = {
+    "${server}" = {
+      networking.firewall.allowedTCPPorts = [ ircPort ];
+      services.ergochat = {
+        enable = true;
+        settings.server.motd = pkgs.writeText "ergo.motd" ''
+          The default MOTD doesn't contain the word "nixos" in it.
+          This one does.
+        '';
+      };
+    };
+  } // lib.listToAttrs (builtins.map (client: lib.nameValuePair client {
+    imports = [
+      ./common/user-account.nix
+    ];
+
+    systemd.services.ii = {
+      requires = [ "network.target" ];
+      wantedBy = [ "default.target" ];
+
+      serviceConfig = {
+        Type = "simple";
+        ExecPreStartPre = "mkdir -p ${iiDir}";
+        ExecStart = ''
+          ${lib.getBin pkgs.ii}/bin/ii -n ${client} -s ${server} -i ${iiDir}
+        '';
+        User = "alice";
+      };
+    };
+  }) clients);
+
+  testScript =
+    let
+      msg = client: "Hello, my name is ${client}";
+      clientScript = client: [
+        ''
+          ${client}.wait_for_unit("network.target")
+          ${client}.systemctl("start ii")
+          ${client}.wait_for_unit("ii")
+          ${client}.wait_for_file("${iiDir}/${server}/out")
+        ''
+        # look for the custom text in the MOTD.
+        ''
+          ${client}.wait_until_succeeds("grep 'nixos' ${iiDir}/${server}/out")
+        ''
+        # wait until first PING from server arrives before joining,
+        # so we don't try it too early
+        ''
+          ${client}.wait_until_succeeds("grep 'PING' ${iiDir}/${server}/out")
+        ''
+        # join ${channel}
+        ''
+          ${client}.succeed("echo '/j #${channel}' > ${iiDir}/${server}/in")
+          ${client}.wait_for_file("${iiDir}/${server}/#${channel}/in")
+        ''
+        # send a greeting
+        ''
+          ${client}.succeed(
+              "echo '${msg client}' > ${iiDir}/${server}/#${channel}/in"
+          )
+        ''
+        # check that all greetings arrived on all clients
+      ] ++ builtins.map (other: ''
+        ${client}.succeed(
+            "grep '${msg other}$' ${iiDir}/${server}/#${channel}/out"
+        )
+      '') clients;
+
+      # foldl', but requires a non-empty list instead of a start value
+      reduce = f: list:
+        builtins.foldl' f (builtins.head list) (builtins.tail list);
+    in ''
+      start_all()
+      ${server}.systemctl("status ergochat")
+      ${server}.wait_for_open_port(${toString ircPort})
+
+      # run clientScript for all clients so that every list
+      # entry is executed by every client before advancing
+      # to the next one.
+    '' + lib.concatStrings
+      (reduce
+        (lib.zipListsWith (cs: c: cs + c))
+        (builtins.map clientScript clients));
+})
diff --git a/nixos/tests/frr.nix b/nixos/tests/frr.nix
new file mode 100644
index 00000000000..598d7a7d286
--- /dev/null
+++ b/nixos/tests/frr.nix
@@ -0,0 +1,104 @@
+# This test runs FRR and checks if OSPF routing works.
+#
+# Network topology:
+#   [ client ]--net1--[ router1 ]--net2--[ router2 ]--net3--[ server ]
+#
+# All interfaces are in OSPF Area 0.
+
+import ./make-test-python.nix ({ pkgs, ... }:
+  let
+
+    ifAddr = node: iface: (pkgs.lib.head node.config.networking.interfaces.${iface}.ipv4.addresses).address;
+
+    ospfConf1 = ''
+      router ospf
+        network 192.168.0.0/16 area 0
+    '';
+
+    ospfConf2 = ''
+      interface eth2
+        ip ospf hello-interval 1
+        ip ospf dead-interval 5
+      !
+      router ospf
+        network 192.168.0.0/16 area 0
+    '';
+
+  in
+    {
+      name = "frr";
+
+      meta = with pkgs.lib.maintainers; {
+        maintainers = [ hexa ];
+      };
+
+      nodes = {
+
+        client =
+          { nodes, ... }:
+          {
+            virtualisation.vlans = [ 1 ];
+            networking.defaultGateway = ifAddr nodes.router1 "eth1";
+          };
+
+        router1 =
+          { ... }:
+          {
+            virtualisation.vlans = [ 1 2 ];
+            boot.kernel.sysctl."net.ipv4.ip_forward" = "1";
+            networking.firewall.extraCommands = "iptables -A nixos-fw -i eth2 -p ospfigp -j ACCEPT";
+            services.frr.ospf = {
+              enable = true;
+              config = ospfConf1;
+            };
+
+            specialisation.ospf.configuration = {
+              services.frr.ospf.config = ospfConf2;
+            };
+          };
+
+        router2 =
+          { ... }:
+          {
+            virtualisation.vlans = [ 3 2 ];
+            boot.kernel.sysctl."net.ipv4.ip_forward" = "1";
+            networking.firewall.extraCommands = "iptables -A nixos-fw -i eth2 -p ospfigp -j ACCEPT";
+            services.frr.ospf = {
+              enable = true;
+              config = ospfConf2;
+            };
+          };
+
+        server =
+          { nodes, ... }:
+          {
+            virtualisation.vlans = [ 3 ];
+            networking.defaultGateway = ifAddr nodes.router2 "eth1";
+          };
+      };
+
+      testScript =
+        { nodes, ... }:
+        ''
+          start_all()
+
+          # Wait for the networking to start on all machines
+          for machine in client, router1, router2, server:
+              machine.wait_for_unit("network.target")
+
+          with subtest("Wait for Zebra and OSPFD"):
+              for gw in router1, router2:
+                  gw.wait_for_unit("zebra")
+                  gw.wait_for_unit("ospfd")
+
+          router1.succeed("${nodes.router1.config.system.build.toplevel}/specialisation/ospf/bin/switch-to-configuration test >&2")
+
+          with subtest("Wait for OSPF to form adjacencies"):
+              for gw in router1, router2:
+                  gw.wait_until_succeeds("vtysh -c 'show ip ospf neighbor' | grep Full")
+                  gw.wait_until_succeeds("vtysh -c 'show ip route' | grep '^O>'")
+
+          with subtest("Test ICMP"):
+              client.wait_until_succeeds("ping -c 3 server >&2")
+        '';
+    })
diff --git a/nixos/tests/google-oslogin/server.nix b/nixos/tests/google-oslogin/server.nix
index fdb7141da31..a0a3144ae69 100644
--- a/nixos/tests/google-oslogin/server.nix
+++ b/nixos/tests/google-oslogin/server.nix
@@ -17,7 +17,7 @@ in {
   };
 
   services.openssh.enable = true;
-  services.openssh.challengeResponseAuthentication = false;
+  services.openssh.kbdInteractiveAuthentication = false;
   services.openssh.passwordAuthentication = false;
 
   security.googleOsLogin.enable = true;
diff --git a/nixos/tests/handbrake.nix b/nixos/tests/handbrake.nix
deleted file mode 100644
index d2d41b372be..00000000000
--- a/nixos/tests/handbrake.nix
+++ /dev/null
@@ -1,33 +0,0 @@
-import ./make-test-python.nix ({ pkgs, ... }:
-
-let
-  # Download Big Buck Bunny example, licensed under CC Attribution 3.0.
-  testMkv = pkgs.fetchurl {
-    url = "https://github.com/Matroska-Org/matroska-test-files/blob/cf0792be144ac470c4b8052cfe19bb691993e3a2/test_files/test1.mkv?raw=true";
-    sha256 = "1hfxbbgxwfkzv85pvpvx55a72qsd0hxjbm9hkl5r3590zw4s75h9";
-    name = "test1.mkv";
-  };
-
-in
-{
-  name = "handbrake";
-
-  meta = {
-    maintainers = with pkgs.lib.maintainers; [ ];
-  };
-
-  machine = { pkgs, ... }: {
-    environment.systemPackages = with pkgs; [ handbrake ];
-  };
-
-  testScript = ''
-    # Test MP4 and MKV transcoding. Since this is a short clip, transcoding typically
-    # only takes a few seconds.
-    start_all()
-
-    machine.succeed("HandBrakeCLI -i ${testMkv} -o test.mp4 -e x264 -q 20 -B 160")
-    machine.succeed("test -e test.mp4")
-    machine.succeed("HandBrakeCLI -i ${testMkv} -o test.mkv -e x264 -q 20 -B 160")
-    machine.succeed("test -e test.mkv")
-  '';
-})
diff --git a/nixos/tests/installer.nix b/nixos/tests/installer.nix
index bc41b6efc2e..cf00bcafe4f 100644
--- a/nixos/tests/installer.nix
+++ b/nixos/tests/installer.nix
@@ -561,26 +561,16 @@ in {
           + " mkpart primary 2048M -1s"  # PV2
           + " set 2 lvm on",
           "udevadm settle",
-          "sleep 1",
           "pvcreate /dev/vda1 /dev/vda2",
-          "sleep 1",
           "vgcreate MyVolGroup /dev/vda1 /dev/vda2",
-          "sleep 1",
           "lvcreate --size 1G --name swap MyVolGroup",
-          "sleep 1",
           "lvcreate --size 3G --name nixos MyVolGroup",
-          "sleep 1",
           "mkswap -f /dev/MyVolGroup/swap -L swap",
           "swapon -L swap",
           "mkfs.xfs -L nixos /dev/MyVolGroup/nixos",
           "mount LABEL=nixos /mnt",
       )
     '';
-    postBootCommands = ''
-      assert "loaded active" in machine.succeed(
-          "systemctl list-units 'lvm2-pvscan@*' -ql --no-legend | tee /dev/stderr"
-      )
-    '';
   };
 
   # Boot off an encrypted root partition with the default LUKS header format
diff --git a/nixos/tests/invoiceplane.nix b/nixos/tests/invoiceplane.nix
new file mode 100644
index 00000000000..4e63f8ac21c
--- /dev/null
+++ b/nixos/tests/invoiceplane.nix
@@ -0,0 +1,82 @@
+import ./make-test-python.nix ({ pkgs, ... }:
+
+{
+  name = "invoiceplane";
+  meta = with pkgs.lib.maintainers; {
+    maintainers = [
+      onny
+    ];
+  };
+
+  nodes = {
+    invoiceplane_caddy = { ... }: {
+      services.invoiceplane.webserver = "caddy";
+      services.invoiceplane.sites = {
+        "site1.local" = {
+          #database.name = "invoiceplane1";
+          database.createLocally = true;
+          enable = true;
+        };
+        "site2.local" = {
+          #database.name = "invoiceplane2";
+          database.createLocally = true;
+          enable = true;
+        };
+      };
+
+      networking.firewall.allowedTCPPorts = [ 80 ];
+      networking.hosts."127.0.0.1" = [ "site1.local" "site2.local" ];
+    };
+  };
+
+  testScript = ''
+    start_all()
+
+    invoiceplane_caddy.wait_for_unit("caddy")
+    invoiceplane_caddy.wait_for_open_port(80)
+    invoiceplane_caddy.wait_for_open_port(3306)
+
+    site_names = ["site1.local", "site2.local"]
+
+    for site_name in site_names:
+        machine.wait_for_unit(f"phpfpm-invoiceplane-{site_name}")
+
+        with subtest("Website returns welcome screen"):
+            assert "Please install InvoicePlane" in machine.succeed(f"curl -L {site_name}")
+
+        with subtest("Finish InvoicePlane setup"):
+          machine.succeed(
+            f"curl -sSfL --cookie-jar cjar {site_name}/index.php/setup/language"
+          )
+          csrf_token = machine.succeed(
+            "grep ip_csrf_cookie cjar | cut -f 7 | tr -d '\n'"
+          )
+          machine.succeed(
+            f"curl -sSfL --cookie cjar --cookie-jar cjar -d '_ip_csrf={csrf_token}&ip_lang=english&btn_continue=Continue' {site_name}/index.php/setup/language"
+          )
+          csrf_token = machine.succeed(
+            "grep ip_csrf_cookie cjar | cut -f 7 | tr -d '\n'"
+          )
+          machine.succeed(
+            f"curl -sSfL --cookie cjar --cookie-jar cjar -d '_ip_csrf={csrf_token}&btn_continue=Continue' {site_name}/index.php/setup/prerequisites"
+          )
+          csrf_token = machine.succeed(
+            "grep ip_csrf_cookie cjar | cut -f 7 | tr -d '\n'"
+          )
+          machine.succeed(
+            f"curl -sSfL --cookie cjar --cookie-jar cjar -d '_ip_csrf={csrf_token}&btn_continue=Continue' {site_name}/index.php/setup/configure_database"
+          )
+          csrf_token = machine.succeed(
+            "grep ip_csrf_cookie cjar | cut -f 7 | tr -d '\n'"
+          )
+          machine.succeed(
+            f"curl -sSfl --cookie cjar --cookie-jar cjar -d '_ip_csrf={csrf_token}&btn_continue=Continue' {site_name}/index.php/setup/install_tables"
+          )
+          csrf_token = machine.succeed(
+            "grep ip_csrf_cookie cjar | cut -f 7 | tr -d '\n'"
+          )
+          machine.succeed(
+            f"curl -sSfl --cookie cjar --cookie-jar cjar -d '_ip_csrf={csrf_token}&btn_continue=Continue' {site_name}/index.php/setup/upgrade_tables"
+          )
+  '';
+})
diff --git a/nixos/tests/jibri.nix b/nixos/tests/jibri.nix
index 3dd28e6aac1..af20e639d30 100644
--- a/nixos/tests/jibri.nix
+++ b/nixos/tests/jibri.nix
@@ -63,7 +63,7 @@ import ./make-test-python.nix ({ pkgs, ... }: {
         """sleep 15 && curl -H "Content-Type: application/json" -X POST http://localhost:2222/jibri/api/v1.0/stopService -d '{"sessionId": "RecordTest","callParams":{"callUrlInfo":{"baseUrl": "https://machine","callName": "TestCall"}},"callLoginParams":{"domain": "recorder.machine", "username": "recorder", "password": "'"$(cat /var/lib/jitsi-meet/jibri-recorder-secret)"'" },"sinkType": "file"}'"""
     )
     machine.wait_until_succeeds(
-        "cat /var/log/jitsi/jibri/log.0.txt | grep -q 'Recording finalize script finished with exit value 0'", timeout=36
+        "cat /var/log/jitsi/jibri/log.0.txt | grep -q 'Finalize script finished with exit value 0'", timeout=36
     )
   '';
 })
diff --git a/nixos/tests/k3s-single-node-docker.nix b/nixos/tests/k3s-single-node-docker.nix
new file mode 100644
index 00000000000..7f3d15788b0
--- /dev/null
+++ b/nixos/tests/k3s-single-node-docker.nix
@@ -0,0 +1,84 @@
+import ./make-test-python.nix ({ pkgs, ... }:
+
+  let
+    imageEnv = pkgs.buildEnv {
+      name = "k3s-pause-image-env";
+      paths = with pkgs; [ tini (hiPrio coreutils) busybox ];
+    };
+    pauseImage = pkgs.dockerTools.streamLayeredImage {
+      name = "test.local/pause";
+      tag = "local";
+      contents = imageEnv;
+      config.Entrypoint = [ "/bin/tini" "--" "/bin/sleep" "inf" ];
+    };
+    # Don't use the default service account because there's a race where it may
+    # not be created yet; make our own instead.
+    testPodYaml = pkgs.writeText "test.yml" ''
+      apiVersion: v1
+      kind: ServiceAccount
+      metadata:
+        name: test
+      ---
+      apiVersion: v1
+      kind: Pod
+      metadata:
+        name: test
+      spec:
+        serviceAccountName: test
+        containers:
+        - name: test
+          image: test.local/pause:local
+          imagePullPolicy: Never
+          command: ["sh", "-c", "sleep inf"]
+    '';
+  in
+  {
+    name = "k3s";
+    meta = with pkgs.lib.maintainers; {
+      maintainers = [ euank ];
+    };
+
+    machine = { pkgs, ... }: {
+      environment.systemPackages = with pkgs; [ k3s gzip ];
+
+      # k3s uses enough resources the default vm fails.
+      virtualisation.memorySize = 1536;
+      virtualisation.diskSize = 4096;
+
+      services.k3s = {
+        enable = true;
+        role = "server";
+        docker = true;
+        # Slightly reduce resource usage
+        extraFlags = "--no-deploy coredns,servicelb,traefik,local-storage,metrics-server --pause-image test.local/pause:local";
+      };
+
+      users.users = {
+        noprivs = {
+          isNormalUser = true;
+          description = "Can't access k3s by default";
+          password = "*";
+        };
+      };
+    };
+
+    testScript = ''
+      start_all()
+
+      machine.wait_for_unit("k3s")
+      machine.succeed("k3s kubectl cluster-info")
+      machine.fail("sudo -u noprivs k3s kubectl cluster-info")
+      # FIXME: this fails with the current nixos kernel config; once it passes, we should uncomment it
+      # machine.succeed("k3s check-config")
+
+      machine.succeed(
+          "${pauseImage} | docker load"
+      )
+
+      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.shutdown()
+    '';
+  })
diff --git a/nixos/tests/k3s-single-node.nix b/nixos/tests/k3s-single-node.nix
new file mode 100644
index 00000000000..d98f20d468c
--- /dev/null
+++ b/nixos/tests/k3s-single-node.nix
@@ -0,0 +1,82 @@
+import ./make-test-python.nix ({ pkgs, ... }:
+
+  let
+    imageEnv = pkgs.buildEnv {
+      name = "k3s-pause-image-env";
+      paths = with pkgs; [ tini (hiPrio coreutils) busybox ];
+    };
+    pauseImage = pkgs.dockerTools.streamLayeredImage {
+      name = "test.local/pause";
+      tag = "local";
+      contents = imageEnv;
+      config.Entrypoint = [ "/bin/tini" "--" "/bin/sleep" "inf" ];
+    };
+    # Don't use the default service account because there's a race where it may
+    # not be created yet; make our own instead.
+    testPodYaml = pkgs.writeText "test.yml" ''
+      apiVersion: v1
+      kind: ServiceAccount
+      metadata:
+        name: test
+      ---
+      apiVersion: v1
+      kind: Pod
+      metadata:
+        name: test
+      spec:
+        serviceAccountName: test
+        containers:
+        - name: test
+          image: test.local/pause:local
+          imagePullPolicy: Never
+          command: ["sh", "-c", "sleep inf"]
+    '';
+  in
+  {
+    name = "k3s";
+    meta = with pkgs.lib.maintainers; {
+      maintainers = [ euank ];
+    };
+
+    machine = { pkgs, ... }: {
+      environment.systemPackages = with pkgs; [ k3s gzip ];
+
+      # k3s uses enough resources the default vm fails.
+      virtualisation.memorySize = 1536;
+      virtualisation.diskSize = 4096;
+
+      services.k3s.enable = true;
+      services.k3s.role = "server";
+      services.k3s.package = pkgs.k3s;
+      # Slightly reduce resource usage
+      services.k3s.extraFlags = "--no-deploy coredns,servicelb,traefik,local-storage,metrics-server --pause-image test.local/pause:local";
+
+      users.users = {
+        noprivs = {
+          isNormalUser = true;
+          description = "Can't access k3s by default";
+          password = "*";
+        };
+      };
+    };
+
+    testScript = ''
+      start_all()
+
+      machine.wait_for_unit("k3s")
+      machine.succeed("k3s kubectl cluster-info")
+      machine.fail("sudo -u noprivs k3s kubectl cluster-info")
+      # FIXME: this fails with the current nixos kernel config; once it passes, we should uncomment it
+      # machine.succeed("k3s check-config")
+
+      machine.succeed(
+          "${pauseImage} | k3s ctr image import -"
+      )
+
+      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.shutdown()
+    '';
+  })
diff --git a/nixos/tests/k3s.nix b/nixos/tests/k3s.nix
deleted file mode 100644
index 494a3b68b59..00000000000
--- a/nixos/tests/k3s.nix
+++ /dev/null
@@ -1,78 +0,0 @@
-import ./make-test-python.nix ({ pkgs, ... }:
-
-let
-  # A suitable k3s pause image, also used for the test pod
-  pauseImage = pkgs.dockerTools.buildImage {
-    name = "test.local/pause";
-    tag = "local";
-    contents = with pkgs; [ tini coreutils busybox ];
-    config.Entrypoint = [ "/bin/tini" "--" "/bin/sleep" "inf" ];
-  };
-  testPodYaml = pkgs.writeText "test.yml" ''
-    # Don't use the default service account because there's a race where it may
-    # not be created yet; make our own instead.
-    apiVersion: v1
-    kind: ServiceAccount
-    metadata:
-      name: test
-    ---
-    apiVersion: v1
-    kind: Pod
-    metadata:
-      name: test
-    spec:
-      serviceAccountName: test
-      containers:
-      - name: test
-        image: test.local/pause:local
-        imagePullPolicy: Never
-        command: ["sh", "-c", "sleep inf"]
-  '';
-in
-{
-  name = "k3s";
-  meta = with pkgs.lib.maintainers; {
-    maintainers = [ euank ];
-  };
-
-  nodes = {
-    k3s =
-      { pkgs, ... }: {
-        environment.systemPackages = [ pkgs.k3s pkgs.gzip ];
-
-        # k3s uses enough resources the default vm fails.
-        virtualisation.memorySize = pkgs.lib.mkDefault 1536;
-        virtualisation.diskSize = pkgs.lib.mkDefault 4096;
-
-        services.k3s.enable = true;
-        services.k3s.role = "server";
-        services.k3s.package = pkgs.k3s;
-        # Slightly reduce resource usage
-        services.k3s.extraFlags = "--no-deploy coredns,servicelb,traefik,local-storage,metrics-server --pause-image test.local/pause:local";
-
-        users.users = {
-          noprivs = {
-            isNormalUser = true;
-            description = "Can't access k3s by default";
-            password = "*";
-          };
-        };
-      };
-  };
-
-  testScript = ''
-    start_all()
-
-    k3s.wait_for_unit("k3s")
-    k3s.succeed("k3s kubectl cluster-info")
-    k3s.fail("sudo -u noprivs k3s kubectl cluster-info")
-    # k3s.succeed("k3s check-config") # fails with the current nixos kernel config, uncomment once this passes
-
-    k3s.succeed(
-        "zcat ${pauseImage} | k3s ctr image import -"
-    )
-
-    k3s.succeed("k3s kubectl apply -f ${testPodYaml}")
-    k3s.succeed("k3s kubectl wait --for 'condition=Ready' pod/test")
-  '';
-})
diff --git a/nixos/tests/kubernetes/base.nix b/nixos/tests/kubernetes/base.nix
index e1736f6fe17..f0c72084be5 100644
--- a/nixos/tests/kubernetes/base.nix
+++ b/nixos/tests/kubernetes/base.nix
@@ -60,13 +60,6 @@ let
                   advertiseAddress = master.ip;
                 };
                 masterAddress = "${masterName}.${config.networking.domain}";
-                # workaround for:
-                #   https://github.com/kubernetes/kubernetes/issues/102676
-                #   (workaround from) https://github.com/kubernetes/kubernetes/issues/95488
-                kubelet.extraOpts = ''\
-                  --cgroups-per-qos=false \
-                  --enforce-node-allocatable="" \
-                '';
               };
             }
             (optionalAttrs (any (role: role == "master") machine.roles) {
diff --git a/nixos/tests/libreswan.nix b/nixos/tests/libreswan.nix
index 56ab908aed9..ff3d2344a67 100644
--- a/nixos/tests/libreswan.nix
+++ b/nixos/tests/libreswan.nix
@@ -89,7 +89,7 @@ in
           """
           Sends a message as Alice to Bob
           """
-          bob.execute("nc -lu ::0 1234 >/tmp/msg >&2 &")
+          bob.execute("nc -lu ::0 1234 >/tmp/msg &")
           alice.sleep(1)
           alice.succeed(f"echo '{msg}' | nc -uw 0 bob 1234")
           bob.succeed(f"grep '{msg}' /tmp/msg")
@@ -100,7 +100,7 @@ in
           Starts eavesdropping on Alice and Bob
           """
           match = "src host alice and dst host bob"
-          eve.execute(f"tcpdump -i br0 -c 1 -Avv {match} >/tmp/log >&2 &")
+          eve.execute(f"tcpdump -i br0 -c 1 -Avv {match} >/tmp/log &")
 
 
       start_all()
diff --git a/nixos/tests/lorri/default.nix b/nixos/tests/lorri/default.nix
index 147ae999fdb..c33c7503993 100644
--- a/nixos/tests/lorri/default.nix
+++ b/nixos/tests/lorri/default.nix
@@ -14,7 +14,7 @@ import ../make-test-python.nix {
     )
 
     # Start the daemon and wait until it is ready
-    machine.execute("lorri daemon > lorri.stdout 2> lorri.stderr >&2 &")
+    machine.execute("lorri daemon > lorri.stdout 2> lorri.stderr &")
     machine.wait_until_succeeds("grep --fixed-strings 'ready' lorri.stdout")
 
     # Ping the daemon
diff --git a/nixos/tests/mysql/common.nix b/nixos/tests/mysql/common.nix
new file mode 100644
index 00000000000..968253d109c
--- /dev/null
+++ b/nixos/tests/mysql/common.nix
@@ -0,0 +1,10 @@
+{ lib, pkgs }: {
+  mariadbPackages = lib.filterAttrs (n: _: lib.hasPrefix "mariadb" n) (pkgs.callPackage ../../../pkgs/servers/sql/mariadb {
+    inherit (pkgs.darwin) cctools;
+    inherit (pkgs.darwin.apple_sdk.frameworks) CoreServices;
+  });
+  mysqlPackage = {
+    inherit (pkgs) mysql57 mysql80;
+  };
+  mkTestName = pkg: "mariadb_${builtins.replaceStrings ["."] [""] (lib.versions.majorMinor pkg.version)}";
+}
diff --git a/nixos/tests/mysql/mariadb-galera-mariabackup.nix b/nixos/tests/mysql/mariadb-galera-mariabackup.nix
deleted file mode 100644
index 10682c361d1..00000000000
--- a/nixos/tests/mysql/mariadb-galera-mariabackup.nix
+++ /dev/null
@@ -1,233 +0,0 @@
-import ./../make-test-python.nix ({ pkgs, ...} :
-
-let
-  mysqlenv-common      = pkgs.buildEnv { name = "mysql-path-env-common";      pathsToLink = [ "/bin" ]; paths = with pkgs; [ bash gawk gnutar inetutils which ]; };
-  mysqlenv-mariabackup = pkgs.buildEnv { name = "mysql-path-env-mariabackup"; pathsToLink = [ "/bin" ]; paths = with pkgs; [ gzip iproute2 netcat procps pv socat ]; };
-
-  # Common user configuration
-  users = { ... }:
-  {
-    users.users.testuser = {
-      isSystemUser = true;
-      group = "testusers";
-    };
-    users.groups.testusers = { };
-  };
-
-in {
-  name = "mariadb-galera-mariabackup";
-  meta = with pkgs.lib.maintainers; {
-    maintainers = [ izorkin ];
-  };
-
-  # The test creates a Galera cluster with 3 nodes and is checking if mariabackup-based SST works. The cluster is tested by creating a DB and an empty table on one node,
-  # and checking the table's presence on the other node.
-
-  nodes = {
-    galera_01 =
-      { pkgs, ... }:
-      {
-      imports = [ users ];
-      networking = {
-        interfaces.eth1 = {
-          ipv4.addresses = [
-            { address = "192.168.1.1"; prefixLength = 24; }
-          ];
-        };
-        extraHosts = ''
-          192.168.1.1 galera_01
-          192.168.1.2 galera_02
-          192.168.1.3 galera_03
-        '';
-        firewall.allowedTCPPorts = [ 3306 4444 4567 4568 ];
-        firewall.allowedUDPPorts = [ 4567 ];
-      };
-      systemd.services.mysql = with pkgs; {
-        path = [ mysqlenv-common mysqlenv-mariabackup ];
-      };
-      services.mysql = {
-        enable = true;
-        package = pkgs.mariadb;
-        ensureDatabases = [ "testdb" ];
-        ensureUsers = [{
-          name = "testuser";
-          ensurePermissions = {
-            "testdb.*" = "ALL PRIVILEGES";
-          };
-        }];
-        initialScript = pkgs.writeText "mariadb-init.sql" ''
-          GRANT ALL PRIVILEGES ON *.* TO 'check_repl'@'localhost' IDENTIFIED BY 'check_pass' WITH GRANT OPTION;
-          FLUSH PRIVILEGES;
-        '';
-        settings = {
-          mysqld = {
-            bind_address = "0.0.0.0";
-          };
-          galera = {
-            wsrep_on = "ON";
-            wsrep_debug = "NONE";
-            wsrep_retry_autocommit = "3";
-            wsrep_provider = "${pkgs.mariadb-galera}/lib/galera/libgalera_smm.so";
-            wsrep_cluster_address = "gcomm://";
-            wsrep_cluster_name = "galera";
-            wsrep_node_address = "192.168.1.1";
-            wsrep_node_name = "galera_01";
-            wsrep_sst_method = "mariabackup";
-            wsrep_sst_auth = "check_repl:check_pass";
-            binlog_format = "ROW";
-            enforce_storage_engine = "InnoDB";
-            innodb_autoinc_lock_mode = "2";
-          };
-        };
-      };
-    };
-
-    galera_02 =
-      { pkgs, ... }:
-      {
-      imports = [ users ];
-      networking = {
-        interfaces.eth1 = {
-          ipv4.addresses = [
-            { address = "192.168.1.2"; prefixLength = 24; }
-          ];
-        };
-        extraHosts = ''
-          192.168.1.1 galera_01
-          192.168.1.2 galera_02
-          192.168.1.3 galera_03
-        '';
-        firewall.allowedTCPPorts = [ 3306 4444 4567 4568 ];
-        firewall.allowedUDPPorts = [ 4567 ];
-      };
-      systemd.services.mysql = with pkgs; {
-        path = [ mysqlenv-common mysqlenv-mariabackup ];
-      };
-      services.mysql = {
-        enable = true;
-        package = pkgs.mariadb;
-        settings = {
-          mysqld = {
-            bind_address = "0.0.0.0";
-          };
-          galera = {
-            wsrep_on = "ON";
-            wsrep_debug = "NONE";
-            wsrep_retry_autocommit = "3";
-            wsrep_provider = "${pkgs.mariadb-galera}/lib/galera/libgalera_smm.so";
-            wsrep_cluster_address = "gcomm://galera_01,galera_02,galera_03";
-            wsrep_cluster_name = "galera";
-            wsrep_node_address = "192.168.1.2";
-            wsrep_node_name = "galera_02";
-            wsrep_sst_method = "mariabackup";
-            wsrep_sst_auth = "check_repl:check_pass";
-            binlog_format = "ROW";
-            enforce_storage_engine = "InnoDB";
-            innodb_autoinc_lock_mode = "2";
-          };
-        };
-      };
-    };
-
-    galera_03 =
-      { pkgs, ... }:
-      {
-      imports = [ users ];
-      networking = {
-        interfaces.eth1 = {
-          ipv4.addresses = [
-            { address = "192.168.1.3"; prefixLength = 24; }
-          ];
-        };
-        extraHosts = ''
-          192.168.1.1 galera_01
-          192.168.1.2 galera_02
-          192.168.1.3 galera_03
-        '';
-        firewall.allowedTCPPorts = [ 3306 4444 4567 4568 ];
-        firewall.allowedUDPPorts = [ 4567 ];
-      };
-      systemd.services.mysql = with pkgs; {
-        path = [ mysqlenv-common mysqlenv-mariabackup ];
-      };
-      services.mysql = {
-        enable = true;
-        package = pkgs.mariadb;
-        settings = {
-          mysqld = {
-            bind_address = "0.0.0.0";
-          };
-          galera = {
-            wsrep_on = "ON";
-            wsrep_debug = "NONE";
-            wsrep_retry_autocommit = "3";
-            wsrep_provider = "${pkgs.mariadb-galera}/lib/galera/libgalera_smm.so";
-            wsrep_cluster_address = "gcomm://galera_01,galera_02,galera_03";
-            wsrep_cluster_name = "galera";
-            wsrep_node_address = "192.168.1.3";
-            wsrep_node_name = "galera_03";
-            wsrep_sst_method = "mariabackup";
-            wsrep_sst_auth = "check_repl:check_pass";
-            binlog_format = "ROW";
-            enforce_storage_engine = "InnoDB";
-            innodb_autoinc_lock_mode = "2";
-          };
-        };
-      };
-    };
-  };
-
-  testScript = ''
-    galera_01.start()
-    galera_01.wait_for_unit("mysql")
-    galera_01.wait_for_open_port(3306)
-    galera_01.succeed(
-        "sudo -u testuser mysql -u testuser -e 'use testdb; create table db1 (test_id INT, PRIMARY KEY (test_id)) ENGINE = InnoDB;'"
-    )
-    galera_01.succeed(
-        "sudo -u testuser mysql -u testuser -e 'use testdb; insert into db1 values (37);'"
-    )
-    galera_02.start()
-    galera_02.wait_for_unit("mysql")
-    galera_02.wait_for_open_port(3306)
-    galera_03.start()
-    galera_03.wait_for_unit("mysql")
-    galera_03.wait_for_open_port(3306)
-    galera_02.succeed(
-        "sudo -u testuser mysql -u testuser -e 'use testdb; select test_id from db1;' -N | grep 37"
-    )
-    galera_02.succeed(
-        "sudo -u testuser mysql -u testuser -e 'use testdb; create table db2 (test_id INT, PRIMARY KEY (test_id)) ENGINE = InnoDB;'"
-    )
-    galera_02.succeed("systemctl stop mysql")
-    galera_01.succeed(
-        "sudo -u testuser mysql -u testuser -e 'use testdb; insert into db2 values (38);'"
-    )
-    galera_03.succeed(
-        "sudo -u testuser mysql -u testuser -e 'use testdb; create table db3 (test_id INT, PRIMARY KEY (test_id)) ENGINE = InnoDB;'"
-    )
-    galera_01.succeed(
-        "sudo -u testuser mysql -u testuser -e 'use testdb; insert into db3 values (39);'"
-    )
-    galera_02.succeed("systemctl start mysql")
-    galera_02.wait_for_open_port(3306)
-    galera_02.succeed(
-        "sudo -u testuser mysql -u testuser -e 'show status' -N | grep 'wsrep_cluster_size.*3'"
-    )
-    galera_03.succeed(
-        "sudo -u testuser mysql -u testuser -e 'show status' -N | grep 'wsrep_local_state_comment.*Synced'"
-    )
-    galera_01.succeed(
-        "sudo -u testuser mysql -u testuser -e 'use testdb; select test_id from db3;' -N | grep 39"
-    )
-    galera_02.succeed(
-        "sudo -u testuser mysql -u testuser -e 'use testdb; select test_id from db2;' -N | grep 38"
-    )
-    galera_03.succeed(
-        "sudo -u testuser mysql -u testuser -e 'use testdb; select test_id from db1;' -N | grep 37"
-    )
-    galera_01.succeed("sudo -u testuser mysql -u testuser -e 'use testdb; drop table db3;'")
-    galera_02.succeed("sudo -u testuser mysql -u testuser -e 'use testdb; drop table db2;'")
-    galera_03.succeed("sudo -u testuser mysql -u testuser -e 'use testdb; drop table db1;'")
-  '';
-})
diff --git a/nixos/tests/mysql/mariadb-galera-rsync.nix b/nixos/tests/mysql/mariadb-galera-rsync.nix
deleted file mode 100644
index 701e01e8871..00000000000
--- a/nixos/tests/mysql/mariadb-galera-rsync.nix
+++ /dev/null
@@ -1,226 +0,0 @@
-import ./../make-test-python.nix ({ pkgs, ...} :
-
-let
-  mysqlenv-common      = pkgs.buildEnv { name = "mysql-path-env-common";      pathsToLink = [ "/bin" ]; paths = with pkgs; [ bash gawk gnutar inetutils which ]; };
-  mysqlenv-rsync       = pkgs.buildEnv { name = "mysql-path-env-rsync";       pathsToLink = [ "/bin" ]; paths = with pkgs; [ lsof procps rsync stunnel ]; };
-
-  # Common user configuration
-  users = { ... }:
-  {
-    users.users.testuser = {
-      isSystemUser = true;
-      group = "testusers";
-    };
-    users.groups.testusers = { };
-  };
-
-in {
-  name = "mariadb-galera-rsync";
-  meta = with pkgs.lib.maintainers; {
-    maintainers = [ izorkin ];
-  };
-
-  # The test creates a Galera cluster with 3 nodes and is checking if rsync-based SST works. The cluster is tested by creating a DB and an empty table on one node,
-  # and checking the table's presence on the other node.
-
-  nodes = {
-    galera_04 =
-      { pkgs, ... }:
-      {
-      imports = [ users ];
-      networking = {
-        interfaces.eth1 = {
-          ipv4.addresses = [
-            { address = "192.168.2.1"; prefixLength = 24; }
-          ];
-        };
-        extraHosts = ''
-          192.168.2.1 galera_04
-          192.168.2.2 galera_05
-          192.168.2.3 galera_06
-        '';
-        firewall.allowedTCPPorts = [ 3306 4444 4567 4568 ];
-        firewall.allowedUDPPorts = [ 4567 ];
-      };
-      systemd.services.mysql = with pkgs; {
-        path = [ mysqlenv-common mysqlenv-rsync ];
-      };
-      services.mysql = {
-        enable = true;
-        package = pkgs.mariadb;
-        ensureDatabases = [ "testdb" ];
-        ensureUsers = [{
-          name = "testuser";
-          ensurePermissions = {
-            "testdb.*" = "ALL PRIVILEGES";
-          };
-        }];
-        settings = {
-          mysqld = {
-            bind_address = "0.0.0.0";
-          };
-          galera = {
-            wsrep_on = "ON";
-            wsrep_debug = "NONE";
-            wsrep_retry_autocommit = "3";
-            wsrep_provider = "${pkgs.mariadb-galera}/lib/galera/libgalera_smm.so";
-            wsrep_cluster_address = "gcomm://";
-            wsrep_cluster_name = "galera-rsync";
-            wsrep_node_address = "192.168.2.1";
-            wsrep_node_name = "galera_04";
-            wsrep_sst_method = "rsync";
-            binlog_format = "ROW";
-            enforce_storage_engine = "InnoDB";
-            innodb_autoinc_lock_mode = "2";
-          };
-        };
-      };
-    };
-
-    galera_05 =
-      { pkgs, ... }:
-      {
-      imports = [ users ];
-      networking = {
-        interfaces.eth1 = {
-          ipv4.addresses = [
-            { address = "192.168.2.2"; prefixLength = 24; }
-          ];
-        };
-        extraHosts = ''
-          192.168.2.1 galera_04
-          192.168.2.2 galera_05
-          192.168.2.3 galera_06
-        '';
-        firewall.allowedTCPPorts = [ 3306 4444 4567 4568 ];
-        firewall.allowedUDPPorts = [ 4567 ];
-      };
-      systemd.services.mysql = with pkgs; {
-        path = [ mysqlenv-common mysqlenv-rsync ];
-      };
-      services.mysql = {
-        enable = true;
-        package = pkgs.mariadb;
-        settings = {
-          mysqld = {
-            bind_address = "0.0.0.0";
-          };
-          galera = {
-            wsrep_on = "ON";
-            wsrep_debug = "NONE";
-            wsrep_retry_autocommit = "3";
-            wsrep_provider = "${pkgs.mariadb-galera}/lib/galera/libgalera_smm.so";
-            wsrep_cluster_address = "gcomm://galera_04,galera_05,galera_06";
-            wsrep_cluster_name = "galera-rsync";
-            wsrep_node_address = "192.168.2.2";
-            wsrep_node_name = "galera_05";
-            wsrep_sst_method = "rsync";
-            binlog_format = "ROW";
-            enforce_storage_engine = "InnoDB";
-            innodb_autoinc_lock_mode = "2";
-          };
-        };
-      };
-    };
-
-    galera_06 =
-      { pkgs, ... }:
-      {
-      imports = [ users ];
-      networking = {
-        interfaces.eth1 = {
-          ipv4.addresses = [
-            { address = "192.168.2.3"; prefixLength = 24; }
-          ];
-        };
-        extraHosts = ''
-          192.168.2.1 galera_04
-          192.168.2.2 galera_05
-          192.168.2.3 galera_06
-        '';
-        firewall.allowedTCPPorts = [ 3306 4444 4567 4568 ];
-        firewall.allowedUDPPorts = [ 4567 ];
-      };
-      systemd.services.mysql = with pkgs; {
-        path = [ mysqlenv-common mysqlenv-rsync ];
-      };
-      services.mysql = {
-        enable = true;
-        package = pkgs.mariadb;
-        settings = {
-          mysqld = {
-            bind_address = "0.0.0.0";
-          };
-          galera = {
-            wsrep_on = "ON";
-            wsrep_debug = "NONE";
-            wsrep_retry_autocommit = "3";
-            wsrep_provider = "${pkgs.mariadb-galera}/lib/galera/libgalera_smm.so";
-            wsrep_cluster_address = "gcomm://galera_04,galera_05,galera_06";
-            wsrep_cluster_name = "galera-rsync";
-            wsrep_node_address = "192.168.2.3";
-            wsrep_node_name = "galera_06";
-            wsrep_sst_method = "rsync";
-            binlog_format = "ROW";
-            enforce_storage_engine = "InnoDB";
-            innodb_autoinc_lock_mode = "2";
-          };
-        };
-      };
-    };
-  };
-
-  testScript = ''
-    galera_04.start()
-    galera_04.wait_for_unit("mysql")
-    galera_04.wait_for_open_port(3306)
-    galera_04.succeed(
-        "sudo -u testuser mysql -u testuser -e 'use testdb; create table db1 (test_id INT, PRIMARY KEY (test_id)) ENGINE = InnoDB;'"
-    )
-    galera_04.succeed(
-        "sudo -u testuser mysql -u testuser -e 'use testdb; insert into db1 values (41);'"
-    )
-    galera_05.start()
-    galera_05.wait_for_unit("mysql")
-    galera_05.wait_for_open_port(3306)
-    galera_06.start()
-    galera_06.wait_for_unit("mysql")
-    galera_06.wait_for_open_port(3306)
-    galera_05.succeed(
-        "sudo -u testuser mysql -u testuser -e 'use testdb; select test_id from db1;' -N | grep 41"
-    )
-    galera_05.succeed(
-        "sudo -u testuser mysql -u testuser -e 'use testdb; create table db2 (test_id INT, PRIMARY KEY (test_id)) ENGINE = InnoDB;'"
-    )
-    galera_05.succeed("systemctl stop mysql")
-    galera_04.succeed(
-        "sudo -u testuser mysql -u testuser -e 'use testdb; insert into db2 values (42);'"
-    )
-    galera_06.succeed(
-        "sudo -u testuser mysql -u testuser -e 'use testdb; create table db3 (test_id INT, PRIMARY KEY (test_id)) ENGINE = InnoDB;'"
-    )
-    galera_04.succeed(
-        "sudo -u testuser mysql -u testuser -e 'use testdb; insert into db3 values (43);'"
-    )
-    galera_05.succeed("systemctl start mysql")
-    galera_05.wait_for_open_port(3306)
-    galera_05.succeed(
-        "sudo -u testuser mysql -u testuser -e 'show status' -N | grep 'wsrep_cluster_size.*3'"
-    )
-    galera_06.succeed(
-        "sudo -u testuser mysql -u testuser -e 'show status' -N | grep 'wsrep_local_state_comment.*Synced'"
-    )
-    galera_04.succeed(
-        "sudo -u testuser mysql -u testuser -e 'use testdb; select test_id from db3;' -N | grep 43"
-    )
-    galera_05.succeed(
-        "sudo -u testuser mysql -u testuser -e 'use testdb; select test_id from db2;' -N | grep 42"
-    )
-    galera_06.succeed(
-        "sudo -u testuser mysql -u testuser -e 'use testdb; select test_id from db1;' -N | grep 41"
-    )
-    galera_04.succeed("sudo -u testuser mysql -u testuser -e 'use testdb; drop table db3;'")
-    galera_05.succeed("sudo -u testuser mysql -u testuser -e 'use testdb; drop table db2;'")
-    galera_06.succeed("sudo -u testuser mysql -u testuser -e 'use testdb; drop table db1;'")
-  '';
-})
diff --git a/nixos/tests/mysql/mariadb-galera.nix b/nixos/tests/mysql/mariadb-galera.nix
new file mode 100644
index 00000000000..c9962f49c02
--- /dev/null
+++ b/nixos/tests/mysql/mariadb-galera.nix
@@ -0,0 +1,250 @@
+{
+  system ? builtins.currentSystem,
+  config ? {},
+  pkgs ? import ../../.. { inherit system config; },
+  lib ? pkgs.lib
+}:
+
+let
+  inherit (import ./common.nix { inherit pkgs lib; }) mkTestName mariadbPackages;
+
+  makeTest = import ./../make-test-python.nix;
+
+  # Common user configuration
+  makeGaleraTest = {
+    mariadbPackage,
+    name ? mkTestName mariadbPackage,
+    galeraPackage ? pkgs.mariadb-galera
+  }: makeTest {
+    name = "${name}-galera-mariabackup";
+    meta = with pkgs.lib.maintainers; {
+      maintainers = [ izorkin ajs124 das_j ];
+    };
+
+    # The test creates a Galera cluster with 3 nodes and is checking if mariabackup-based SST works. The cluster is tested by creating a DB and an empty table on one node,
+    # and checking the table's presence on the other node.
+    nodes = let
+      mkGaleraNode = {
+        id,
+        method
+      }: let
+        address = "192.168.1.${toString id}";
+        isFirstClusterNode = id == 1 || id == 4;
+      in {
+        users = {
+          users.testuser = {
+            isSystemUser = true;
+            group = "testusers";
+          };
+          groups.testusers = { };
+        };
+
+        networking = {
+          interfaces.eth1 = {
+            ipv4.addresses = [
+              { inherit address; prefixLength = 24; }
+            ];
+          };
+          extraHosts = lib.concatMapStringsSep "\n" (i: "192.168.1.${toString i} galera_0${toString i}") (lib.range 1 6);
+          firewall.allowedTCPPorts = [ 3306 4444 4567 4568 ];
+          firewall.allowedUDPPorts = [ 4567 ];
+        };
+        systemd.services.mysql = with pkgs; {
+          path = with pkgs; [
+            bash
+            gawk
+            gnutar
+            gzip
+            inetutils
+            iproute2
+            netcat
+            procps
+            pv
+            rsync
+            socat
+            stunnel
+            which
+          ];
+        };
+        services.mysql = {
+          enable = true;
+          package = mariadbPackage;
+          ensureDatabases = lib.mkIf isFirstClusterNode [ "testdb" ];
+          ensureUsers = lib.mkIf isFirstClusterNode [{
+            name = "testuser";
+            ensurePermissions = {
+              "testdb.*" = "ALL PRIVILEGES";
+            };
+          }];
+          initialScript = lib.mkIf isFirstClusterNode (pkgs.writeText "mariadb-init.sql" ''
+            GRANT ALL PRIVILEGES ON *.* TO 'check_repl'@'localhost' IDENTIFIED BY 'check_pass' WITH GRANT OPTION;
+            FLUSH PRIVILEGES;
+          '');
+          settings = {
+            mysqld = {
+              bind_address = "0.0.0.0";
+            };
+            galera = {
+              wsrep_on = "ON";
+              wsrep_debug = "NONE";
+              wsrep_retry_autocommit = "3";
+              wsrep_provider = "${galeraPackage}/lib/galera/libgalera_smm.so";
+              wsrep_cluster_address = "gcomm://"
+                + lib.optionalString (id == 2 || id == 3) "galera_01,galera_02,galera_03"
+                + lib.optionalString (id == 5 || id == 6) "galera_04,galera_05,galera_06";
+              wsrep_cluster_name = "galera";
+              wsrep_node_address = address;
+              wsrep_node_name = "galera_0${toString id}";
+              wsrep_sst_method = method;
+              wsrep_sst_auth = "check_repl:check_pass";
+              binlog_format = "ROW";
+              enforce_storage_engine = "InnoDB";
+              innodb_autoinc_lock_mode = "2";
+            };
+          };
+        };
+      };
+    in {
+      galera_01 = mkGaleraNode {
+        id = 1;
+        method = "mariabackup";
+      };
+
+      galera_02 = mkGaleraNode {
+        id = 2;
+        method = "mariabackup";
+      };
+
+      galera_03 = mkGaleraNode {
+        id = 3;
+        method = "mariabackup";
+      };
+
+      galera_04 = mkGaleraNode {
+        id = 4;
+        method = "rsync";
+      };
+
+      galera_05 = mkGaleraNode {
+        id = 5;
+        method = "rsync";
+      };
+
+      galera_06 = mkGaleraNode {
+        id = 6;
+        method = "rsync";
+      };
+
+    };
+
+    testScript = ''
+      galera_01.start()
+      galera_01.wait_for_unit("mysql")
+      galera_01.wait_for_open_port(3306)
+      galera_01.succeed(
+          "sudo -u testuser mysql -u testuser -e 'use testdb; create table db1 (test_id INT, PRIMARY KEY (test_id)) ENGINE = InnoDB;'"
+      )
+      galera_01.succeed(
+          "sudo -u testuser mysql -u testuser -e 'use testdb; insert into db1 values (37);'"
+      )
+      galera_02.start()
+      galera_02.wait_for_unit("mysql")
+      galera_02.wait_for_open_port(3306)
+      galera_03.start()
+      galera_03.wait_for_unit("mysql")
+      galera_03.wait_for_open_port(3306)
+      galera_02.succeed(
+          "sudo -u testuser mysql -u testuser -e 'use testdb; select test_id from db1;' -N | grep 37"
+      )
+      galera_02.succeed(
+          "sudo -u testuser mysql -u testuser -e 'use testdb; create table db2 (test_id INT, PRIMARY KEY (test_id)) ENGINE = InnoDB;'"
+      )
+      galera_02.succeed("systemctl stop mysql")
+      galera_01.succeed(
+          "sudo -u testuser mysql -u testuser -e 'use testdb; insert into db2 values (38);'"
+      )
+      galera_03.succeed(
+          "sudo -u testuser mysql -u testuser -e 'use testdb; create table db3 (test_id INT, PRIMARY KEY (test_id)) ENGINE = InnoDB;'"
+      )
+      galera_01.succeed(
+          "sudo -u testuser mysql -u testuser -e 'use testdb; insert into db3 values (39);'"
+      )
+      galera_02.succeed("systemctl start mysql")
+      galera_02.wait_for_open_port(3306)
+      galera_02.succeed(
+          "sudo -u testuser mysql -u testuser -e 'show status' -N | grep 'wsrep_cluster_size.*3'"
+      )
+      galera_03.succeed(
+          "sudo -u testuser mysql -u testuser -e 'show status' -N | grep 'wsrep_local_state_comment.*Synced'"
+      )
+      galera_01.succeed(
+          "sudo -u testuser mysql -u testuser -e 'use testdb; select test_id from db3;' -N | grep 39"
+      )
+      galera_02.succeed(
+          "sudo -u testuser mysql -u testuser -e 'use testdb; select test_id from db2;' -N | grep 38"
+      )
+      galera_03.succeed(
+          "sudo -u testuser mysql -u testuser -e 'use testdb; select test_id from db1;' -N | grep 37"
+      )
+      galera_01.succeed("sudo -u testuser mysql -u testuser -e 'use testdb; drop table db3;'")
+      galera_02.succeed("sudo -u testuser mysql -u testuser -e 'use testdb; drop table db2;'")
+      galera_03.succeed("sudo -u testuser mysql -u testuser -e 'use testdb; drop table db1;'")
+      galera_01.crash()
+      galera_02.crash()
+      galera_03.crash()
+
+      galera_04.start()
+      galera_04.wait_for_unit("mysql")
+      galera_04.wait_for_open_port(3306)
+      galera_04.succeed(
+          "sudo -u testuser mysql -u testuser -e 'use testdb; create table db1 (test_id INT, PRIMARY KEY (test_id)) ENGINE = InnoDB;'"
+      )
+      galera_04.succeed(
+          "sudo -u testuser mysql -u testuser -e 'use testdb; insert into db1 values (41);'"
+      )
+      galera_05.start()
+      galera_05.wait_for_unit("mysql")
+      galera_05.wait_for_open_port(3306)
+      galera_06.start()
+      galera_06.wait_for_unit("mysql")
+      galera_06.wait_for_open_port(3306)
+      galera_05.succeed(
+          "sudo -u testuser mysql -u testuser -e 'use testdb; select test_id from db1;' -N | grep 41"
+      )
+      galera_05.succeed(
+          "sudo -u testuser mysql -u testuser -e 'use testdb; create table db2 (test_id INT, PRIMARY KEY (test_id)) ENGINE = InnoDB;'"
+      )
+      galera_05.succeed("systemctl stop mysql")
+      galera_04.succeed(
+          "sudo -u testuser mysql -u testuser -e 'use testdb; insert into db2 values (42);'"
+      )
+      galera_06.succeed(
+          "sudo -u testuser mysql -u testuser -e 'use testdb; create table db3 (test_id INT, PRIMARY KEY (test_id)) ENGINE = InnoDB;'"
+      )
+      galera_04.succeed(
+          "sudo -u testuser mysql -u testuser -e 'use testdb; insert into db3 values (43);'"
+      )
+      galera_05.succeed("systemctl start mysql")
+      galera_05.wait_for_open_port(3306)
+      galera_05.succeed(
+          "sudo -u testuser mysql -u testuser -e 'show status' -N | grep 'wsrep_cluster_size.*3'"
+      )
+      galera_06.succeed(
+          "sudo -u testuser mysql -u testuser -e 'show status' -N | grep 'wsrep_local_state_comment.*Synced'"
+      )
+      galera_04.succeed(
+          "sudo -u testuser mysql -u testuser -e 'use testdb; select test_id from db3;' -N | grep 43"
+      )
+      galera_05.succeed(
+          "sudo -u testuser mysql -u testuser -e 'use testdb; select test_id from db2;' -N | grep 42"
+      )
+      galera_06.succeed(
+          "sudo -u testuser mysql -u testuser -e 'use testdb; select test_id from db1;' -N | grep 41"
+      )
+      galera_04.succeed("sudo -u testuser mysql -u testuser -e 'use testdb; drop table db3;'")
+      galera_05.succeed("sudo -u testuser mysql -u testuser -e 'use testdb; drop table db2;'")
+      galera_06.succeed("sudo -u testuser mysql -u testuser -e 'use testdb; drop table db1;'")
+    '';
+  };
+in
+  lib.mapAttrs (_: mariadbPackage: makeGaleraTest { inherit mariadbPackage; }) mariadbPackages
diff --git a/nixos/tests/mysql/mysql-autobackup.nix b/nixos/tests/mysql/mysql-autobackup.nix
index b0ec7daaf05..101122f7bde 100644
--- a/nixos/tests/mysql/mysql-autobackup.nix
+++ b/nixos/tests/mysql/mysql-autobackup.nix
@@ -1,38 +1,53 @@
-import ./../make-test-python.nix ({ pkgs, lib, ... }:
-
 {
-  name = "automysqlbackup";
-  meta.maintainers = [ lib.maintainers.aanderse ];
-
-  machine =
-    { pkgs, ... }:
-    {
-      services.mysql.enable = true;
-      services.mysql.package = pkgs.mariadb;
-      services.mysql.initialDatabases = [ { name = "testdb"; schema = ./testdb.sql; } ];
+  system ? builtins.currentSystem,
+  config ? {},
+  pkgs ? import ../../.. { inherit system config; },
+  lib ? pkgs.lib
+}:
+
+let
+  inherit (import ./common.nix { inherit pkgs lib; }) mkTestName mariadbPackages;
+
+  makeTest = import ./../make-test-python.nix;
+
+  makeAutobackupTest = {
+    package,
+    name ? mkTestName package,
+  }: makeTest {
+    name = "${name}-automysqlbackup";
+    meta.maintainers = [ lib.maintainers.aanderse ];
+
+    machine = {
+      services.mysql = {
+        inherit package;
+        enable = true;
+        initialDatabases = [ { name = "testdb"; schema = ./testdb.sql; } ];
+      };
 
       services.automysqlbackup.enable = true;
     };
 
-  testScript = ''
-    start_all()
-
-    # Need to have mysql started so that it can be populated with data.
-    machine.wait_for_unit("mysql.service")
-
-    with subtest("Wait for testdb to be fully populated (5 rows)."):
-        machine.wait_until_succeeds(
-            "mysql -u root -D testdb -N -B -e 'select count(id) from tests' | grep -q 5"
-        )
-
-    with subtest("Do a backup and wait for it to start"):
-        machine.start_job("automysqlbackup.service")
-        machine.wait_for_job("automysqlbackup.service")
-
-    with subtest("wait for backup file and check that data appears in backup"):
-        machine.wait_for_file("/var/backup/mysql/daily/testdb")
-        machine.succeed(
-            "${pkgs.gzip}/bin/zcat /var/backup/mysql/daily/testdb/daily_testdb_*.sql.gz | grep hello"
-        )
-    '';
-})
+    testScript = ''
+      start_all()
+
+      # Need to have mysql started so that it can be populated with data.
+      machine.wait_for_unit("mysql.service")
+
+      with subtest("Wait for testdb to be fully populated (5 rows)."):
+          machine.wait_until_succeeds(
+              "mysql -u root -D testdb -N -B -e 'select count(id) from tests' | grep -q 5"
+          )
+
+      with subtest("Do a backup and wait for it to start"):
+          machine.start_job("automysqlbackup.service")
+          machine.wait_for_job("automysqlbackup.service")
+
+      with subtest("wait for backup file and check that data appears in backup"):
+          machine.wait_for_file("/var/backup/mysql/daily/testdb")
+          machine.succeed(
+              "${pkgs.gzip}/bin/zcat /var/backup/mysql/daily/testdb/daily_testdb_*.sql.gz | grep hello"
+          )
+      '';
+  };
+in
+  lib.mapAttrs (_: package: makeAutobackupTest { inherit package; }) mariadbPackages
diff --git a/nixos/tests/mysql/mysql-backup.nix b/nixos/tests/mysql/mysql-backup.nix
index 269fddc66e1..9335b233327 100644
--- a/nixos/tests/mysql/mysql-backup.nix
+++ b/nixos/tests/mysql/mysql-backup.nix
@@ -1,56 +1,72 @@
-# Test whether mysqlBackup option works
-import ./../make-test-python.nix ({ pkgs, ... } : {
-  name = "mysql-backup";
-  meta = with pkgs.lib.maintainers; {
-    maintainers = [ rvl ];
-  };
+{
+  system ? builtins.currentSystem,
+  config ? {},
+  pkgs ? import ../../.. { inherit system config; },
+  lib ? pkgs.lib
+}:
 
-  nodes = {
-    master = { pkgs, ... }: {
-      services.mysql = {
-        enable = true;
-        initialDatabases = [ { name = "testdb"; schema = ./testdb.sql; } ];
-        package = pkgs.mariadb;
-      };
+let
+  inherit (import ./common.nix { inherit pkgs lib; }) mkTestName mariadbPackages;
+
+  makeTest = import ./../make-test-python.nix;
+
+  makeBackupTest = {
+    package,
+    name ? mkTestName package
+  }: makeTest {
+    name = "${name}-backup";
+    meta = with pkgs.lib.maintainers; {
+      maintainers = [ rvl ];
+    };
+
+    nodes = {
+      master = { pkgs, ... }: {
+        services.mysql = {
+          inherit package;
+          enable = true;
+          initialDatabases = [ { name = "testdb"; schema = ./testdb.sql; } ];
+        };
 
-      services.mysqlBackup = {
-        enable = true;
-        databases = [ "doesnotexist" "testdb" ];
+        services.mysqlBackup = {
+          enable = true;
+          databases = [ "doesnotexist" "testdb" ];
+        };
       };
     };
-  };
 
-  testScript = ''
-    start_all()
+    testScript = ''
+      start_all()
 
-    # Delete backup file that may be left over from a previous test run.
-    # This is not needed on Hydra but useful for repeated local test runs.
-    master.execute("rm -f /var/backup/mysql/testdb.gz")
+      # Delete backup file that may be left over from a previous test run.
+      # This is not needed on Hydra but useful for repeated local test runs.
+      master.execute("rm -f /var/backup/mysql/testdb.gz")
 
-    # Need to have mysql started so that it can be populated with data.
-    master.wait_for_unit("mysql.service")
+      # Need to have mysql started so that it can be populated with data.
+      master.wait_for_unit("mysql.service")
 
-    # Wait for testdb to be fully populated (5 rows).
-    master.wait_until_succeeds(
-        "mysql -u root -D testdb -N -B -e 'select count(id) from tests' | grep -q 5"
-    )
+      # Wait for testdb to be fully populated (5 rows).
+      master.wait_until_succeeds(
+          "mysql -u root -D testdb -N -B -e 'select count(id) from tests' | grep -q 5"
+      )
 
-    # Do a backup and wait for it to start
-    master.start_job("mysql-backup.service")
-    master.wait_for_unit("mysql-backup.service")
+      # Do a backup and wait for it to start
+      master.start_job("mysql-backup.service")
+      master.wait_for_unit("mysql-backup.service")
 
-    # wait for backup to fail, because of database 'doesnotexist'
-    master.wait_until_fails("systemctl is-active -q mysql-backup.service")
+      # wait for backup to fail, because of database 'doesnotexist'
+      master.wait_until_fails("systemctl is-active -q mysql-backup.service")
 
-    # wait for backup file and check that data appears in backup
-    master.wait_for_file("/var/backup/mysql/testdb.gz")
-    master.succeed(
-        "${pkgs.gzip}/bin/zcat /var/backup/mysql/testdb.gz | grep hello"
-    )
+      # wait for backup file and check that data appears in backup
+      master.wait_for_file("/var/backup/mysql/testdb.gz")
+      master.succeed(
+          "${pkgs.gzip}/bin/zcat /var/backup/mysql/testdb.gz | grep hello"
+      )
 
-    # Check that a failed backup is logged
-    master.succeed(
-        "journalctl -u mysql-backup.service | grep 'fail.*doesnotexist' > /dev/null"
-    )
-  '';
-})
+      # Check that a failed backup is logged
+      master.succeed(
+          "journalctl -u mysql-backup.service | grep 'fail.*doesnotexist' > /dev/null"
+      )
+    '';
+  };
+in
+  lib.mapAttrs (_: package: makeBackupTest { inherit package; }) mariadbPackages
diff --git a/nixos/tests/mysql/mysql-replication.nix b/nixos/tests/mysql/mysql-replication.nix
index a52372ca47c..f6014019bd5 100644
--- a/nixos/tests/mysql/mysql-replication.nix
+++ b/nixos/tests/mysql/mysql-replication.nix
@@ -1,91 +1,101 @@
-import ./../make-test-python.nix ({ pkgs, ...} :
+{
+  system ? builtins.currentSystem,
+  config ? {},
+  pkgs ? import ../../.. { inherit system config; },
+  lib ? pkgs.lib
+}:
 
 let
+  inherit (import ./common.nix { inherit pkgs lib; }) mkTestName mariadbPackages;
+
   replicateUser = "replicate";
   replicatePassword = "secret";
-in
 
-{
-  name = "mysql-replication";
-  meta = with pkgs.lib.maintainers; {
-    maintainers = [ eelco shlevy ];
-  };
+  makeTest = import ./../make-test-python.nix;
 
-  nodes = {
-    master =
-      { pkgs, ... }:
+  makeReplicationTest = {
+    package,
+    name ? mkTestName package,
+  }: makeTest {
+    name = "${name}-replication";
+    meta = with pkgs.lib.maintainers; {
+      maintainers = [ ajs124 das_j ];
+    };
 
-      {
-        services.mysql.enable = true;
-        services.mysql.package = pkgs.mariadb;
-        services.mysql.replication.role = "master";
-        services.mysql.replication.slaveHost = "%";
-        services.mysql.replication.masterUser = replicateUser;
-        services.mysql.replication.masterPassword = replicatePassword;
-        services.mysql.initialDatabases = [ { name = "testdb"; schema = ./testdb.sql; } ];
+    nodes = {
+      primary = {
+        services.mysql = {
+          inherit package;
+          enable = true;
+          replication.role = "master";
+          replication.slaveHost = "%";
+          replication.masterUser = replicateUser;
+          replication.masterPassword = replicatePassword;
+          initialDatabases = [ { name = "testdb"; schema = ./testdb.sql; } ];
+        };
         networking.firewall.allowedTCPPorts = [ 3306 ];
       };
 
-    slave1 =
-      { pkgs, nodes, ... }:
-
-      {
-        services.mysql.enable = true;
-        services.mysql.package = pkgs.mariadb;
-        services.mysql.replication.role = "slave";
-        services.mysql.replication.serverId = 2;
-        services.mysql.replication.masterHost = nodes.master.config.networking.hostName;
-        services.mysql.replication.masterUser = replicateUser;
-        services.mysql.replication.masterPassword = replicatePassword;
+      secondary1 = { nodes, ... }: {
+        services.mysql = {
+          inherit package;
+          enable = true;
+          replication.role = "slave";
+          replication.serverId = 2;
+          replication.masterHost = nodes.primary.config.networking.hostName;
+          replication.masterUser = replicateUser;
+          replication.masterPassword = replicatePassword;
+        };
       };
 
-    slave2 =
-      { pkgs, nodes, ... }:
-
-      {
-        services.mysql.enable = true;
-        services.mysql.package = pkgs.mariadb;
-        services.mysql.replication.role = "slave";
-        services.mysql.replication.serverId = 3;
-        services.mysql.replication.masterHost = nodes.master.config.networking.hostName;
-        services.mysql.replication.masterUser = replicateUser;
-        services.mysql.replication.masterPassword = replicatePassword;
+      secondary2 = { nodes, ... }: {
+        services.mysql = {
+          inherit package;
+          enable = true;
+          replication.role = "slave";
+          replication.serverId = 3;
+          replication.masterHost = nodes.primary.config.networking.hostName;
+          replication.masterUser = replicateUser;
+          replication.masterPassword = replicatePassword;
+        };
       };
-  };
+    };
 
-  testScript = ''
-    master.start()
-    master.wait_for_unit("mysql")
-    master.wait_for_open_port(3306)
-    # Wait for testdb to be fully populated (5 rows).
-    master.wait_until_succeeds(
-        "sudo -u mysql mysql -u mysql -D testdb -N -B -e 'select count(id) from tests' | grep -q 5"
-    )
+    testScript = ''
+      primary.start()
+      primary.wait_for_unit("mysql")
+      primary.wait_for_open_port(3306)
+      # Wait for testdb to be fully populated (5 rows).
+      primary.wait_until_succeeds(
+          "sudo -u mysql mysql -u mysql -D testdb -N -B -e 'select count(id) from tests' | grep -q 5"
+      )
 
-    slave1.start()
-    slave2.start()
-    slave1.wait_for_unit("mysql")
-    slave1.wait_for_open_port(3306)
-    slave2.wait_for_unit("mysql")
-    slave2.wait_for_open_port(3306)
+      secondary1.start()
+      secondary2.start()
+      secondary1.wait_for_unit("mysql")
+      secondary1.wait_for_open_port(3306)
+      secondary2.wait_for_unit("mysql")
+      secondary2.wait_for_open_port(3306)
 
-    # wait for replications to finish
-    slave1.wait_until_succeeds(
-        "sudo -u mysql mysql -u mysql -D testdb -N -B -e 'select count(id) from tests' | grep -q 5"
-    )
-    slave2.wait_until_succeeds(
-        "sudo -u mysql mysql -u mysql -D testdb -N -B -e 'select count(id) from tests' | grep -q 5"
-    )
+      # wait for replications to finish
+      secondary1.wait_until_succeeds(
+          "sudo -u mysql mysql -u mysql -D testdb -N -B -e 'select count(id) from tests' | grep -q 5"
+      )
+      secondary2.wait_until_succeeds(
+          "sudo -u mysql mysql -u mysql -D testdb -N -B -e 'select count(id) from tests' | grep -q 5"
+      )
 
-    slave2.succeed("systemctl stop mysql")
-    master.succeed(
-        "echo 'insert into testdb.tests values (123, 456);' | sudo -u mysql mysql -u mysql -N"
-    )
-    slave2.succeed("systemctl start mysql")
-    slave2.wait_for_unit("mysql")
-    slave2.wait_for_open_port(3306)
-    slave2.wait_until_succeeds(
-        "echo 'select * from testdb.tests where Id = 123;' | sudo -u mysql mysql -u mysql -N | grep 456"
-    )
-  '';
-})
+      secondary2.succeed("systemctl stop mysql")
+      primary.succeed(
+          "echo 'insert into testdb.tests values (123, 456);' | sudo -u mysql mysql -u mysql -N"
+      )
+      secondary2.succeed("systemctl start mysql")
+      secondary2.wait_for_unit("mysql")
+      secondary2.wait_for_open_port(3306)
+      secondary2.wait_until_succeeds(
+          "echo 'select * from testdb.tests where Id = 123;' | sudo -u mysql mysql -u mysql -N | grep 456"
+      )
+    '';
+  };
+in
+  lib.mapAttrs (_: package: makeReplicationTest { inherit package; }) mariadbPackages
diff --git a/nixos/tests/mysql/mysql.nix b/nixos/tests/mysql/mysql.nix
index 2ac2b34a18e..a5e42f85a7f 100644
--- a/nixos/tests/mysql/mysql.nix
+++ b/nixos/tests/mysql/mysql.nix
@@ -1,221 +1,149 @@
-import ./../make-test-python.nix ({ pkgs, ...}:
-
+{
+  system ? builtins.currentSystem,
+  config ? {},
+  pkgs ? import ../../.. { inherit system config; },
+  lib ? pkgs.lib
+}:
 
 let
-  # Setup common users
-  users = { ... }:
-  {
-    users.groups.testusers = { };
+  inherit (import ./common.nix { inherit pkgs lib; }) mkTestName mariadbPackages mysqlPackages;
 
-    users.users.testuser = {
-      isSystemUser = true;
-      group = "testusers";
+  makeTest = import ./../make-test-python.nix;
+  # Setup common users
+  makeMySQLTest = {
+    package,
+    name ? mkTestName package,
+    useSocketAuth ? true,
+    hasMroonga ? true,
+    hasRocksDB ? true
+  }: makeTest {
+    inherit name;
+    meta = with lib.maintainers; {
+      maintainers = [ ajs124 das_j ];
     };
 
-    users.users.testuser2 = {
-      isSystemUser = true;
-      group = "testusers";
-    };
-  };
+    nodes = {
+      ${name} =
+        { pkgs, ... }: {
 
-in
+          users = {
+            groups.testusers = { };
 
-{
-  name = "mysql";
-  meta = with pkgs.lib.maintainers; {
-    maintainers = [ eelco shlevy ];
-  };
-
-  nodes = {
-    mysql57 =
-      { pkgs, ... }:
+            users.testuser = {
+              isSystemUser = true;
+              group = "testusers";
+            };
 
-      {
-        imports = [ users ];
-
-        services.mysql.enable = true;
-        services.mysql.initialDatabases = [
-          { name = "testdb3"; schema = ./testdb.sql; }
-        ];
-        # note that using pkgs.writeText here is generally not a good idea,
-        # as it will store the password in world-readable /nix/store ;)
-        services.mysql.initialScript = pkgs.writeText "mysql-init.sql" ''
-          CREATE USER 'testuser3'@'localhost' IDENTIFIED BY 'secure';
-          GRANT ALL PRIVILEGES ON testdb3.* TO 'testuser3'@'localhost';
-        '';
-        services.mysql.ensureDatabases = [ "testdb" "testdb2" ];
-        services.mysql.ensureUsers = [{
-          name = "testuser";
-          ensurePermissions = {
-            "testdb.*" = "ALL PRIVILEGES";
-          };
-        } {
-          name = "testuser2";
-          ensurePermissions = {
-            "testdb2.*" = "ALL PRIVILEGES";
+            users.testuser2 = {
+              isSystemUser = true;
+              group = "testusers";
+            };
           };
-        }];
-        services.mysql.package = pkgs.mysql57;
-      };
 
-    mysql80 =
-      { pkgs, ... }:
-
-      {
-        imports = [ users ];
-
-        services.mysql.enable = true;
-        services.mysql.initialDatabases = [
-          { name = "testdb3"; schema = ./testdb.sql; }
-        ];
-        # note that using pkgs.writeText here is generally not a good idea,
-        # as it will store the password in world-readable /nix/store ;)
-        services.mysql.initialScript = pkgs.writeText "mysql-init.sql" ''
-          CREATE USER 'testuser3'@'localhost' IDENTIFIED BY 'secure';
-          GRANT ALL PRIVILEGES ON testdb3.* TO 'testuser3'@'localhost';
-        '';
-        services.mysql.ensureDatabases = [ "testdb" "testdb2" ];
-        services.mysql.ensureUsers = [{
-          name = "testuser";
-          ensurePermissions = {
-            "testdb.*" = "ALL PRIVILEGES";
-          };
-        } {
-          name = "testuser2";
-          ensurePermissions = {
-            "testdb2.*" = "ALL PRIVILEGES";
+          services.mysql = {
+            enable = true;
+            initialDatabases = [
+              { name = "testdb3"; schema = ./testdb.sql; }
+            ];
+            # note that using pkgs.writeText here is generally not a good idea,
+            # as it will store the password in world-readable /nix/store ;)
+            initialScript = pkgs.writeText "mysql-init.sql" (if (!useSocketAuth) then ''
+              CREATE USER 'testuser3'@'localhost' IDENTIFIED BY 'secure';
+              GRANT ALL PRIVILEGES ON testdb3.* TO 'testuser3'@'localhost';
+            '' else ''
+              ALTER USER root@localhost IDENTIFIED WITH unix_socket;
+              DELETE FROM mysql.user WHERE password = ''' AND plugin = ''';
+              DELETE FROM mysql.user WHERE user = ''';
+              FLUSH PRIVILEGES;
+            '');
+
+            ensureDatabases = [ "testdb" "testdb2" ];
+            ensureUsers = [{
+              name = "testuser";
+              ensurePermissions = {
+                "testdb.*" = "ALL PRIVILEGES";
+              };
+            } {
+              name = "testuser2";
+              ensurePermissions = {
+                "testdb2.*" = "ALL PRIVILEGES";
+              };
+            }];
+            package = package;
+            settings = {
+              mysqld = {
+                plugin-load-add = lib.optional hasMroonga "ha_mroonga.so"
+                  ++ lib.optional hasRocksDB "ha_rocksdb.so";
+              };
+            };
           };
-        }];
-        services.mysql.package = pkgs.mysql80;
-      };
-
-    mariadb =
-      { pkgs, ... }:
-
-      {
-        imports = [ users ];
+        };
 
-        services.mysql.enable = true;
-        services.mysql.initialScript = pkgs.writeText "mariadb-init.sql" ''
-          ALTER USER root@localhost IDENTIFIED WITH unix_socket;
-          DELETE FROM mysql.user WHERE password = ''' AND plugin = ''';
-          DELETE FROM mysql.user WHERE user = ''';
-          FLUSH PRIVILEGES;
-        '';
-        services.mysql.ensureDatabases = [ "testdb" "testdb2" ];
-        services.mysql.ensureUsers = [{
-          name = "testuser";
-          ensurePermissions = {
-            "testdb.*" = "ALL PRIVILEGES";
-          };
-        } {
-          name = "testuser2";
-          ensurePermissions = {
-            "testdb2.*" = "ALL PRIVILEGES";
-          };
-        }];
-        services.mysql.settings = {
-          mysqld = {
-            plugin-load-add = [ "ha_mroonga.so" "ha_rocksdb.so" ];
-          };
+      mariadb =        {
         };
-        services.mysql.package = pkgs.mariadb;
-      };
+    };
 
+    testScript = ''
+      start_all()
+
+      machine = ${name}
+      machine.wait_for_unit("mysql")
+      machine.succeed(
+          "echo 'use testdb; create table tests (test_id INT, PRIMARY KEY (test_id));' | sudo -u testuser mysql -u testuser"
+      )
+      machine.succeed(
+          "echo 'use testdb; insert into tests values (42);' | sudo -u testuser mysql -u testuser"
+      )
+      # Ensure testuser2 is not able to insert into testdb as mysql testuser2
+      machine.fail(
+          "echo 'use testdb; insert into tests values (23);' | sudo -u testuser2 mysql -u testuser2"
+      )
+      # Ensure testuser2 is not able to authenticate as mysql testuser
+      machine.fail(
+          "echo 'use testdb; insert into tests values (23);' | sudo -u testuser2 mysql -u testuser"
+      )
+      machine.succeed(
+          "echo 'use testdb; select test_id from tests;' | sudo -u testuser mysql -u testuser -N | grep 42"
+      )
+
+      ${lib.optionalString hasMroonga ''
+        # Check if Mroonga plugin works
+        machine.succeed(
+            "echo 'use testdb; create table mroongadb (test_id INT, PRIMARY KEY (test_id)) ENGINE = Mroonga;' | sudo -u testuser mysql -u testuser"
+        )
+        machine.succeed(
+            "echo 'use testdb; insert into mroongadb values (25);' | sudo -u testuser mysql -u testuser"
+        )
+        machine.succeed(
+            "echo 'use testdb; select test_id from mroongadb;' | sudo -u testuser mysql -u testuser -N | grep 25"
+        )
+        machine.succeed(
+            "echo 'use testdb; drop table mroongadb;' | sudo -u testuser mysql -u testuser"
+        )
+      ''}
+
+      ${lib.optionalString hasRocksDB ''
+        # Check if RocksDB plugin works
+        machine.succeed(
+            "echo 'use testdb; create table rocksdb (test_id INT, PRIMARY KEY (test_id)) ENGINE = RocksDB;' | sudo -u testuser mysql -u testuser"
+        )
+        machine.succeed(
+            "echo 'use testdb; insert into rocksdb values (28);' | sudo -u testuser mysql -u testuser"
+        )
+        machine.succeed(
+            "echo 'use testdb; select test_id from rocksdb;' | sudo -u testuser mysql -u testuser -N | grep 28"
+        )
+        machine.succeed(
+            "echo 'use testdb; drop table rocksdb;' | sudo -u testuser mysql -u testuser"
+        )
+      ''}
+    '';
   };
-
-  testScript = ''
-    start_all()
-
-    mysql57.wait_for_unit("mysql")
-    mysql57.succeed(
-        "echo 'use testdb; create table tests (test_id INT, PRIMARY KEY (test_id));' | sudo -u testuser mysql -u testuser"
-    )
-    mysql57.succeed(
-        "echo 'use testdb; insert into tests values (41);' | sudo -u testuser mysql -u testuser"
-    )
-    # Ensure testuser2 is not able to insert into testdb as mysql testuser2
-    mysql57.fail(
-        "echo 'use testdb; insert into tests values (22);' | sudo -u testuser2 mysql -u testuser2"
-    )
-    # Ensure testuser2 is not able to authenticate as mysql testuser
-    mysql57.fail(
-        "echo 'use testdb; insert into tests values (22);' | sudo -u testuser2 mysql -u testuser"
-    )
-    mysql57.succeed(
-        "echo 'use testdb; select test_id from tests;' | sudo -u testuser mysql -u testuser -N | grep 41"
-    )
-    mysql57.succeed(
-        "echo 'use testdb3; select * from tests;' | mysql -u testuser3 --password=secure -N | grep 4"
-    )
-
-    mysql80.wait_for_unit("mysql")
-    mysql80.succeed(
-        "echo 'use testdb; create table tests (test_id INT, PRIMARY KEY (test_id));' | sudo -u testuser mysql -u testuser"
-    )
-    mysql80.succeed(
-        "echo 'use testdb; insert into tests values (41);' | sudo -u testuser mysql -u testuser"
-    )
-    # Ensure testuser2 is not able to insert into testdb as mysql testuser2
-    mysql80.fail(
-        "echo 'use testdb; insert into tests values (22);' | sudo -u testuser2 mysql -u testuser2"
-    )
-    # Ensure testuser2 is not able to authenticate as mysql testuser
-    mysql80.fail(
-        "echo 'use testdb; insert into tests values (22);' | sudo -u testuser2 mysql -u testuser"
-    )
-    mysql80.succeed(
-        "echo 'use testdb; select test_id from tests;' | sudo -u testuser mysql -u testuser -N | grep 41"
-    )
-    mysql80.succeed(
-        "echo 'use testdb3; select * from tests;' | mysql -u testuser3 --password=secure -N | grep 4"
-    )
-
-    mariadb.wait_for_unit("mysql")
-    mariadb.succeed(
-        "echo 'use testdb; create table tests (test_id INT, PRIMARY KEY (test_id));' | sudo -u testuser mysql -u testuser"
-    )
-    mariadb.succeed(
-        "echo 'use testdb; insert into tests values (42);' | sudo -u testuser mysql -u testuser"
-    )
-    # Ensure testuser2 is not able to insert into testdb as mysql testuser2
-    mariadb.fail(
-        "echo 'use testdb; insert into tests values (23);' | sudo -u testuser2 mysql -u testuser2"
-    )
-    # Ensure testuser2 is not able to authenticate as mysql testuser
-    mariadb.fail(
-        "echo 'use testdb; insert into tests values (23);' | sudo -u testuser2 mysql -u testuser"
-    )
-    mariadb.succeed(
-        "echo 'use testdb; select test_id from tests;' | sudo -u testuser mysql -u testuser -N | grep 42"
-    )
-
-    # Check if Mroonga plugin works
-    mariadb.succeed(
-        "echo 'use testdb; create table mroongadb (test_id INT, PRIMARY KEY (test_id)) ENGINE = Mroonga;' | sudo -u testuser mysql -u testuser"
-    )
-    mariadb.succeed(
-        "echo 'use testdb; insert into mroongadb values (25);' | sudo -u testuser mysql -u testuser"
-    )
-    mariadb.succeed(
-        "echo 'use testdb; select test_id from mroongadb;' | sudo -u testuser mysql -u testuser -N | grep 25"
-    )
-    mariadb.succeed(
-        "echo 'use testdb; drop table mroongadb;' | sudo -u testuser mysql -u testuser"
-    )
-
-    # Check if RocksDB plugin works
-    mariadb.succeed(
-        "echo 'use testdb; create table rocksdb (test_id INT, PRIMARY KEY (test_id)) ENGINE = RocksDB;' | sudo -u testuser mysql -u testuser"
-    )
-    mariadb.succeed(
-        "echo 'use testdb; insert into rocksdb values (28);' | sudo -u testuser mysql -u testuser"
-    )
-    mariadb.succeed(
-        "echo 'use testdb; select test_id from rocksdb;' | sudo -u testuser mysql -u testuser -N | grep 28"
-    )
-    mariadb.succeed(
-        "echo 'use testdb; drop table rocksdb;' | sudo -u testuser mysql -u testuser"
-    )
-  '';
-})
+in
+  lib.mapAttrs (_: package: makeMySQLTest {
+    inherit package;
+    hasRocksDB = false; hasMroonga = false;
+  }) mysqlPackages
+  // (lib.mapAttrs (_: package: makeMySQLTest {
+    inherit package;
+  }) mariadbPackages)
diff --git a/nixos/tests/networking.nix b/nixos/tests/networking.nix
index f46a115a07d..2510937b5dc 100644
--- a/nixos/tests/networking.nix
+++ b/nixos/tests/networking.nix
@@ -740,6 +740,7 @@ let
     routes = {
       name = "routes";
       machine = {
+        networking.useNetworkd = networkd;
         networking.useDHCP = false;
         networking.interfaces.eth0 = {
           ipv4.addresses = [ { address = "192.168.1.2"; prefixLength = 24; } ];
@@ -749,7 +750,13 @@ let
             { address = "2001:1470:fffd:2098::"; prefixLength = 64; via = "fdfd:b3f0::1"; }
           ];
           ipv4.routes = [
-            { address = "10.0.0.0"; prefixLength = 16; options = { mtu = "1500"; }; }
+            { address = "10.0.0.0"; prefixLength = 16; options = {
+              mtu = "1500";
+              # Explicitly set scope because iproute and systemd-networkd
+              # disagree on what the scope should be
+              # if the type is the default "unicast"
+              scope = "link";
+            }; }
             { address = "192.168.2.0"; prefixLength = 24; via = "192.168.1.1"; }
           ];
         };
@@ -798,6 +805,7 @@ let
                 ipv6Table, targetIPv6Table
             )
 
+      '' + optionalString (!networkd) ''
         with subtest("test clean-up of the tables"):
             machine.succeed("systemctl stop network-addresses-eth0")
             ipv4Residue = machine.succeed("ip -4 route list dev eth0 | head -n-3").strip()
diff --git a/nixos/tests/nextcloud/with-mysql-and-memcached.nix b/nixos/tests/nextcloud/with-mysql-and-memcached.nix
index 80cb63df5db..891001e30b2 100644
--- a/nixos/tests/nextcloud/with-mysql-and-memcached.nix
+++ b/nixos/tests/nextcloud/with-mysql-and-memcached.nix
@@ -40,15 +40,16 @@ in {
 
       services.mysql = {
         enable = true;
-        bind = "127.0.0.1";
+        settings.mysqld = {
+          bind-address = "127.0.0.1";
+
+          # FIXME(@Ma27) Nextcloud isn't compatible with mariadb 10.6,
+          # this is a workaround.
+          # See https://help.nextcloud.com/t/update-to-next-cloud-21-0-2-has-get-an-error/117028/22
+          innodb_read_only_compressed = 0;
+        };
         package = pkgs.mariadb;
 
-        # FIXME(@Ma27) Nextcloud isn't compatible with mariadb 10.6,
-        # this is a workaround.
-        # See https://help.nextcloud.com/t/update-to-next-cloud-21-0-2-has-get-an-error/117028/22
-        extraOptions = ''
-          innodb_read_only_compressed=0
-        '';
         initialScript = pkgs.writeText "mysql-init" ''
           CREATE USER 'nextcloud'@'localhost' IDENTIFIED BY 'hunter2';
           CREATE DATABASE IF NOT EXISTS nextcloud;
diff --git a/nixos/tests/rstudio-server.nix b/nixos/tests/rstudio-server.nix
new file mode 100644
index 00000000000..c7ac7670fbd
--- /dev/null
+++ b/nixos/tests/rstudio-server.nix
@@ -0,0 +1,30 @@
+import ./make-test-python.nix ({ pkgs, ... }:
+  {
+    name = "rstudio-server-test";
+    meta.maintainers = with pkgs.lib.maintainers; [ jbedo cfhammill ];
+
+    nodes.machine = { config, lib, pkgs, ... }: {
+      services.rstudio-server.enable = true;
+    };
+
+    nodes.customPackageMachine = { config, lib, pkgs, ... }: {
+      services.rstudio-server = {
+        enable = true;
+        package = pkgs.rstudioServerWrapper.override { packages = [ pkgs.rPackages.ggplot2 ]; };
+      };
+    };
+
+    users.testuser = {
+      uid = 1000;
+      group = "testgroup";
+    };
+    groups.testgroup.gid = 1000;
+
+    testScript = ''
+      machine.wait_for_unit("rstudio-server.service")
+      machine.succeed("curl -f -vvv -s http://127.0.0.1:8787")
+
+      customPackageMachine.wait_for_unit("rstudio-server.service")
+      customPackageMachine.succeed("curl -f -vvv -s http://127.0.0.1:8787")
+    '';
+  })
diff --git a/nixos/tests/starship.nix b/nixos/tests/starship.nix
new file mode 100644
index 00000000000..33e9a72f700
--- /dev/null
+++ b/nixos/tests/starship.nix
@@ -0,0 +1,42 @@
+import ./make-test-python.nix ({ pkgs, ... }: {
+  name = "starship";
+  meta.maintainers = pkgs.starship.meta.maintainers;
+
+  machine = {
+    programs = {
+      fish.enable = true;
+      zsh.enable = true;
+
+      starship = {
+        enable = true;
+        settings.format = "<starship>";
+      };
+    };
+
+    environment.systemPackages = map
+      (shell: pkgs.writeScriptBin "expect-${shell}" ''
+        #!${pkgs.expect}/bin/expect -f
+
+        spawn env TERM=xterm ${shell} -i
+
+        expect "<starship>" {
+          send "exit\n"
+        } timeout {
+          send_user "\n${shell} failed to display Starship\n"
+          exit 1
+        }
+
+        expect eof
+      '')
+      [ "bash" "fish" "zsh" ];
+  };
+
+  testScript = ''
+    start_all()
+    machine.wait_for_unit("default.target")
+
+    machine.succeed("expect-bash")
+    machine.succeed("expect-fish")
+    machine.succeed("expect-zsh")
+  '';
+})
diff --git a/nixos/tests/sway.nix b/nixos/tests/sway.nix
index 3476ebab3e2..43b8c847304 100644
--- a/nixos/tests/sway.nix
+++ b/nixos/tests/sway.nix
@@ -1,4 +1,4 @@
-import ./make-test-python.nix ({ pkgs, lib, ...} :
+import ./make-test-python.nix ({ pkgs, lib, ... }:
 
 {
   name = "sway";
@@ -13,19 +13,38 @@ import ./make-test-python.nix ({ pkgs, lib, ...} :
 
     environment = {
       # For glinfo and wayland-info:
-      systemPackages = with pkgs; [ mesa-demos wayland-utils ];
+      systemPackages = with pkgs; [ mesa-demos wayland-utils alacritty ];
       # Use a fixed SWAYSOCK path (for swaymsg):
       variables = {
         "SWAYSOCK" = "/tmp/sway-ipc.sock";
-        "WLR_RENDERER_ALLOW_SOFTWARE" = "1";
+        # TODO: Investigate if we can get hardware acceleration to work (via
+        # virtio-gpu and Virgil). We currently have to use the Pixman software
+        # renderer since the GLES2 renderer doesn't work inside the VM (even
+        # with WLR_RENDERER_ALLOW_SOFTWARE):
+        # "WLR_RENDERER_ALLOW_SOFTWARE" = "1";
+        "WLR_RENDERER" = "pixman";
       };
       # For convenience:
       shellAliases = {
-        test-x11 = "glinfo | head -n 3 | tee /tmp/test-x11.out && touch /tmp/test-x11-exit-ok";
+        test-x11 = "glinfo | tee /tmp/test-x11.out && touch /tmp/test-x11-exit-ok";
         test-wayland = "wayland-info | tee /tmp/test-wayland.out && touch /tmp/test-wayland-exit-ok";
       };
+
+      # To help with OCR:
+      etc."xdg/foot/foot.ini".text = lib.generators.toINI { } {
+        main = {
+          font = "inconsolata:size=14";
+        };
+        colors = rec {
+          foreground = "000000";
+          background = "ffffff";
+          regular2 = foreground;
+        };
+      };
     };
 
+    fonts.fonts = [ pkgs.inconsolata ];
+
     # Automatically configure and start Sway when logging in on tty1:
     programs.bash.loginShellInit = ''
       if [ "$(tty)" = "/dev/tty1" ]; then
@@ -61,7 +80,7 @@ import ./make-test-python.nix ({ pkgs, lib, ...} :
     machine.wait_for_file("/run/user/1000/wayland-1")
     machine.wait_for_file("/tmp/sway-ipc.sock")
 
-    # Test XWayland:
+    # Test XWayland (foot does not support X):
     machine.succeed(
         "su - alice -c 'swaymsg exec WINIT_UNIX_BACKEND=x11 WAYLAND_DISPLAY=invalid alacritty'"
     )
@@ -69,21 +88,22 @@ import ./make-test-python.nix ({ pkgs, lib, ...} :
     machine.send_chars("test-x11\n")
     machine.wait_for_file("/tmp/test-x11-exit-ok")
     print(machine.succeed("cat /tmp/test-x11.out"))
+    machine.copy_from_vm("/tmp/test-x11.out")
     machine.screenshot("alacritty_glinfo")
     machine.succeed("pkill alacritty")
 
-    # Start a terminal (Alacritty) on workspace 3:
+    # Start a terminal (foot) on workspace 3:
     machine.send_key("alt-3")
-    machine.succeed(
-        "su - alice -c 'swaymsg exec WINIT_UNIX_BACKEND=wayland DISPLAY=invalid alacritty'"
-    )
+    machine.sleep(3)
+    machine.send_key("alt-ret")
     machine.wait_for_text("alice@machine")
     machine.send_chars("test-wayland\n")
     machine.wait_for_file("/tmp/test-wayland-exit-ok")
     print(machine.succeed("cat /tmp/test-wayland.out"))
-    machine.screenshot("alacritty_wayland_info")
+    machine.copy_from_vm("/tmp/test-wayland.out")
+    machine.screenshot("foot_wayland_info")
     machine.send_key("alt-shift-q")
-    machine.wait_until_fails("pgrep alacritty")
+    machine.wait_until_fails("pgrep foot")
 
     # Test gpg-agent starting pinentry-gnome3 via D-Bus (tests if
     # $WAYLAND_DISPLAY is correctly imported into the D-Bus user env):
@@ -104,9 +124,6 @@ import ./make-test-python.nix ({ pkgs, lib, ...} :
     # Exit Sway and verify process exit status 0:
     machine.succeed("su - alice -c 'swaymsg exit || true'")
     machine.wait_until_fails("pgrep -x sway")
-
-    # TODO: Sway currently segfaults after "swaymsg exit" but only in this VM test:
-    # machine # [  104.090032] sway[921]: segfault at 3f800008 ip 00007f7dbdc25f10 sp 00007ffe282182f8 error 4 in libwayland-server.so.0.1.0[7f7dbdc1f000+8000]
-    # machine.wait_for_file("/tmp/sway-exit-ok")
+    machine.wait_for_file("/tmp/sway-exit-ok")
   '';
 })
diff --git a/nixos/tests/switch-test.nix b/nixos/tests/switch-test.nix
index daad9134885..1c32bf6beb9 100644
--- a/nixos/tests/switch-test.nix
+++ b/nixos/tests/switch-test.nix
@@ -45,6 +45,50 @@ import ./make-test-python.nix ({ pkgs, ...} : {
           systemd.services.test.restartIfChanged = false;
         };
 
+        restart-and-reload-by-activation-script.configuration = {
+          systemd.services = rec {
+            simple-service = {
+              # No wantedBy so we can check if the activation script restart triggers them
+              serviceConfig = {
+                Type = "oneshot";
+                RemainAfterExit = true;
+                ExecStart = "${pkgs.coreutils}/bin/true";
+                ExecReload = "${pkgs.coreutils}/bin/true";
+              };
+            };
+
+            simple-restart-service = simple-service // {
+              stopIfChanged = false;
+            };
+
+            simple-reload-service = simple-service // {
+              reloadIfChanged = true;
+            };
+
+            no-restart-service = simple-service // {
+              restartIfChanged = false;
+            };
+          };
+
+          system.activationScripts.restart-and-reload-test = {
+            supportsDryActivation = true;
+            deps = [];
+            text = ''
+              if [ "$NIXOS_ACTION" = dry-activate ]; then
+                f=/run/nixos/dry-activation-restart-list
+              else
+                f=/run/nixos/activation-restart-list
+              fi
+              cat <<EOF >> "$f"
+              simple-service.service
+              simple-restart-service.service
+              simple-reload-service.service
+              no-restart-service.service
+              EOF
+            '';
+          };
+        };
+
         mount.configuration = {
           systemd.mounts = [
             {
@@ -261,6 +305,32 @@ import ./make-test-python.nix ({ pkgs, ...} : {
         assert_lacks(out, "as well:")
         assert_contains(out, "would start the following units: test.service\n")
 
+    with subtest("restart and reload by activation script"):
+        out = switch_to_specialisation("${machine}", "restart-and-reload-by-activation-script")
+        assert_contains(out, "stopping the following units: test.service\n")
+        assert_lacks(out, "NOT restarting the following changed units:")
+        assert_lacks(out, "reloading the following units:")
+        assert_lacks(out, "restarting the following units:")
+        assert_contains(out, "\nstarting the following units: no-restart-service.service, simple-reload-service.service, simple-restart-service.service, simple-service.service\n")
+        assert_lacks(out, "as well:")
+        # Switch to the same system where the example services get restarted
+        # by the activation script
+        out = switch_to_specialisation("${machine}", "restart-and-reload-by-activation-script")
+        assert_lacks(out, "stopping the following units:")
+        assert_lacks(out, "NOT restarting the following changed units:")
+        assert_contains(out, "reloading the following units: simple-reload-service.service\n")
+        assert_contains(out, "restarting the following units: simple-restart-service.service, simple-service.service\n")
+        assert_lacks(out, "\nstarting the following units:")
+        assert_lacks(out, "as well:")
+        # The same, but in dry mode
+        out = switch_to_specialisation("${machine}", "restart-and-reload-by-activation-script", action="dry-activate")
+        assert_lacks(out, "would stop the following units:")
+        assert_lacks(out, "would NOT stop the following changed units:")
+        assert_contains(out, "would reload the following units: simple-reload-service.service\n")
+        assert_contains(out, "would restart the following units: simple-restart-service.service, simple-service.service\n")
+        assert_lacks(out, "\nwould start the following units:")
+        assert_lacks(out, "as well:")
+
     with subtest("mounts"):
         switch_to_specialisation("${machine}", "mount")
         out = machine.succeed("mount | grep 'on /testmount'")
diff --git a/nixos/tests/systemd-boot.nix b/nixos/tests/systemd-boot.nix
index c3899b58d6b..51cfd82e6c4 100644
--- a/nixos/tests/systemd-boot.nix
+++ b/nixos/tests/systemd-boot.nix
@@ -110,4 +110,145 @@ in
       assert "updating systemd-boot from (000.0-1-notnixos) to " in output
     '';
   };
+
+  memtest86 = makeTest {
+    name = "systemd-boot-memtest86";
+    meta.maintainers = with pkgs.lib.maintainers; [ Enzime ];
+
+    machine = { pkgs, lib, ... }: {
+      imports = [ common ];
+      boot.loader.systemd-boot.memtest86.enable = true;
+      nixpkgs.config.allowUnfreePredicate = pkg: builtins.elem (lib.getName pkg) [
+        "memtest86-efi"
+      ];
+    };
+
+    testScript = ''
+      machine.succeed("test -e /boot/loader/entries/memtest86.conf")
+      machine.succeed("test -e /boot/efi/memtest86/BOOTX64.efi")
+    '';
+  };
+
+  netbootxyz = makeTest {
+    name = "systemd-boot-netbootxyz";
+    meta.maintainers = with pkgs.lib.maintainers; [ Enzime ];
+
+    machine = { pkgs, lib, ... }: {
+      imports = [ common ];
+      boot.loader.systemd-boot.netbootxyz.enable = true;
+    };
+
+    testScript = ''
+      machine.succeed("test -e /boot/loader/entries/o_netbootxyz.conf")
+      machine.succeed("test -e /boot/efi/netbootxyz/netboot.xyz.efi")
+    '';
+  };
+
+  entryFilename = makeTest {
+    name = "systemd-boot-entry-filename";
+    meta.maintainers = with pkgs.lib.maintainers; [ Enzime ];
+
+    machine = { pkgs, lib, ... }: {
+      imports = [ common ];
+      boot.loader.systemd-boot.memtest86.enable = true;
+      boot.loader.systemd-boot.memtest86.entryFilename = "apple.conf";
+      nixpkgs.config.allowUnfreePredicate = pkg: builtins.elem (lib.getName pkg) [
+        "memtest86-efi"
+      ];
+    };
+
+    testScript = ''
+      machine.fail("test -e /boot/loader/entries/memtest86.conf")
+      machine.succeed("test -e /boot/loader/entries/apple.conf")
+      machine.succeed("test -e /boot/efi/memtest86/BOOTX64.efi")
+    '';
+  };
+
+  extraEntries = makeTest {
+    name = "systemd-boot-extra-entries";
+    meta.maintainers = with pkgs.lib.maintainers; [ Enzime ];
+
+    machine = { pkgs, lib, ... }: {
+      imports = [ common ];
+      boot.loader.systemd-boot.extraEntries = {
+        "banana.conf" = ''
+          title banana
+        '';
+      };
+    };
+
+    testScript = ''
+      machine.succeed("test -e /boot/loader/entries/banana.conf")
+      machine.succeed("test -e /boot/efi/nixos/.extra-files/loader/entries/banana.conf")
+    '';
+  };
+
+  extraFiles = makeTest {
+    name = "systemd-boot-extra-files";
+    meta.maintainers = with pkgs.lib.maintainers; [ Enzime ];
+
+    machine = { pkgs, lib, ... }: {
+      imports = [ common ];
+      boot.loader.systemd-boot.extraFiles = {
+        "efi/fruits/tomato.efi" = pkgs.netbootxyz-efi;
+      };
+    };
+
+    testScript = ''
+      machine.succeed("test -e /boot/efi/fruits/tomato.efi")
+      machine.succeed("test -e /boot/efi/nixos/.extra-files/efi/fruits/tomato.efi")
+    '';
+  };
+
+  switch-test = makeTest {
+    name = "systemd-boot-switch-test";
+    meta.maintainers = with pkgs.lib.maintainers; [ Enzime ];
+
+    nodes = {
+      inherit common;
+
+      machine = { pkgs, ... }: {
+        imports = [ common ];
+        boot.loader.systemd-boot.extraFiles = {
+          "efi/fruits/tomato.efi" = pkgs.netbootxyz-efi;
+        };
+      };
+
+      with_netbootxyz = { pkgs, ... }: {
+        imports = [ common ];
+        boot.loader.systemd-boot.netbootxyz.enable = true;
+      };
+    };
+
+    testScript = { nodes, ... }: let
+      originalSystem = nodes.machine.config.system.build.toplevel;
+      baseSystem = nodes.common.config.system.build.toplevel;
+      finalSystem = nodes.with_netbootxyz.config.system.build.toplevel;
+    in ''
+      machine.succeed("test -e /boot/efi/fruits/tomato.efi")
+      machine.succeed("test -e /boot/efi/nixos/.extra-files/efi/fruits/tomato.efi")
+
+      with subtest("remove files when no longer needed"):
+          machine.succeed("${baseSystem}/bin/switch-to-configuration boot")
+          machine.fail("test -e /boot/efi/fruits/tomato.efi")
+          machine.fail("test -d /boot/efi/fruits")
+          machine.succeed("test -d /boot/efi/nixos/.extra-files")
+          machine.fail("test -e /boot/efi/nixos/.extra-files/efi/fruits/tomato.efi")
+          machine.fail("test -d /boot/efi/nixos/.extra-files/efi/fruits")
+
+      with subtest("files are added back when needed again"):
+          machine.succeed("${originalSystem}/bin/switch-to-configuration boot")
+          machine.succeed("test -e /boot/efi/fruits/tomato.efi")
+          machine.succeed("test -e /boot/efi/nixos/.extra-files/efi/fruits/tomato.efi")
+
+      with subtest("simultaneously removing and adding files works"):
+          machine.succeed("${finalSystem}/bin/switch-to-configuration boot")
+          machine.fail("test -e /boot/efi/fruits/tomato.efi")
+          machine.fail("test -e /boot/efi/nixos/.extra-files/efi/fruits/tomato.efi")
+          machine.succeed("test -e /boot/loader/entries/o_netbootxyz.conf")
+          machine.succeed("test -e /boot/efi/netbootxyz/netboot.xyz.efi")
+          machine.succeed("test -e /boot/efi/nixos/.extra-files/loader/entries/o_netbootxyz.conf")
+          machine.succeed("test -e /boot/efi/nixos/.extra-files/efi/netbootxyz/netboot.xyz.efi")
+    '';
+  };
 }
diff --git a/nixos/tests/systemd-networkd-vrf.nix b/nixos/tests/systemd-networkd-vrf.nix
index 9f09d801f77..8a1580fc2ad 100644
--- a/nixos/tests/systemd-networkd-vrf.nix
+++ b/nixos/tests/systemd-networkd-vrf.nix
@@ -161,6 +161,7 @@ in {
 
     # NOTE: please keep in mind that the trailing whitespaces in the following strings
     # are intentional as the output is compared against the raw `iproute2`-output.
+    # editorconfig-checker-disable
     client_ipv4_table = """
     192.168.1.2 dev vrf1 proto static metric 100 
     192.168.2.3 dev vrf2 proto static metric 100
@@ -177,6 +178,7 @@ in {
     local 192.168.2.1 dev eth2 proto kernel scope host src 192.168.2.1 
     broadcast 192.168.2.255 dev eth2 proto kernel scope link src 192.168.2.1
     """.strip()
+    # editorconfig-checker-enable
 
     # Check that networkd properly configures the main routing table
     # and the routing tables for the VRF.
diff --git a/nixos/tests/teleport.nix b/nixos/tests/teleport.nix
new file mode 100644
index 00000000000..15b16e44409
--- /dev/null
+++ b/nixos/tests/teleport.nix
@@ -0,0 +1,99 @@
+{ system ? builtins.currentSystem
+, config ? { }
+, pkgs ? import ../.. { inherit system config; }
+}:
+
+with import ../lib/testing-python.nix { inherit system pkgs; };
+
+let
+  minimal = { config, ... }: {
+    services.teleport.enable = true;
+  };
+
+  client = { config, ... }: {
+    services.teleport = {
+      enable = true;
+      settings = {
+        teleport = {
+          nodename = "client";
+          advertise_ip = "192.168.1.20";
+          auth_token = "8d1957b2-2ded-40e6-8297-d48156a898a9";
+          auth_servers = [ "192.168.1.10:3025" ];
+          log.severity = "DEBUG";
+        };
+        ssh_service = {
+          enabled = true;
+          labels = {
+            role = "client";
+          };
+        };
+        proxy_service.enabled = false;
+        auth_service.enabled = false;
+      };
+    };
+    networking.interfaces.eth1.ipv4.addresses = [{
+      address = "192.168.1.20";
+      prefixLength = 24;
+    }];
+  };
+
+  server = { config, ... }: {
+    services.teleport = {
+      enable = true;
+      settings = {
+        teleport = {
+          nodename = "server";
+          advertise_ip = "192.168.1.10";
+        };
+        ssh_service.enabled = true;
+        proxy_service.enabled = true;
+        auth_service = {
+          enabled = true;
+          tokens = [ "node:8d1957b2-2ded-40e6-8297-d48156a898a9" ];
+        };
+      };
+      diag.enable = true;
+      insecure.enable = true;
+    };
+    networking = {
+      firewall.allowedTCPPorts = [ 3025 ];
+      interfaces.eth1.ipv4.addresses = [{
+        address = "192.168.1.10";
+        prefixLength = 24;
+      }];
+    };
+  };
+in
+{
+  minimal = makeTest {
+    # minimal setup should always work
+    name = "teleport-minimal-setup";
+    meta.maintainers = with pkgs.lib.maintainers; [ ymatsiuk ];
+    nodes = { inherit minimal; };
+
+    testScript = ''
+      minimal.wait_for_open_port("3025")
+      minimal.wait_for_open_port("3080")
+      minimal.wait_for_open_port("3022")
+    '';
+  };
+
+  basic = makeTest {
+    # basic server and client test
+    name = "teleport-server-client";
+    meta.maintainers = with pkgs.lib.maintainers; [ ymatsiuk ];
+    nodes = { inherit server client; };
+
+    testScript = ''
+      with subtest("teleport ready"):
+          server.wait_for_open_port("3025")
+          client.wait_for_open_port("3022")
+
+      with subtest("check applied configuration"):
+          server.wait_until_succeeds("tctl get nodes --format=json | ${pkgs.jq}/bin/jq -e '.[] | select(.spec.hostname==\"client\") | .metadata.labels.role==\"client\"'")
+          server.wait_for_open_port("3000")
+          client.succeed("journalctl -u teleport.service --grep='DEBU'")
+          server.succeed("journalctl -u teleport.service --grep='Starting teleport in insecure mode.'")
+    '';
+  };
+}
diff --git a/nixos/tests/thelounge.nix b/nixos/tests/thelounge.nix
new file mode 100644
index 00000000000..e9b85685bf2
--- /dev/null
+++ b/nixos/tests/thelounge.nix
@@ -0,0 +1,29 @@
+import ./make-test-python.nix {
+  nodes = {
+    private = { config, pkgs, ... }: {
+      services.thelounge = {
+        enable = true;
+        plugins = [ pkgs.theLoungePlugins.themes.solarized ];
+      };
+    };
+
+    public = { config, pkgs, ... }: {
+      services.thelounge = {
+        enable = true;
+        public = true;
+      };
+    };
+  };
+
+  testScript = ''
+    start_all()
+
+    for machine in machines:
+      machine.wait_for_unit("thelounge.service")
+      machine.wait_for_open_port(9000)
+
+    private.wait_until_succeeds("journalctl -u thelounge.service | grep thelounge-theme-solarized")
+    private.wait_until_succeeds("journalctl -u thelounge.service | grep 'in private mode'")
+    public.wait_until_succeeds("journalctl -u thelounge.service | grep 'in public mode'")
+  '';
+}
diff --git a/nixos/tests/tinywl.nix b/nixos/tests/tinywl.nix
new file mode 100644
index 00000000000..b286cab7794
--- /dev/null
+++ b/nixos/tests/tinywl.nix
@@ -0,0 +1,56 @@
+import ./make-test-python.nix ({ pkgs, lib, ... }:
+
+  {
+    name = "tinywl";
+    meta = {
+      maintainers = with lib.maintainers; [ primeos ];
+    };
+
+    machine = { config, ... }: {
+      # Automatically login on tty1 as a normal user:
+      imports = [ ./common/user-account.nix ];
+      services.getty.autologinUser = "alice";
+
+      environment = {
+        systemPackages = with pkgs; [ tinywl foot wayland-utils ];
+      };
+
+      # Automatically start TinyWL when logging in on tty1:
+      programs.bash.loginShellInit = ''
+        if [ "$(tty)" = "/dev/tty1" ]; then
+          set -e
+          test ! -e /tmp/tinywl.log # Only start tinywl once
+          readonly TEST_CMD="wayland-info |& tee /tmp/test-wayland.out && touch /tmp/test-wayland-exit-ok; read"
+          readonly FOOT_CMD="foot sh -c '$TEST_CMD'"
+          tinywl -s "$FOOT_CMD" |& tee /tmp/tinywl.log
+          touch /tmp/tinywl-exit-ok
+        fi
+      '';
+
+      # Switch to a different GPU driver (default: -vga std), otherwise TinyWL segfaults:
+      virtualisation.qemu.options = [ "-vga none -device virtio-gpu-pci" ];
+    };
+
+    testScript = { nodes, ... }: ''
+      start_all()
+      machine.wait_for_unit("multi-user.target")
+
+      # Wait for complete startup:
+      machine.wait_until_succeeds("pgrep tinywl")
+      machine.wait_for_file("/run/user/1000/wayland-0")
+      machine.wait_until_succeeds("pgrep foot")
+      machine.wait_for_file("/tmp/test-wayland-exit-ok")
+
+      # Make a screenshot and save the result:
+      machine.screenshot("tinywl_foot")
+      print(machine.succeed("cat /tmp/test-wayland.out"))
+      machine.copy_from_vm("/tmp/test-wayland.out")
+
+      # Terminate cleanly:
+      machine.send_key("alt-esc")
+      machine.wait_until_fails("pgrep foot")
+      machine.wait_until_fails("pgrep tinywl")
+      machine.wait_for_file("/tmp/tinywl-exit-ok")
+      machine.copy_from_vm("/tmp/tinywl.log")
+    '';
+  })
diff --git a/nixos/tests/tsm-client-gui.nix b/nixos/tests/tsm-client-gui.nix
new file mode 100644
index 00000000000..e4bcd344a89
--- /dev/null
+++ b/nixos/tests/tsm-client-gui.nix
@@ -0,0 +1,57 @@
+# The tsm-client GUI first tries to connect to a server.
+# We can't simulate a server, so we just check if
+# it reports the correct connection failure error.
+# After that the test persuades the GUI
+# to show its main application window
+# and verifies some configuration information.
+
+import ./make-test-python.nix ({ lib, pkgs, ... }: {
+  name = "tsm-client";
+
+  enableOCR = true;
+
+  machine = { pkgs, ... }: {
+    imports = [ ./common/x11.nix ];
+    programs.tsmClient = {
+      enable = true;
+      package = pkgs.tsm-client-withGui;
+      defaultServername = "testserver";
+      servers.testserver = {
+        # 192.0.0.8 is a "dummy address" according to RFC 7600
+        server = "192.0.0.8";
+        node = "SOME-NODE";
+        passwdDir = "/tmp";
+      };
+    };
+  };
+
+  testScript = ''
+    machine.succeed("which dsmj")  # fail early if this is missing
+    machine.wait_for_x()
+    machine.execute("DSM_LOG=/tmp dsmj -optfile=/dev/null >&2 &")
+
+    # does it report the "TCP/IP connection failure" error code?
+    machine.wait_for_window("IBM Spectrum Protect")
+    machine.wait_for_text("ANS2610S")
+    machine.send_key("esc")
+
+    # it asks to continue to restore a local backupset now;
+    # "yes" (return) leads to the main application window
+    machine.wait_for_text("backupset")
+    machine.send_key("ret")
+
+    # main window: navigate to "Connection Information"
+    machine.wait_for_text("Welcome")
+    machine.send_key("alt-f")  # "File" menu
+    machine.send_key("c")  # "Connection Information"
+
+    # "Connection Information" dialog box
+    machine.wait_for_window("Connection Information")
+    machine.wait_for_text("SOME-NODE")
+    machine.wait_for_text("${pkgs.tsm-client.passthru.unwrapped.version}")
+
+    machine.shutdown()
+  '';
+
+  meta.maintainers = [ lib.maintainers.yarny ];
+})
diff --git a/nixos/tests/vscodium.nix b/nixos/tests/vscodium.nix
index 43a0d61c856..66baea73ec6 100644
--- a/nixos/tests/vscodium.nix
+++ b/nixos/tests/vscodium.nix
@@ -34,36 +34,46 @@ let
       };
       enableOCR = true;
       testScript = ''
+        @polling_condition
+        def codium_running():
+            machine.succeed('pgrep -x codium')
+
+
         start_all()
 
         machine.wait_for_unit('graphical.target')
         machine.wait_until_succeeds('pgrep -x codium')
 
-        # Wait until vscodium is visible. "File" is in the menu bar.
-        machine.wait_for_text('File')
-        machine.screenshot('start_screen')
+        with codium_running:
+            # Wait until vscodium is visible. "File" is in the menu bar.
+            machine.wait_for_text('Get Started')
+            machine.screenshot('start_screen')
+
+            test_string = 'testfile'
 
-        test_string = 'testfile'
+            # Create a new file
+            machine.send_key('ctrl-n')
+            machine.wait_for_text('Untitled')
+            machine.screenshot('empty_editor')
 
-        # Create a new file
-        machine.send_key('ctrl-n')
-        machine.wait_for_text('Untitled')
-        machine.screenshot('empty_editor')
+            # Type a string
+            machine.send_chars(test_string)
+            machine.wait_for_text(test_string)
+            machine.screenshot('editor')
 
-        # Type a string
-        machine.send_chars(test_string)
-        machine.wait_for_text(test_string)
-        machine.screenshot('editor')
+            # Save the file
+            machine.send_key('ctrl-s')
+            machine.wait_for_text('Save')
+            machine.screenshot('save_window')
+            machine.send_key('ret')
 
-        # Save the file
-        machine.send_key('ctrl-s')
-        machine.wait_for_text('Save')
-        machine.screenshot('save_window')
-        machine.send_key('ret')
+            # (the default filename is the first line of the file)
+            machine.wait_for_file(f'/home/alice/{test_string}')
 
-        # (the default filename is the first line of the file)
-        machine.wait_for_file(f'/home/alice/{test_string}')
+        machine.send_key('ctrl-q')
+        machine.wait_until_fails('pgrep -x codium')
       '';
     });
 
-in builtins.mapAttrs (k: v: mkTest k v { }) tests
+in
+builtins.mapAttrs (k: v: mkTest k v { }) tests
diff --git a/nixos/tests/wordpress.nix b/nixos/tests/wordpress.nix
index f7f39668c86..416a20aa7fe 100644
--- a/nixos/tests/wordpress.nix
+++ b/nixos/tests/wordpress.nix
@@ -15,15 +15,12 @@ import ./make-test-python.nix ({ pkgs, ... }:
       services.httpd.adminAddr = "webmaster@site.local";
       services.httpd.logPerVirtualHost = true;
 
-      services.wordpress = {
-        # Test support for old interface
+      services.wordpress.sites = {
         "site1.local" = {
           database.tablePrefix = "site1_";
         };
-        sites = {
-          "site2.local" = {
-            database.tablePrefix = "site2_";
-          };
+        "site2.local" = {
+          database.tablePrefix = "site2_";
         };
       };
 
diff --git a/nixos/tests/xmonad.nix b/nixos/tests/xmonad.nix
index 078cd211810..a2fb38e53bd 100644
--- a/nixos/tests/xmonad.nix
+++ b/nixos/tests/xmonad.nix
@@ -1,7 +1,58 @@
-import ./make-test-python.nix ({ pkgs, ...} : {
+import ./make-test-python.nix ({ pkgs, ...}:
+
+let
+  mkConfig = name: keys: ''
+    import XMonad
+    import XMonad.Operations (restart)
+    import XMonad.Util.EZConfig
+    import XMonad.Util.SessionStart
+    import Control.Monad (when)
+    import Text.Printf (printf)
+    import System.Posix.Process (executeFile)
+    import System.Info (arch,os)
+    import System.Environment (getArgs)
+    import System.FilePath ((</>))
+
+    main = launch $ def { startupHook = startup } `additionalKeysP` myKeys
+
+    startup = isSessionStart >>= \sessInit ->
+      spawn "touch /tmp/${name}"
+        >> if sessInit then setSessionStarted else spawn "xterm"
+
+    myKeys = [${builtins.concatStringsSep ", " keys}]
+
+    compiledConfig = printf "xmonad-%s-%s" arch os
+
+    compileRestart resume =
+      whenX (recompile True) $
+        when resume writeStateToFile
+          *> catchIO
+            ( do
+                dir <- getXMonadDataDir
+                args <- getArgs
+                executeFile (dir </> compiledConfig) False args Nothing
+            )
+  '';
+
+  oldKeys =
+    [ ''("M-C-x", spawn "xterm")''
+      ''("M-q", restart "xmonad" True)''
+      ''("M-C-q", compileRestart True)''
+      ''("M-C-t", spawn "touch /tmp/somefile")'' # create somefile
+    ];
+
+  newKeys =
+    [ ''("M-C-x", spawn "xterm")''
+      ''("M-q", restart "xmonad" True)''
+      ''("M-C-q", compileRestart True)''
+      ''("M-C-r", spawn "rm /tmp/somefile")'' # delete somefile
+    ];
+
+  newConfig = pkgs.writeText "xmonad.hs" (mkConfig "newXMonad" newKeys);
+in {
   name = "xmonad";
   meta = with pkgs.lib.maintainers; {
-    maintainers = [ nequissimus ];
+    maintainers = [ nequissimus ivanbrennan ];
   };
 
   machine = { pkgs, ... }: {
@@ -10,21 +61,10 @@ import ./make-test-python.nix ({ pkgs, ...} : {
     services.xserver.displayManager.defaultSession = "none+xmonad";
     services.xserver.windowManager.xmonad = {
       enable = true;
+      enableConfiguredRecompile = true;
       enableContribAndExtras = true;
       extraPackages = with pkgs.haskellPackages; haskellPackages: [ xmobar ];
-      config = ''
-        import XMonad
-        import XMonad.Operations (restart)
-        import XMonad.Util.EZConfig
-        import XMonad.Util.SessionStart
-
-        main = launch $ def { startupHook = startup } `additionalKeysP` myKeys
-
-        startup = isSessionStart >>= \sessInit ->
-          if sessInit then setSessionStarted else spawn "xterm"
-
-        myKeys = [ ("M-C-x", spawn "xterm"), ("M-q", restart "xmonad" True) ]
-      '';
+      config = mkConfig "oldXMonad" oldKeys;
     };
   };
 
@@ -38,10 +78,37 @@ import ./make-test-python.nix ({ pkgs, ...} : {
     machine.wait_for_window("${user.name}.*machine")
     machine.sleep(1)
     machine.screenshot("terminal1")
+    machine.succeed("rm /tmp/oldXMonad")
     machine.send_key("alt-q")
-    machine.sleep(3)
+    machine.wait_for_file("/tmp/oldXMonad")
     machine.wait_for_window("${user.name}.*machine")
     machine.sleep(1)
     machine.screenshot("terminal2")
+
+    # /tmp/somefile should not exist yet
+    machine.fail("stat /tmp/somefile")
+
+    # original config has a keybinding that creates somefile
+    machine.send_key("alt-ctrl-t")
+    machine.wait_for_file("/tmp/somefile")
+
+    # set up the new config
+    machine.succeed("mkdir -p ${user.home}/.xmonad")
+    machine.copy_from_host("${newConfig}", "${user.home}/.xmonad/xmonad.hs")
+
+    # recompile xmonad using the new config
+    machine.send_key("alt-ctrl-q")
+    machine.wait_for_file("/tmp/newXMonad")
+
+    # new config has a keybinding that deletes somefile
+    machine.send_key("alt-ctrl-r")
+    machine.wait_until_fails("stat /tmp/somefile", timeout=30)
+
+    # restart with the old config, and confirm the old keybinding is back
+    machine.succeed("rm /tmp/oldXMonad")
+    machine.send_key("alt-q")
+    machine.wait_for_file("/tmp/oldXMonad")
+    machine.send_key("alt-ctrl-t")
+    machine.wait_for_file("/tmp/somefile")
   '';
 })
diff --git a/nixos/tests/xmpp/prosody-mysql.nix b/nixos/tests/xmpp/prosody-mysql.nix
deleted file mode 100644
index 9a00bcabf38..00000000000
--- a/nixos/tests/xmpp/prosody-mysql.nix
+++ /dev/null
@@ -1,92 +0,0 @@
-import ../make-test-python.nix {
-  name = "prosody-mysql";
-
-  nodes = {
-    client = { nodes, pkgs, ... }: {
-      environment.systemPackages = [
-        (pkgs.callPackage ./xmpp-sendmessage.nix { connectTo = nodes.server.config.networking.primaryIPAddress; })
-      ];
-      networking.extraHosts = ''
-        ${nodes.server.config.networking.primaryIPAddress} example.com
-        ${nodes.server.config.networking.primaryIPAddress} conference.example.com
-        ${nodes.server.config.networking.primaryIPAddress} uploads.example.com
-      '';
-    };
-    server = { config, pkgs, ... }: {
-      nixpkgs.overlays = [
-        (self: super: {
-          prosody = super.prosody.override {
-            withDBI = true;
-            withExtraLibs = [ pkgs.luaPackages.luadbi-mysql ];
-          };
-        })
-      ];
-      networking.extraHosts = ''
-        ${config.networking.primaryIPAddress} example.com
-        ${config.networking.primaryIPAddress} conference.example.com
-        ${config.networking.primaryIPAddress} uploads.example.com
-      '';
-      networking.firewall.enable = false;
-      services.prosody = {
-        enable = true;
-        # TODO: use a self-signed certificate
-        c2sRequireEncryption = false;
-        extraConfig = ''
-          storage = "sql"
-          sql = {
-            driver = "MySQL";
-            database = "prosody";
-            host = "mysql";
-            port = 3306;
-            username = "prosody";
-            password = "password123";
-          };
-        '';
-        virtualHosts.test = {
-          domain = "example.com";
-          enabled = true;
-        };
-        muc = [
-          {
-            domain = "conference.example.com";
-          }
-        ];
-        uploadHttp = {
-          domain = "uploads.example.com";
-        };
-      };
-    };
-    mysql = { config, pkgs, ... }: {
-      networking.firewall.enable = false;
-      services.mysql = {
-        enable = true;
-        initialScript = pkgs.writeText "mysql_init.sql" ''
-          CREATE DATABASE prosody;
-          CREATE USER 'prosody'@'server' IDENTIFIED BY 'password123';
-          GRANT ALL PRIVILEGES ON prosody.* TO 'prosody'@'server';
-          FLUSH PRIVILEGES;
-        '';
-        package = pkgs.mariadb;
-      };
-    };
-  };
-
-  testScript = { nodes, ... }: ''
-    mysql.wait_for_unit("mysql.service")
-    server.wait_for_unit("prosody.service")
-    server.succeed('prosodyctl status | grep "Prosody is running"')
-
-    # set password to 'nothunter2' (it's asked twice)
-    server.succeed("yes nothunter2 | prosodyctl adduser cthon98@example.com")
-    # set password to 'y'
-    server.succeed("yes | prosodyctl adduser azurediamond@example.com")
-    # correct password to 'hunter2'
-    server.succeed("yes hunter2 | prosodyctl passwd azurediamond@example.com")
-
-    client.succeed("send-message")
-
-    server.succeed("prosodyctl deluser cthon98@example.com")
-    server.succeed("prosodyctl deluser azurediamond@example.com")
-  '';
-}
-