about summary refs log tree commit diff
path: root/nixpkgs/nixos/tests
diff options
context:
space:
mode:
Diffstat (limited to 'nixpkgs/nixos/tests')
-rw-r--r--nixpkgs/nixos/tests/all-tests.nix2
-rw-r--r--nixpkgs/nixos/tests/docker-tools.nix54
-rw-r--r--nixpkgs/nixos/tests/go-camo.nix30
-rw-r--r--nixpkgs/nixos/tests/incus/default.nix1
-rw-r--r--nixpkgs/nixos/tests/incus/ui.nix63
-rw-r--r--nixpkgs/nixos/tests/keepalived.nix2
-rw-r--r--nixpkgs/nixos/tests/knot.nix11
-rw-r--r--nixpkgs/nixos/tests/lemmy.nix3
-rw-r--r--nixpkgs/nixos/tests/opensnitch.nix2
-rw-r--r--nixpkgs/nixos/tests/prometheus-exporters.nix3
-rw-r--r--nixpkgs/nixos/tests/qownnotes.nix16
-rw-r--r--nixpkgs/nixos/tests/slurm.nix2
-rw-r--r--nixpkgs/nixos/tests/sourcehut.nix252
-rw-r--r--nixpkgs/nixos/tests/sourcehut/builds.nix54
-rw-r--r--nixpkgs/nixos/tests/sourcehut/default.nix6
-rw-r--r--nixpkgs/nixos/tests/sourcehut/git.nix96
-rw-r--r--nixpkgs/nixos/tests/sourcehut/nodes/common.nix107
-rw-r--r--nixpkgs/nixos/tests/stalwart-mail.nix11
-rw-r--r--nixpkgs/nixos/tests/web-apps/mastodon/default.nix2
-rw-r--r--nixpkgs/nixos/tests/web-apps/mastodon/remote-databases.nix (renamed from nixpkgs/nixos/tests/web-apps/mastodon/remote-postgresql.nix)38
-rw-r--r--nixpkgs/nixos/tests/web-apps/mastodon/script.nix1
-rw-r--r--nixpkgs/nixos/tests/web-apps/mastodon/standard.nix7
-rw-r--r--nixpkgs/nixos/tests/web-servers/ttyd.nix20
23 files changed, 498 insertions, 285 deletions
diff --git a/nixpkgs/nixos/tests/all-tests.nix b/nixpkgs/nixos/tests/all-tests.nix
index 31af6ec64214..8193c3dfe840 100644
--- a/nixpkgs/nixos/tests/all-tests.nix
+++ b/nixpkgs/nixos/tests/all-tests.nix
@@ -799,7 +799,7 @@ in {
   solanum = handleTest ./solanum.nix {};
   sonarr = handleTest ./sonarr.nix {};
   sonic-server = handleTest ./sonic-server.nix {};
-  sourcehut = handleTest ./sourcehut.nix {};
+  sourcehut = handleTest ./sourcehut {};
   spacecookie = handleTest ./spacecookie.nix {};
   spark = handleTestOn [ "x86_64-linux" "aarch64-linux" ] ./spark {};
   sqlite3-to-mysql = handleTest ./sqlite3-to-mysql.nix {};
diff --git a/nixpkgs/nixos/tests/docker-tools.nix b/nixpkgs/nixos/tests/docker-tools.nix
index 90af817e75ed..f252eb9ff61e 100644
--- a/nixpkgs/nixos/tests/docker-tools.nix
+++ b/nixpkgs/nixos/tests/docker-tools.nix
@@ -46,6 +46,18 @@ let
         echo 'runAsRoot has run.'
       '';
     };
+
+  chownTestImage =
+    pkgs.dockerTools.streamLayeredImage {
+      name = "chown-test";
+      tag = "latest";
+      enableFakechroot = true;
+      fakeRootCommands = ''
+        touch /testfile
+        chown 12345:12345 /testfile
+      '';
+      config.Cmd = [ "${pkgs.coreutils}/bin/stat" "-c" "%u:%g" "/testfile" ];
+    };
 in {
   name = "docker-tools";
   meta = with pkgs.lib.maintainers; {
@@ -71,14 +83,29 @@ in {
             docker.succeed("${examples.helloOnRoot} | docker load")
             docker.succeed("docker run --rm hello | grep -i hello")
             docker.succeed("docker image rm hello:latest")
+
         with subtest("includeStorePath = false; breaks example"):
             docker.succeed("${examples.helloOnRootNoStore} | docker load")
             docker.fail("docker run --rm hello | grep -i hello")
             docker.succeed("docker image rm hello:latest")
+        with subtest("includeStorePath = false; breaks example (fakechroot)"):
+            docker.succeed("${examples.helloOnRootNoStoreFakechroot} | docker load")
+            docker.fail("docker run --rm hello | grep -i hello")
+            docker.succeed("docker image rm hello:latest")
+
+        with subtest("Ensure ZERO paths are added to the store"):
+            docker.fail("${examples.helloOnRootNoStore} | ${pkgs.crane}/bin/crane export - - | tar t | grep 'nix/store/'")
+        with subtest("Ensure ZERO paths are added to the store (fakechroot)"):
+            docker.fail("${examples.helloOnRootNoStoreFakechroot} | ${pkgs.crane}/bin/crane export - - | tar t | grep 'nix/store/'")
+
         with subtest("includeStorePath = false; works with mounted store"):
             docker.succeed("${examples.helloOnRootNoStore} | docker load")
             docker.succeed("docker run --rm --volume ${builtins.storeDir}:${builtins.storeDir}:ro hello | grep -i hello")
             docker.succeed("docker image rm hello:latest")
+        with subtest("includeStorePath = false; works with mounted store (fakechroot)"):
+            docker.succeed("${examples.helloOnRootNoStoreFakechroot} | docker load")
+            docker.succeed("docker run --rm --volume ${builtins.storeDir}:${builtins.storeDir}:ro hello | grep -i hello")
+            docker.succeed("docker image rm hello:latest")
 
     with subtest("Ensure Docker images use a stable date by default"):
         docker.succeed(
@@ -128,6 +155,15 @@ in {
         docker.succeed("docker images --format '{{.Tag}}' | grep -F '${examples.nixLayered.imageTag}'")
         docker.succeed("docker rmi ${examples.nixLayered.imageName}")
 
+    with subtest("Check that images with alternative compression schemas load"):
+        docker.succeed(
+            "docker load --input='${examples.bashZstdCompressed}'",
+            "docker rmi ${examples.bashZstdCompressed.imageName}",
+        )
+        docker.succeed(
+            "docker load --input='${examples.bashUncompressed}'",
+            "docker rmi ${examples.bashUncompressed.imageName}",
+        )
 
     with subtest(
         "Check if the nix store is correctly initialized by listing "
@@ -449,6 +485,18 @@ in {
             "docker run --rm ${examples.layeredImageWithFakeRootCommands.imageName} /hello/bin/layeredImageWithFakeRootCommands-hello"
         )
 
+    with subtest("mergeImage correctly deals with varying compression schemas in inputs"):
+        docker.succeed("docker load --input='${examples.mergeVaryingCompressor}'")
+
+        for sub_image, tag in [
+            ("${examples.redis.imageName}", "${examples.redis.imageTag}"),
+            ("${examples.bashUncompressed.imageName}", "${examples.bashUncompressed.imageTag}"),
+            ("${examples.bashZstdCompressed.imageName}", "${examples.bashZstdCompressed.imageTag}"),
+        ]:
+            docker.succeed(f"docker images --format '{{{{.Repository}}}}-{{{{.Tag}}}}' | grep -F '{sub_image}-{tag}'")
+            docker.succeed(f"docker rmi {sub_image}")
+
+
     with subtest("exportImage produces a valid tarball"):
         docker.succeed(
             "tar -tf ${examples.exportBash} | grep '\./bin/bash' > /dev/null"
@@ -550,5 +598,11 @@ in {
             "${examples.nix-shell-build-derivation} | docker load",
             "docker run --rm -it nix-shell-build-derivation"
         )
+
+    with subtest("streamLayeredImage: chown is persistent in fakeRootCommands"):
+        docker.succeed(
+            "${chownTestImage} | docker load",
+            "docker run --rm ${chownTestImage.imageName} | diff /dev/stdin <(echo 12345:12345)"
+        )
   '';
 })
diff --git a/nixpkgs/nixos/tests/go-camo.nix b/nixpkgs/nixos/tests/go-camo.nix
new file mode 100644
index 000000000000..513964c31c43
--- /dev/null
+++ b/nixpkgs/nixos/tests/go-camo.nix
@@ -0,0 +1,30 @@
+{ system ? builtins.currentSystem, config ? { }
+, pkgs ? import ../.. { inherit system config; } }:
+
+with import ../lib/testing-python.nix { inherit system pkgs; };
+
+{
+  gocamo_file_key = let
+      key_val = "12345678";
+    in
+    makeTest {
+    name = "go-camo-file-key";
+    meta = {
+      maintainers = [ pkgs.lib.maintainers.viraptor ];
+    };
+
+    nodes.machine = { config, pkgs, ... }: {
+      services.go-camo = {
+        enable = true;
+        keyFile = pkgs.writeText "foo" key_val;
+      };
+    };
+
+    # go-camo responds to http requests
+    testScript = ''
+      machine.wait_for_unit("go-camo.service")
+      machine.wait_for_open_port(8080)
+      machine.succeed("curl http://localhost:8080")
+    '';
+  };
+}
diff --git a/nixpkgs/nixos/tests/incus/default.nix b/nixpkgs/nixos/tests/incus/default.nix
index 26e8a4ac4c77..c8e53774599b 100644
--- a/nixpkgs/nixos/tests/incus/default.nix
+++ b/nixpkgs/nixos/tests/incus/default.nix
@@ -9,5 +9,6 @@
   lxd-to-incus = import ./lxd-to-incus.nix { inherit system pkgs; };
   preseed = import ./preseed.nix { inherit system pkgs; };
   socket-activated = import ./socket-activated.nix { inherit system pkgs; };
+  ui = import ./ui.nix {inherit system pkgs;};
   virtual-machine = handleTestOn [ "x86_64-linux" ] ./virtual-machine.nix { inherit system pkgs; };
 }
diff --git a/nixpkgs/nixos/tests/incus/ui.nix b/nixpkgs/nixos/tests/incus/ui.nix
new file mode 100644
index 000000000000..24ce1217d8df
--- /dev/null
+++ b/nixpkgs/nixos/tests/incus/ui.nix
@@ -0,0 +1,63 @@
+import ../make-test-python.nix ({ pkgs, lib, ... }: {
+  name = "incus-ui";
+
+  meta = {
+    maintainers = lib.teams.lxc.members;
+  };
+
+  nodes.machine = { lib, ... }: {
+    virtualisation = {
+      incus.enable = true;
+      incus.ui.enable = true;
+    };
+
+    environment.systemPackages =
+      let
+        seleniumScript = pkgs.writers.writePython3Bin "selenium-script"
+          {
+            libraries = with pkgs.python3Packages; [ selenium ];
+          } ''
+          from selenium import webdriver
+          from selenium.webdriver.common.by import By
+          from selenium.webdriver.firefox.options import Options
+          from selenium.webdriver.support.ui import WebDriverWait
+
+          options = Options()
+          options.add_argument("--headless")
+          service = webdriver.FirefoxService(executable_path="${lib.getExe pkgs.geckodriver}")  # noqa: E501
+
+          driver = webdriver.Firefox(options=options, service=service)
+          driver.implicitly_wait(10)
+          driver.get("https://localhost:8443/ui")
+
+          wait = WebDriverWait(driver, 60)
+
+          assert len(driver.find_elements(By.CLASS_NAME, "l-application")) > 0
+          assert len(driver.find_elements(By.CLASS_NAME, "l-navigation__drawer")) > 0
+
+          driver.close()
+        '';
+      in
+      with pkgs; [ curl firefox-unwrapped geckodriver seleniumScript ];
+  };
+
+
+  testScript = ''
+    machine.wait_for_unit("sockets.target")
+    machine.wait_for_unit("incus.service")
+    machine.wait_for_file("/var/lib/incus/unix.socket")
+
+    # Configure incus listen address
+    machine.succeed("incus config set core.https_address :8443")
+    machine.succeed("systemctl restart incus")
+
+    # Check that the INCUS_UI environment variable is populated in the systemd unit
+    machine.succeed("cat /etc/systemd/system/incus.service | grep 'INCUS_UI'")
+
+    # Ensure the endpoint returns an HTML page with 'Incus UI' in the title
+    machine.succeed("curl -kLs https://localhost:8443/ui | grep '<title>Incus UI</title>'")
+
+    # Ensure the application is actually rendered by the Javascript
+    machine.succeed("PYTHONUNBUFFERED=1 selenium-script")
+  '';
+})
diff --git a/nixpkgs/nixos/tests/keepalived.nix b/nixpkgs/nixos/tests/keepalived.nix
index ce291514591f..16564511d85d 100644
--- a/nixpkgs/nixos/tests/keepalived.nix
+++ b/nixpkgs/nixos/tests/keepalived.nix
@@ -1,6 +1,6 @@
 import ./make-test-python.nix ({ pkgs, lib, ... }: {
   name = "keepalived";
-  maintainers = [ lib.maintainers.raitobezarius ];
+  meta.maintainers = [ lib.maintainers.raitobezarius ];
 
   nodes = {
     node1 = { pkgs, ... }: {
diff --git a/nixpkgs/nixos/tests/knot.nix b/nixpkgs/nixos/tests/knot.nix
index 44efd93b6fa9..c5af8bf1edcc 100644
--- a/nixpkgs/nixos/tests/knot.nix
+++ b/nixpkgs/nixos/tests/knot.nix
@@ -114,11 +114,14 @@ in {
       services.knot.extraArgs = [ "-v" ];
       services.knot.settings = {
         server = {
+          automatic-acl = true;
+        };
+
+        xdp = {
           listen = [
-            "0.0.0.0@53"
-            "::@53"
+            "eth1"
           ];
-          automatic-acl = true;
+          tcp = true;
         };
 
         remote.primary = {
@@ -140,7 +143,7 @@ in {
           "sub.example.com".file = "sub.example.com.zone";
         };
 
-        log.syslog.any = "info";
+        log.syslog.any = "debug";
       };
     };
     client = { lib, nodes, ... }: {
diff --git a/nixpkgs/nixos/tests/lemmy.nix b/nixpkgs/nixos/tests/lemmy.nix
index e8d747f89a9e..d93df3646837 100644
--- a/nixpkgs/nixos/tests/lemmy.nix
+++ b/nixpkgs/nixos/tests/lemmy.nix
@@ -51,7 +51,8 @@ in
 
     with subtest("the backend starts and responds"):
         server.wait_for_open_port(${toString backendPort})
-        server.succeed("curl --fail localhost:${toString backendPort}/api/v3/site")
+        # wait until succeeds, it just needs few seconds for migrations, but lets give it 10s max
+        server.wait_until_succeeds("curl --fail localhost:${toString backendPort}/api/v3/site", 10)
 
     with subtest("the UI starts and responds"):
         server.wait_for_unit("lemmy-ui.service")
diff --git a/nixpkgs/nixos/tests/opensnitch.nix b/nixpkgs/nixos/tests/opensnitch.nix
index d84e4e0a935b..a1af07647f71 100644
--- a/nixpkgs/nixos/tests/opensnitch.nix
+++ b/nixpkgs/nixos/tests/opensnitch.nix
@@ -31,7 +31,7 @@ import ./make-test-python.nix ({ pkgs, ... }: {
           enable = true;
           settings.DefaultAction = "deny";
           rules = {
-            opensnitch = {
+            curl = {
               name = "curl";
               enabled = true;
               action = "allow";
diff --git a/nixpkgs/nixos/tests/prometheus-exporters.nix b/nixpkgs/nixos/tests/prometheus-exporters.nix
index 7e74f27174ec..632656ad5795 100644
--- a/nixpkgs/nixos/tests/prometheus-exporters.nix
+++ b/nixpkgs/nixos/tests/prometheus-exporters.nix
@@ -218,6 +218,9 @@ let
         services.dnsmasq.enable = true;
       };
       exporterTest = ''
+        wait_for_unit("dnsmasq.service")
+        wait_for_open_port(53)
+        wait_for_file("/var/lib/dnsmasq/dnsmasq.leases")
         wait_for_unit("prometheus-dnsmasq-exporter.service")
         wait_for_open_port(9153)
         succeed("curl -sSf http://localhost:9153/metrics | grep 'dnsmasq_leases 0'")
diff --git a/nixpkgs/nixos/tests/qownnotes.nix b/nixpkgs/nixos/tests/qownnotes.nix
index 93801cb98702..3390ba6d9025 100644
--- a/nixpkgs/nixos/tests/qownnotes.nix
+++ b/nixpkgs/nixos/tests/qownnotes.nix
@@ -21,6 +21,7 @@ import ./make-test-python.nix ({ lib, pkgs, ...} :
 
   enableOCR = true;
 
+  # https://nixos.org/manual/nixos/stable/#ssec-machine-objects
   testScript = { nodes, ... }: let
     aliceDo = cmd: ''machine.succeed("su - alice -c '${cmd}' >&2 &");'';
     in ''
@@ -52,8 +53,13 @@ import ./make-test-python.nix ({ lib, pkgs, ...} :
         machine.wait_for_text("App metric")
         machine.send_key("ret")
 
+        # Doesn't work for non-root
+        #machine.wait_for_window("QOwnNotes - ${pkgs.qownnotes.version}")
+
+        # OCR doesn't seem to be able any more to handle the main window
+        #machine.wait_for_text("QOwnNotes - ${pkgs.qownnotes.version}")
+
         # The main window should now show up
-        machine.wait_for_text("QOwnNotes - ${pkgs.qownnotes.version}")
         machine.wait_for_open_port(22222)
         machine.wait_for_console_text("QOwnNotes server listening on port 22222")
 
@@ -63,7 +69,13 @@ import ./make-test-python.nix ({ lib, pkgs, ...} :
         machine.send_key("ctrl-n")
         machine.sleep(1)
         machine.send_chars("This is a NixOS test!\n")
-        machine.wait_for_text("This is a NixOS test!")
+        machine.wait_until_succeeds("find /home/alice/Notes -type f | grep -qi 'Note 2'")
+
+        # OCR doesn't seem to be able any more to handle the main window
+        #machine.wait_for_text("This is a NixOS test!")
+
+        # Doesn't work for non-root
+        #machine.wait_for_window("- QOwnNotes - ${pkgs.qownnotes.version}")
 
         machine.screenshot("QOwnNotes-NewNote")
   '';
diff --git a/nixpkgs/nixos/tests/slurm.nix b/nixpkgs/nixos/tests/slurm.nix
index a6b02e970b0c..ad516b6e8d2b 100644
--- a/nixpkgs/nixos/tests/slurm.nix
+++ b/nixpkgs/nixos/tests/slurm.nix
@@ -45,7 +45,7 @@ let
       '';
     in pkgs.runCommand "mpitest" {} ''
       mkdir -p $out/bin
-      ${pkgs.openmpi}/bin/mpicc ${mpitestC} -o $out/bin/mpitest
+      ${lib.getDev pkgs.mpi}/bin/mpicc ${mpitestC} -o $out/bin/mpitest
     '';
 in {
   name = "slurm";
diff --git a/nixpkgs/nixos/tests/sourcehut.nix b/nixpkgs/nixos/tests/sourcehut.nix
deleted file mode 100644
index 0b258acc2af1..000000000000
--- a/nixpkgs/nixos/tests/sourcehut.nix
+++ /dev/null
@@ -1,252 +0,0 @@
-import ./make-test-python.nix ({ pkgs, lib, ... }:
-let
-  domain = "sourcehut.localdomain";
-
-  # Note that wildcard certificates just under the TLD (eg. *.com)
-  # would be rejected by clients like curl.
-  tls-cert = pkgs.runCommand "selfSignedCerts" { buildInputs = [ pkgs.openssl ]; } ''
-    openssl req -x509 -newkey rsa:4096 -keyout key.pem -out cert.pem -nodes -days 36500 \
-      -subj '/CN=${domain}' -extensions v3_req \
-      -addext 'subjectAltName = DNS:*.${domain}'
-    install -D -t $out key.pem cert.pem
-  '';
-
-  images = {
-    nixos.unstable.x86_64 =
-      let
-        systemConfig = { pkgs, ... }: {
-          # passwordless ssh server
-          services.openssh = {
-            enable = true;
-            settings = {
-              PermitRootLogin = "yes";
-              PermitEmptyPasswords = true;
-            };
-          };
-
-          users = {
-            mutableUsers = false;
-            # build user
-            extraUsers."build" = {
-              isNormalUser = true;
-              uid = 1000;
-              extraGroups = [ "wheel" ];
-              password = "";
-            };
-            users.root.password = "";
-          };
-
-          security.sudo.wheelNeedsPassword = false;
-          nix.settings.trusted-users = [ "root" "build" ];
-          documentation.nixos.enable = false;
-
-          # builds.sr.ht-image-specific network settings
-          networking = {
-            hostName = "build";
-            dhcpcd.enable = false;
-            defaultGateway.address = "10.0.2.2";
-            usePredictableInterfaceNames = false;
-            interfaces."eth0".ipv4.addresses = [{
-              address = "10.0.2.15";
-              prefixLength = 25;
-            }];
-            enableIPv6 = false;
-            nameservers = [
-              # OpenNIC anycast
-              "185.121.177.177"
-              "169.239.202.202"
-              # Google
-              "8.8.8.8"
-            ];
-            firewall.allowedTCPPorts = [ 22 ];
-          };
-
-          environment.systemPackages = [
-            pkgs.gitMinimal
-            #pkgs.mercurial
-            pkgs.curl
-            pkgs.gnupg
-          ];
-        };
-        qemuConfig = { pkgs, ... }: {
-          imports = [ systemConfig ];
-          fileSystems."/".device = "/dev/disk/by-label/nixos";
-          boot.initrd.availableKernelModules = [
-            "ahci"
-            "ehci_pci"
-            "sd_mod"
-            "usb_storage"
-            "usbhid"
-            "virtio_balloon"
-            "virtio_blk"
-            "virtio_pci"
-            "virtio_ring"
-            "xhci_pci"
-          ];
-          boot.loader = {
-            grub = {
-              version = 2;
-              device = "/dev/vda";
-            };
-            timeout = 0;
-          };
-        };
-        config = (import (pkgs.path + "/nixos/lib/eval-config.nix") {
-          inherit pkgs; modules = [ qemuConfig ];
-          system = "x86_64-linux";
-        }).config;
-      in
-      import (pkgs.path + "/nixos/lib/make-disk-image.nix") {
-        inherit pkgs lib config;
-        diskSize = 16000;
-        format = "qcow2-compressed";
-        contents = [
-          { source = pkgs.writeText "gitconfig" ''
-              [user]
-                name = builds.sr.ht
-                email = build@sr.ht
-            '';
-            target = "/home/build/.gitconfig";
-            user = "build";
-            group = "users";
-            mode = "644";
-          }
-        ];
-      };
-  };
-
-in
-{
-  name = "sourcehut";
-
-  meta.maintainers = [ pkgs.lib.maintainers.tomberek ];
-
-  nodes.machine = { config, pkgs, nodes, ... }: {
-    # buildsrht needs space
-    virtualisation.diskSize = 4 * 1024;
-    virtualisation.memorySize = 2 * 1024;
-    networking.domain = domain;
-    networking.enableIPv6 = false;
-    networking.extraHosts = ''
-      ${config.networking.primaryIPAddress} builds.${domain}
-      ${config.networking.primaryIPAddress} git.${domain}
-      ${config.networking.primaryIPAddress} meta.${domain}
-    '';
-
-    services.sourcehut = {
-      enable = true;
-      nginx.enable = true;
-      nginx.virtualHost = {
-        forceSSL = true;
-        sslCertificate = "${tls-cert}/cert.pem";
-        sslCertificateKey = "${tls-cert}/key.pem";
-      };
-      postgresql.enable = true;
-      redis.enable = true;
-
-      meta.enable = true;
-      builds = {
-        enable = true;
-        # FIXME: see why it does not seem to activate fully.
-        #enableWorker = true;
-        inherit images;
-      };
-      git.enable = true;
-
-      settings."sr.ht" = {
-        global-domain = config.networking.domain;
-        service-key = pkgs.writeText "service-key" "8b327279b77e32a3620e2fc9aabce491cc46e7d821fd6713b2a2e650ce114d01";
-        network-key = pkgs.writeText "network-key" "cEEmc30BRBGkgQZcHFksiG7hjc6_dK1XR2Oo5Jb9_nQ=";
-      };
-      settings."builds.sr.ht" = {
-        oauth-client-secret = pkgs.writeText "buildsrht-oauth-client-secret" "2260e9c4d9b8dcedcef642860e0504bc";
-        oauth-client-id = "299db9f9c2013170";
-      };
-      settings."git.sr.ht" = {
-        oauth-client-secret = pkgs.writeText "gitsrht-oauth-client-secret" "3597288dc2c716e567db5384f493b09d";
-        oauth-client-id = "d07cb713d920702e";
-      };
-      settings.webhooks.private-key = pkgs.writeText "webhook-key" "Ra3IjxgFiwG9jxgp4WALQIZw/BMYt30xWiOsqD0J7EA=";
-      settings.mail = {
-        smtp-from = "root+hut@${domain}";
-        # WARNING: take care to keep pgp-privkey outside the Nix store in production,
-        # or use LoadCredentialEncrypted=
-        pgp-privkey = toString (pkgs.writeText "sourcehut.pgp-privkey" ''
-          -----BEGIN PGP PRIVATE KEY BLOCK-----
-
-          lFgEYqDRORYJKwYBBAHaRw8BAQdAehGoy36FUx2OesYm07be2rtLyvR5Pb/ltstd
-          Gk7hYQoAAP9X4oPmxxrHN8LewBpWITdBomNqlHoiP7mI0nz/BOPJHxEktDZuaXhv
-          cy90ZXN0cy9zb3VyY2VodXQgPHJvb3QraHV0QHNvdXJjZWh1dC5sb2NhbGRvbWFp
-          bj6IlwQTFgoAPxYhBPqjgjnL8RHN4JnADNicgXaYm0jJBQJioNE5AhsDBQkDwmcA
-          BgsJCAcDCgUVCgkICwUWAwIBAAIeBQIXgAAKCRDYnIF2mJtIySVCAP9e2nHsVHSi
-          2B1YGZpVG7Xf36vxljmMkbroQy+0gBPwRwEAq+jaiQqlbGhQ7R/HMFcAxBIVsq8h
-          Aw1rngsUd0o3dAicXQRioNE5EgorBgEEAZdVAQUBAQdAXZV2Sd5ZNBVTBbTGavMv
-          D6ORrUh8z7TI/3CsxCE7+yADAQgHAAD/c1RU9xH+V/uI1fE7HIn/zL0LUPpsuce2
-          cH++g4u3kBgTOYh+BBgWCgAmFiEE+qOCOcvxEc3gmcAM2JyBdpibSMkFAmKg0TkC
-          GwwFCQPCZwAACgkQ2JyBdpibSMlKagD/cTre6p1m8QuJ7kwmCFRSz5tBzIuYMMgN
-          xtT7dmS91csA/35fWsOykSiFRojQ7ccCSUTHL7ApF2EbL968tP/D2hIG
-          =Hjoc
-          -----END PGP PRIVATE KEY BLOCK-----
-        '');
-        pgp-pubkey = pkgs.writeText "sourcehut.pgp-pubkey" ''
-          -----BEGIN PGP PUBLIC KEY BLOCK-----
-
-          mDMEYqDRORYJKwYBBAHaRw8BAQdAehGoy36FUx2OesYm07be2rtLyvR5Pb/ltstd
-          Gk7hYQq0Nm5peG9zL3Rlc3RzL3NvdXJjZWh1dCA8cm9vdCtodXRAc291cmNlaHV0
-          LmxvY2FsZG9tYWluPoiXBBMWCgA/FiEE+qOCOcvxEc3gmcAM2JyBdpibSMkFAmKg
-          0TkCGwMFCQPCZwAGCwkIBwMKBRUKCQgLBRYDAgEAAh4FAheAAAoJENicgXaYm0jJ
-          JUIA/17acexUdKLYHVgZmlUbtd/fq/GWOYyRuuhDL7SAE/BHAQCr6NqJCqVsaFDt
-          H8cwVwDEEhWyryEDDWueCxR3Sjd0CLg4BGKg0TkSCisGAQQBl1UBBQEBB0BdlXZJ
-          3lk0FVMFtMZq8y8Po5GtSHzPtMj/cKzEITv7IAMBCAeIfgQYFgoAJhYhBPqjgjnL
-          8RHN4JnADNicgXaYm0jJBQJioNE5AhsMBQkDwmcAAAoJENicgXaYm0jJSmoA/3E6
-          3uqdZvELie5MJghUUs+bQcyLmDDIDcbU+3ZkvdXLAP9+X1rDspEohUaI0O3HAklE
-          xy+wKRdhGy/evLT/w9oSBg==
-          =pJD7
-          -----END PGP PUBLIC KEY BLOCK-----
-        '';
-        pgp-key-id = "0xFAA38239CBF111CDE099C00CD89C8176989B48C9";
-      };
-    };
-
-    networking.firewall.allowedTCPPorts = [ 443 ];
-    security.pki.certificateFiles = [ "${tls-cert}/cert.pem" ];
-    services.nginx = {
-      enable = true;
-      recommendedGzipSettings = true;
-      recommendedOptimisation = true;
-      recommendedTlsSettings = true;
-      recommendedProxySettings = true;
-    };
-
-    services.postgresql = {
-      enable = true;
-      enableTCPIP = false;
-      settings.unix_socket_permissions = "0770";
-    };
-  };
-
-  testScript = ''
-    start_all()
-    machine.wait_for_unit("multi-user.target")
-
-    # Testing metasrht
-    machine.wait_for_unit("metasrht-api.service")
-    machine.wait_for_unit("metasrht.service")
-    machine.wait_for_unit("metasrht-webhooks.service")
-    machine.wait_for_open_port(5000)
-    machine.succeed("curl -sL http://localhost:5000 | grep meta.${domain}")
-    machine.succeed("curl -sL http://meta.${domain} | grep meta.${domain}")
-
-    # Testing buildsrht
-    machine.wait_for_unit("buildsrht.service")
-    machine.wait_for_open_port(5002)
-    machine.succeed("curl -sL http://localhost:5002 | grep builds.${domain}")
-    #machine.wait_for_unit("buildsrht-worker.service")
-
-    # Testing gitsrht
-    machine.wait_for_unit("gitsrht-api.service")
-    machine.wait_for_unit("gitsrht.service")
-    machine.wait_for_unit("gitsrht-webhooks.service")
-    machine.succeed("curl -sL http://git.${domain} | grep git.${domain}")
-  '';
-})
diff --git a/nixpkgs/nixos/tests/sourcehut/builds.nix b/nixpkgs/nixos/tests/sourcehut/builds.nix
new file mode 100644
index 000000000000..f1f928ecc3d0
--- /dev/null
+++ b/nixpkgs/nixos/tests/sourcehut/builds.nix
@@ -0,0 +1,54 @@
+import ../make-test-python.nix ({ pkgs, lib, ... }:
+let
+  domain = "sourcehut.localdomain";
+in
+{
+  name = "sourcehut";
+
+  meta.maintainers = with pkgs.lib.maintainers; [ tomberek nessdoor ];
+
+  nodes.machine = { config, pkgs, nodes, ... }: {
+    imports = [
+      ./nodes/common.nix
+    ];
+
+    networking.domain = domain;
+    networking.extraHosts = ''
+      ${config.networking.primaryIPAddress} builds.${domain}
+      ${config.networking.primaryIPAddress} meta.${domain}
+    '';
+
+    services.sourcehut = {
+      builds = {
+        enable = true;
+        # FIXME: see why it does not seem to activate fully.
+        #enableWorker = true;
+        images = { };
+      };
+
+      settings."builds.sr.ht" = {
+        oauth-client-secret = pkgs.writeText "buildsrht-oauth-client-secret" "2260e9c4d9b8dcedcef642860e0504bc";
+        oauth-client-id = "299db9f9c2013170";
+      };
+    };
+  };
+
+  testScript = ''
+    start_all()
+    machine.wait_for_unit("multi-user.target")
+
+    with subtest("Check whether meta comes up"):
+         machine.wait_for_unit("metasrht-api.service")
+         machine.wait_for_unit("metasrht.service")
+         machine.wait_for_unit("metasrht-webhooks.service")
+         machine.wait_for_open_port(5000)
+         machine.succeed("curl -sL http://localhost:5000 | grep meta.${domain}")
+         machine.succeed("curl -sL http://meta.${domain} | grep meta.${domain}")
+
+    with subtest("Check whether builds comes up"):
+         machine.wait_for_unit("buildsrht.service")
+         machine.wait_for_open_port(5002)
+         machine.succeed("curl -sL http://localhost:5002 | grep builds.${domain}")
+         #machine.wait_for_unit("buildsrht-worker.service")
+  '';
+})
diff --git a/nixpkgs/nixos/tests/sourcehut/default.nix b/nixpkgs/nixos/tests/sourcehut/default.nix
new file mode 100644
index 000000000000..04f1551d70d9
--- /dev/null
+++ b/nixpkgs/nixos/tests/sourcehut/default.nix
@@ -0,0 +1,6 @@
+{ system, pkgs, ... }:
+
+{
+  git = import ./git.nix { inherit system pkgs; };
+  builds = import ./builds.nix { inherit system pkgs; };
+}
diff --git a/nixpkgs/nixos/tests/sourcehut/git.nix b/nixpkgs/nixos/tests/sourcehut/git.nix
new file mode 100644
index 000000000000..ed184d5d5518
--- /dev/null
+++ b/nixpkgs/nixos/tests/sourcehut/git.nix
@@ -0,0 +1,96 @@
+import ../make-test-python.nix ({ pkgs, lib, ... }:
+let
+  domain = "sourcehut.localdomain";
+in
+{
+  name = "sourcehut";
+
+  meta.maintainers = with pkgs.lib.maintainers; [ tomberek nessdoor ];
+
+  nodes.machine = { config, pkgs, nodes, ... }: {
+    imports = [
+      ./nodes/common.nix
+    ];
+
+    networking.domain = domain;
+    networking.extraHosts = ''
+      ${config.networking.primaryIPAddress} git.${domain}
+      ${config.networking.primaryIPAddress} meta.${domain}
+    '';
+
+    services.sourcehut = {
+      git.enable = true;
+      settings."git.sr.ht" = {
+        oauth-client-secret = pkgs.writeText "gitsrht-oauth-client-secret" "3597288dc2c716e567db5384f493b09d";
+        oauth-client-id = "d07cb713d920702e";
+      };
+    };
+
+    environment.systemPackages = with pkgs; [
+      git
+    ];
+  };
+
+  testScript =
+    let
+      userName = "nixos-test";
+      userPass = "AutoNixosTestPwd";
+      hutConfig = pkgs.writeText "hut-config" ''
+        instance "${domain}" {
+          # Will be replaced at runtime with the generated token
+          access-token "OAUTH-TOKEN"
+        }
+      '';
+      sshConfig = pkgs.writeText "ssh-config" ''
+        Host git.${domain}
+             IdentityFile = ~/.ssh/id_rsa
+      '';
+    in
+    ''
+      start_all()
+      machine.wait_for_unit("multi-user.target")
+      machine.wait_for_unit("sshd.service")
+
+      with subtest("Check whether meta comes up"):
+           machine.wait_for_unit("metasrht-api.service")
+           machine.wait_for_unit("metasrht.service")
+           machine.wait_for_unit("metasrht-webhooks.service")
+           machine.wait_for_open_port(5000)
+           machine.succeed("curl -sL http://localhost:5000 | grep meta.${domain}")
+           machine.succeed("curl -sL http://meta.${domain} | grep meta.${domain}")
+
+      with subtest("Create a new user account and OAuth access key"):
+           machine.succeed("echo ${userPass} | metasrht-manageuser -ps -e ${userName}@${domain}\
+                            -t active_paying ${userName}");
+           (_, token) = machine.execute("srht-gen-oauth-tok -i ${domain} -q ${userName} ${userPass}")
+           token = token.strip().replace("/", r"\\/") # Escape slashes in token before passing it to sed
+           machine.execute("mkdir -p ~/.config/hut/")
+           machine.execute("sed s/OAUTH-TOKEN/" + token + "/ ${hutConfig} > ~/.config/hut/config")
+
+      with subtest("Check whether git comes up"):
+           machine.wait_for_unit("gitsrht-api.service")
+           machine.wait_for_unit("gitsrht.service")
+           machine.wait_for_unit("gitsrht-webhooks.service")
+           machine.succeed("curl -sL http://git.${domain} | grep git.${domain}")
+
+      with subtest("Add an SSH key for Git access"):
+           machine.execute("ssh-keygen -q -N \"\" -t rsa -f ~/.ssh/id_rsa")
+           machine.execute("cat ${sshConfig} > ~/.ssh/config")
+           machine.succeed("hut meta ssh-key create ~/.ssh/id_rsa.pub")
+
+      with subtest("Create a new repo and push contents to it"):
+           machine.execute("git init test")
+           machine.execute("echo \"Hello world!\" > test/hello.txt")
+           machine.execute("cd test && git add .")
+           machine.execute("cd test && git commit -m \"Initial commit\"")
+           machine.execute("cd test && git tag v0.1")
+           machine.succeed("cd test && git remote add origin gitsrht@git.${domain}:~${userName}/test")
+           machine.execute("( echo -n 'git.${domain} '; cat /etc/ssh/ssh_host_ed25519_key.pub ) > ~/.ssh/known_hosts")
+           machine.succeed("hut git create test")
+           machine.succeed("cd test && git push --tags --set-upstream origin master")
+
+      with subtest("Verify that the repo is downloadable and its contents match the original"):
+           machine.succeed("curl https://git.${domain}/~${userName}/test/archive/v0.1.tar.gz | tar -xz")
+           machine.succeed("diff test-v0.1/hello.txt test/hello.txt")
+    '';
+})
diff --git a/nixpkgs/nixos/tests/sourcehut/nodes/common.nix b/nixpkgs/nixos/tests/sourcehut/nodes/common.nix
new file mode 100644
index 000000000000..f0a81358f972
--- /dev/null
+++ b/nixpkgs/nixos/tests/sourcehut/nodes/common.nix
@@ -0,0 +1,107 @@
+{ config, pkgs, nodes, ... }:
+let
+  domain = config.networking.domain;
+
+  # Note that wildcard certificates just under the TLD (eg. *.com)
+  # would be rejected by clients like curl.
+  tls-cert = pkgs.runCommand "selfSignedCerts" { buildInputs = [ pkgs.openssl ]; } ''
+    openssl req -x509 -newkey rsa:4096 -keyout key.pem -out cert.pem -nodes -days 36500 \
+      -subj '/CN=${domain}' -extensions v3_req \
+      -addext 'subjectAltName = DNS:*.${domain}'
+    install -D -t $out key.pem cert.pem
+  '';
+in
+{
+  # buildsrht needs space
+  virtualisation.diskSize = 4 * 1024;
+  virtualisation.memorySize = 2 * 1024;
+  networking.enableIPv6 = false;
+
+  services.sourcehut = {
+    enable = true;
+    nginx.enable = true;
+    nginx.virtualHost = {
+      forceSSL = true;
+      sslCertificate = "${tls-cert}/cert.pem";
+      sslCertificateKey = "${tls-cert}/key.pem";
+    };
+    postgresql.enable = true;
+    redis.enable = true;
+
+    meta.enable = true;
+
+    settings."sr.ht" = {
+      environment = "production";
+      global-domain = config.networking.domain;
+      service-key = pkgs.writeText "service-key" "8b327279b77e32a3620e2fc9aabce491cc46e7d821fd6713b2a2e650ce114d01";
+      network-key = pkgs.writeText "network-key" "cEEmc30BRBGkgQZcHFksiG7hjc6_dK1XR2Oo5Jb9_nQ=";
+    };
+    settings.webhooks.private-key = pkgs.writeText "webhook-key" "Ra3IjxgFiwG9jxgp4WALQIZw/BMYt30xWiOsqD0J7EA=";
+    settings.mail = {
+      smtp-from = "root+hut@${domain}";
+      # WARNING: take care to keep pgp-privkey outside the Nix store in production,
+      # or use LoadCredentialEncrypted=
+      pgp-privkey = toString (pkgs.writeText "sourcehut.pgp-privkey" ''
+        -----BEGIN PGP PRIVATE KEY BLOCK-----
+
+        lFgEYqDRORYJKwYBBAHaRw8BAQdAehGoy36FUx2OesYm07be2rtLyvR5Pb/ltstd
+        Gk7hYQoAAP9X4oPmxxrHN8LewBpWITdBomNqlHoiP7mI0nz/BOPJHxEktDZuaXhv
+        cy90ZXN0cy9zb3VyY2VodXQgPHJvb3QraHV0QHNvdXJjZWh1dC5sb2NhbGRvbWFp
+        bj6IlwQTFgoAPxYhBPqjgjnL8RHN4JnADNicgXaYm0jJBQJioNE5AhsDBQkDwmcA
+        BgsJCAcDCgUVCgkICwUWAwIBAAIeBQIXgAAKCRDYnIF2mJtIySVCAP9e2nHsVHSi
+        2B1YGZpVG7Xf36vxljmMkbroQy+0gBPwRwEAq+jaiQqlbGhQ7R/HMFcAxBIVsq8h
+        Aw1rngsUd0o3dAicXQRioNE5EgorBgEEAZdVAQUBAQdAXZV2Sd5ZNBVTBbTGavMv
+        D6ORrUh8z7TI/3CsxCE7+yADAQgHAAD/c1RU9xH+V/uI1fE7HIn/zL0LUPpsuce2
+        cH++g4u3kBgTOYh+BBgWCgAmFiEE+qOCOcvxEc3gmcAM2JyBdpibSMkFAmKg0TkC
+        GwwFCQPCZwAACgkQ2JyBdpibSMlKagD/cTre6p1m8QuJ7kwmCFRSz5tBzIuYMMgN
+        xtT7dmS91csA/35fWsOykSiFRojQ7ccCSUTHL7ApF2EbL968tP/D2hIG
+        =Hjoc
+        -----END PGP PRIVATE KEY BLOCK-----
+      '');
+      pgp-pubkey = pkgs.writeText "sourcehut.pgp-pubkey" ''
+        -----BEGIN PGP PUBLIC KEY BLOCK-----
+
+        mDMEYqDRORYJKwYBBAHaRw8BAQdAehGoy36FUx2OesYm07be2rtLyvR5Pb/ltstd
+        Gk7hYQq0Nm5peG9zL3Rlc3RzL3NvdXJjZWh1dCA8cm9vdCtodXRAc291cmNlaHV0
+        LmxvY2FsZG9tYWluPoiXBBMWCgA/FiEE+qOCOcvxEc3gmcAM2JyBdpibSMkFAmKg
+        0TkCGwMFCQPCZwAGCwkIBwMKBRUKCQgLBRYDAgEAAh4FAheAAAoJENicgXaYm0jJ
+        JUIA/17acexUdKLYHVgZmlUbtd/fq/GWOYyRuuhDL7SAE/BHAQCr6NqJCqVsaFDt
+        H8cwVwDEEhWyryEDDWueCxR3Sjd0CLg4BGKg0TkSCisGAQQBl1UBBQEBB0BdlXZJ
+        3lk0FVMFtMZq8y8Po5GtSHzPtMj/cKzEITv7IAMBCAeIfgQYFgoAJhYhBPqjgjnL
+        8RHN4JnADNicgXaYm0jJBQJioNE5AhsMBQkDwmcAAAoJENicgXaYm0jJSmoA/3E6
+        3uqdZvELie5MJghUUs+bQcyLmDDIDcbU+3ZkvdXLAP9+X1rDspEohUaI0O3HAklE
+        xy+wKRdhGy/evLT/w9oSBg==
+        =pJD7
+        -----END PGP PUBLIC KEY BLOCK-----
+      '';
+      pgp-key-id = "0xFAA38239CBF111CDE099C00CD89C8176989B48C9";
+    };
+  };
+
+  networking.firewall.allowedTCPPorts = [ 80 443 ];
+  security.pki.certificateFiles = [ "${tls-cert}/cert.pem" ];
+  services.nginx = {
+    enable = true;
+    recommendedGzipSettings = true;
+    recommendedOptimisation = true;
+    recommendedTlsSettings = true;
+    recommendedProxySettings = true;
+  };
+
+  services.postgresql = {
+    enable = true;
+    enableTCPIP = false;
+    settings.unix_socket_permissions = "0770";
+  };
+
+  services.openssh = {
+    enable = true;
+    settings.PasswordAuthentication = false;
+    settings.PermitRootLogin = "no";
+  };
+
+  environment.systemPackages = with pkgs; [
+    hut # For interacting with the Sourcehut APIs via CLI
+    srht-gen-oauth-tok # To automatically generate user OAuth tokens
+  ];
+}
diff --git a/nixpkgs/nixos/tests/stalwart-mail.nix b/nixpkgs/nixos/tests/stalwart-mail.nix
index b5589966a160..634c0e2e3926 100644
--- a/nixpkgs/nixos/tests/stalwart-mail.nix
+++ b/nixpkgs/nixos/tests/stalwart-mail.nix
@@ -42,20 +42,22 @@ in import ./make-test-python.nix ({ lib, ... }: {
 
         session.auth.mechanisms = [ "PLAIN" ];
         session.auth.directory = "in-memory";
-        jmap.directory = "in-memory";  # shared with imap
+        storage.directory = "in-memory";  # shared with imap
 
         session.rcpt.directory = "in-memory";
         queue.outbound.next-hop = [ "local" ];
 
         directory."in-memory" = {
           type = "memory";
-          users = [
+          principals = [
             {
+              type = "individual";
               name = "alice";
               secret = "foobar";
               email = [ "alice@${domain}" ];
             }
             {
+              type = "individual";
               name = "bob";
               secret = "foobar";
               email = [ "bob@${domain}" ];
@@ -90,8 +92,9 @@ in import ./make-test-python.nix ({ lib, ... }: {
 
         with IMAP4('localhost') as imap:
             imap.starttls()
-            imap.login('bob', 'foobar')
-            imap.select('"All Mail"')
+            status, [caps] = imap.login('bob', 'foobar')
+            assert status == 'OK'
+            imap.select()
             status, [ref] = imap.search(None, 'ALL')
             assert status == 'OK'
             [msgId] = ref.split()
diff --git a/nixpkgs/nixos/tests/web-apps/mastodon/default.nix b/nixpkgs/nixos/tests/web-apps/mastodon/default.nix
index 411ebfcd731b..178590d13b63 100644
--- a/nixpkgs/nixos/tests/web-apps/mastodon/default.nix
+++ b/nixpkgs/nixos/tests/web-apps/mastodon/default.nix
@@ -5,5 +5,5 @@ let
 in
 {
   standard = handleTestOn supportedSystems ./standard.nix { inherit system; };
-  remote-postgresql = handleTestOn supportedSystems ./remote-postgresql.nix { inherit system; };
+  remote-databases = handleTestOn supportedSystems ./remote-databases.nix { inherit system; };
 }
diff --git a/nixpkgs/nixos/tests/web-apps/mastodon/remote-postgresql.nix b/nixpkgs/nixos/tests/web-apps/mastodon/remote-databases.nix
index 6548883db452..fa6430a99353 100644
--- a/nixpkgs/nixos/tests/web-apps/mastodon/remote-postgresql.nix
+++ b/nixpkgs/nixos/tests/web-apps/mastodon/remote-databases.nix
@@ -16,7 +16,14 @@ in
   meta.maintainers = with pkgs.lib.maintainers; [ erictapen izorkin ];
 
   nodes = {
-    database = { config, ... }: {
+    databases = { config, ... }: {
+      environment = {
+        etc = {
+          "redis/password-redis-db".text = ''
+            ogjhJL8ynrP7MazjYOF6
+          '';
+        };
+      };
       networking = {
         interfaces.eth1 = {
           ipv4.addresses = [
@@ -24,7 +31,17 @@ in
           ];
         };
         extraHosts = hosts;
-        firewall.allowedTCPPorts = [ config.services.postgresql.port ];
+        firewall.allowedTCPPorts = [
+          config.services.redis.servers.mastodon.port
+          config.services.postgresql.port
+        ];
+      };
+
+      services.redis.servers.mastodon = {
+        enable = true;
+        bind = "0.0.0.0";
+        port = 31637;
+        requirePassFile = "/etc/redis/password-redis-db";
       };
 
       services.postgresql = {
@@ -83,6 +100,9 @@ in
 
       environment = {
         etc = {
+          "mastodon/password-redis-db".text = ''
+            ogjhJL8ynrP7MazjYOF6
+          '';
           "mastodon/password-posgressql-db".text = ''
             SoDTZcISc3f1M1LJsRLT
           '';
@@ -108,6 +128,12 @@ in
         localDomain = "mastodon.local";
         enableUnixSocket = false;
         streamingProcesses = 2;
+        redis = {
+          createLocally = false;
+          host = "192.168.2.102";
+          port = 31637;
+          passwordFile = "/etc/mastodon/password-redis-db";
+        };
         database = {
           createLocally = false;
           host = "192.168.2.102";
@@ -151,12 +177,14 @@ in
     extraInit = ''
       nginx.wait_for_unit("nginx.service")
       nginx.wait_for_open_port(443)
-      database.wait_for_unit("postgresql.service")
-      database.wait_for_open_port(5432)
+      databases.wait_for_unit("redis-mastodon.service")
+      databases.wait_for_unit("postgresql.service")
+      databases.wait_for_open_port(31637)
+      databases.wait_for_open_port(5432)
     '';
     extraShutdown = ''
       nginx.shutdown()
-      database.shutdown()
+      databases.shutdown()
     '';
   };
 })
diff --git a/nixpkgs/nixos/tests/web-apps/mastodon/script.nix b/nixpkgs/nixos/tests/web-apps/mastodon/script.nix
index afb7c0e0a0eb..9184c63c8941 100644
--- a/nixpkgs/nixos/tests/web-apps/mastodon/script.nix
+++ b/nixpkgs/nixos/tests/web-apps/mastodon/script.nix
@@ -8,7 +8,6 @@
 
   ${extraInit}
 
-  server.wait_for_unit("redis-mastodon.service")
   server.wait_for_unit("mastodon-sidekiq-all.service")
   server.wait_for_unit("mastodon-streaming.target")
   server.wait_for_unit("mastodon-web.service")
diff --git a/nixpkgs/nixos/tests/web-apps/mastodon/standard.nix b/nixpkgs/nixos/tests/web-apps/mastodon/standard.nix
index e5eb30fef597..ddc764e2168c 100644
--- a/nixpkgs/nixos/tests/web-apps/mastodon/standard.nix
+++ b/nixpkgs/nixos/tests/web-apps/mastodon/standard.nix
@@ -34,12 +34,6 @@ in
         pki.certificateFiles = [ "${cert pkgs}/cert.pem" ];
       };
 
-      services.redis.servers.mastodon = {
-        enable = true;
-        bind = "127.0.0.1";
-        port = 31637;
-      };
-
       # TODO remove once https://github.com/NixOS/nixpkgs/pull/266270 is resolved.
       services.postgresql.package = pkgs.postgresql_14;
 
@@ -89,6 +83,7 @@ in
     extraInit = ''
       server.wait_for_unit("nginx.service")
       server.wait_for_open_port(443)
+      server.wait_for_unit("redis-mastodon.service")
       server.wait_for_unit("postgresql.service")
       server.wait_for_open_port(5432)
     '';
diff --git a/nixpkgs/nixos/tests/web-servers/ttyd.nix b/nixpkgs/nixos/tests/web-servers/ttyd.nix
index d161673684b3..b79a2032ec75 100644
--- a/nixpkgs/nixos/tests/web-servers/ttyd.nix
+++ b/nixpkgs/nixos/tests/web-servers/ttyd.nix
@@ -2,18 +2,28 @@ import ../make-test-python.nix ({ lib, pkgs, ... }: {
   name = "ttyd";
   meta.maintainers = with lib.maintainers; [ stunkymonkey ];
 
-  nodes.machine = { pkgs, ... }: {
+  nodes.readonly = { pkgs, ... }: {
+    services.ttyd = {
+      enable = true;
+      entrypoint = [ (lib.getExe pkgs.htop) ];
+      writeable = false;
+    };
+  };
+
+  nodes.writeable = { pkgs, ... }: {
     services.ttyd = {
       enable = true;
       username = "foo";
       passwordFile = pkgs.writeText "password" "bar";
+      writeable = true;
     };
   };
 
   testScript = ''
-    machine.wait_for_unit("ttyd.service")
-    machine.wait_for_open_port(7681)
-    response = machine.succeed("curl -vvv -u foo:bar -s -H 'Host: ttyd' http://127.0.0.1:7681/")
-    assert '<title>ttyd - Terminal</title>' in response, "Page didn't load successfully"
+    for machine in [readonly, writeable]:
+      machine.wait_for_unit("ttyd.service")
+      machine.wait_for_open_port(7681)
+      response = machine.succeed("curl -vvv -u foo:bar -s -H 'Host: ttyd' http://127.0.0.1:7681/")
+      assert '<title>ttyd - Terminal</title>' in response, "Page didn't load successfully"
   '';
 })