about summary refs log tree commit diff
path: root/nixos/tests
diff options
context:
space:
mode:
Diffstat (limited to 'nixos/tests')
-rw-r--r--nixos/tests/all-tests.nix7
-rw-r--r--nixos/tests/discourse.nix197
-rw-r--r--nixos/tests/doh-proxy-rust.nix43
-rw-r--r--nixos/tests/dovecot.nix2
-rw-r--r--nixos/tests/gitea.nix2
-rw-r--r--nixos/tests/gobgpd.nix71
-rw-r--r--nixos/tests/home-assistant.nix10
-rw-r--r--nixos/tests/kernel-generic.nix37
-rw-r--r--nixos/tests/kernel-latest.nix17
-rw-r--r--nixos/tests/kernel-lts.nix17
-rw-r--r--nixos/tests/kernel-testing.nix17
-rw-r--r--nixos/tests/mysql/mariadb-galera-mariabackup.nix6
-rw-r--r--nixos/tests/mysql/mariadb-galera-rsync.nix6
-rw-r--r--nixos/tests/mysql/mysql.nix12
-rw-r--r--nixos/tests/redis.nix5
-rw-r--r--nixos/tests/rspamd.nix5
-rw-r--r--nixos/tests/shadow.nix3
-rw-r--r--nixos/tests/spacecookie.nix33
-rw-r--r--nixos/tests/systemd-confinement.nix1
-rw-r--r--nixos/tests/systemd-networkd-ipv6-prefix-delegation.nix2
-rw-r--r--nixos/tests/unbound.nix11
-rw-r--r--nixos/tests/wmderland.nix54
22 files changed, 462 insertions, 96 deletions
diff --git a/nixos/tests/all-tests.nix b/nixos/tests/all-tests.nix
index 62188ddf9e8d..47384cf5a8a2 100644
--- a/nixos/tests/all-tests.nix
+++ b/nixos/tests/all-tests.nix
@@ -88,6 +88,7 @@ in
   croc = handleTest ./croc.nix {};
   deluge = handleTest ./deluge.nix {};
   dhparams = handleTest ./dhparams.nix {};
+  discourse = handleTest ./discourse.nix {};
   dnscrypt-proxy2 = handleTestOn ["x86_64-linux"] ./dnscrypt-proxy2.nix {};
   dnscrypt-wrapper = handleTestOn ["x86_64-linux"] ./dnscrypt-wrapper {};
   doas = handleTest ./doas.nix {};
@@ -137,6 +138,7 @@ in
   gnome3 = handleTest ./gnome3.nix {};
   gnome3-xorg = handleTest ./gnome3-xorg.nix {};
   go-neb = handleTest ./go-neb.nix {};
+  gobgpd = handleTest ./gobgpd.nix {};
   gocd-agent = handleTest ./gocd-agent.nix {};
   gocd-server = handleTest ./gocd-server.nix {};
   google-oslogin = handleTest ./google-oslogin {};
@@ -193,9 +195,7 @@ in
   keepalived = handleTest ./keepalived.nix {};
   keepassxc = handleTest ./keepassxc.nix {};
   kerberos = handleTest ./kerberos/default.nix {};
-  kernel-latest = handleTest ./kernel-latest.nix {};
-  kernel-lts = handleTest ./kernel-lts.nix {};
-  kernel-testing = handleTest ./kernel-testing.nix {};
+  kernel-generic = handleTest ./kernel-generic.nix {};
   kernel-latest-ath-user-regd = handleTest ./kernel-latest-ath-user-regd.nix {};
   keycloak = discoverTests (import ./keycloak.nix);
   keymap = handleTest ./keymap.nix {};
@@ -433,6 +433,7 @@ in
   wasabibackend = handleTest ./wasabibackend.nix {};
   wiki-js = handleTest ./wiki-js.nix {};
   wireguard = handleTest ./wireguard {};
+  wmderland = handleTest ./wmderland.nix {};
   wordpress = handleTest ./wordpress.nix {};
   xandikos = handleTest ./xandikos.nix {};
   xautolock = handleTest ./xautolock.nix {};
diff --git a/nixos/tests/discourse.nix b/nixos/tests/discourse.nix
new file mode 100644
index 000000000000..3c965550fe0a
--- /dev/null
+++ b/nixos/tests/discourse.nix
@@ -0,0 +1,197 @@
+# This tests Discourse by:
+#  1. logging in as the admin user
+#  2. sending a private message to the admin user through the API
+#  3. replying to that message via email.
+
+import ./make-test-python.nix (
+  { pkgs, lib, ... }:
+  let
+    certs = import ./common/acme/server/snakeoil-certs.nix;
+    clientDomain = "client.fake.domain";
+    discourseDomain = certs.domain;
+    adminPassword = "eYAX85qmMJ5GZIHLaXGDAoszD7HSZp5d";
+    secretKeyBase = "381f4ac6d8f5e49d804dae72aa9c046431d2f34c656a705c41cd52fed9b4f6f76f51549f0b55db3b8b0dded7a00d6a381ebe9a4367d2d44f5e743af6628b4d42";
+    admin = {
+      email = "alice@${clientDomain}";
+      username = "alice";
+      fullName = "Alice Admin";
+      passwordFile = "${pkgs.writeText "admin-pass" adminPassword}";
+    };
+  in
+  {
+    name = "discourse";
+    meta = with pkgs.lib.maintainers; {
+      maintainers = [ talyz ];
+    };
+
+    nodes.discourse =
+      { nodes, ... }:
+      {
+        virtualisation.memorySize = 2048;
+
+        imports = [ common/user-account.nix ];
+
+        security.pki.certificateFiles = [
+          certs.ca.cert
+        ];
+
+        networking.extraHosts = ''
+          127.0.0.1 ${discourseDomain}
+          ${nodes.client.config.networking.primaryIPAddress} ${clientDomain}
+        '';
+
+        services.postfix = {
+          enableSubmission = true;
+          enableSubmissions = true;
+          submissionsOptions = {
+            smtpd_sasl_auth_enable = "yes";
+            smtpd_client_restrictions = "permit";
+          };
+        };
+
+        environment.systemPackages = [ pkgs.jq ];
+
+        services.discourse = {
+          enable = true;
+          inherit admin;
+          hostname = discourseDomain;
+          sslCertificate = "${certs.${discourseDomain}.cert}";
+          sslCertificateKey = "${certs.${discourseDomain}.key}";
+          secretKeyBaseFile = "${pkgs.writeText "secret-key-base" secretKeyBase}";
+          enableACME = false;
+          mail.outgoing.serverAddress = clientDomain;
+          mail.incoming.enable = true;
+          siteSettings = {
+            posting = {
+              min_post_length = 5;
+              min_first_post_length = 5;
+              min_personal_message_post_length = 5;
+            };
+          };
+          unicornTimeout = 900;
+        };
+
+        networking.firewall.allowedTCPPorts = [ 25 465 ];
+      };
+
+    nodes.client =
+      { nodes, ... }:
+      {
+        imports = [ common/user-account.nix ];
+
+        security.pki.certificateFiles = [
+          certs.ca.cert
+        ];
+
+        networking.extraHosts = ''
+          127.0.0.1 ${clientDomain}
+          ${nodes.discourse.config.networking.primaryIPAddress} ${discourseDomain}
+        '';
+
+        services.dovecot2 = {
+          enable = true;
+          protocols = [ "imap" ];
+          modules = [ pkgs.dovecot_pigeonhole ];
+        };
+
+        services.postfix = {
+          enable = true;
+          origin = clientDomain;
+          relayDomains = [ clientDomain ];
+          config = {
+            compatibility_level = "2";
+            smtpd_banner = "ESMTP server";
+            myhostname = clientDomain;
+            mydestination = clientDomain;
+          };
+        };
+
+        environment.systemPackages =
+          let
+            replyToEmail = pkgs.writeScriptBin "reply-to-email" ''
+              #!${pkgs.python3.interpreter}
+              import imaplib
+              import smtplib
+              import ssl
+              import email.header
+              from email import message_from_bytes
+              from email.message import EmailMessage
+
+              with imaplib.IMAP4('localhost') as imap:
+                  imap.login('alice', 'foobar')
+                  imap.select()
+                  status, data = imap.search(None, 'ALL')
+                  assert status == 'OK'
+
+                  nums = data[0].split()
+                  assert len(nums) == 1
+
+                  status, msg_data = imap.fetch(nums[0], '(RFC822)')
+                  assert status == 'OK'
+
+              msg = email.message_from_bytes(msg_data[0][1])
+              subject = str(email.header.make_header(email.header.decode_header(msg['Subject'])))
+              reply_to = email.header.decode_header(msg['Reply-To'])[0][0]
+              message_id = email.header.decode_header(msg['Message-ID'])[0][0]
+              date = email.header.decode_header(msg['Date'])[0][0]
+
+              ctx = ssl.create_default_context()
+              with smtplib.SMTP_SSL(host='${discourseDomain}', context=ctx) as smtp:
+                  reply = EmailMessage()
+                  reply['Subject'] = 'Re: ' + subject
+                  reply['To'] = reply_to
+                  reply['From'] = 'alice@${clientDomain}'
+                  reply['In-Reply-To'] = message_id
+                  reply['References'] = message_id
+                  reply['Date'] = date
+                  reply.set_content("Test reply.")
+
+                  smtp.send_message(reply)
+                  smtp.quit()
+            '';
+          in
+            [ replyToEmail ];
+
+        networking.firewall.allowedTCPPorts = [ 25 ];
+      };
+
+
+    testScript = { nodes }:
+      let
+        request = builtins.toJSON {
+          title = "Private message";
+          raw = "This is a test message.";
+          target_usernames = admin.username;
+          archetype = "private_message";
+        };
+      in ''
+        discourse.start()
+        client.start()
+
+        discourse.wait_for_unit("discourse.service")
+        discourse.wait_for_file("/run/discourse/sockets/unicorn.sock")
+        discourse.wait_until_succeeds("curl -sS -f https://${discourseDomain}")
+        discourse.succeed(
+            "curl -sS -f https://${discourseDomain}/session/csrf -c cookie -b cookie -H 'Accept: application/json' | jq -r '\"X-CSRF-Token: \" + .csrf' > csrf_token",
+            "curl -sS -f https://${discourseDomain}/session -c cookie -b cookie -H @csrf_token -H 'Accept: application/json' -d 'login=${nodes.discourse.config.services.discourse.admin.username}' -d \"password=${adminPassword}\" | jq -e '.user.username == \"${nodes.discourse.config.services.discourse.admin.username}\"'",
+            "curl -sS -f https://${discourseDomain}/login -v -H 'Accept: application/json' -c cookie -b cookie 2>&1 | grep ${nodes.discourse.config.services.discourse.admin.username}",
+        )
+
+        client.wait_for_unit("postfix.service")
+        client.wait_for_unit("dovecot2.service")
+
+        discourse.succeed(
+            "sudo -u discourse discourse-rake api_key:create_master[master] >api_key",
+            'curl -sS -f https://${discourseDomain}/posts -X POST -H "Content-Type: application/json" -H "Api-Key: $(<api_key)" -H "Api-Username: system" -d \'${request}\' ',
+        )
+
+        client.wait_until_succeeds("reply-to-email")
+
+        discourse.wait_until_succeeds(
+            'curl -sS -f https://${discourseDomain}/topics/private-messages/system -H "Accept: application/json" -H "Api-Key: $(<api_key)" -H "Api-Username: system" | jq -e \'if .topic_list.topics[0].id != null then .topic_list.topics[0].id else null end\' >topic_id'
+        )
+        discourse.succeed(
+            'curl -sS -f https://${discourseDomain}/t/$(<topic_id) -H "Accept: application/json" -H "Api-Key: $(<api_key)" -H "Api-Username: system" | jq -e \'if .post_stream.posts[1].cooked == "<p>Test reply.</p>" then true else null end\' '
+        )
+      '';
+  })
diff --git a/nixos/tests/doh-proxy-rust.nix b/nixos/tests/doh-proxy-rust.nix
new file mode 100644
index 000000000000..ca150cafab50
--- /dev/null
+++ b/nixos/tests/doh-proxy-rust.nix
@@ -0,0 +1,43 @@
+import ./make-test-python.nix ({ lib, pkgs, ... }: {
+  name = "doh-proxy-rust";
+  meta = with lib.maintainers; {
+    maintainers = [ stephank ];
+  };
+
+  nodes = {
+    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
+          '';
+        };
+      };
+      services.doh-proxy-rust = {
+        enable = true;
+        flags = [
+          "--server-address=127.0.0.1:53"
+        ];
+      };
+    };
+  };
+
+  testScript = { nodes, ... }: ''
+    url = "http://localhost:3000/dns-query"
+    query = "AAABAAABAAAAAAAAAm5zB2V4YW1wbGUDb3JnAAABAAE="  # IN A ns.example.org.
+    bin_ip = r"$'\xC0\xA8\x00\x01'"  # 192.168.0.1, as shell binary string
+
+    machine.wait_for_unit("bind.service")
+    machine.wait_for_unit("doh-proxy-rust.service")
+    machine.wait_for_open_port(53)
+    machine.wait_for_open_port(3000)
+    machine.succeed(f"curl --fail '{url}?dns={query}' | grep -qF {bin_ip}")
+  '';
+})
diff --git a/nixos/tests/dovecot.nix b/nixos/tests/dovecot.nix
index 1129e3b45d9d..8913c2a6a7e8 100644
--- a/nixos/tests/dovecot.nix
+++ b/nixos/tests/dovecot.nix
@@ -8,6 +8,8 @@ import ./make-test-python.nix {
       enable = true;
       protocols = [ "imap" "pop3" ];
       modules = [ pkgs.dovecot_pigeonhole ];
+      mailUser = "vmail";
+      mailGroup = "vmail";
     };
     environment.systemPackages = let
       sendTestMail = pkgs.writeScriptBin "send-testmail" ''
diff --git a/nixos/tests/gitea.nix b/nixos/tests/gitea.nix
index 1fb27593f056..037fc7b31bfa 100644
--- a/nixos/tests/gitea.nix
+++ b/nixos/tests/gitea.nix
@@ -61,7 +61,7 @@ let
           + "Please contact your site administrator.'"
       )
       server.succeed(
-          "su -l gitea -c 'GITEA_WORK_DIR=/var/lib/gitea gitea admin create-user "
+          "su -l gitea -c 'GITEA_WORK_DIR=/var/lib/gitea gitea admin user create "
           + "--username test --password totallysafe --email test@localhost'"
       )
 
diff --git a/nixos/tests/gobgpd.nix b/nixos/tests/gobgpd.nix
new file mode 100644
index 000000000000..775f65d1199f
--- /dev/null
+++ b/nixos/tests/gobgpd.nix
@@ -0,0 +1,71 @@
+import ./make-test-python.nix ({ pkgs, ... }:
+  let
+    ifAddr = node: iface: (pkgs.lib.head node.config.networking.interfaces.${iface}.ipv4.addresses).address;
+  in {
+    name = "gobgpd";
+
+    meta = with pkgs.lib.maintainers; { maintainers = [ higebu ]; };
+
+    nodes = {
+      node1 = { nodes, ... }: {
+        environment.systemPackages = [ pkgs.gobgp ];
+        networking.firewall.allowedTCPPorts = [ 179 ];
+        services.gobgpd = {
+          enable = true;
+          settings = {
+            global = {
+              config = {
+                as = 64512;
+                router-id = "192.168.255.1";
+              };
+            };
+            neighbors = [{
+              config = {
+                neighbor-address = ifAddr nodes.node2 "eth1";
+                peer-as = 64513;
+              };
+            }];
+          };
+        };
+      };
+      node2 = { nodes, ... }: {
+        environment.systemPackages = [ pkgs.gobgp ];
+        networking.firewall.allowedTCPPorts = [ 179 ];
+        services.gobgpd = {
+          enable = true;
+          settings = {
+            global = {
+              config = {
+                as = 64513;
+                router-id = "192.168.255.2";
+              };
+            };
+            neighbors = [{
+              config = {
+                neighbor-address = ifAddr nodes.node1 "eth1";
+                peer-as = 64512;
+              };
+            }];
+          };
+        };
+      };
+    };
+
+    testScript = { nodes, ... }: let
+      addr1 = ifAddr nodes.node1 "eth1";
+      addr2 = ifAddr nodes.node2 "eth1";
+    in
+      ''
+      start_all()
+
+      for node in node1, node2:
+          with subtest("should start gobgpd node"):
+              node.wait_for_unit("gobgpd.service")
+          with subtest("should open port 179"):
+              node.wait_for_open_port(179)
+
+      with subtest("should show neighbors by gobgp cli and BGP state should be ESTABLISHED"):
+          node1.wait_until_succeeds("gobgp neighbor ${addr2} | grep -q ESTABLISHED")
+          node2.wait_until_succeeds("gobgp neighbor ${addr1} | grep -q ESTABLISHED")
+    '';
+  })
diff --git a/nixos/tests/home-assistant.nix b/nixos/tests/home-assistant.nix
index 726c7eb6acb6..3b7295324a18 100644
--- a/nixos/tests/home-assistant.nix
+++ b/nixos/tests/home-assistant.nix
@@ -14,9 +14,10 @@ in {
     environment.systemPackages = with pkgs; [ mosquitto ];
     services.mosquitto = {
       enable = true;
+      checkPasswords = true;
       users = {
         "${mqttUsername}" = {
-          acl = [ "pattern readwrite #" ];
+          acl = [ "topic readwrite #" ];
           password = mqttPassword;
         };
       };
@@ -77,12 +78,9 @@ in {
         hass.wait_for_open_port(8123)
         hass.succeed("curl --fail http://localhost:8123/lovelace")
     with subtest("Toggle a binary sensor using MQTT"):
-        # wait for broker to become available
-        hass.wait_until_succeeds(
-            "mosquitto_sub -V mqttv311 -t home-assistant/test -u ${mqttUsername} -P '${mqttPassword}' -W 1 -t '*'"
-        )
+        hass.wait_for_open_port(1883)
         hass.succeed(
-            "mosquitto_pub -V mqttv311 -t home-assistant/test -u ${mqttUsername} -P '${mqttPassword}' -m let_there_be_light"
+            "mosquitto_pub -V mqttv5 -t home-assistant/test -u ${mqttUsername} -P '${mqttPassword}' -m let_there_be_light"
         )
     with subtest("Print log to ease debugging"):
         output_log = hass.succeed("cat ${configDir}/home-assistant.log")
diff --git a/nixos/tests/kernel-generic.nix b/nixos/tests/kernel-generic.nix
new file mode 100644
index 000000000000..17089141e9e4
--- /dev/null
+++ b/nixos/tests/kernel-generic.nix
@@ -0,0 +1,37 @@
+{ system ? builtins.currentSystem
+, config ? { }
+, pkgs ? import ../.. { inherit system config; }
+}@args:
+
+with pkgs.lib;
+
+let
+  makeKernelTest = version: linuxPackages: (import ./make-test-python.nix ({ pkgs, ... }: {
+    name = "kernel-${version}";
+    meta = with pkgs.lib.maintainers; {
+      maintainers = [ nequissimus ];
+    };
+
+    machine = { ... }:
+      {
+        boot.kernelPackages = linuxPackages;
+      };
+
+    testScript =
+      ''
+        assert "Linux" in machine.succeed("uname -s")
+        assert "${linuxPackages.kernel.modDirVersion}" in machine.succeed("uname -a")
+      '';
+  }) args);
+in
+with pkgs; {
+  linux_4_4 = makeKernelTest "4.4" linuxPackages_4_4;
+  linux_4_9 = makeKernelTest "4.9" linuxPackages_4_9;
+  linux_4_14 = makeKernelTest "4.14" linuxPackages_4_14;
+  linux_4_19 = makeKernelTest "4.19" linuxPackages_4_19;
+  linux_5_4 = makeKernelTest "5.4" linuxPackages_5_4;
+  linux_5_10 = makeKernelTest "5.10" linuxPackages_5_10;
+  linux_5_11 = makeKernelTest "5.11" linuxPackages_5_11;
+
+  linux_testing = makeKernelTest "testing" linuxPackages_testing;
+}
diff --git a/nixos/tests/kernel-latest.nix b/nixos/tests/kernel-latest.nix
deleted file mode 100644
index 323dde267a42..000000000000
--- a/nixos/tests/kernel-latest.nix
+++ /dev/null
@@ -1,17 +0,0 @@
-import ./make-test-python.nix ({ pkgs, ...} : {
-  name = "kernel-latest";
-  meta = with pkgs.lib.maintainers; {
-    maintainers = [ nequissimus ];
-  };
-
-  machine = { pkgs, ... }:
-    {
-      boot.kernelPackages = pkgs.linuxPackages_latest;
-    };
-
-  testScript =
-    ''
-      assert "Linux" in machine.succeed("uname -s")
-      assert "${pkgs.linuxPackages_latest.kernel.version}" in machine.succeed("uname -a")
-    '';
-})
diff --git a/nixos/tests/kernel-lts.nix b/nixos/tests/kernel-lts.nix
deleted file mode 100644
index 9b03e9db6d84..000000000000
--- a/nixos/tests/kernel-lts.nix
+++ /dev/null
@@ -1,17 +0,0 @@
-import ./make-test-python.nix ({ pkgs, ...} : {
-  name = "kernel-lts";
-  meta = with pkgs.lib.maintainers; {
-    maintainers = [ nequissimus ];
-  };
-
-  machine = { pkgs, ... }:
-    {
-      boot.kernelPackages = pkgs.linuxPackages;
-    };
-
-  testScript =
-    ''
-      assert "Linux" in machine.succeed("uname -s")
-      assert "${pkgs.linuxPackages.kernel.version}" in machine.succeed("uname -a")
-    '';
-})
diff --git a/nixos/tests/kernel-testing.nix b/nixos/tests/kernel-testing.nix
deleted file mode 100644
index 017007c0aec8..000000000000
--- a/nixos/tests/kernel-testing.nix
+++ /dev/null
@@ -1,17 +0,0 @@
-import ./make-test-python.nix ({ pkgs, ...} : {
-  name = "kernel-testing";
-  meta = with pkgs.lib.maintainers; {
-    maintainers = [ nequissimus ];
-  };
-
-  machine = { pkgs, ... }:
-    {
-      boot.kernelPackages = pkgs.linuxPackages_testing;
-    };
-
-  testScript =
-    ''
-      assert "Linux" in machine.succeed("uname -s")
-      assert "${pkgs.linuxPackages_testing.kernel.modDirVersion}" in machine.succeed("uname -a")
-    '';
-})
diff --git a/nixos/tests/mysql/mariadb-galera-mariabackup.nix b/nixos/tests/mysql/mariadb-galera-mariabackup.nix
index 0a40c010a471..1c73bc854a57 100644
--- a/nixos/tests/mysql/mariadb-galera-mariabackup.nix
+++ b/nixos/tests/mysql/mariadb-galera-mariabackup.nix
@@ -31,7 +31,7 @@ in {
         firewall.allowedTCPPorts = [ 3306 4444 4567 4568 ];
         firewall.allowedUDPPorts = [ 4567 ];
       };
-      users.users.testuser = { };
+      users.users.testuser = { isSystemUser = true; };
       systemd.services.mysql = with pkgs; {
         path = [ mysqlenv-common mysqlenv-mariabackup ];
       };
@@ -89,7 +89,7 @@ in {
         firewall.allowedTCPPorts = [ 3306 4444 4567 4568 ];
         firewall.allowedUDPPorts = [ 4567 ];
       };
-      users.users.testuser = { };
+      users.users.testuser = { isSystemUser = true; };
       systemd.services.mysql = with pkgs; {
         path = [ mysqlenv-common mysqlenv-mariabackup ];
       };
@@ -136,7 +136,7 @@ in {
         firewall.allowedTCPPorts = [ 3306 4444 4567 4568 ];
         firewall.allowedUDPPorts = [ 4567 ];
       };
-      users.users.testuser = { };
+      users.users.testuser = { isSystemUser = true; };
       systemd.services.mysql = with pkgs; {
         path = [ mysqlenv-common mysqlenv-mariabackup ];
       };
diff --git a/nixos/tests/mysql/mariadb-galera-rsync.nix b/nixos/tests/mysql/mariadb-galera-rsync.nix
index 6fb3cfef8d73..709a8b5085cb 100644
--- a/nixos/tests/mysql/mariadb-galera-rsync.nix
+++ b/nixos/tests/mysql/mariadb-galera-rsync.nix
@@ -31,7 +31,7 @@ in {
         firewall.allowedTCPPorts = [ 3306 4444 4567 4568 ];
         firewall.allowedUDPPorts = [ 4567 ];
       };
-      users.users.testuser = { };
+      users.users.testuser = { isSystemUser = true; };
       systemd.services.mysql = with pkgs; {
         path = [ mysqlenv-common mysqlenv-rsync ];
       };
@@ -84,7 +84,7 @@ in {
         firewall.allowedTCPPorts = [ 3306 4444 4567 4568 ];
         firewall.allowedUDPPorts = [ 4567 ];
       };
-      users.users.testuser = { };
+      users.users.testuser = { isSystemUser = true; };
       systemd.services.mysql = with pkgs; {
         path = [ mysqlenv-common mysqlenv-rsync ];
       };
@@ -130,7 +130,7 @@ in {
         firewall.allowedTCPPorts = [ 3306 4444 4567 4568 ];
         firewall.allowedUDPPorts = [ 4567 ];
       };
-      users.users.testuser = { };
+      users.users.testuser = { isSystemUser = true; };
       systemd.services.mysql = with pkgs; {
         path = [ mysqlenv-common mysqlenv-rsync ];
       };
diff --git a/nixos/tests/mysql/mysql.nix b/nixos/tests/mysql/mysql.nix
index 50ad5c68aef1..c21136416d47 100644
--- a/nixos/tests/mysql/mysql.nix
+++ b/nixos/tests/mysql/mysql.nix
@@ -9,8 +9,8 @@ import ./../make-test-python.nix ({ pkgs, ...} : {
       { pkgs, ... }:
 
       {
-        users.users.testuser = { };
-        users.users.testuser2 = { };
+        users.users.testuser = { isSystemUser = true; };
+        users.users.testuser2 = { isSystemUser = true; };
         services.mysql.enable = true;
         services.mysql.initialDatabases = [
           { name = "testdb3"; schema = ./testdb.sql; }
@@ -44,8 +44,8 @@ import ./../make-test-python.nix ({ pkgs, ...} : {
         # Kernel panic - not syncing: Out of memory: compulsory panic_on_oom is enabled
         virtualisation.memorySize = 1024;
 
-        users.users.testuser = { };
-        users.users.testuser2 = { };
+        users.users.testuser = { isSystemUser = true; };
+        users.users.testuser2 = { isSystemUser = true; };
         services.mysql.enable = true;
         services.mysql.initialDatabases = [
           { name = "testdb3"; schema = ./testdb.sql; }
@@ -75,8 +75,8 @@ import ./../make-test-python.nix ({ pkgs, ...} : {
       { pkgs, ... }:
 
       {
-        users.users.testuser = { };
-        users.users.testuser2 = { };
+        users.users.testuser = { isSystemUser = true; };
+        users.users.testuser2 = { isSystemUser = true; };
         services.mysql.enable = true;
         services.mysql.initialScript = pkgs.writeText "mariadb-init.sql" ''
           ALTER USER root@localhost IDENTIFIED WITH unix_socket;
diff --git a/nixos/tests/redis.nix b/nixos/tests/redis.nix
index ca1715614359..28b6058c2c02 100644
--- a/nixos/tests/redis.nix
+++ b/nixos/tests/redis.nix
@@ -17,16 +17,15 @@ in
         services.redis.unixSocket = redisSocket;
 
         # Allow access to the unix socket for the "redis" group.
-        services.redis.settings.unixsocketperm = "770";
+        services.redis.unixSocketPerm = 770;
 
         users.users."member" = {
           createHome = false;
           description = "A member of the redis group";
+          isNormalUser = true;
           extraGroups = [
             "redis"
           ];
-          group = "users";
-          shell = "/bin/sh";
         };
       };
   };
diff --git a/nixos/tests/rspamd.nix b/nixos/tests/rspamd.nix
index 7f41e1a79566..f0ccfe7ea0e6 100644
--- a/nixos/tests/rspamd.nix
+++ b/nixos/tests/rspamd.nix
@@ -274,7 +274,10 @@ in
 
         I find cows to be evil don't you?
       '';
-      users.users.tester.password = "test";
+      users.users.tester = {
+        isNormalUser = true;
+        password = "test";
+      };
       services.postfix = {
         enable = true;
         destination = ["example.com"];
diff --git a/nixos/tests/shadow.nix b/nixos/tests/shadow.nix
index e5755e8e0878..c51961e1fc68 100644
--- a/nixos/tests/shadow.nix
+++ b/nixos/tests/shadow.nix
@@ -13,14 +13,17 @@ in import ./make-test-python.nix ({ pkgs, ... }: {
     users = {
       mutableUsers = true;
       users.emma = {
+        isNormalUser = true;
         password = password1;
         shell = pkgs.bash;
       };
       users.layla = {
+        isNormalUser = true;
         password = password2;
         shell = pkgs.shadow;
       };
       users.ash = {
+        isNormalUser = true;
         password = password4;
         shell = pkgs.bash;
       };
diff --git a/nixos/tests/spacecookie.nix b/nixos/tests/spacecookie.nix
index 5b5022a74278..a640657d8a6b 100644
--- a/nixos/tests/spacecookie.nix
+++ b/nixos/tests/spacecookie.nix
@@ -1,47 +1,52 @@
 let
-  gopherRoot  = "/tmp/gopher";
-  gopherHost  = "gopherd";
-  fileContent = "Hello Gopher!";
-  fileName    = "file.txt";
+  gopherRoot   = "/tmp/gopher";
+  gopherHost   = "gopherd";
+  gopherClient = "client";
+  fileContent  = "Hello Gopher!\n";
+  fileName     = "file.txt";
 in
   import ./make-test-python.nix ({...}: {
     name = "spacecookie";
     nodes = {
       ${gopherHost} = {
-        networking.firewall.allowedTCPPorts = [ 70 ];
         systemd.services.spacecookie = {
           preStart = ''
             mkdir -p ${gopherRoot}/directory
-            echo "${fileContent}" > ${gopherRoot}/${fileName}
+            printf "%s" "${fileContent}" > ${gopherRoot}/${fileName}
           '';
         };
 
         services.spacecookie = {
           enable = true;
-          root = gopherRoot;
-          hostname = gopherHost;
+          openFirewall = true;
+          settings = {
+            root = gopherRoot;
+            hostname = gopherHost;
+          };
         };
       };
 
-      client = {};
+      ${gopherClient} = {};
     };
 
     testScript = ''
       start_all()
-      ${gopherHost}.wait_for_open_port(70)
+
+      # with daemon type notify, the unit being started
+      # should also mean the port is open
       ${gopherHost}.wait_for_unit("spacecookie.service")
-      client.wait_for_unit("network.target")
+      ${gopherClient}.wait_for_unit("network.target")
 
-      fileResponse = client.succeed("curl -f -s gopher://${gopherHost}//${fileName}")
+      fileResponse = ${gopherClient}.succeed("curl -f -s gopher://${gopherHost}/0/${fileName}")
 
       # the file response should return our created file exactly
-      if not (fileResponse == "${fileContent}\n"):
+      if not (fileResponse == "${builtins.replaceStrings [ "\n" ] [ "\\n" ] fileContent}"):
           raise Exception("Unexpected file response")
 
       # sanity check on the directory listing: we serve a directory and a file
       # via gopher, so the directory listing should have exactly two entries,
       # one with gopher file type 0 (file) and one with file type 1 (directory).
-      dirResponse = client.succeed("curl -f -s gopher://${gopherHost}")
+      dirResponse = ${gopherClient}.succeed("curl -f -s gopher://${gopherHost}")
       dirEntries = [l[0] for l in dirResponse.split("\n") if len(l) > 0]
       dirEntries.sort()
 
diff --git a/nixos/tests/systemd-confinement.nix b/nixos/tests/systemd-confinement.nix
index ebf6d218fd68..d04e4a3f867c 100644
--- a/nixos/tests/systemd-confinement.nix
+++ b/nixos/tests/systemd-confinement.nix
@@ -150,6 +150,7 @@ import ./make-test-python.nix {
 
     config.users.groups.chroot-testgroup = {};
     config.users.users.chroot-testuser = {
+      isSystemUser = true;
       description = "Chroot Test User";
       group = "chroot-testgroup";
     };
diff --git a/nixos/tests/systemd-networkd-ipv6-prefix-delegation.nix b/nixos/tests/systemd-networkd-ipv6-prefix-delegation.nix
index 5831c8692f69..94f17605e001 100644
--- a/nixos/tests/systemd-networkd-ipv6-prefix-delegation.nix
+++ b/nixos/tests/systemd-networkd-ipv6-prefix-delegation.nix
@@ -43,7 +43,7 @@ import ./make-test-python.nix ({pkgs, ...}: {
       # Everyone on the "isp" machine will be able to add routes to the kernel.
       security.wrappers.add-dhcpd-lease = {
         source = pkgs.writeShellScript "add-dhcpd-lease" ''
-          exec ${pkgs.iproute}/bin/ip -6 route replace "$1" via "$2"
+          exec ${pkgs.iproute2}/bin/ip -6 route replace "$1" via "$2"
         '';
         capabilities = "cap_net_admin+ep";
       };
diff --git a/nixos/tests/unbound.nix b/nixos/tests/unbound.nix
index d4b8bb15ced6..ca9718ac633e 100644
--- a/nixos/tests/unbound.nix
+++ b/nixos/tests/unbound.nix
@@ -132,12 +132,15 @@ import ./make-test-python.nix ({ pkgs, lib, ... }:
 
         users.users = {
           # user that is permitted to access the unix socket
-          someuser.extraGroups = [
-            config.users.users.unbound.group
-          ];
+          someuser = {
+            isSystemUser = true;
+            extraGroups = [
+              config.users.users.unbound.group
+            ];
+          };
 
           # user that is not permitted to access the unix socket
-          unauthorizeduser = {};
+          unauthorizeduser = { isSystemUser = true; };
         };
 
         environment.etc = {
diff --git a/nixos/tests/wmderland.nix b/nixos/tests/wmderland.nix
new file mode 100644
index 000000000000..d121ed98b7ac
--- /dev/null
+++ b/nixos/tests/wmderland.nix
@@ -0,0 +1,54 @@
+import ./make-test-python.nix ({ pkgs, ...} : {
+  name = "wmderland";
+  meta = with pkgs.stdenv.lib.maintainers; {
+    maintainers = [ takagiy ];
+  };
+
+  machine = { lib, ... }: {
+    imports = [ ./common/x11.nix ./common/user-account.nix ];
+    test-support.displayManager.auto.user = "alice";
+    services.xserver.displayManager.defaultSession = lib.mkForce "none+wmderland";
+    services.xserver.windowManager.wmderland.enable = true;
+
+    systemd.services.setupWmderlandConfig = {
+      wantedBy = [ "multi-user.target" ];
+      before = [ "multi-user.target" ];
+      environment = {
+        HOME = "/home/alice";
+      };
+      unitConfig = {
+        type = "oneshot";
+        RemainAfterExit = true;
+        user = "alice";
+      };
+      script = let
+        config = pkgs.writeText "config" ''
+             set $Mod = Mod1
+             bindsym $Mod+Return exec ${pkgs.xterm}/bin/xterm -cm -pc
+        '';
+      in ''
+        mkdir -p $HOME/.config/wmderland
+        cp ${config} $HOME/.config/wmderland/config
+      '';
+    };
+  };
+
+  testScript = { ... }: ''
+    with subtest("ensure x starts"):
+        machine.wait_for_x()
+        machine.wait_for_file("/home/alice/.Xauthority")
+        machine.succeed("xauth merge ~alice/.Xauthority")
+
+    with subtest("ensure we can open a new terminal"):
+        machine.send_key("alt-ret")
+        machine.wait_until_succeeds("pgrep xterm")
+        machine.wait_for_window(r"alice.*?machine")
+        machine.screenshot("terminal")
+
+    with subtest("ensure we can communicate through ipc with wmderlandc"):
+        # Kills the previously open xterm
+        machine.succeed("pgrep xterm")
+        machine.execute("DISPLAY=:0 wmderlandc kill")
+        machine.fail("pgrep xterm")
+  '';
+})