about summary refs log tree commit diff
path: root/nixos/tests
diff options
context:
space:
mode:
authorDanylo Hlynskyi <abcz2.uprola@gmail.com>2019-08-05 14:09:28 +0300
committerGitHub <noreply@github.com>2019-08-05 14:09:28 +0300
commit7585496effbf7fe7815265c2211e8745a90d3136 (patch)
tree38eda227ff9b4ef2c467388a6a399d4daf929b06 /nixos/tests
parentd0413360d3a6c51dc56d4ce0ab07ad4678a83ada (diff)
parent4e795680bef5fb8740442451496f890c301d8592 (diff)
downloadnixlib-7585496effbf7fe7815265c2211e8745a90d3136.tar
nixlib-7585496effbf7fe7815265c2211e8745a90d3136.tar.gz
nixlib-7585496effbf7fe7815265c2211e8745a90d3136.tar.bz2
nixlib-7585496effbf7fe7815265c2211e8745a90d3136.tar.lz
nixlib-7585496effbf7fe7815265c2211e8745a90d3136.tar.xz
nixlib-7585496effbf7fe7815265c2211e8745a90d3136.tar.zst
nixlib-7585496effbf7fe7815265c2211e8745a90d3136.zip
Merge branch 'master' into flip-map-foreach
Diffstat (limited to 'nixos/tests')
-rw-r--r--nixos/tests/all-tests.nix5
-rw-r--r--nixos/tests/boot.nix107
-rw-r--r--nixos/tests/cassandra.nix66
-rw-r--r--nixos/tests/flatpak-builder.nix1
-rw-r--r--nixos/tests/fluentd.nix46
-rw-r--r--nixos/tests/gdk-pixbuf.nix4
-rw-r--r--nixos/tests/grafana.nix90
-rw-r--r--nixos/tests/graylog.nix111
-rw-r--r--nixos/tests/ipv6.nix11
-rw-r--r--nixos/tests/mediawiki.nix19
-rw-r--r--nixos/tests/networking.nix22
-rw-r--r--nixos/tests/nextcloud/with-postgresql-and-redis.nix15
-rw-r--r--nixos/tests/ostree.nix2
-rw-r--r--nixos/tests/prometheus-2.nix208
-rw-r--r--nixos/tests/prometheus-exporters.nix78
-rw-r--r--nixos/tests/redmine.nix24
-rw-r--r--nixos/tests/tiddlywiki.nix67
-rw-r--r--nixos/tests/tomcat.nix30
18 files changed, 722 insertions, 184 deletions
diff --git a/nixos/tests/all-tests.nix b/nixos/tests/all-tests.nix
index 665a75e47dad..c3fa53ac544f 100644
--- a/nixos/tests/all-tests.nix
+++ b/nixos/tests/all-tests.nix
@@ -85,6 +85,7 @@ in
   flannel = handleTestOn ["x86_64-linux"] ./flannel.nix {};
   flatpak = handleTest ./flatpak.nix {};
   flatpak-builder = handleTest ./flatpak-builder.nix {};
+  fluentd = handleTest ./fluentd.nix {};
   fsck = handleTest ./fsck.nix {};
   fwupd = handleTestOn ["x86_64-linux"] ./fwupd.nix {}; # libsmbios is unsupported on aarch64
   gdk-pixbuf = handleTest ./gdk-pixbuf.nix {};
@@ -100,6 +101,7 @@ in
   graphene = handleTest ./graphene.nix {};
   grafana = handleTest ./grafana.nix {};
   graphite = handleTest ./graphite.nix {};
+  graylog = handleTest ./graylog.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 {};
@@ -145,6 +147,7 @@ in
   mailcatcher = handleTest ./mailcatcher.nix {};
   mathics = handleTest ./mathics.nix {};
   matrix-synapse = handleTest ./matrix-synapse.nix {};
+  mediawiki = handleTest ./mediawiki.nix {};
   memcached = handleTest ./memcached.nix {};
   mesos = handleTest ./mesos.nix {};
   miniflux = handleTest ./miniflux.nix {};
@@ -247,8 +250,8 @@ in
   pdns-recursor = handleTest ./pdns-recursor.nix {};
   taskserver = handleTest ./taskserver.nix {};
   telegraf = handleTest ./telegraf.nix {};
+  tiddlywiki = handleTest ./tiddlywiki.nix {};
   tinydns = handleTest ./tinydns.nix {};
-  tomcat = handleTest ./tomcat.nix {};
   tor = handleTest ./tor.nix {};
   transmission = handleTest ./transmission.nix {};
   udisks2 = handleTest ./udisks2.nix {};
diff --git a/nixos/tests/boot.nix b/nixos/tests/boot.nix
index c9bb1e77c6d0..57d8006d7ac3 100644
--- a/nixos/tests/boot.nix
+++ b/nixos/tests/boot.nix
@@ -17,46 +17,33 @@ let
         ];
     }).config.system.build.isoImage;
 
-  makeBootTest = name: machineConfig:
-    makeTest {
-      inherit iso;
-      name = "boot-" + name;
-      nodes = { };
-      testScript =
-        ''
-          my $machine = createMachine({ ${machineConfig}, qemuFlags => '-m 768' });
-          $machine->start;
-          $machine->waitForUnit("multi-user.target");
-          $machine->succeed("nix verify -r --no-trust /run/current-system");
+  perlAttrs = params: "{ ${concatStringsSep ", " (mapAttrsToList (name: param: "${name} => ${builtins.toJSON param}") params)} }";
 
-          # Test whether the channel got installed correctly.
-          $machine->succeed("nix-instantiate --dry-run '<nixpkgs>' -A hello");
-          $machine->succeed("nix-env --dry-run -iA nixos.procps");
-
-          $machine->shutdown;
-        '';
-    };
-in {
-
-    biosCdrom = makeBootTest "bios-cdrom" ''
-        cdrom => glob("${iso}/iso/*.iso")
-      '';
-
-    biosUsb = makeBootTest "bios-usb" ''
-        usb => glob("${iso}/iso/*.iso")
-      '';
+  makeBootTest = name: extraConfig:
+    let
+      machineConfig = perlAttrs ({ qemuFlags = "-m 768"; } // extraConfig);
+    in
+      makeTest {
+        inherit iso;
+        name = "boot-" + name;
+        nodes = { };
+        testScript =
+          ''
+            my $machine = createMachine(${machineConfig});
+            $machine->start;
+            $machine->waitForUnit("multi-user.target");
+            $machine->succeed("nix verify -r --no-trust /run/current-system");
 
-    uefiCdrom = makeBootTest "uefi-cdrom" ''
-        cdrom => glob("${iso}/iso/*.iso"),
-        bios => '${pkgs.OVMF.fd}/FV/OVMF.fd'
-      '';
+            # Test whether the channel got installed correctly.
+            $machine->succeed("nix-instantiate --dry-run '<nixpkgs>' -A hello");
+            $machine->succeed("nix-env --dry-run -iA nixos.procps");
 
-    uefiUsb = makeBootTest "uefi-usb" ''
-        usb => glob("${iso}/iso/*.iso"),
-        bios => '${pkgs.OVMF.fd}/FV/OVMF.fd'
-      '';
+            $machine->shutdown;
+          '';
+      };
 
-    netboot = let
+  makeNetbootTest = name: extraConfig:
+    let
       config = (import ../lib/eval-config.nix {
           inherit system;
           modules =
@@ -65,35 +52,55 @@ in {
               { key = "serial"; }
             ];
         }).config;
-      ipxeScriptDir = pkgs.writeTextFile {
-        name = "ipxeScriptDir";
-        text = ''
-          #!ipxe
-          dhcp
-          kernel bzImage init=${config.system.build.toplevel}/init ${toString config.boot.kernelParams} console=ttyS0
-          initrd initrd
-          boot
-        '';
-        destination = "/boot.ipxe";
-      };
       ipxeBootDir = pkgs.symlinkJoin {
         name = "ipxeBootDir";
         paths = [
           config.system.build.netbootRamdisk
           config.system.build.kernel
-          ipxeScriptDir
+          config.system.build.netbootIpxeScript
         ];
       };
+      machineConfig = perlAttrs ({
+        qemuFlags = "-boot order=n -m 2000";
+        netBackendArgs = "tftp=${ipxeBootDir},bootfile=netboot.ipxe";
+      } // extraConfig);
     in
       makeTest {
-        name = "boot-netboot";
+        name = "boot-netboot-" + name;
         nodes = { };
         testScript =
           ''
-            my $machine = createMachine({ qemuFlags => '-boot order=n -net nic,model=e1000 -net user,tftp=${ipxeBootDir}/,bootfile=boot.ipxe -m 2000M' });
+            my $machine = createMachine(${machineConfig});
             $machine->start;
             $machine->waitForUnit("multi-user.target");
             $machine->shutdown;
           '';
       };
+in {
+
+    biosCdrom = makeBootTest "bios-cdrom" {
+      cdrom = "${iso}/iso/${iso.isoName}";
+    };
+
+    biosUsb = makeBootTest "bios-usb" {
+      usb = "${iso}/iso/${iso.isoName}";
+    };
+
+    uefiCdrom = makeBootTest "uefi-cdrom" {
+      cdrom = "${iso}/iso/${iso.isoName}";
+      bios = "${pkgs.OVMF.fd}/FV/OVMF.fd";
+    };
+
+    uefiUsb = makeBootTest "uefi-usb" {
+      usb = "${iso}/iso/${iso.isoName}";
+      bios = "${pkgs.OVMF.fd}/FV/OVMF.fd";
+    };
+
+    biosNetboot = makeNetbootTest "bios" {};
+
+    uefiNetboot = makeNetbootTest "uefi" {
+      bios = "${pkgs.OVMF.fd}/FV/OVMF.fd";
+      # Custom ROM is needed for EFI PXE boot. I failed to understand exactly why, because QEMU should still use iPXE for EFI.
+      netFrontendArgs = "romfile=${pkgs.ipxe}/ipxe.efirom";
+    };
 }
diff --git a/nixos/tests/cassandra.nix b/nixos/tests/cassandra.nix
index aea4fa4d1c95..c55733c9be7b 100644
--- a/nixos/tests/cassandra.nix
+++ b/nixos/tests/cassandra.nix
@@ -8,11 +8,12 @@ let
   jmxRoles = [{ username = "me"; password = "password"; }];
   jmxRolesFile = ./cassandra-jmx-roles;
   jmxAuthArgs = "-u ${(builtins.elemAt jmxRoles 0).username} -pw ${(builtins.elemAt jmxRoles 0).password}";
+  jmxPort = 7200;  # Non-standard port so it doesn't accidentally work
 
   # Would usually be assigned to 512M
   numMaxHeapSize = "400";
   getHeapLimitCommand = ''
-    nodetool info | grep "^Heap Memory" | awk \'{print $NF}\'
+    nodetool info -p ${toString jmxPort} | grep "^Heap Memory" | awk \'{print $NF}\'
   '';
   checkHeapLimitCommand = ''
     [ 1 -eq "$(echo "$(${getHeapLimitCommand}) < ${numMaxHeapSize}" | ${pkgs.bc}/bin/bc)" ]
@@ -27,19 +28,20 @@ let
       package = testPackage;
       maxHeapSize = "${numMaxHeapSize}M";
       heapNewSize = "100M";
+      inherit jmxPort;
     };
-  nodeCfg = ipAddress: extra: {pkgs, config, ...}:
-    { environment.systemPackages = [ testPackage ];
-      networking = {
-        firewall.allowedTCPPorts = [ 7000 7199 9042 ];
-        useDHCP = false;
-        interfaces.eth1.ipv4.addresses = pkgs.lib.mkOverride 0 [
-          { address = ipAddress; prefixLength = 24; }
-        ];
-      };
-      services.cassandra = cassandraCfg ipAddress // extra;
-      virtualisation.memorySize = 1024;
+  nodeCfg = ipAddress: extra: {pkgs, config, ...}: rec {
+    environment.systemPackages = [ testPackage ];
+    networking = {
+      firewall.allowedTCPPorts = [ 7000 9042 services.cassandra.jmxPort ];
+      useDHCP = false;
+      interfaces.eth1.ipv4.addresses = pkgs.lib.mkOverride 0 [
+        { address = ipAddress; prefixLength = 24; }
+      ];
     };
+    services.cassandra = cassandraCfg ipAddress // extra;
+    virtualisation.memorySize = 1024;
+  };
 in
 {
   name = "cassandra-ci";
@@ -50,7 +52,9 @@ in
     cass2 = nodeCfg "192.168.1.3" { jvmOpts = [ "-Dcassandra.replace_address=cass1" ]; };
   };
 
-  testScript = ''
+  testScript = let
+    jmxPortS = toString jmxPort;
+  in ''
     # Check configuration
     subtest "Timers exist", sub {
       $cass0->succeed("systemctl list-timers | grep cassandra-full-repair.timer");
@@ -63,51 +67,51 @@ in
     };
     subtest "Nodetool is operational", sub {
       $cass0->waitForUnit("cassandra.service");
-      $cass0->waitUntilSucceeds("nc -z localhost 7199");
-      $cass0->succeed("nodetool status --resolve-ip | egrep '^UN[[:space:]]+cass0'");
+      $cass0->waitUntilSucceeds("nc -z localhost ${jmxPortS}");
+      $cass0->succeed("nodetool status -p ${jmxPortS} --resolve-ip | egrep '^UN[[:space:]]+cass0'");
     };
     subtest "Cluster name was set", sub {
       $cass0->waitForUnit("cassandra.service");
-      $cass0->waitUntilSucceeds("nc -z localhost 7199");
-      $cass0->waitUntilSucceeds("nodetool describecluster | grep 'Name: ${clusterName}'");
+      $cass0->waitUntilSucceeds("nc -z localhost ${jmxPortS}");
+      $cass0->waitUntilSucceeds("nodetool describecluster -p ${jmxPortS} | grep 'Name: ${clusterName}'");
     };
     subtest "Heap limit set correctly", sub {
       # Nodetool takes a while until it can display info
-      $cass0->waitUntilSucceeds('nodetool info');
+      $cass0->waitUntilSucceeds('nodetool info -p ${jmxPortS}');
       $cass0->succeed('${checkHeapLimitCommand}');
     };
 
     # Check cluster interaction
     subtest "Bring up cluster", sub {
       $cass1->waitForUnit("cassandra.service");
-      $cass1->waitUntilSucceeds("nodetool ${jmxAuthArgs} status | egrep -c '^UN' | grep 2");
-      $cass0->succeed("nodetool status --resolve-ip | egrep '^UN[[:space:]]+cass1'");
+      $cass1->waitUntilSucceeds("nodetool -p ${jmxPortS} ${jmxAuthArgs} status | egrep -c '^UN' | grep 2");
+      $cass0->succeed("nodetool status -p ${jmxPortS} --resolve-ip | egrep '^UN[[:space:]]+cass1'");
     };
   '' + lib.optionalString testRemoteAuth ''
     subtest "Remote authenticated jmx", sub {
       # Doesn't work if not enabled
-      $cass0->waitUntilSucceeds("nc -z localhost 7199");
-      $cass1->fail("nc -z 192.168.1.1 7199");
-      $cass1->fail("nodetool -h 192.168.1.1 status");
+      $cass0->waitUntilSucceeds("nc -z localhost ${jmxPortS}");
+      $cass1->fail("nc -z 192.168.1.1 ${toString jmxPort}");
+      $cass1->fail("nodetool -p ${jmxPortS} -h 192.168.1.1 status");
 
       # Works if enabled
-      $cass1->waitUntilSucceeds("nc -z localhost 7199");
-      $cass0->succeed("nodetool -h 192.168.1.2 ${jmxAuthArgs} status");
+      $cass1->waitUntilSucceeds("nc -z localhost ${toString jmxPort}");
+      $cass0->succeed("nodetool -p ${jmxPortS} -h 192.168.1.2 ${jmxAuthArgs} status");
     };
   '' + ''
     subtest "Break and fix node", sub {
       $cass1->block;
-      $cass0->waitUntilSucceeds("nodetool status --resolve-ip | egrep -c '^DN[[:space:]]+cass1'");
-      $cass0->succeed("nodetool status | egrep -c '^UN'  | grep 1");
+      $cass0->waitUntilSucceeds("nodetool status -p ${jmxPortS} --resolve-ip | egrep -c '^DN[[:space:]]+cass1'");
+      $cass0->succeed("nodetool status -p ${jmxPortS} | egrep -c '^UN'  | grep 1");
       $cass1->unblock;
-      $cass1->waitUntilSucceeds("nodetool ${jmxAuthArgs} status | egrep -c '^UN'  | grep 2");
-      $cass0->succeed("nodetool status | egrep -c '^UN'  | grep 2");
+      $cass1->waitUntilSucceeds("nodetool -p ${jmxPortS} ${jmxAuthArgs} status | egrep -c '^UN'  | grep 2");
+      $cass0->succeed("nodetool status -p ${jmxPortS} | egrep -c '^UN'  | grep 2");
     };
     subtest "Replace crashed node", sub {
       $cass1->crash;
       $cass2->waitForUnit("cassandra.service");
-      $cass0->waitUntilFails("nodetool status --resolve-ip | egrep '^UN[[:space:]]+cass1'");
-      $cass0->waitUntilSucceeds("nodetool status --resolve-ip | egrep '^UN[[:space:]]+cass2'");
+      $cass0->waitUntilFails("nodetool status -p ${jmxPortS} --resolve-ip | egrep '^UN[[:space:]]+cass1'");
+      $cass0->waitUntilSucceeds("nodetool status -p ${jmxPortS} --resolve-ip | egrep '^UN[[:space:]]+cass2'");
     };
   '';
 })
diff --git a/nixos/tests/flatpak-builder.nix b/nixos/tests/flatpak-builder.nix
index 2100631ec7f4..49b97e8ca99e 100644
--- a/nixos/tests/flatpak-builder.nix
+++ b/nixos/tests/flatpak-builder.nix
@@ -9,6 +9,7 @@ import ./make-test.nix ({ pkgs, ... }:
 
   machine = { pkgs, ... }: {
     services.flatpak.enable = true;
+    xdg.portal.enable = true;
     environment.systemPackages = with pkgs; [ gnome-desktop-testing flatpak-builder ] ++ flatpak-builder.installedTestsDependencies;
     virtualisation.diskSize = 2048;
   };
diff --git a/nixos/tests/fluentd.nix b/nixos/tests/fluentd.nix
new file mode 100644
index 000000000000..e5c4c3d21631
--- /dev/null
+++ b/nixos/tests/fluentd.nix
@@ -0,0 +1,46 @@
+import ./make-test.nix ({ pkgs, lib, ... }: {
+  name = "fluentd";
+
+  machine = { pkgs, ... }: {
+    services.fluentd = {
+      enable = true;
+      config = ''
+        <source>
+          @type http
+          port 9880
+        </source>
+
+        <match **>
+          type copy
+          <store>
+            @type file
+            format json
+            path /tmp/fluentd
+            symlink_path /tmp/current-log
+          </store>
+          <store>
+            @type stdout
+          </store>
+        </match>
+      '';
+    };
+  };
+
+  testScript = let
+    testMessage = "an example log message";
+
+    payload = pkgs.writeText "test-message.json" (builtins.toJSON {
+      inherit testMessage;
+    });
+  in ''
+    $machine->start;
+    $machine->waitForUnit('fluentd.service');
+    $machine->waitForOpenPort(9880);
+
+    $machine->succeed("curl -fsSL -X POST -H 'Content-type: application/json' -d @${payload} http://localhost:9880/test.tag");
+
+    $machine->succeed("systemctl stop fluentd"); # blocking flush
+
+    $machine->succeed("grep '${testMessage}' /tmp/current-log");
+  '';
+})
diff --git a/nixos/tests/gdk-pixbuf.nix b/nixos/tests/gdk-pixbuf.nix
index 005c5111da2b..9a62b593f46d 100644
--- a/nixos/tests/gdk-pixbuf.nix
+++ b/nixos/tests/gdk-pixbuf.nix
@@ -3,12 +3,12 @@ import ./make-test.nix ({ pkgs, ... }: {
   name = "gdk-pixbuf";
 
   meta = {
-    maintainers = pkgs.gdk_pixbuf.meta.maintainers;
+    maintainers = pkgs.gdk-pixbuf.meta.maintainers;
   };
 
   machine = { pkgs, ... }: {
     environment.systemPackages = with pkgs; [ gnome-desktop-testing ];
-    environment.variables.XDG_DATA_DIRS = [ "${pkgs.gdk_pixbuf.installedTests}/share" ];
+    environment.variables.XDG_DATA_DIRS = [ "${pkgs.gdk-pixbuf.installedTests}/share" ];
 
     # Tests allocate a lot of memory trying to exploit a CVE
     # but qemu-system-i386 has a 2047M memory limit
diff --git a/nixos/tests/grafana.nix b/nixos/tests/grafana.nix
index 9dc765a879bc..7a1b4c8ffbbc 100644
--- a/nixos/tests/grafana.nix
+++ b/nixos/tests/grafana.nix
@@ -1,25 +1,91 @@
-import ./make-test.nix ({ lib, ... }:
-{
-  name = "grafana";
+import ./make-test.nix ({ lib, pkgs, ... }:
 
-  meta = with lib.maintainers; {
-    maintainers = [ willibutz ];
-  };
+let
+  inherit (lib) mkMerge nameValuePair maintainers;
 
-  machine = { ... }: {
+  baseGrafanaConf = {
     services.grafana = {
       enable = true;
       addr = "localhost";
       analytics.reporting.enable = false;
       domain = "localhost";
-      security.adminUser = "testusername";
+      security = {
+        adminUser = "testadmin";
+        adminPassword = "snakeoilpwd";
+      };
     };
   };
 
+  extraNodeConfs = {
+    postgresql = {
+      services.grafana.database = {
+        host = "127.0.0.1:5432";
+        user = "grafana";
+      };
+      services.postgresql = {
+        enable = true;
+        ensureDatabases = [ "grafana" ];
+        ensureUsers = [{
+          name = "grafana";
+          ensurePermissions."DATABASE grafana" = "ALL PRIVILEGES";
+        }];
+      };
+      systemd.services.grafana.after = [ "postgresql.service" ];
+    };
+
+    mysql = {
+      services.grafana.database.user = "grafana";
+      services.mysql = {
+        enable = true;
+        ensureDatabases = [ "grafana" ];
+        ensureUsers = [{
+          name = "grafana";
+          ensurePermissions."grafana.*" = "ALL PRIVILEGES";
+        }];
+        package = pkgs.mariadb;
+      };
+      systemd.services.grafana.after = [ "mysql.service" ];
+    };
+  };
+
+  nodes = builtins.listToAttrs (map (dbName:
+    nameValuePair dbName (mkMerge [
+    baseGrafanaConf
+    (extraNodeConfs.${dbName} or {})
+  ])) [ "sqlite" "postgresql" "mysql" ]);
+
+in {
+  name = "grafana";
+
+  meta = with maintainers; {
+    maintainers = [ willibutz ];
+  };
+
+  inherit nodes;
+
   testScript = ''
-    $machine->start;
-    $machine->waitForUnit("grafana.service");
-    $machine->waitForOpenPort(3000);
-    $machine->succeed("curl -sSfL http://127.0.0.1:3000/");
+    startAll();
+
+    subtest "Grafana sqlite", sub {
+      $sqlite->waitForUnit("grafana.service");
+      $sqlite->waitForOpenPort(3000);
+      $sqlite->succeed("curl -sSfN -u testadmin:snakeoilpwd http://127.0.0.1:3000/api/org/users | grep -q testadmin\@localhost");
+    };
+
+    subtest "Grafana postgresql", sub {
+      $postgresql->waitForUnit("grafana.service");
+      $postgresql->waitForUnit("postgresql.service");
+      $postgresql->waitForOpenPort(3000);
+      $postgresql->waitForOpenPort(5432);
+      $postgresql->succeed("curl -sSfN -u testadmin:snakeoilpwd http://127.0.0.1:3000/api/org/users | grep -q testadmin\@localhost");
+    };
+
+    subtest "Grafana mysql", sub {
+      $mysql->waitForUnit("grafana.service");
+      $mysql->waitForUnit("mysql.service");
+      $mysql->waitForOpenPort(3000);
+      $mysql->waitForOpenPort(3306);
+      $mysql->succeed("curl -sSfN -u testadmin:snakeoilpwd http://127.0.0.1:3000/api/org/users | grep -q testadmin\@localhost");
+    };
   '';
 })
diff --git a/nixos/tests/graylog.nix b/nixos/tests/graylog.nix
new file mode 100644
index 000000000000..dc54afd1d26d
--- /dev/null
+++ b/nixos/tests/graylog.nix
@@ -0,0 +1,111 @@
+import ./make-test.nix ({ pkgs, lib, ... }: {
+  name = "graylog";
+  meta.maintainers = with lib.maintainers; [ ma27 ];
+
+  machine = { pkgs, ... }: {
+    virtualisation.memorySize = 4096;
+    virtualisation.diskSize = 4096;
+
+    services.mongodb.enable = true;
+    services.elasticsearch.enable = true;
+    services.elasticsearch.package = pkgs.elasticsearch-oss;
+    services.elasticsearch.extraConf = ''
+      network.publish_host: 127.0.0.1
+      network.bind_host: 127.0.0.1
+    '';
+
+    services.graylog = {
+      enable = true;
+      passwordSecret = "YGhZ59wXMrYOojx5xdgEpBpDw2N6FbhM4lTtaJ1KPxxmKrUvSlDbtWArwAWMQ5LKx1ojHEVrQrBMVRdXbRyZLqffoUzHfssc";
+      elasticsearchHosts = [ "http://localhost:9200" ];
+
+      # `echo -n "nixos" | shasum -a 256`
+      rootPasswordSha2 = "6ed332bcfa615381511d4d5ba44a293bb476f368f7e9e304f0dff50230d1a85b";
+    };
+
+    environment.systemPackages = [ pkgs.jq ];
+
+    systemd.services.graylog.path = [ pkgs.netcat ];
+    systemd.services.graylog.preStart = ''
+      until nc -z localhost 9200; do
+        sleep 2
+      done
+    '';
+  };
+
+  testScript = let
+    payloads.login = pkgs.writeText "login.json" (builtins.toJSON {
+      host = "127.0.0.1:9000";
+      username = "admin";
+      password = "nixos";
+    });
+
+    payloads.input = pkgs.writeText "input.json" (builtins.toJSON {
+      title = "Demo";
+      global = false;
+      type = "org.graylog2.inputs.gelf.udp.GELFUDPInput";
+      node = "@node@";
+      configuration = {
+        bind_address = "0.0.0.0";
+        decompress_size_limit = 8388608;
+        number_worker_threads = 1;
+        override_source = null;
+        port = 12201;
+        recv_buffer_size = 262144;
+      };
+    });
+
+    payloads.gelf_message = pkgs.writeText "gelf.json" (builtins.toJSON {
+      host = "example.org";
+      short_message = "A short message";
+      full_message = "A long message";
+      version = "1.1";
+      level = 5;
+      facility = "Test";
+    });
+  in ''
+    $machine->start;
+    $machine->waitForUnit("graylog.service");
+    $machine->waitForOpenPort(9000);
+    $machine->succeed("curl -sSfL http://127.0.0.1:9000/");
+
+    my $session = $machine->succeed("curl -X POST "
+                                  . "-sSfL http://127.0.0.1:9000/api/system/sessions "
+                                  . "-d \$(cat ${payloads.login}) "
+                                  . "-H 'Content-Type: application/json' "
+                                  . "-H 'Accept: application/json' "
+                                  . "-H 'x-requested-by: cli' "
+                                  . "| jq .session_id | xargs echo"
+                                  );
+
+    chomp($session);
+
+    $machine->succeed("curl -X POST "
+                    . "-sSfL http://127.0.0.1:9000/api/system/inputs -u $session:session "
+                    . "-d \$(cat ${payloads.input} | sed -e \"s,\@node\@,\$(cat /var/lib/graylog/server/node-id),\") "
+                    . "-H 'Accept: application/json' "
+                    . "-H 'Content-Type: application/json' "
+                    . "-H 'x-requested-by: cli' "
+                    );
+
+    $machine->waitUntilSucceeds("test \"\$(curl -sSfL 'http://127.0.0.1:9000/api/cluster/inputstates' "
+                              . "-u $session:session "
+                              . "-H 'Accept: application/json' "
+                              . "-H 'Content-Type: application/json' "
+                              . "-H 'x-requested-by: cli'"
+                              . "| jq 'to_entries[]|.value|.[0]|.state' | xargs echo"
+                              . ")\" = \"RUNNING\""
+                              );
+
+    $machine->succeed("echo -n \$(cat ${payloads.gelf_message}) | nc -w10 -u 127.0.0.1 12201");
+
+    $machine->succeed("test \"\$(curl -X GET "
+                    . "-sSfL 'http://127.0.0.1:9000/api/search/universal/relative?query=*' "
+                    . "-u $session:session "
+                    . "-H 'Accept: application/json' "
+                    . "-H 'Content-Type: application/json' "
+                    . "-H 'x-requested-by: cli'"
+                    . " | jq '.total_results' | xargs echo)\" = \"1\""
+                    );
+  '';
+})
diff --git a/nixos/tests/ipv6.nix b/nixos/tests/ipv6.nix
index 14f24c29cfe2..d11eba764da3 100644
--- a/nixos/tests/ipv6.nix
+++ b/nixos/tests/ipv6.nix
@@ -1,14 +1,16 @@
 # Test of IPv6 functionality in NixOS, including whether router
 # solicication/advertisement using radvd works.
 
-import ./make-test.nix ({ pkgs, ...} : {
+import ./make-test.nix ({ pkgs, lib, ...} : {
   name = "ipv6";
   meta = with pkgs.stdenv.lib.maintainers; {
     maintainers = [ eelco ];
   };
 
   nodes =
-    { client = { ... }: { };
+    # Remove the interface configuration provided by makeTest so that the
+    # interfaces are all configured implicitly
+    { client = { ... }: { networking.interfaces = lib.mkForce {}; };
 
       server =
         { ... }:
@@ -73,6 +75,11 @@ import ./make-test.nix ({ pkgs, ...} : {
           $client->succeed("curl --fail -g http://[$serverIp]");
           $client->fail("curl --fail -g http://[$clientIp]");
       };
+      subtest "privacy extensions", sub {
+          my $ip = waitForAddress $client, "eth1", "global temporary";
+          # Default route should have "src <temporary address>" in it
+          $client->succeed("ip r g ::2 | grep $ip");
+      };
 
       # TODO: test reachability of a machine on another network.
     '';
diff --git a/nixos/tests/mediawiki.nix b/nixos/tests/mediawiki.nix
new file mode 100644
index 000000000000..6293e8a2f465
--- /dev/null
+++ b/nixos/tests/mediawiki.nix
@@ -0,0 +1,19 @@
+import ./make-test.nix ({ pkgs, lib, ... }: {
+  name = "mediawiki";
+  meta.maintainers = [ lib.maintainers.aanderse ];
+
+  machine =
+    { ... }:
+    { services.mediawiki.enable = true;
+      services.mediawiki.virtualHost.hostName = "localhost";
+      services.mediawiki.virtualHost.adminAddr = "root@example.com";
+      services.mediawiki.passwordFile = pkgs.writeText "password" "correcthorsebatterystaple";
+    };
+
+  testScript = ''
+    startAll;
+
+    $machine->waitForUnit('phpfpm-mediawiki.service');
+    $machine->succeed('curl -L http://localhost/') =~ /MediaWiki has been installed/ or die;
+  '';
+})
diff --git a/nixos/tests/networking.nix b/nixos/tests/networking.nix
index ff769886c57b..6ce64dcebea0 100644
--- a/nixos/tests/networking.nix
+++ b/nixos/tests/networking.nix
@@ -510,7 +510,7 @@ let
           '';
         };
       };
-      nodes.client = { pkgs, ... }: with pkgs.lib; {
+      nodes.clientWithPrivacy = { pkgs, ... }: with pkgs.lib; {
         virtualisation.vlans = [ 1 ];
         networking = {
           useNetworkd = networkd;
@@ -522,21 +522,39 @@ let
           };
         };
       };
+      nodes.client = { pkgs, ... }: with pkgs.lib; {
+        virtualisation.vlans = [ 1 ];
+        networking = {
+          useNetworkd = networkd;
+          useDHCP = true;
+          interfaces.eth1 = {
+            preferTempAddress = false;
+            ipv4.addresses = mkOverride 0 [ ];
+            ipv6.addresses = mkOverride 0 [ ];
+          };
+        };
+      };
       testScript = { ... }:
         ''
           startAll;
 
           $client->waitForUnit("network.target");
+          $clientWithPrivacy->waitForUnit("network.target");
           $router->waitForUnit("network-online.target");
 
           # Wait until we have an ip address
+          $clientWithPrivacy->waitUntilSucceeds("ip addr show dev eth1 | grep -q 'fd00:1234:5678:1:'");
           $client->waitUntilSucceeds("ip addr show dev eth1 | grep -q 'fd00:1234:5678:1:'");
 
           # Test vlan 1
+          $clientWithPrivacy->waitUntilSucceeds("ping -c 1 fd00:1234:5678:1::1");
           $client->waitUntilSucceeds("ping -c 1 fd00:1234:5678:1::1");
 
           # Test address used is temporary
-          $client->waitUntilSucceeds("! ip route get fd00:1234:5678:1::1 | grep -q ':[a-f0-9]*ff:fe[a-f0-9]*:'");
+          $clientWithPrivacy->waitUntilSucceeds("! ip route get fd00:1234:5678:1::1 | grep -q ':[a-f0-9]*ff:fe[a-f0-9]*:'");
+
+          # Test address used is EUI-64
+          $client->waitUntilSucceeds("ip route get fd00:1234:5678:1::1 | grep -q ':[a-f0-9]*ff:fe[a-f0-9]*:'");
         '';
     };
     routes = {
diff --git a/nixos/tests/nextcloud/with-postgresql-and-redis.nix b/nixos/tests/nextcloud/with-postgresql-and-redis.nix
index 0351d4db69ac..8a840a608753 100644
--- a/nixos/tests/nextcloud/with-postgresql-and-redis.nix
+++ b/nixos/tests/nextcloud/with-postgresql-and-redis.nix
@@ -27,10 +27,7 @@ in {
           dbtype = "pgsql";
           dbname = "nextcloud";
           dbuser = "nextcloud";
-          dbhost = "localhost";
-          dbpassFile = toString (pkgs.writeText "db-pass-file" ''
-            hunter2
-          '');
+          dbhost = "/run/postgresql";
           inherit adminuser;
           adminpassFile = toString (pkgs.writeText "admin-pass-file" ''
             ${adminpass}
@@ -84,10 +81,12 @@ in {
 
       services.postgresql = {
         enable = true;
-        initialScript = pkgs.writeText "psql-init" ''
-          create role nextcloud with login password 'hunter2';
-          create database nextcloud with owner nextcloud;
-        '';
+        ensureDatabases = [ "nextcloud" ];
+        ensureUsers = [
+          { name = "nextcloud";
+            ensurePermissions."DATABASE nextcloud" = "ALL PRIVILEGES";
+          }
+        ];
       };
     };
   };
diff --git a/nixos/tests/ostree.nix b/nixos/tests/ostree.nix
index 8b19004874e7..d7ad84a1a5f0 100644
--- a/nixos/tests/ostree.nix
+++ b/nixos/tests/ostree.nix
@@ -12,7 +12,7 @@ import ./make-test.nix ({ pkgs, lib, ... }: {
       gnome-desktop-testing ostree gnupg (python3.withPackages (p: with p; [ pyyaml ]))
     ];
 
-    environment.variables.GI_TYPELIB_PATH = lib.makeSearchPath "lib/girepository-1.0" (with pkgs; [ gtk3 pango.out ostree gdk_pixbuf atk ]); # for GJS tests
+    environment.variables.GI_TYPELIB_PATH = lib.makeSearchPath "lib/girepository-1.0" (with pkgs; [ gtk3 pango.out ostree gdk-pixbuf atk ]); # for GJS tests
   };
 
   testScript = ''
diff --git a/nixos/tests/prometheus-2.nix b/nixos/tests/prometheus-2.nix
index d7035d49ad4d..219c47c73d95 100644
--- a/nixos/tests/prometheus-2.nix
+++ b/nixos/tests/prometheus-2.nix
@@ -1,9 +1,44 @@
-import ./make-test.nix {
+let
+  grpcPort   = 19090;
+  queryPort  =  9090;
+  minioPort  =  9000;
+  pushgwPort =  9091;
+
+  s3 = {
+    accessKey = "BKIKJAA5BMMU2RHO6IBB";
+    secretKey = "V7f1CwQqAcwo80UEIJEjc5gVQUSSx5ohQ9GSrr12";
+  };
+
+  objstore.config = {
+    type = "S3";
+    config = {
+      bucket = "thanos-bucket";
+      endpoint = "s3:${toString minioPort}";
+      region =  "us-east-1";
+      access_key = s3.accessKey;
+      secret_key = s3.secretKey;
+      insecure = true;
+      signature_version2 = false;
+      encrypt_sse =  false;
+      put_user_metadata = {};
+      http_config = {
+        idle_conn_timeout = "0s";
+        insecure_skip_verify = false;
+      };
+      trace = {
+        enable = false;
+      };
+    };
+  };
+
+in import ./make-test.nix {
   name = "prometheus-2";
 
   nodes = {
-    one = { pkgs, ... }: {
+    prometheus = { pkgs, ... }: {
+      virtualisation.diskSize = 2 * 1024;
       environment.systemPackages = [ pkgs.jq ];
+      networking.firewall.allowedTCPPorts = [ grpcPort ];
       services.prometheus2 = {
         enable = true;
         scrapeConfigs = [
@@ -11,7 +46,7 @@ import ./make-test.nix {
             job_name = "prometheus";
             static_configs = [
               {
-                targets = [ "127.0.0.1:9090" ];
+                targets = [ "127.0.0.1:${toString queryPort}" ];
                 labels = { instance = "localhost"; };
               }
             ];
@@ -21,7 +56,7 @@ import ./make-test.nix {
             scrape_interval = "1s";
             static_configs = [
               {
-                targets = [ "127.0.0.1:9091" ];
+                targets = [ "127.0.0.1:${toString pushgwPort}" ];
               }
             ];
           }
@@ -35,33 +70,170 @@ import ./make-test.nix {
                     expr: count(up{job="prometheus"})
           ''
         ];
+        globalConfig = {
+          external_labels = {
+            some_label = "required by thanos";
+          };
+        };
+        extraFlags = [
+          # Required by thanos
+          "--storage.tsdb.min-block-duration=5s"
+          "--storage.tsdb.max-block-duration=5s"
+        ];
       };
       services.prometheus.pushgateway = {
         enable = true;
+        web.listen-address = ":${toString pushgwPort}";
         persistMetrics = true;
         persistence.interval = "1s";
         stateDir = "prometheus-pushgateway";
       };
+      services.thanos = {
+        sidecar = {
+          enable = true;
+          grpc-address = "0.0.0.0:${toString grpcPort}";
+          inherit objstore;
+        };
+
+        # TODO: Add some tests for these services:
+        #rule = {
+        #  enable = true;
+        #  http-address = "0.0.0.0:19194";
+        #  grpc-address = "0.0.0.0:19193";
+        #  query.addresses = [
+        #    "localhost:19191"
+        #  ];
+        #  labels = {
+        #    just = "some";
+        #    nice = "labels";
+        #  };
+        #};
+        #
+        #receive = {
+        #  http-address = "0.0.0.0:19195";
+        #  enable = true;
+        #  labels = {
+        #    just = "some";
+        #    nice = "labels";
+        #  };
+        #};
+      };
+    };
+
+    query = { pkgs, ... }: {
+      environment.systemPackages = [ pkgs.jq ];
+      services.thanos.query = {
+        enable = true;
+        http-address = "0.0.0.0:${toString queryPort}";
+        store.addresses = [
+          "prometheus:${toString grpcPort}"
+        ];
+      };
+    };
+
+    store = { pkgs, ... }: {
+      virtualisation.diskSize = 2 * 1024;
+      environment.systemPackages = with pkgs; [ jq thanos ];
+      services.thanos.store = {
+        enable = true;
+        http-address = "0.0.0.0:10902";
+        grpc-address = "0.0.0.0:${toString grpcPort}";
+        inherit objstore;
+        sync-block-duration = "1s";
+      };
+      services.thanos.compact = {
+        enable = true;
+        http-address = "0.0.0.0:10903";
+        inherit objstore;
+        consistency-delay = "5s";
+      };
+      services.thanos.query = {
+        enable = true;
+        http-address = "0.0.0.0:${toString queryPort}";
+        store.addresses = [
+          "localhost:${toString grpcPort}"
+        ];
+      };
+    };
+
+    s3 = { pkgs, ... } : {
+      # Minio requires at least 1GiB of free disk space to run.
+      virtualisation.diskSize = 2 * 1024;
+      networking.firewall.allowedTCPPorts = [ minioPort ];
+
+      services.minio = {
+        enable = true;
+        inherit (s3) accessKey secretKey;
+      };
+
+      environment.systemPackages = [ pkgs.minio-client ];
     };
   };
 
-  testScript = ''
-    startAll;
-    $one->waitForUnit("prometheus2.service");
-    $one->waitForOpenPort(9090);
-    $one->succeed("curl -s http://127.0.0.1:9090/metrics");
+  testScript = { nodes, ... } : ''
+    # Before starting the other machines we first make sure that our S3 service is online
+    # and has a bucket added for thanos:
+    $s3->start;
+    $s3->waitForUnit("minio.service");
+    $s3->waitForOpenPort(${toString minioPort});
+    $s3->succeed(
+      "mc config host add minio " .
+      "http://localhost:${toString minioPort} ${s3.accessKey} ${s3.secretKey} S3v4");
+    $s3->succeed("mc mb minio/thanos-bucket");
+
+    # Now that s3 has started we can start the other machines:
+    $prometheus->start;
+    $query->start;
+    $store->start;
 
-    # Let's test if pushing a metric to the pushgateway succeeds
-    # and whether that metric gets ingested by prometheus.
-    $one->waitForUnit("pushgateway.service");
-    $one->succeed(
+    # Check if prometheus responds to requests:
+    $prometheus->waitForUnit("prometheus2.service");
+    $prometheus->waitForOpenPort(${toString queryPort});
+    $prometheus->succeed("curl -s http://127.0.0.1:${toString queryPort}/metrics");
+
+    # Let's test if pushing a metric to the pushgateway succeeds:
+    $prometheus->waitForUnit("pushgateway.service");
+    $prometheus->succeed(
       "echo 'some_metric 3.14' | " .
-      "curl --data-binary \@- http://127.0.0.1:9091/metrics/job/some_job");
-    $one->waitUntilSucceeds(
-      "curl -sf 'http://127.0.0.1:9090/api/v1/query?query=some_metric' " .
-      "| jq '.data.result[0].value[1]' | grep '\"3.14\"'");
+      "curl --data-binary \@- http://127.0.0.1:${toString pushgwPort}/metrics/job/some_job");
+
+    # Now check whether that metric gets ingested by prometheus.
+    # Since we'll check for the metric several times on different machines
+    # we abstract the test using the following function:
+
+    # Function to check if the metric "some_metric" has been received and returns the correct value.
+    local *Machine::waitForMetric = sub {
+      my ($self) = @_;
+      $self->waitUntilSucceeds(
+        "curl -sf 'http://127.0.0.1:${toString queryPort}/api/v1/query?query=some_metric' " .
+        "| jq '.data.result[0].value[1]' | grep '\"3.14\"'");
+    };
+
+    $prometheus->waitForMetric;
 
     # Let's test if the pushgateway persists metrics to the configured location.
-    $one->waitUntilSucceeds("test -e /var/lib/prometheus-pushgateway/metrics");
+    $prometheus->waitUntilSucceeds("test -e /var/lib/prometheus-pushgateway/metrics");
+
+    # Test thanos
+    $prometheus->waitForUnit("thanos-sidecar.service");
+
+    # Test if the Thanos query service can correctly retrieve the metric that was send above.
+    $query->waitForUnit("thanos-query.service");
+    $query->waitForMetric;
+
+    # Test if the Thanos sidecar has correctly uploaded its TSDB to S3, if the
+    # Thanos storage service has correctly downloaded it from S3 and if the Thanos
+    # query service running on $store can correctly retrieve the metric:
+    $store->waitForUnit("thanos-store.service");
+    $store->waitForMetric;
+
+    $store->waitForUnit("thanos-compact.service");
+
+    # Test if the Thanos bucket command is able to retrieve blocks from the S3 bucket
+    # and check if the blocks have the correct labels:
+    $store->succeed(
+      "thanos bucket ls" .
+      " --objstore.config-file=${nodes.store.config.services.thanos.store.objstore.config-file}" .
+      " --output=json | jq .thanos.labels.some_label | grep 'required by thanos'");
   '';
 }
diff --git a/nixos/tests/prometheus-exporters.nix b/nixos/tests/prometheus-exporters.nix
index 90c7c9701f60..02d83f82f338 100644
--- a/nixos/tests/prometheus-exporters.nix
+++ b/nixos/tests/prometheus-exporters.nix
@@ -3,10 +3,11 @@
 , pkgs ? import ../.. { inherit system config; }
 }:
 
-with pkgs.lib;
-with import ../lib/testing.nix { inherit system pkgs; };
-
 let
+  inherit (import ../lib/testing.nix { inherit system pkgs; }) makeTest;
+  inherit (pkgs.lib) concatStringsSep maintainers mapAttrs mkMerge
+                     removeSuffix replaceChars singleton splitString;
+
   escape' = str: replaceChars [''"'' "$" "\n"] [''\\\"'' "\\$" ""] str;
 
 /*
@@ -73,7 +74,7 @@ let
       exporterTest = ''
         waitForUnit("prometheus-bind-exporter.service");
         waitForOpenPort(9119);
-        succeed("curl -sSf http://localhost:9119/metrics" | grep -q 'bind_query_recursions_total 0');
+        succeed("curl -sSf http://localhost:9119/metrics | grep -q 'bind_query_recursions_total 0'");
       '';
     };
 
@@ -187,6 +188,47 @@ let
       '';
     };
 
+    mail = {
+      exporterConfig = {
+        enable = true;
+        configuration = {
+          monitoringInterval = "2s";
+          mailCheckTimeout = "10s";
+          servers = [ {
+            name = "testserver";
+            server = "localhost";
+            port = 25;
+            from = "mail-exporter@localhost";
+            to = "mail-exporter@localhost";
+            detectionDir = "/var/spool/mail/mail-exporter/new";
+          } ];
+        };
+      };
+      metricProvider = {
+        services.postfix.enable = true;
+        systemd.services.prometheus-mail-exporter = {
+          after = [ "postfix.service" ];
+          requires = [ "postfix.service" ];
+          preStart = ''
+            mkdir -p 0600 mail-exporter/new
+          '';
+          serviceConfig = {
+            ProtectHome = true;
+            ReadOnlyPaths = "/";
+            ReadWritePaths = "/var/spool/mail";
+            WorkingDirectory = "/var/spool/mail";
+          };
+        };
+        users.users.mailexporter.isSystemUser = true;
+      };
+      exporterTest = ''
+        waitForUnit("postfix.service")
+        waitForUnit("prometheus-mail-exporter.service")
+        waitForOpenPort(9225)
+        waitUntilSucceeds("curl -sSf http://localhost:9225/metrics | grep -q 'mail_deliver_success{configname=\"testserver\"} 1'")
+      '';
+    };
+
     nginx = {
       exporterConfig = {
         enable = true;
@@ -231,6 +273,30 @@ let
       '';
     };
 
+    postgres = {
+      exporterConfig = {
+        enable = true;
+        runAsLocalSuperUser = true;
+      };
+      metricProvider = {
+        services.postgresql.enable = true;
+      };
+      exporterTest = ''
+        waitForUnit("prometheus-postgres-exporter.service");
+        waitForOpenPort(9187);
+        waitForUnit("postgresql.service");
+        succeed("curl -sSf http://localhost:9187/metrics | grep -q 'pg_exporter_last_scrape_error 0'");
+        succeed("curl -sSf http://localhost:9187/metrics | grep -q 'pg_up 1'");
+        systemctl("stop postgresql.service");
+        succeed("curl -sSf http://localhost:9187/metrics | grep -qv 'pg_exporter_last_scrape_error 0'");
+        succeed("curl -sSf http://localhost:9187/metrics | grep -q 'pg_up 0'");
+        systemctl("start postgresql.service");
+        waitForUnit("postgresql.service");
+        succeed("curl -sSf http://localhost:9187/metrics | grep -q 'pg_exporter_last_scrape_error 0'");
+        succeed("curl -sSf http://localhost:9187/metrics | grep -q 'pg_up 1'");
+      '';
+    };
+
     snmp = {
       exporterConfig = {
         enable = true;
@@ -311,6 +377,7 @@ let
       };
       exporterTest = ''
         waitForUnit("prometheus-varnish-exporter.service");
+        waitForOpenPort(6081);
         waitForOpenPort(9131);
         succeed("curl -sSf http://localhost:9131/metrics | grep -q 'varnish_up 1'");
       '';
@@ -331,11 +398,12 @@ let
             inherit (snakeoil.peer1) publicKey;
           };
         };
+        systemd.services.prometheus-wireguard-exporter.after = [ "wireguard-wg0.service" ];
       };
       exporterTest = ''
         waitForUnit("prometheus-wireguard-exporter.service");
         waitForOpenPort(9586);
-        succeed("curl -sSf http://localhost:9586/metrics | grep '${snakeoil.peer1.publicKey}'");
+        waitUntilSucceeds("curl -sSf http://localhost:9586/metrics | grep '${snakeoil.peer1.publicKey}'");
       '';
     };
   };
diff --git a/nixos/tests/redmine.nix b/nixos/tests/redmine.nix
index cbdb5c8d2954..2d4df288b055 100644
--- a/nixos/tests/redmine.nix
+++ b/nixos/tests/redmine.nix
@@ -10,19 +10,9 @@ let
   mysqlTest = package: makeTest {
     machine =
       { config, pkgs, ... }:
-      { services.mysql.enable = true;
-        services.mysql.package = pkgs.mariadb;
-        services.mysql.ensureDatabases = [ "redmine" ];
-        services.mysql.ensureUsers = [
-          { name = "redmine";
-            ensurePermissions = { "redmine.*" = "ALL PRIVILEGES"; };
-          }
-        ];
-
-        services.redmine.enable = true;
+      { services.redmine.enable = true;
         services.redmine.package = package;
         services.redmine.database.type = "mysql2";
-        services.redmine.database.socket = "/run/mysqld/mysqld.sock";
         services.redmine.plugins = {
           redmine_env_auth = pkgs.fetchurl {
             url = https://github.com/Intera/redmine_env_auth/archive/0.7.zip;
@@ -48,19 +38,9 @@ let
   pgsqlTest = package: makeTest {
     machine =
       { config, pkgs, ... }:
-      { services.postgresql.enable = true;
-        services.postgresql.ensureDatabases = [ "redmine" ];
-        services.postgresql.ensureUsers = [
-          { name = "redmine";
-            ensurePermissions = { "DATABASE redmine" = "ALL PRIVILEGES"; };
-          }
-        ];
-
-        services.redmine.enable = true;
+      { services.redmine.enable = true;
         services.redmine.package = package;
         services.redmine.database.type = "postgresql";
-        services.redmine.database.host = "";
-        services.redmine.database.port = 5432;
         services.redmine.plugins = {
           redmine_env_auth = pkgs.fetchurl {
             url = https://github.com/Intera/redmine_env_auth/archive/0.7.zip;
diff --git a/nixos/tests/tiddlywiki.nix b/nixos/tests/tiddlywiki.nix
new file mode 100644
index 000000000000..4a2014a4ec91
--- /dev/null
+++ b/nixos/tests/tiddlywiki.nix
@@ -0,0 +1,67 @@
+import ./make-test.nix ({ ... }: {
+  name = "tiddlywiki";
+  nodes = {
+    default = {
+      services.tiddlywiki.enable = true;
+    };
+    configured = {
+      boot.postBootCommands = ''
+        echo "username,password
+        somelogin,somesecret" > /var/lib/wikiusers.csv
+      '';
+      services.tiddlywiki = {
+        enable = true;
+        listenOptions = {
+          port = 3000;
+          credentials="../wikiusers.csv";
+          readers="(authenticated)";
+        };
+      };
+    };
+  };
+
+  testScript = ''
+    startAll;
+
+    subtest "by default works without configuration", sub {
+      $default->waitForUnit("tiddlywiki.service");
+    };
+
+    subtest "by default available on port 8080 without auth", sub {
+      $default->waitForUnit("tiddlywiki.service");
+      $default->waitForOpenPort(8080);
+      $default->succeed("curl --fail 127.0.0.1:8080");
+    };
+
+    subtest "by default creates empty wiki", sub {
+      $default->succeed("test -f /var/lib/tiddlywiki/tiddlywiki.info");
+    };
+
+    subtest "configured on port 3000 with basic auth", sub {
+      $configured->waitForUnit("tiddlywiki.service");
+      $configured->waitForOpenPort(3000);
+      $configured->fail("curl --fail 127.0.0.1:3000");
+      $configured->succeed("curl --fail 127.0.0.1:3000 --user somelogin:somesecret");
+    };
+
+    subtest "configured with different wikifolder", sub {
+      $configured->succeed("test -f /var/lib/tiddlywiki/tiddlywiki.info");
+    };
+
+    subtest "restart preserves changes", sub {
+      # given running wiki
+      $default->waitForUnit("tiddlywiki.service");
+      # with some changes
+      $default->succeed("curl --fail --request PUT --header 'X-Requested-With:TiddlyWiki' --data '{ \"title\": \"title\", \"text\": \"content\" }' --url 127.0.0.1:8080/recipes/default/tiddlers/somepage ");
+      $default->succeed("sleep 2"); # server syncs to filesystem on timer
+
+      # when wiki is cycled
+      $default->systemctl("restart tiddlywiki.service");
+      $default->waitForUnit("tiddlywiki.service");
+      $default->waitForOpenPort(8080);
+
+      # the change is preserved
+      $default->succeed("curl --fail 127.0.0.1:8080/recipes/default/tiddlers/somepage");
+    };
+  '';
+})
diff --git a/nixos/tests/tomcat.nix b/nixos/tests/tomcat.nix
deleted file mode 100644
index 8e7b886dd302..000000000000
--- a/nixos/tests/tomcat.nix
+++ /dev/null
@@ -1,30 +0,0 @@
-import ./make-test.nix ({ pkgs, ...} : {
-  name = "tomcat";
-  meta = with pkgs.stdenv.lib.maintainers; {
-    maintainers = [ eelco ];
-  };
-
-  nodes = {
-    server =
-      { ... }:
-
-      { services.tomcat.enable = true;
-        services.httpd.enable = true;
-        services.httpd.adminAddr = "foo@bar.com";
-        services.httpd.extraSubservices =
-          [ { serviceType = "tomcat-connector"; } ];
-        networking.firewall.allowedTCPPorts = [ 80 ];
-      };
-
-    client = { };
-  };
-
-  testScript = ''
-    startAll;
-
-    $server->waitForUnit("tomcat");
-    $client->waitForUnit("network.target");
-    $client->waitUntilSucceeds("curl --fail http://server/examples/servlets/servlet/HelloWorldExample");
-    $client->waitUntilSucceeds("curl --fail http://server/examples/jsp/jsp2/simpletag/hello.jsp");
-  '';
-})