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.nix4
-rw-r--r--nixos/tests/corerad.nix35
-rw-r--r--nixos/tests/go-neb.nix44
-rw-r--r--nixos/tests/haproxy.nix8
-rw-r--r--nixos/tests/hydra/db-migration.nix6
-rw-r--r--nixos/tests/ipfs.nix7
-rw-r--r--nixos/tests/lxd-nftables.nix50
-rw-r--r--nixos/tests/lxd.nix135
-rw-r--r--nixos/tests/mysql/mysql.nix92
-rw-r--r--nixos/tests/prometheus-exporters.nix78
-rw-r--r--nixos/tests/qboot.nix13
-rw-r--r--nixos/tests/systemd-boot.nix57
-rw-r--r--nixos/tests/teeworlds.nix55
-rw-r--r--nixos/tests/web-servers/unit-php.nix14
14 files changed, 554 insertions, 44 deletions
diff --git a/nixos/tests/all-tests.nix b/nixos/tests/all-tests.nix
index 796c626f3dde..bd26fc906aaa 100644
--- a/nixos/tests/all-tests.nix
+++ b/nixos/tests/all-tests.nix
@@ -119,6 +119,7 @@ in
   installed-tests = pkgs.recurseIntoAttrs (handleTest ./installed-tests {});
   gocd-agent = handleTest ./gocd-agent.nix {};
   gocd-server = handleTest ./gocd-server.nix {};
+  go-neb = handleTest ./go-neb.nix {};
   google-oslogin = handleTest ./google-oslogin {};
   grafana = handleTest ./grafana.nix {};
   graphite = handleTest ./graphite.nix {};
@@ -178,6 +179,8 @@ in
   limesurvey = handleTest ./limesurvey.nix {};
   login = handleTest ./login.nix {};
   loki = handleTest ./loki.nix {};
+  lxd = handleTest ./lxd.nix {};
+  lxd-nftables = handleTest ./lxd-nftables.nix {};
   #logstash = handleTest ./logstash.nix {};
   lorri = handleTest ./lorri/default.nix {};
   magnetico = handleTest ./magnetico.nix {};
@@ -273,6 +276,7 @@ in
   prosody = handleTest ./xmpp/prosody.nix {};
   prosodyMysql = handleTest ./xmpp/prosody-mysql.nix {};
   proxy = handleTest ./proxy.nix {};
+  qboot = handleTestOn ["x86_64-linux" "i686-linux"] ./qboot.nix {};
   quagga = handleTest ./quagga.nix {};
   quorum = handleTest ./quorum.nix {};
   rabbitmq = handleTest ./rabbitmq.nix {};
diff --git a/nixos/tests/corerad.nix b/nixos/tests/corerad.nix
index 741fa448f680..72ab255b1916 100644
--- a/nixos/tests/corerad.nix
+++ b/nixos/tests/corerad.nix
@@ -3,7 +3,7 @@ import ./make-test-python.nix (
     nodes = {
       router = {config, pkgs, ...}: { 
         config = {
-          # This machines simulates a router with IPv6 forwarding and a static IPv6 address.
+          # This machine simulates a router with IPv6 forwarding and a static IPv6 address.
           boot.kernel.sysctl = {
             "net.ipv6.conf.all.forwarding" = true;
           };
@@ -14,13 +14,25 @@ import ./make-test-python.nix (
             enable = true;
             # Serve router advertisements to the client machine with prefix information matching
             # any IPv6 /64 prefixes configured on this interface.
-            configFile = pkgs.writeText "corerad.toml" ''
-              [[interfaces]]
-              name = "eth1"
-              advertise = true
-                [[interfaces.prefix]]
-                prefix = "::/64"
-            '';
+            #
+            # This configuration is identical to the example in the CoreRAD NixOS module.
+            settings = {
+              interfaces = [
+                {
+                  name = "eth0";
+                  monitor = true;
+                }
+                {
+                  name = "eth1";
+                  advertise = true;
+                  prefix = [{ prefix = "::/64"; }];
+                }
+              ];
+              debug = {
+                address = "localhost:9430";
+                prometheus = true;
+              };
+            };
           };
         };
       };
@@ -66,5 +78,12 @@ import ./make-test-python.nix (
           assert (
               "/64 scope global temporary" in addrs
           ), "SLAAC temporary address was not configured on client after router advertisement"
+
+      with subtest("Verify HTTP debug server is configured"):
+          out = router.succeed("curl localhost:9430/metrics")
+
+          assert (
+              "corerad_build_info" in out
+          ), "Build info metric was not found in Prometheus output"
     '';
   })
diff --git a/nixos/tests/go-neb.nix b/nixos/tests/go-neb.nix
new file mode 100644
index 000000000000..d9e5db0b4a53
--- /dev/null
+++ b/nixos/tests/go-neb.nix
@@ -0,0 +1,44 @@
+import ./make-test-python.nix ({ pkgs, ... }:
+{
+  name = "go-neb";
+  meta = with pkgs.stdenv.lib.maintainers; {
+    maintainers = [ hexa maralorn ];
+  };
+
+  nodes = {
+    server = {
+      services.go-neb = {
+        enable = true;
+        baseUrl = "http://localhost";
+        config = {
+          clients = [ {
+            UserId = "@test:localhost";
+            AccessToken = "changeme";
+            HomeServerUrl = "http://localhost";
+            Sync = false;
+            AutoJoinRooms = false;
+            DisplayName = "neverbeseen";
+          } ];
+          services = [ {
+            ID = "wikipedia_service";
+            Type = "wikipedia";
+            UserID = "@test:localhost";
+            Config = { };
+          } ];
+        };
+      };
+    };
+  };
+
+  testScript = ''
+    start_all()
+    server.wait_for_unit("go-neb.service")
+    server.wait_until_succeeds(
+        "curl -L http://localhost:4050/services/hooks/d2lraXBlZGlhX3NlcnZpY2U"
+    )
+    server.wait_until_succeeds(
+        "journalctl -eu go-neb -o cat | grep -q service_id=wikipedia_service"
+    )
+  '';
+
+})
diff --git a/nixos/tests/haproxy.nix b/nixos/tests/haproxy.nix
index 79f34b07faf4..ffb77c052a24 100644
--- a/nixos/tests/haproxy.nix
+++ b/nixos/tests/haproxy.nix
@@ -43,5 +43,13 @@ import ./make-test-python.nix ({ pkgs, ...}: {
     assert "haproxy_process_pool_allocated_bytes" in machine.succeed(
         "curl -k http://localhost:80/metrics"
     )
+
+    with subtest("reload"):
+        machine.succeed("systemctl reload haproxy")
+        # wait some time to ensure the following request hits the reloaded haproxy
+        machine.sleep(5)
+        assert "We are all good!" in machine.succeed(
+            "curl -k http://localhost:80/index.txt"
+        )
   '';
 })
diff --git a/nixos/tests/hydra/db-migration.nix b/nixos/tests/hydra/db-migration.nix
index cf74acfd67aa..ca65e2e66aa1 100644
--- a/nixos/tests/hydra/db-migration.nix
+++ b/nixos/tests/hydra/db-migration.nix
@@ -61,7 +61,7 @@ with pkgs.lib;
           'curl -L -s http://localhost:3000/build/1 -H "Accept: application/json" |  jq .buildstatus | xargs test 0 -eq'
       )
 
-      out = original.succeed("su -l postgres -c 'psql -d hydra <<< \"\\d+ jobs\" -A'")
+      out = original.succeed("su -l postgres -c 'psql -d hydra <<< \"\\d+ builds\" -A'")
       assert "jobset_id" not in out
 
       original.succeed(
@@ -69,7 +69,7 @@ with pkgs.lib;
       )
       original.wait_for_unit("hydra-init.service")
 
-      out = original.succeed("su -l postgres -c 'psql -d hydra <<< \"\\d+ jobs\" -A'")
+      out = original.succeed("su -l postgres -c 'psql -d hydra <<< \"\\d+ builds\" -A'")
       assert "jobset_id|integer|||" in out
 
       original.succeed("hydra-backfill-ids")
@@ -79,7 +79,7 @@ with pkgs.lib;
       )
       original.wait_for_unit("hydra-init.service")
 
-      out = original.succeed("su -l postgres -c 'psql -d hydra <<< \"\\d+ jobs\" -A'")
+      out = original.succeed("su -l postgres -c 'psql -d hydra <<< \"\\d+ builds\" -A'")
       assert "jobset_id|integer||not null|" in out
 
       original.wait_until_succeeds(
diff --git a/nixos/tests/ipfs.nix b/nixos/tests/ipfs.nix
index 4d721aec0c73..82234f969226 100644
--- a/nixos/tests/ipfs.nix
+++ b/nixos/tests/ipfs.nix
@@ -21,5 +21,12 @@ import ./make-test-python.nix ({ pkgs, ...} : {
     )
 
     machine.succeed(f"ipfs cat /ipfs/{ipfs_hash.strip()} | grep fnord")
+
+    ipfs_hash = machine.succeed(
+        "echo fnord2 | ipfs --api /unix/run/ipfs.sock add | awk '{ print $2 }'"
+    )
+    machine.succeed(
+        f"ipfs --api /unix/run/ipfs.sock cat /ipfs/{ipfs_hash.strip()} | grep fnord2"
+    )
   '';
 })
diff --git a/nixos/tests/lxd-nftables.nix b/nixos/tests/lxd-nftables.nix
new file mode 100644
index 000000000000..25517914db85
--- /dev/null
+++ b/nixos/tests/lxd-nftables.nix
@@ -0,0 +1,50 @@
+# This test makes sure that lxd stops implicitly depending on iptables when
+# user enabled nftables.
+#
+# It has been extracted from `lxd.nix` for clarity, and because switching from
+# iptables to nftables requires a full reboot, which is a bit hard inside NixOS
+# tests.
+
+import ./make-test-python.nix ({ pkgs, ...} : {
+  name = "lxd-nftables";
+  meta = with pkgs.stdenv.lib.maintainers; {
+    maintainers = [ patryk27 ];
+  };
+
+  machine = { lib, ... }: {
+    virtualisation = {
+      lxd.enable = true;
+    };
+
+    networking = {
+      firewall.enable = false;
+      nftables.enable = true;
+      nftables.ruleset = ''
+        table inet filter {
+          chain incoming {
+            type filter hook input priority 0;
+            policy accept;
+          }
+
+          chain forward {
+            type filter hook forward priority 0;
+            policy accept;
+          }
+
+          chain output {
+            type filter hook output priority 0;
+            policy accept;
+          }
+        }
+      '';
+    };
+  };
+
+  testScript = ''
+    machine.wait_for_unit("network.target")
+
+    with subtest("When nftables are enabled, lxd doesn't depend on iptables anymore"):
+        machine.succeed("lsmod | grep nf_tables")
+        machine.fail("lsmod | grep ip_tables")
+  '';
+})
diff --git a/nixos/tests/lxd.nix b/nixos/tests/lxd.nix
new file mode 100644
index 000000000000..db2d44dff557
--- /dev/null
+++ b/nixos/tests/lxd.nix
@@ -0,0 +1,135 @@
+import ./make-test-python.nix ({ pkgs, ...} :
+
+let
+  # Since we don't have access to the internet during the tests, we have to
+  # pre-fetch lxd containers beforehand.
+  #
+  # I've chosen to import Alpine Linux, because its image is turbo-tiny and,
+  # generally, sufficient for our tests.
+
+  alpine-meta = pkgs.fetchurl {
+    url = "https://uk.images.linuxcontainers.org/images/alpine/3.11/i386/default/20200608_13:00/lxd.tar.xz";
+    sha256 = "1hkvaj3rr333zmx1759njy435lps33gl4ks8zfm7m4nqvipm26a0";
+  };
+
+  alpine-rootfs = pkgs.fetchurl {
+    url = "https://uk.images.linuxcontainers.org/images/alpine/3.11/i386/default/20200608_13:00/rootfs.tar.xz";
+    sha256 = "1v82zdra4j5xwsff09qlp7h5vbsg54s0j7rdg4rynichfid3r347";
+  };
+
+  lxd-config = pkgs.writeText "config.yaml" ''
+    storage_pools:
+      - name: default
+        driver: dir
+        config:
+          source: /var/lxd-pool
+
+    networks:
+      - name: lxdbr0
+        type: bridge
+        config:
+          ipv4.address: auto
+          ipv6.address: none
+
+    profiles:
+      - name: default
+        devices:
+          eth0:
+            name: eth0
+            network: lxdbr0
+            type: nic
+          root:
+            path: /
+            pool: default
+            type: disk
+  '';
+
+in {
+  name = "lxd";
+  meta = with pkgs.stdenv.lib.maintainers; {
+    maintainers = [ patryk27 ];
+  };
+
+  machine = { lib, ... }: {
+    virtualisation = {
+      # Since we're testing `limits.cpu`, we've gotta have a known number of
+      # cores to lay on
+      cores = 2;
+
+      # Ditto, for `limits.memory`
+      memorySize = 512;
+
+      lxc.lxcfs.enable = true;
+      lxd.enable = true;
+    };
+  };
+
+  testScript = ''
+    machine.wait_for_unit("sockets.target")
+    machine.wait_for_unit("lxd.service")
+
+    # It takes additional second for lxd to settle
+    machine.sleep(1)
+
+    # lxd expects the pool's directory to already exist
+    machine.succeed("mkdir /var/lxd-pool")
+
+    machine.succeed(
+        "cat ${lxd-config} | lxd init --preseed"
+    )
+
+    machine.succeed(
+        "lxc image import ${alpine-meta} ${alpine-rootfs} --alias alpine"
+    )
+
+    with subtest("Containers can be launched and destroyed"):
+        machine.succeed("lxc launch alpine test")
+        machine.succeed("lxc exec test true")
+        machine.succeed("lxc delete -f test")
+
+    with subtest("Containers are being mounted with lxcfs inside"):
+        machine.succeed("lxc launch alpine test")
+
+        ## ---------- ##
+        ## limits.cpu ##
+
+        machine.succeed("lxc config set test limits.cpu 1")
+
+        # Since Alpine doesn't have `nproc` pre-installed, we've gotta resort
+        # to the primal methods
+        assert (
+            "1"
+            == machine.succeed("lxc exec test grep -- -c ^processor /proc/cpuinfo").strip()
+        )
+
+        machine.succeed("lxc config set test limits.cpu 2")
+
+        assert (
+            "2"
+            == machine.succeed("lxc exec test grep -- -c ^processor /proc/cpuinfo").strip()
+        )
+
+        ## ------------- ##
+        ## limits.memory ##
+
+        machine.succeed("lxc config set test limits.memory 64MB")
+
+        assert (
+            "MemTotal:          62500 kB"
+            == machine.succeed("lxc exec test grep -- MemTotal /proc/meminfo").strip()
+        )
+
+        machine.succeed("lxc config set test limits.memory 128MB")
+
+        assert (
+            "MemTotal:         125000 kB"
+            == machine.succeed("lxc exec test grep -- MemTotal /proc/meminfo").strip()
+        )
+
+        machine.succeed("lxc delete -f test")
+
+    with subtest("Unless explicitly changed, lxd leans on iptables"):
+        machine.succeed("lsmod | grep ip_tables")
+        machine.fail("lsmod | grep nf_tables")
+  '';
+})
diff --git a/nixos/tests/mysql/mysql.nix b/nixos/tests/mysql/mysql.nix
index d236ce946328..50e1c76e9fd0 100644
--- a/nixos/tests/mysql/mysql.nix
+++ b/nixos/tests/mysql/mysql.nix
@@ -5,20 +5,34 @@ import ./../make-test-python.nix ({ pkgs, ...} : {
   };
 
   nodes = {
-    mysql =
+    mysql57 =
       { pkgs, ... }:
 
       {
+        users.users.testuser = { };
+        users.users.testuser2 = { };
         services.mysql.enable = true;
         services.mysql.initialDatabases = [
-          { name = "testdb"; schema = ./testdb.sql; }
-          { name = "empty_testdb"; }
+          { name = "testdb3"; schema = ./testdb.sql; }
         ];
         # note that using pkgs.writeText here is generally not a good idea,
         # as it will store the password in world-readable /nix/store ;)
         services.mysql.initialScript = pkgs.writeText "mysql-init.sql" ''
-          CREATE USER 'passworduser'@'localhost' IDENTIFIED BY 'password123';
+          CREATE USER 'testuser3'@'localhost' IDENTIFIED BY 'secure';
+          GRANT ALL PRIVILEGES ON testdb3.* TO 'testuser3'@'localhost';
         '';
+        services.mysql.ensureDatabases = [ "testdb" "testdb2" ];
+        services.mysql.ensureUsers = [{
+          name = "testuser";
+          ensurePermissions = {
+            "testdb.*" = "ALL PRIVILEGES";
+          };
+        } {
+          name = "testuser2";
+          ensurePermissions = {
+            "testdb2.*" = "ALL PRIVILEGES";
+          };
+        }];
         services.mysql.package = pkgs.mysql57;
       };
 
@@ -30,16 +44,30 @@ 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 = { };
         services.mysql.enable = true;
         services.mysql.initialDatabases = [
-          { name = "testdb"; schema = ./testdb.sql; }
-          { name = "empty_testdb"; }
+          { name = "testdb3"; schema = ./testdb.sql; }
         ];
         # note that using pkgs.writeText here is generally not a good idea,
         # as it will store the password in world-readable /nix/store ;)
         services.mysql.initialScript = pkgs.writeText "mysql-init.sql" ''
-          CREATE USER 'passworduser'@'localhost' IDENTIFIED BY 'password123';
+          CREATE USER 'testuser3'@'localhost' IDENTIFIED BY 'secure';
+          GRANT ALL PRIVILEGES ON testdb3.* TO 'testuser3'@'localhost';
         '';
+        services.mysql.ensureDatabases = [ "testdb" "testdb2" ];
+        services.mysql.ensureUsers = [{
+          name = "testuser";
+          ensurePermissions = {
+            "testdb.*" = "ALL PRIVILEGES";
+          };
+        } {
+          name = "testuser2";
+          ensurePermissions = {
+            "testdb2.*" = "ALL PRIVILEGES";
+          };
+        }];
         services.mysql.package = pkgs.mysql80;
       };
 
@@ -81,17 +109,49 @@ import ./../make-test-python.nix ({ pkgs, ...} : {
   testScript = ''
     start_all()
 
-    mysql.wait_for_unit("mysql")
-    mysql.succeed("echo 'use empty_testdb;' | mysql -u root")
-    mysql.succeed("echo 'use testdb; select * from tests;' | mysql -u root -N | grep 4")
-    # ';' acts as no-op, just check whether login succeeds with the user created from the initialScript
-    mysql.succeed("echo ';' | mysql -u passworduser --password=password123")
+    mysql57.wait_for_unit("mysql")
+    mysql57.succeed(
+        "echo 'use testdb; create table tests (test_id INT, PRIMARY KEY (test_id));' | sudo -u testuser mysql -u testuser"
+    )
+    mysql57.succeed(
+        "echo 'use testdb; insert into tests values (41);' | sudo -u testuser mysql -u testuser"
+    )
+    # Ensure testuser2 is not able to insert into testdb as mysql testuser2
+    mysql57.fail(
+        "echo 'use testdb; insert into tests values (22);' | sudo -u testuser2 mysql -u testuser2"
+    )
+    # Ensure testuser2 is not able to authenticate as mysql testuser
+    mysql57.fail(
+        "echo 'use testdb; insert into tests values (22);' | sudo -u testuser2 mysql -u testuser"
+    )
+    mysql57.succeed(
+        "echo 'use testdb; select test_id from tests;' | sudo -u testuser mysql -u testuser -N | grep 41"
+    )
+    mysql57.succeed(
+        "echo 'use testdb3; select * from tests;' | mysql -u testuser3 --password=secure -N | grep 4"
+    )
 
     mysql80.wait_for_unit("mysql")
-    mysql80.succeed("echo 'use empty_testdb;' | mysql -u root")
-    mysql80.succeed("echo 'use testdb; select * from tests;' | mysql -u root -N | grep 4")
-    # ';' acts as no-op, just check whether login succeeds with the user created from the initialScript
-    mysql80.succeed("echo ';' | mysql -u passworduser --password=password123")
+    mysql80.succeed(
+        "echo 'use testdb; create table tests (test_id INT, PRIMARY KEY (test_id));' | sudo -u testuser mysql -u testuser"
+    )
+    mysql80.succeed(
+        "echo 'use testdb; insert into tests values (41);' | sudo -u testuser mysql -u testuser"
+    )
+    # Ensure testuser2 is not able to insert into testdb as mysql testuser2
+    mysql80.fail(
+        "echo 'use testdb; insert into tests values (22);' | sudo -u testuser2 mysql -u testuser2"
+    )
+    # Ensure testuser2 is not able to authenticate as mysql testuser
+    mysql80.fail(
+        "echo 'use testdb; insert into tests values (22);' | sudo -u testuser2 mysql -u testuser"
+    )
+    mysql80.succeed(
+        "echo 'use testdb; select test_id from tests;' | sudo -u testuser mysql -u testuser -N | grep 41"
+    )
+    mysql80.succeed(
+        "echo 'use testdb3; select * from tests;' | mysql -u testuser3 --password=secure -N | grep 4"
+    )
 
     mariadb.wait_for_unit("mysql")
     mariadb.succeed(
diff --git a/nixos/tests/prometheus-exporters.nix b/nixos/tests/prometheus-exporters.nix
index 4fc3668cfafb..4dbd64312224 100644
--- a/nixos/tests/prometheus-exporters.nix
+++ b/nixos/tests/prometheus-exporters.nix
@@ -56,6 +56,21 @@ let
  */
 
   exporterTests = {
+     apcupsd = {
+      exporterConfig = {
+        enable = true;
+      };
+      metricProvider = {
+        services.apcupsd.enable = true;
+      };
+      exporterTest = ''
+        wait_for_unit("apcupsd.service")
+        wait_for_open_port(3551)
+        wait_for_unit("prometheus-apcupsd-exporter.service")
+        wait_for_open_port(9162)
+        succeed("curl -sSf http://localhost:9162/metrics | grep -q 'apcupsd_info'")
+      '';
+    };
 
     bind = {
       exporterConfig = {
@@ -202,6 +217,69 @@ let
       '';
     };
 
+    keylight = {
+      # A hardware device is required to properly test this exporter, so just
+      # perform a couple of basic sanity checks that the exporter is running
+      # and requires a target, but cannot reach a specified target.
+      exporterConfig = {
+        enable = true;
+      };
+      exporterTest = ''
+        wait_for_unit("prometheus-keylight-exporter.service")
+        wait_for_open_port(9288)
+        succeed(
+            "curl -sS --write-out '%{http_code}' -o /dev/null http://localhost:9288/metrics | grep -q '400'"
+        )
+        succeed(
+            "curl -sS --write-out '%{http_code}' -o /dev/null http://localhost:9288/metrics?target=nosuchdevice | grep -q '500'"
+        )
+      '';
+    };
+
+    lnd = {
+      exporterConfig = {
+        enable = true;
+        lndTlsPath = "/var/lib/lnd/tls.cert";
+        lndMacaroonDir = "/var/lib/lnd";
+      };
+      metricProvider = {
+        systemd.services.prometheus-lnd-exporter.serviceConfig.DynamicUser = false;
+        services.bitcoind.enable = true;
+        services.bitcoind.extraConfig = ''
+          rpcauth=bitcoinrpc:e8fe33f797e698ac258c16c8d7aadfbe$872bdb8f4d787367c26bcfd75e6c23c4f19d44a69f5d1ad329e5adf3f82710f7
+          bitcoind.zmqpubrawblock=tcp://127.0.0.1:28332
+          bitcoind.zmqpubrawtx=tcp://127.0.0.1:28333
+        '';
+        systemd.services.lnd = {
+          serviceConfig.ExecStart = ''
+          ${pkgs.lnd}/bin/lnd \
+            --datadir=/var/lib/lnd \
+            --tlscertpath=/var/lib/lnd/tls.cert \
+            --tlskeypath=/var/lib/lnd/tls.key \
+            --logdir=/var/log/lnd \
+            --bitcoin.active \
+            --bitcoin.mainnet \
+            --bitcoin.node=bitcoind \
+            --bitcoind.rpcuser=bitcoinrpc \
+            --bitcoind.rpcpass=hunter2 \
+            --bitcoind.zmqpubrawblock=tcp://127.0.0.1:28332 \
+            --bitcoind.zmqpubrawtx=tcp://127.0.0.1:28333 \
+            --readonlymacaroonpath=/var/lib/lnd/readonly.macaroon
+          '';
+          serviceConfig.StateDirectory = "lnd";
+          wantedBy = [ "multi-user.target" ];
+          after = [ "network.target" ];
+        };
+      };
+      exporterTest = ''
+        wait_for_unit("lnd.service")
+        wait_for_open_port(10009)
+        wait_for_unit("prometheus-lnd-exporter.service")
+        wait_for_open_port(9092)
+        succeed("curl -sSf localhost:9092/metrics | grep -q '^promhttp_metric_handler'")
+      '';
+    };
+
     mail = {
       exporterConfig = {
         enable = true;
diff --git a/nixos/tests/qboot.nix b/nixos/tests/qboot.nix
new file mode 100644
index 000000000000..12aef6decfae
--- /dev/null
+++ b/nixos/tests/qboot.nix
@@ -0,0 +1,13 @@
+import ./make-test-python.nix ({ pkgs, ...} : {
+  name = "qboot";
+
+  machine = { ... }: {
+    virtualisation.bios = pkgs.qboot;
+  };
+
+  testScript =
+    ''
+      start_all()
+      machine.wait_for_unit("multi-user.target")
+    '';
+})
diff --git a/nixos/tests/systemd-boot.nix b/nixos/tests/systemd-boot.nix
index e911c3933616..eba4729d6de8 100644
--- a/nixos/tests/systemd-boot.nix
+++ b/nixos/tests/systemd-boot.nix
@@ -6,26 +6,53 @@
 with import ../lib/testing-python.nix { inherit system pkgs; };
 with pkgs.lib;
 
-makeTest {
-  name = "systemd-boot";
-  meta.maintainers = with pkgs.stdenv.lib.maintainers; [ danielfullmer ];
-
-  machine = { pkgs, lib, ... }: {
+let
+  common = {
     virtualisation.useBootLoader = true;
     virtualisation.useEFIBoot = true;
     boot.loader.systemd-boot.enable = true;
   };
+in
+{
+  basic = makeTest {
+    name = "systemd-boot";
+    meta.maintainers = with pkgs.stdenv.lib.maintainers; [ danielfullmer ];
+
+    machine = common;
+
+    testScript = ''
+      machine.start()
+      machine.wait_for_unit("multi-user.target")
 
-  testScript = ''
-    machine.start()
-    machine.wait_for_unit("multi-user.target")
+      machine.succeed("test -e /boot/loader/entries/nixos-generation-1.conf")
+
+      # Ensure we actually booted using systemd-boot
+      # Magic number is the vendor UUID used by systemd-boot.
+      machine.succeed(
+          "test -e /sys/firmware/efi/efivars/LoaderEntrySelected-4a67b082-0a4c-41cf-b6c7-440b29bb8c4f"
+      )
+    '';
+  };
 
-    machine.succeed("test -e /boot/loader/entries/nixos-generation-1.conf")
+  update = makeTest {
+    name = "systemd-boot-update";
+    meta.maintainers = with pkgs.stdenv.lib.maintainers; [ danielfullmer ];
 
-    # Ensure we actually booted using systemd-boot.
-    # Magic number is the vendor UUID used by systemd-boot.
-    machine.succeed(
-        "test -e /sys/firmware/efi/efivars/LoaderEntrySelected-4a67b082-0a4c-41cf-b6c7-440b29bb8c4f"
-    )
-  '';
+    machine = common;
+
+    testScript = ''
+      machine.succeed("mount -o remount,rw /boot")
+
+      # Replace version inside sd-boot with something older. See magic[] string in systemd src/boot/efi/boot.c
+      machine.succeed(
+          """
+        find /boot -iname '*.efi' -print0 | \
+        xargs -0 -I '{}' sed -i 's/#### LoaderInfo: systemd-boot .* ####/#### LoaderInfo: systemd-boot 001 ####/' '{}'
+      """
+      )
+
+      output = machine.succeed("/run/current-system/bin/switch-to-configuration boot")
+      assert "updating systemd-boot from 001 to " in output
+    '';
+  };
 }
diff --git a/nixos/tests/teeworlds.nix b/nixos/tests/teeworlds.nix
new file mode 100644
index 000000000000..edf588968788
--- /dev/null
+++ b/nixos/tests/teeworlds.nix
@@ -0,0 +1,55 @@
+import ./make-test-python.nix ({ pkgs, ... }:
+
+let
+  client =
+    { pkgs, ... }:
+
+    { imports = [ ./common/x11.nix ];
+      environment.systemPackages = [ pkgs.teeworlds ];
+    };
+
+in {
+  name = "teeworlds";
+  meta = with pkgs.stdenv.lib.maintainers; {
+    maintainers = [ hax404 ];
+  };
+
+  nodes =
+    { server =
+      { services.teeworlds = {
+          enable = true;
+          openPorts = true;
+        };
+      };
+
+      client1 = client;
+      client2 = client;
+    };
+
+    testScript =
+    ''
+      start_all()
+
+      server.wait_for_unit("teeworlds.service")
+      server.wait_until_succeeds("ss --numeric --udp --listening | grep -q 8303")
+
+      client1.wait_for_x()
+      client2.wait_for_x()
+
+      client1.execute("teeworlds 'player_name Alice;connect server'&")
+      server.wait_until_succeeds(
+          'journalctl -u teeworlds -e | grep --extended-regexp -q "team_join player=\'[0-9]:Alice"'
+      )
+
+      client2.execute("teeworlds 'player_name Bob;connect server'&")
+      server.wait_until_succeeds(
+          'journalctl -u teeworlds -e | grep --extended-regexp -q "team_join player=\'[0-9]:Bob"'
+      )
+
+      server.sleep(10)  # wait for a while to get a nice screenshot
+
+      client1.screenshot("screen_client1")
+      client2.screenshot("screen_client2")
+    '';
+
+})
diff --git a/nixos/tests/web-servers/unit-php.nix b/nixos/tests/web-servers/unit-php.nix
index c6327a1f825d..2a0a5bdaa5d5 100644
--- a/nixos/tests/web-servers/unit-php.nix
+++ b/nixos/tests/web-servers/unit-php.nix
@@ -23,7 +23,10 @@ in {
               "user": "testuser",
               "group": "testgroup",
               "root": "${testdir}/www",
-              "index": "info.php"
+              "index": "info.php",
+              "options": {
+                "file": "${pkgs.unit.usedPhp74}/lib/php.ini"
+              }
             }
           }
         }
@@ -42,6 +45,13 @@ in {
   };
   testScript = ''
     machine.wait_for_unit("unit.service")
-    assert "PHP Version ${pkgs.php74.version}" in machine.succeed("curl -vvv -s http://127.0.0.1:9074/")
+
+    # Check so we get an evaluated PHP back
+    response = machine.succeed("curl -vvv -s http://127.0.0.1:9074/")
+    assert "PHP Version ${pkgs.unit.usedPhp74.version}" in response, "PHP version not detected"
+
+    # Check so we have database and some other extensions loaded
+    for ext in ["json", "opcache", "pdo_mysql", "pdo_pgsql", "pdo_sqlite"]:
+        assert ext in response, f"Missing {ext} extension"
   '';
 })