diff options
Diffstat (limited to 'nixos/tests')
37 files changed, 1159 insertions, 195 deletions
diff --git a/nixos/tests/adguardhome.nix b/nixos/tests/adguardhome.nix index 80613ce825340..005d54e17dfdc 100644 --- a/nixos/tests/adguardhome.nix +++ b/nixos/tests/adguardhome.nix @@ -2,41 +2,39 @@ name = "adguardhome"; nodes = { - nullConf = { ... }: { services.adguardhome = { enable = true; }; }; + nullConf = { services.adguardhome.enable = true; }; - emptyConf = { lib, ... }: { + emptyConf = { services.adguardhome = { enable = true; + + settings = { }; + }; + }; + + schemaVersionBefore23 = { + services.adguardhome = { + enable = true; + + settings.schema_version = 20; }; }; - declarativeConf = { ... }: { + declarativeConf = { services.adguardhome = { enable = true; mutableSettings = false; - settings = { - schema_version = 0; - dns = { - bind_host = "0.0.0.0"; - bootstrap_dns = "127.0.0.1"; - }; - }; + settings.dns.bootstrap_dns = [ "127.0.0.1" ]; }; }; - mixedConf = { ... }: { + mixedConf = { services.adguardhome = { enable = true; mutableSettings = true; - settings = { - schema_version = 0; - dns = { - bind_host = "0.0.0.0"; - bootstrap_dns = "127.0.0.1"; - }; - }; + settings.dns.bootstrap_dns = [ "127.0.0.1" ]; }; }; @@ -70,11 +68,7 @@ allowDHCP = true; mutableSettings = false; settings = { - schema_version = 0; - dns = { - bind_host = "0.0.0.0"; - bootstrap_dns = "127.0.0.1"; - }; + dns.bootstrap_dns = [ "127.0.0.1" ]; dhcp = { # This implicitly enables CAP_NET_RAW enabled = true; @@ -104,33 +98,38 @@ testScript = '' with subtest("Minimal (settings = null) config test"): - nullConf.wait_for_unit("adguardhome.service") + nullConf.wait_for_unit("adguardhome.service") + nullConf.wait_for_open_port(3000) with subtest("Default config test"): - emptyConf.wait_for_unit("adguardhome.service") - emptyConf.wait_for_open_port(3000) + emptyConf.wait_for_unit("adguardhome.service") + emptyConf.wait_for_open_port(3000) + + with subtest("Default schema_version 23 config test"): + schemaVersionBefore23.wait_for_unit("adguardhome.service") + schemaVersionBefore23.wait_for_open_port(3000) with subtest("Declarative config test, DNS will be reachable"): - declarativeConf.wait_for_unit("adguardhome.service") - declarativeConf.wait_for_open_port(53) - declarativeConf.wait_for_open_port(3000) + declarativeConf.wait_for_unit("adguardhome.service") + declarativeConf.wait_for_open_port(53) + declarativeConf.wait_for_open_port(3000) with subtest("Mixed config test, check whether merging works"): - mixedConf.wait_for_unit("adguardhome.service") - mixedConf.wait_for_open_port(53) - mixedConf.wait_for_open_port(3000) - # Test whether merging works properly, even if nothing is changed - mixedConf.systemctl("restart adguardhome.service") - mixedConf.wait_for_unit("adguardhome.service") - mixedConf.wait_for_open_port(3000) + mixedConf.wait_for_unit("adguardhome.service") + mixedConf.wait_for_open_port(53) + mixedConf.wait_for_open_port(3000) + # Test whether merging works properly, even if nothing is changed + mixedConf.systemctl("restart adguardhome.service") + mixedConf.wait_for_unit("adguardhome.service") + mixedConf.wait_for_open_port(3000) with subtest("Testing successful DHCP start"): - dhcpConf.wait_for_unit("adguardhome.service") - client.systemctl("start network-online.target") - client.wait_for_unit("network-online.target") - # Test IP assignment via DHCP - dhcpConf.wait_until_succeeds("ping -c 5 10.0.10.100") - # Test hostname resolution over DHCP-provided DNS - dhcpConf.wait_until_succeeds("ping -c 5 client.lan") + dhcpConf.wait_for_unit("adguardhome.service") + client.systemctl("start network-online.target") + client.wait_for_unit("network-online.target") + # Test IP assignment via DHCP + dhcpConf.wait_until_succeeds("ping -c 5 10.0.10.100") + # Test hostname resolution over DHCP-provided DNS + dhcpConf.wait_until_succeeds("ping -c 5 client.lan") ''; } diff --git a/nixos/tests/all-tests.nix b/nixos/tests/all-tests.nix index fd5a0186bdecf..6ef1d8d537980 100644 --- a/nixos/tests/all-tests.nix +++ b/nixos/tests/all-tests.nix @@ -236,6 +236,7 @@ in { darling = handleTest ./darling.nix {}; dae = handleTest ./dae.nix {}; davis = handleTest ./davis.nix {}; + db-rest = handleTest ./db-rest.nix {}; dconf = handleTest ./dconf.nix {}; deconz = handleTest ./deconz.nix {}; deepin = handleTest ./deepin.nix {}; @@ -307,6 +308,7 @@ in { ferm = handleTest ./ferm.nix {}; ferretdb = handleTest ./ferretdb.nix {}; filesystems-overlayfs = runTest ./filesystems-overlayfs.nix; + firefly-iii = handleTest ./firefly-iii.nix {}; firefox = handleTest ./firefox.nix { firefoxPackage = pkgs.firefox; }; firefox-beta = handleTest ./firefox.nix { firefoxPackage = pkgs.firefox-beta; }; firefox-devedition = handleTest ./firefox.nix { firefoxPackage = pkgs.firefox-devedition; }; @@ -329,6 +331,7 @@ in { freshrss-sqlite = handleTest ./freshrss-sqlite.nix {}; freshrss-pgsql = handleTest ./freshrss-pgsql.nix {}; freshrss-http-auth = handleTest ./freshrss-http-auth.nix {}; + freshrss-none-auth = handleTest ./freshrss-none-auth.nix {}; frigate = handleTest ./frigate.nix {}; frp = handleTest ./frp.nix {}; frr = handleTest ./frr.nix {}; @@ -396,6 +399,7 @@ in { honk = runTest ./honk.nix; installed-tests = pkgs.recurseIntoAttrs (handleTest ./installed-tests {}); invidious = handleTest ./invidious.nix {}; + isolate = handleTest ./isolate.nix {}; livebook-service = handleTest ./livebook-service.nix {}; pyload = handleTest ./pyload.nix {}; oci-containers = handleTestOn ["aarch64-linux" "x86_64-linux"] ./oci-containers.nix {}; @@ -597,6 +601,7 @@ in { netdata = handleTest ./netdata.nix {}; networking.scripted = handleTest ./networking/networkd-and-scripted.nix { networkd = false; }; networking.networkd = handleTest ./networking/networkd-and-scripted.nix { networkd = true; }; + networking.networkmanager = handleTest ./networking/networkmanager.nix {}; netbox_3_6 = handleTest ./web-apps/netbox.nix { netbox = pkgs.netbox_3_6; }; netbox_3_7 = handleTest ./web-apps/netbox.nix { netbox = pkgs.netbox_3_7; }; netbox-upgrade = handleTest ./web-apps/netbox-upgrade.nix {}; @@ -768,6 +773,7 @@ in { qgis = handleTest ./qgis.nix { qgisPackage = pkgs.qgis; }; qgis-ltr = handleTest ./qgis.nix { qgisPackage = pkgs.qgis-ltr; }; qownnotes = handleTest ./qownnotes.nix {}; + qtile = handleTest ./qtile.nix {}; quake3 = handleTest ./quake3.nix {}; quicktun = handleTest ./quicktun.nix {}; quorum = handleTest ./quorum.nix {}; @@ -778,6 +784,7 @@ in { rasdaemon = handleTest ./rasdaemon.nix {}; readarr = handleTest ./readarr.nix {}; redis = handleTest ./redis.nix {}; + redlib = handleTest ./redlib.nix {}; redmine = handleTest ./redmine.nix {}; restartByActivationScript = handleTest ./restart-by-activation-script.nix {}; restic-rest-server = handleTest ./restic-rest-server.nix {}; @@ -854,11 +861,13 @@ in { stunnel = handleTest ./stunnel.nix {}; sudo = handleTest ./sudo.nix {}; sudo-rs = handleTest ./sudo-rs.nix {}; + sunshine = handleTest ./sunshine.nix {}; suwayomi-server = handleTest ./suwayomi-server.nix {}; swap-file-btrfs = handleTest ./swap-file-btrfs.nix {}; swap-partition = handleTest ./swap-partition.nix {}; swap-random-encryption = handleTest ./swap-random-encryption.nix {}; sway = handleTest ./sway.nix {}; + swayfx = handleTest ./swayfx.nix {}; switchTest = handleTest ./switch-test.nix {}; sympa = handleTest ./sympa.nix {}; syncthing = handleTest ./syncthing.nix {}; @@ -900,6 +909,7 @@ in { systemd-lock-handler = runTestOn ["aarch64-linux" "x86_64-linux"] ./systemd-lock-handler.nix; systemd-machinectl = handleTest ./systemd-machinectl.nix {}; systemd-networkd = handleTest ./systemd-networkd.nix {}; + systemd-networkd-bridge = handleTest ./systemd-networkd-bridge.nix {}; systemd-networkd-dhcpserver = handleTest ./systemd-networkd-dhcpserver.nix {}; systemd-networkd-dhcpserver-static-leases = handleTest ./systemd-networkd-dhcpserver-static-leases.nix {}; systemd-networkd-ipv6-prefix-delegation = handleTest ./systemd-networkd-ipv6-prefix-delegation.nix {}; @@ -935,7 +945,6 @@ in { tiddlywiki = handleTest ./tiddlywiki.nix {}; tigervnc = handleTest ./tigervnc.nix {}; timescaledb = handleTest ./timescaledb.nix {}; - promscale = handleTest ./promscale.nix {}; timezone = handleTest ./timezone.nix {}; tinc = handleTest ./tinc {}; tinydns = handleTest ./tinydns.nix {}; diff --git a/nixos/tests/caddy.nix b/nixos/tests/caddy.nix index 41d8e57de4686..0efe8f94e39dd 100644 --- a/nixos/tests/caddy.nix +++ b/nixos/tests/caddy.nix @@ -1,7 +1,7 @@ import ./make-test-python.nix ({ pkgs, ... }: { name = "caddy"; meta = with pkgs.lib.maintainers; { - maintainers = [ xfix Br1ght0ne ]; + maintainers = [ Br1ght0ne ]; }; nodes = { diff --git a/nixos/tests/castopod.nix b/nixos/tests/castopod.nix index 29bf8e8cacd89..3257cd3d363c7 100644 --- a/nixos/tests/castopod.nix +++ b/nixos/tests/castopod.nix @@ -2,7 +2,7 @@ import ./make-test-python.nix ({ pkgs, lib, ... }: { name = "castopod"; meta = with lib.maintainers; { - maintainers = [ alexoundos misuzu ]; + maintainers = [ alexoundos ]; }; nodes.castopod = { nodes, ... }: { diff --git a/nixos/tests/coder.nix b/nixos/tests/coder.nix index 12813827284b9..fd1fa0cc3031f 100644 --- a/nixos/tests/coder.nix +++ b/nixos/tests/coder.nix @@ -1,8 +1,6 @@ import ./make-test-python.nix ({ pkgs, ... }: { name = "coder"; - meta = with pkgs.lib.maintainers; { - maintainers = [ shyim ghuntley ]; - }; + meta.maintainers = pkgs.coder.meta.maintainers; nodes.machine = { pkgs, ... }: diff --git a/nixos/tests/db-rest.nix b/nixos/tests/db-rest.nix new file mode 100644 index 0000000000000..9249da904acbe --- /dev/null +++ b/nixos/tests/db-rest.nix @@ -0,0 +1,107 @@ +import ./make-test-python.nix ({ pkgs, ... }: +{ + name = "db-rest"; + meta.maintainers = with pkgs.lib.maintainers; [ marie ]; + + nodes = { + database = { + networking = { + interfaces.eth1 = { + ipv4.addresses = [ + { address = "192.168.2.10"; prefixLength = 24; } + ]; + }; + firewall.allowedTCPPorts = [ 31638 ]; + }; + + services.redis.servers.db-rest = { + enable = true; + bind = "0.0.0.0"; + requirePass = "choochoo"; + port = 31638; + }; + }; + + serverWithTcp = { pkgs, ... }: { + environment = { + etc = { + "db-rest/password-redis-db".text = '' + choochoo + ''; + }; + }; + + networking = { + interfaces.eth1 = { + ipv4.addresses = [ + { address = "192.168.2.11"; prefixLength = 24; } + ]; + }; + firewall.allowedTCPPorts = [ 3000 ]; + }; + + services.db-rest = { + enable = true; + host = "0.0.0.0"; + redis = { + enable = true; + createLocally = false; + host = "192.168.2.10"; + port = 31638; + passwordFile = "/etc/db-rest/password-redis-db"; + useSSL = false; + }; + }; + }; + + serverWithUnixSocket = { pkgs, ... }: { + networking = { + interfaces.eth1 = { + ipv4.addresses = [ + { address = "192.168.2.12"; prefixLength = 24; } + ]; + }; + firewall.allowedTCPPorts = [ 3000 ]; + }; + + services.db-rest = { + enable = true; + host = "0.0.0.0"; + redis = { + enable = true; + createLocally = true; + }; + }; + }; + + client = { + environment.systemPackages = [ pkgs.jq ]; + networking = { + interfaces.eth1 = { + ipv4.addresses = [ + { address = "192.168.2.13"; prefixLength = 24; } + ]; + }; + }; + }; + }; + + testScript = '' + start_all() + + with subtest("db-rest redis with TCP socket"): + database.wait_for_unit("redis-db-rest.service") + database.wait_for_open_port(31638) + + serverWithTcp.wait_for_unit("db-rest.service") + serverWithTcp.wait_for_open_port(3000) + + client.succeed("curl --fail --get http://192.168.2.11:3000/stations --data-urlencode 'query=Köln Hbf' | jq -r '.\"8000207\".name' | grep 'Köln Hbf'") + + with subtest("db-rest redis with Unix socket"): + serverWithUnixSocket.wait_for_unit("db-rest.service") + serverWithUnixSocket.wait_for_open_port(3000) + + client.succeed("curl --fail --get http://192.168.2.12:3000/stations --data-urlencode 'query=Köln Hbf' | jq -r '.\"8000207\".name' | grep 'Köln Hbf'") + ''; +}) diff --git a/nixos/tests/docker-registry.nix b/nixos/tests/docker-registry.nix index 3969ef3f0226f..4f033fc30b191 100644 --- a/nixos/tests/docker-registry.nix +++ b/nixos/tests/docker-registry.nix @@ -3,7 +3,7 @@ import ./make-test-python.nix ({ pkgs, ...} : { name = "docker-registry"; meta = with pkgs.lib.maintainers; { - maintainers = [ globin ironpinguin ]; + maintainers = [ globin ironpinguin cafkafk ]; }; nodes = { diff --git a/nixos/tests/earlyoom.nix b/nixos/tests/earlyoom.nix index 75bdf56899b30..b7850ddeaaab3 100644 --- a/nixos/tests/earlyoom.nix +++ b/nixos/tests/earlyoom.nix @@ -1,7 +1,7 @@ import ./make-test-python.nix ({ lib, ... }: { name = "earlyoom"; meta = { - maintainers = with lib.maintainers; [ ncfavier ]; + maintainers = with lib.maintainers; [ ncfavier AndersonTorres ]; }; machine = { diff --git a/nixos/tests/firefly-iii.nix b/nixos/tests/firefly-iii.nix new file mode 100644 index 0000000000000..c93d799320a48 --- /dev/null +++ b/nixos/tests/firefly-iii.nix @@ -0,0 +1,26 @@ +import ./make-test-python.nix ({ lib, pkgs, ... }: { + name = "firefly-iii"; + meta.maintainers = [ lib.maintainers.savyajha ]; + + nodes.machine = { config, ... }: { + environment.etc = { + "firefly-iii-appkey".text = "TestTestTestTestTestTestTestTest"; + }; + services.firefly-iii = { + enable = true; + virtualHost = "http://localhost"; + enableNginx = true; + settings = { + APP_KEY_FILE = "/etc/firefly-iii-appkey"; + LOG_CHANNEL = "stdout"; + SITE_OWNER = "mail@example.com"; + }; + }; + }; + + testScript = '' + machine.wait_for_unit("phpfpm-firefly-iii.service") + machine.wait_for_unit("nginx.service") + machine.succeed("curl -fvvv -Ls http://localhost/ | grep 'Firefly III'") + ''; +}) diff --git a/nixos/tests/forgejo.nix b/nixos/tests/forgejo.nix index b14df0a2c74f9..827fae2790c6e 100644 --- a/nixos/tests/forgejo.nix +++ b/nixos/tests/forgejo.nix @@ -22,8 +22,27 @@ let ''; signingPrivateKeyId = "4D642DE8B678C79D"; + actionsWorkflowYaml = '' + run-name: dummy workflow + on: + push: + jobs: + cat: + runs-on: native + steps: + - uses: http://localhost:3000/test/checkout@main + - run: cat testfile + ''; + # https://github.com/actions/checkout/releases + checkoutActionSource = pkgs.fetchFromGitHub { + owner = "actions"; + repo = "checkout"; + rev = "v4.1.1"; + hash = "sha256-h2/UIp8IjPo3eE4Gzx52Fb7pcgG/Ww7u31w5fdKVMos="; + }; + supportedDbTypes = [ "mysql" "postgres" "sqlite3" ]; - makeGForgejoTest = type: nameValuePair type (makeTest { + makeForgejoTest = type: nameValuePair type (makeTest { name = "forgejo-${type}"; meta.maintainers = with maintainers; [ bendlas emilylange ]; @@ -36,21 +55,28 @@ let settings.service.DISABLE_REGISTRATION = true; settings."repository.signing".SIGNING_KEY = signingPrivateKeyId; settings.actions.ENABLED = true; + settings.repository = { + ENABLE_PUSH_CREATE_USER = true; + DEFAULT_PUSH_CREATE_PRIVATE = false; + }; }; - environment.systemPackages = [ config.services.forgejo.package pkgs.gnupg pkgs.jq pkgs.file ]; + environment.systemPackages = [ config.services.forgejo.package pkgs.gnupg pkgs.jq pkgs.file pkgs.htmlq ]; services.openssh.enable = true; specialisation.runner = { inheritParentConfig = true; - configuration.services.gitea-actions-runner.instances."test" = { - enable = true; - name = "ci"; - url = "http://localhost:3000"; - labels = [ - # don't require docker/podman - "native:host" - ]; - tokenFile = "/var/lib/forgejo/runner_token"; + configuration.services.gitea-actions-runner = { + package = pkgs.forgejo-runner; + instances."test" = { + enable = true; + name = "ci"; + url = "http://localhost:3000"; + labels = [ + # type ":host" does not depend on docker/podman/lxc + "native:host" + ]; + tokenFile = "/var/lib/forgejo/runner_token"; + }; }; }; specialisation.dump = { @@ -62,11 +88,20 @@ let }; }; }; - client1 = { config, pkgs, ... }: { - environment.systemPackages = [ pkgs.git ]; - }; - client2 = { config, pkgs, ... }: { - environment.systemPackages = [ pkgs.git ]; + client = { ... }: { + programs.git = { + enable = true; + config = { + user.email = "test@localhost"; + user.name = "test"; + init.defaultBranch = "main"; + }; + }; + programs.ssh.extraConfig = '' + Host * + StrictHostKeyChecking no + IdentityFile ~/.ssh/privk + ''; }; }; @@ -75,26 +110,23 @@ let inherit (import ./ssh-keys.nix pkgs) snakeOilPrivateKey snakeOilPublicKey; serverSystem = nodes.server.system.build.toplevel; dumpFile = with nodes.server.specialisation.dump.configuration.services.forgejo.dump; "${backupDir}/${file}"; + remoteUri = "forgejo@server:test/repo"; + remoteUriCheckoutAction = "forgejo@server:test/checkout"; in '' import json - GIT_SSH_COMMAND = "ssh -i $HOME/.ssh/privk -o StrictHostKeyChecking=no" - REPO = "forgejo@server:test/repo" - PRIVK = "${snakeOilPrivateKey}" start_all() - client1.succeed("mkdir /tmp/repo") - client1.succeed("mkdir -p $HOME/.ssh") - client1.succeed(f"cat {PRIVK} > $HOME/.ssh/privk") - client1.succeed("chmod 0400 $HOME/.ssh/privk") - client1.succeed("git -C /tmp/repo init") - client1.succeed("echo hello world > /tmp/repo/testfile") - client1.succeed("git -C /tmp/repo add .") - client1.succeed("git config --global user.email test@localhost") - client1.succeed("git config --global user.name test") - client1.succeed("git -C /tmp/repo commit -m 'Initial import'") - client1.succeed(f"git -C /tmp/repo remote add origin {REPO}") + client.succeed("mkdir -p ~/.ssh") + client.succeed("(umask 0077; cat ${snakeOilPrivateKey} > ~/.ssh/privk)") + + client.succeed("mkdir /tmp/repo") + client.succeed("git -C /tmp/repo init") + client.succeed("echo 'hello world' > /tmp/repo/testfile") + client.succeed("git -C /tmp/repo add .") + client.succeed("git -C /tmp/repo commit -m 'Initial import'") + client.succeed("git -C /tmp/repo remote add origin ${remoteUri}") server.wait_for_unit("forgejo.service") server.wait_for_open_port(3000) @@ -109,9 +141,9 @@ let assert "BEGIN PGP PUBLIC KEY BLOCK" in server.succeed("curl http://localhost:3000/api/v1/signing-key.gpg") api_version = json.loads(server.succeed("curl http://localhost:3000/api/forgejo/v1/version")).get("version") - assert "development" != api_version and "-gitea-" in api_version, ( + assert "development" != api_version and "${pkgs.forgejo.version}+gitea-" in api_version, ( "/api/forgejo/v1/version should not return 'development' " - + f"but should contain a gitea compatibility version string. Got '{api_version}' instead." + + f"but should contain a forgejo+gitea compatibility version string. Got '{api_version}' instead." ) server.succeed( @@ -120,7 +152,7 @@ let ) server.succeed( "su -l forgejo -c 'GITEA_WORK_DIR=/var/lib/forgejo gitea admin user create " - + "--username test --password totallysafe --email test@localhost'" + + "--username test --password totallysafe --email test@localhost --must-change-password=false'" ) api_token = server.succeed( @@ -143,18 +175,14 @@ let + ' -d \'{"key":"${snakeOilPublicKey}","read_only":true,"title":"SSH"}\''' ) - client1.succeed( - f"GIT_SSH_COMMAND='{GIT_SSH_COMMAND}' git -C /tmp/repo push origin master" - ) + client.succeed("git -C /tmp/repo push origin main") - client2.succeed("mkdir -p $HOME/.ssh") - client2.succeed(f"cat {PRIVK} > $HOME/.ssh/privk") - client2.succeed("chmod 0400 $HOME/.ssh/privk") - client2.succeed(f"GIT_SSH_COMMAND='{GIT_SSH_COMMAND}' git clone {REPO}") - client2.succeed('test "$(cat repo/testfile | xargs echo -n)" = "hello world"') + client.succeed("git clone ${remoteUri} /tmp/repo-clone") + print(client.succeed("ls -lash /tmp/repo-clone")) + assert "hello world" == client.succeed("cat /tmp/repo-clone/testfile").strip() with subtest("Testing git protocol version=2 over ssh"): - git_protocol = client2.succeed(f"GIT_SSH_COMMAND='{GIT_SSH_COMMAND}' GIT_TRACE2_EVENT=true git -C repo fetch |& grep negotiated-version") + git_protocol = client.succeed("GIT_TRACE2_EVENT=true git -C /tmp/repo-clone fetch |& grep negotiated-version") version = json.loads(git_protocol).get("value") assert version == "2", f"git did not negotiate protocol version 2, but version {version} instead." @@ -164,7 +192,7 @@ let timeout=10 ) - with subtest("Testing runner registration"): + with subtest("Testing runner registration and action workflow"): server.succeed( "su -l forgejo -c 'GITEA_WORK_DIR=/var/lib/forgejo gitea actions generate-runner-token' | sed 's/^/TOKEN=/' | tee /var/lib/forgejo/runner_token" ) @@ -172,6 +200,52 @@ let server.wait_for_unit("gitea-runner-test.service") server.succeed("journalctl -o cat -u gitea-runner-test.service | grep -q 'Runner registered successfully'") + # enable actions feature for this repository, defaults to disabled + server.succeed( + "curl --fail -X PATCH http://localhost:3000/api/v1/repos/test/repo " + + "-H 'Accept: application/json' -H 'Content-Type: application/json' " + + f"-H 'Authorization: token {api_token}'" + + ' -d \'{"has_actions":true}\''' + ) + + # mirror "actions/checkout" action + client.succeed("cp -R ${checkoutActionSource}/ /tmp/checkout") + client.succeed("git -C /tmp/checkout init") + client.succeed("git -C /tmp/checkout add .") + client.succeed("git -C /tmp/checkout commit -m 'Initial import'") + client.succeed("git -C /tmp/checkout remote add origin ${remoteUriCheckoutAction}") + client.succeed("git -C /tmp/checkout push origin main") + + # push workflow to initial repo + client.succeed("mkdir -p /tmp/repo/.forgejo/workflows") + client.succeed("cp ${pkgs.writeText "dummy-workflow.yml" actionsWorkflowYaml} /tmp/repo/.forgejo/workflows/") + client.succeed("git -C /tmp/repo add .") + client.succeed("git -C /tmp/repo commit -m 'Add dummy workflow'") + client.succeed("git -C /tmp/repo push origin main") + + def poll_workflow_action_status(_) -> bool: + output = server.succeed( + "curl --fail http://localhost:3000/test/repo/actions | " + + 'htmlq ".flex-item-leading span" --attribute "data-tooltip-content"' + ).strip() + + # values taken from https://codeberg.org/forgejo/forgejo/src/commit/af47c583b4fb3190fa4c4c414500f9941cc02389/options/locale/locale_en-US.ini#L3649-L3661 + if output in [ "Failure", "Canceled", "Skipped", "Blocked" ]: + raise Exception(f"Workflow status is '{output}', which we consider failed.") + server.log(f"Command returned '{output}', which we consider failed.") + + elif output in [ "Unknown", "Waiting", "Running", "" ]: + server.log(f"Workflow status is '{output}'. Waiting some more...") + return False + + elif output in [ "Success" ]: + return True + + raise Exception(f"Workflow status is '{output}', which we don't know. Value mappings likely need updating.") + + with server.nested("Waiting for the workflow run to be successful"): + retry(poll_workflow_action_status) + with subtest("Testing backup service"): server.succeed("${serverSystem}/specialisation/dump/bin/switch-to-configuration test") server.systemctl("start forgejo-dump") @@ -181,4 +255,4 @@ let }); in -listToAttrs (map makeGForgejoTest supportedDbTypes) +listToAttrs (map makeForgejoTest supportedDbTypes) diff --git a/nixos/tests/freshrss-none-auth.nix b/nixos/tests/freshrss-none-auth.nix new file mode 100644 index 0000000000000..fd63470386a05 --- /dev/null +++ b/nixos/tests/freshrss-none-auth.nix @@ -0,0 +1,19 @@ +import ./make-test-python.nix ({ lib, pkgs, ... }: { + name = "freshrss"; + meta.maintainers = with lib.maintainers; [ mattchrist ]; + + nodes.machine = { pkgs, ... }: { + services.freshrss = { + enable = true; + baseUrl = "http://localhost"; + authType = "none"; + }; + }; + + testScript = '' + machine.wait_for_unit("multi-user.target") + machine.wait_for_open_port(80) + response = machine.succeed("curl -vvv -s http://127.0.0.1:80/i/") + assert '<title>Main stream · FreshRSS</title>' in response, "FreshRSS stream page didn't load successfully" + ''; +}) diff --git a/nixos/tests/gnome-extensions.nix b/nixos/tests/gnome-extensions.nix index a9bb5e3766b7b..51ccabd7e6a65 100644 --- a/nixos/tests/gnome-extensions.nix +++ b/nixos/tests/gnome-extensions.nix @@ -2,7 +2,7 @@ import ./make-test-python.nix ( { pkgs, lib, ...}: { name = "gnome-extensions"; - meta.maintainers = [ lib.maintainers.piegames ]; + meta.maintainers = [ ]; nodes.machine = { pkgs, ... }: diff --git a/nixos/tests/gonic.nix b/nixos/tests/gonic.nix index 726d7da0970f7..adf0f511a9cf7 100644 --- a/nixos/tests/gonic.nix +++ b/nixos/tests/gonic.nix @@ -2,11 +2,19 @@ import ./make-test-python.nix ({ pkgs, ... }: { name = "gonic"; nodes.machine = { ... }: { + systemd.tmpfiles.settings = { + "10-gonic" = { + "/tmp/music"."d" = {}; + "/tmp/podcast"."d" = {}; + "/tmp/playlists"."d" = {}; + }; + }; services.gonic = { enable = true; settings = { - music-path = [ "/tmp" ]; - podcast-path = "/tmp"; + music-path = [ "/tmp/music" ]; + podcast-path = "/tmp/podcast"; + playlists-path = "/tmp/playlists"; }; }; }; diff --git a/nixos/tests/gvisor.nix b/nixos/tests/gvisor.nix index 7f130b709fc9d..5c9447b07118d 100644 --- a/nixos/tests/gvisor.nix +++ b/nixos/tests/gvisor.nix @@ -3,7 +3,7 @@ import ./make-test-python.nix ({ pkgs, ... }: { name = "gvisor"; meta = with pkgs.lib.maintainers; { - maintainers = [ andrew-d ]; + maintainers = [ ]; }; nodes = { diff --git a/nixos/tests/incus/virtual-machine.nix b/nixos/tests/incus/virtual-machine.nix index ab378c7b9490e..eebbbd113ed16 100644 --- a/nixos/tests/incus/virtual-machine.nix +++ b/nixos/tests/incus/virtual-machine.nix @@ -30,6 +30,9 @@ in memorySize = 1024; diskSize = 4096; + # Provide a TPM to test vTPM support for guests + tpm.enable = true; + incus.enable = true; }; networking.nftables.enable = true; @@ -47,8 +50,14 @@ in with subtest("virtual-machine image can be imported"): machine.succeed("incus image import ${vm-image-metadata}/*/*.tar.xz ${vm-image-disk}/nixos.qcow2 --alias nixos") + with subtest("virtual-machine can be created"): + machine.succeed("incus create nixos ${instance-name} --vm --config limits.memory=512MB --config security.secureboot=false") + + with subtest("virtual tpm can be configured"): + machine.succeed("incus config device add ${instance-name} vtpm tpm path=/dev/tpm0") + with subtest("virtual-machine can be launched and become available"): - machine.succeed("incus launch nixos ${instance-name} --vm --config limits.memory=512MB --config security.secureboot=false") + machine.succeed("incus start ${instance-name}") with machine.nested("Waiting for instance to start and be usable"): retry(instance_is_up) @@ -57,5 +66,14 @@ in with subtest("lxd-agent has a valid path"): machine.succeed("incus exec ${instance-name} -- bash -c 'true'") + + with subtest("guest supports cpu hotplug"): + machine.succeed("incus config set ${instance-name} limits.cpu=1") + count = int(machine.succeed("incus exec ${instance-name} -- nproc").strip()) + assert count == 1, f"Wrong number of CPUs reported, want: 1, got: {count}" + + machine.succeed("incus config set ${instance-name} limits.cpu=2") + count = int(machine.succeed("incus exec ${instance-name} -- nproc").strip()) + assert count == 2, f"Wrong number of CPUs reported, want: 2, got: {count}" ''; }) diff --git a/nixos/tests/isolate.nix b/nixos/tests/isolate.nix new file mode 100644 index 0000000000000..327231be1cd4a --- /dev/null +++ b/nixos/tests/isolate.nix @@ -0,0 +1,38 @@ +import ./make-test-python.nix ({ lib, ... }: +{ + name = "isolate"; + meta.maintainers = with lib.maintainers; [ virchau13 ]; + + nodes.machine = + { ... }: + { + security.isolate = { + enable = true; + }; + }; + + testScript = '' + bash_path = machine.succeed('realpath $(which bash)').strip() + sleep_path = machine.succeed('realpath $(which sleep)').strip() + def sleep_test(walltime, sleeptime): + return f'isolate --no-default-dirs --wall-time {walltime} ' + \ + f'--dir=/box={box_path} --dir=/nix=/nix --run -- ' + \ + f"{bash_path} -c 'exec -a sleep {sleep_path} {sleeptime}'" + + def sleep_test_cg(walltime, sleeptime): + return f'isolate --cg --no-default-dirs --wall-time {walltime} ' + \ + f'--dir=/box={box_path} --dir=/nix=/nix --processes=2 --run -- ' + \ + f"{bash_path} -c '( exec -a sleep {sleep_path} {sleeptime} )'" + + with subtest("without cgroups"): + box_path = machine.succeed('isolate --init').strip() + machine.succeed(sleep_test(1, 0.5)) + machine.fail(sleep_test(0.5, 1)) + machine.succeed('isolate --cleanup') + with subtest("with cgroups"): + box_path = machine.succeed('isolate --cg --init').strip() + machine.succeed(sleep_test_cg(1, 0.5)) + machine.fail(sleep_test_cg(0.5, 1)) + machine.succeed('isolate --cg --cleanup') + ''; +}) diff --git a/nixos/tests/libinput.nix b/nixos/tests/libinput.nix index 9b6fa159b999c..b002492b16657 100644 --- a/nixos/tests/libinput.nix +++ b/nixos/tests/libinput.nix @@ -12,7 +12,7 @@ import ./make-test-python.nix ({ ... }: test-support.displayManager.auto.user = "alice"; - services.xserver.libinput = { + services.libinput = { enable = true; mouse = { naturalScrolling = true; diff --git a/nixos/tests/lomiri.nix b/nixos/tests/lomiri.nix index c1e777873b08f..9d6337e9977cb 100644 --- a/nixos/tests/lomiri.nix +++ b/nixos/tests/lomiri.nix @@ -253,22 +253,35 @@ in { with subtest("ayatana indicators work"): open_starter() machine.send_chars("Indicators\n") - machine.wait_for_text(r"(Indicators|Client|List|datetime|session)") + machine.wait_for_text(r"(Indicators|Client|List|network|datetime|session)") machine.screenshot("indicators_open") # Element tab order within the indicator menus is not fully deterministic # Only check that the indicators are listed & their items load + with subtest("lomiri indicator network works"): + # Select indicator-network + machine.send_key("tab") + # Don't go further down, first entry + machine.send_key("ret") + machine.wait_for_text(r"(Flight|Wi-Fi)") + machine.screenshot("indicators_network") + + machine.send_key("shift-tab") + machine.send_key("ret") + machine.wait_for_text(r"(Indicators|Client|List|network|datetime|session)") + with subtest("ayatana indicator datetime works"): # Select ayatana-indicator-datetime machine.send_key("tab") + machine.send_key("down") machine.send_key("ret") machine.wait_for_text("Time and Date Settings") machine.screenshot("indicators_timedate") machine.send_key("shift-tab") machine.send_key("ret") - machine.wait_for_text(r"(Indicators|Client|List|datetime|session)") + machine.wait_for_text(r"(Indicators|Client|List|network|datetime|session)") with subtest("ayatana indicator session works"): # Select ayatana-indicator-session diff --git a/nixos/tests/networking/networkmanager.nix b/nixos/tests/networking/networkmanager.nix new file mode 100644 index 0000000000000..e654e37d7efb7 --- /dev/null +++ b/nixos/tests/networking/networkmanager.nix @@ -0,0 +1,172 @@ +{ system ? builtins.currentSystem +, config ? {} +, pkgs ? import ../.. { inherit system config; } +}: + +with import ../../lib/testing-python.nix { inherit system pkgs; }; + +let + lib = pkgs.lib; + # this is intended as a client test since you shouldn't use NetworkManager for a router or server + # so using systemd-networkd for the router vm is fine in these tests. + router = import ./router.nix { networkd = true; }; + qemu-common = import ../../lib/qemu-common.nix { inherit (pkgs) lib pkgs; }; + clientConfig = extraConfig: lib.recursiveUpdate { + networking.useDHCP = false; + + # Make sure that only NetworkManager configures the interface + networking.interfaces = lib.mkForce { + eth1 = {}; + }; + networking.networkmanager = { + enable = true; + # this is needed so NM doesn't generate 'Wired Connection' profiles and instead uses the default one + settings.main.no-auto-default = "*"; + ensureProfiles.profiles.default = { + connection = { + id = "default"; + type = "ethernet"; + interface-name = "eth1"; + autoconnect = true; + }; + }; + }; + } extraConfig; + testCases = { + static = { + name = "static"; + nodes = { + inherit router; + client = clientConfig { + networking.networkmanager.ensureProfiles.profiles.default = { + ipv4.method = "manual"; + ipv4.addresses = "192.168.1.42/24"; + ipv4.gateway = "192.168.1.1"; + ipv6.method = "manual"; + ipv6.addresses = "fd00:1234:5678:1::42/64"; + ipv6.gateway = "fd00:1234:5678:1::1"; + }; + }; + }; + testScript = '' + start_all() + router.systemctl("start network-online.target") + router.wait_for_unit("network-online.target") + client.wait_for_unit("NetworkManager.service") + + with subtest("Wait until we have an ip address on each interface"): + client.wait_until_succeeds("ip addr show dev eth1 | grep -q '192.168.1'") + client.wait_until_succeeds("ip addr show dev eth1 | grep -q 'fd00:1234:5678:1:'") + + with subtest("Test if icmp echo works"): + client.wait_until_succeeds("ping -c 1 192.168.3.1") + client.wait_until_succeeds("ping -c 1 fd00:1234:5678:3::1") + router.wait_until_succeeds("ping -c 1 192.168.1.42") + router.wait_until_succeeds("ping -c 1 fd00:1234:5678:1::42") + ''; + }; + auto = { + name = "auto"; + nodes = { + inherit router; + client = clientConfig { + networking.networkmanager.ensureProfiles.profiles.default = { + ipv4.method = "auto"; + ipv6.method = "auto"; + }; + }; + }; + testScript = '' + start_all() + router.systemctl("start network-online.target") + router.wait_for_unit("network-online.target") + client.wait_for_unit("NetworkManager.service") + + with subtest("Wait until we have an ip address on each interface"): + client.wait_until_succeeds("ip addr show dev eth1 | grep -q '192.168.1'") + client.wait_until_succeeds("ip addr show dev eth1 | grep -q 'fd00:1234:5678:1:'") + + with subtest("Test if icmp echo works"): + client.wait_until_succeeds("ping -c 1 192.168.1.1") + client.wait_until_succeeds("ping -c 1 fd00:1234:5678:1::1") + router.wait_until_succeeds("ping -c 1 192.168.1.2") + router.wait_until_succeeds("ping -c 1 fd00:1234:5678:1::2") + ''; + }; + dns = { + name = "dns"; + nodes = { + inherit router; + dynamic = clientConfig { + networking.networkmanager.ensureProfiles.profiles.default = { + ipv4.method = "auto"; + }; + }; + static = clientConfig { + networking.networkmanager.ensureProfiles.profiles.default = { + ipv4 = { + method = "auto"; + ignore-auto-dns = "true"; + dns = "10.10.10.10"; + dns-search = ""; + }; + }; + }; + }; + testScript = '' + start_all() + router.systemctl("start network-online.target") + router.wait_for_unit("network-online.target") + dynamic.wait_for_unit("NetworkManager.service") + static.wait_for_unit("NetworkManager.service") + + dynamic.wait_until_succeeds("cat /etc/resolv.conf | grep -q '192.168.1.1'") + static.wait_until_succeeds("cat /etc/resolv.conf | grep -q '10.10.10.10'") + static.wait_until_fails("cat /etc/resolv.conf | grep -q '192.168.1.1'") + ''; + }; + dispatcherScripts = { + name = "dispatcherScripts"; + nodes.client = clientConfig { + networking.networkmanager.dispatcherScripts = [{ + type = "pre-up"; + source = pkgs.writeText "testHook" '' + touch /tmp/dispatcher-scripts-are-working + ''; + }]; + }; + testScript = '' + start_all() + client.wait_for_unit("NetworkManager.service") + client.wait_until_succeeds("stat /tmp/dispatcher-scripts-are-working") + ''; + }; + envsubst = { + name = "envsubst"; + nodes.client = let + # you should never write secrets in to your nixos configuration, please use tools like sops-nix or agenix + secretFile = pkgs.writeText "my-secret.env" '' + MY_SECRET_IP=fd00:1234:5678:1::23/64 + ''; + in clientConfig { + networking.networkmanager.ensureProfiles.environmentFiles = [ secretFile ]; + networking.networkmanager.ensureProfiles.profiles.default = { + ipv6.method = "manual"; + ipv6.addresses = "$MY_SECRET_IP"; + }; + }; + testScript = '' + start_all() + client.wait_for_unit("NetworkManager.service") + client.wait_until_succeeds("ip addr show dev eth1 | grep -q 'fd00:1234:5678:1:'") + client.wait_until_succeeds("ping -c 1 fd00:1234:5678:1::23") + ''; + }; + }; +in lib.mapAttrs (lib.const (attrs: makeTest (attrs // { + name = "${attrs.name}-Networking-NetworkManager"; + meta = { + maintainers = with lib.maintainers; [ janik ]; + }; + +}))) testCases diff --git a/nixos/tests/nginx-sso.nix b/nixos/tests/nginx-sso.nix index 221c5f4ed9058..2bb9c7a1c3bb7 100644 --- a/nixos/tests/nginx-sso.nix +++ b/nixos/tests/nginx-sso.nix @@ -1,7 +1,7 @@ import ./make-test-python.nix ({ pkgs, ... }: { name = "nginx-sso"; meta = { - maintainers = with pkgs.lib.maintainers; [ delroth ]; + maintainers = with pkgs.lib.maintainers; [ ambroisie ]; }; nodes.machine = { diff --git a/nixos/tests/openssh.nix b/nixos/tests/openssh.nix index 8074fd2ed4838..a039986621cab 100644 --- a/nixos/tests/openssh.nix +++ b/nixos/tests/openssh.nix @@ -22,6 +22,19 @@ in { ]; }; + server-allowed-users = + { ... }: + + { + services.openssh = { enable = true; settings.AllowUsers = [ "alice" "bob" ]; }; + users.groups = { alice = { }; bob = { }; carol = { }; }; + users.users = { + alice = { isNormalUser = true; group = "alice"; openssh.authorizedKeys.keys = [ snakeOilPublicKey ]; }; + bob = { isNormalUser = true; group = "bob"; openssh.authorizedKeys.keys = [ snakeOilPublicKey ]; }; + carol = { isNormalUser = true; group = "carol"; openssh.authorizedKeys.keys = [ snakeOilPublicKey ]; }; + }; + }; + server-lazy = { ... }: @@ -95,17 +108,21 @@ in { }; }; - server_allowedusers = - { ... }: - + server-no-pam = + { pkgs, ... }: { - services.openssh = { enable = true; settings.AllowUsers = [ "alice" "bob" ]; }; - users.groups = { alice = { }; bob = { }; carol = { }; }; - users.users = { - alice = { isNormalUser = true; group = "alice"; openssh.authorizedKeys.keys = [ snakeOilPublicKey ]; }; - bob = { isNormalUser = true; group = "bob"; openssh.authorizedKeys.keys = [ snakeOilPublicKey ]; }; - carol = { isNormalUser = true; group = "carol"; openssh.authorizedKeys.keys = [ snakeOilPublicKey ]; }; + programs.ssh.package = pkgs.opensshPackages.openssh.override { + withPAM = false; + }; + services.openssh = { + enable = true; + settings = { + UsePAM = false; + }; }; + users.users.root.openssh.authorizedKeys.keys = [ + snakeOilPublicKey + ]; }; client = @@ -119,8 +136,10 @@ in { start_all() server.wait_for_unit("sshd", timeout=30) + server_allowed_users.wait_for_unit("sshd", timeout=30) server_localhost_only.wait_for_unit("sshd", timeout=30) server_match_rule.wait_for_unit("sshd", timeout=30) + server_no_pam.wait_for_unit("sshd", timeout=30) server_lazy.wait_for_unit("sshd.socket", timeout=30) server_localhost_only_lazy.wait_for_unit("sshd.socket", timeout=30) @@ -166,8 +185,9 @@ in { "cat ${snakeOilPrivateKey} > privkey.snakeoil" ) client.succeed("chmod 600 privkey.snakeoil") + # The final segment in this IP is allocated according to the alphabetical order of machines in this test. client.succeed( - "ssh -p 2222 -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no -i privkey.snakeoil root@192.168.2.4 true", + "ssh -p 2222 -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no -i privkey.snakeoil root@192.168.2.5 true", timeout=30 ) @@ -198,15 +218,25 @@ in { ) client.succeed("chmod 600 privkey.snakeoil") client.succeed( - "ssh -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no -i privkey.snakeoil alice@server_allowedusers true", + "ssh -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no -i privkey.snakeoil alice@server-allowed-users true", timeout=30 ) client.succeed( - "ssh -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no -i privkey.snakeoil bob@server_allowedusers true", + "ssh -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no -i privkey.snakeoil bob@server-allowed-users true", timeout=30 ) client.fail( - "ssh -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no -i privkey.snakeoil carol@server_allowedusers true", + "ssh -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no -i privkey.snakeoil carol@server-allowed-users true", + timeout=30 + ) + + with subtest("no-pam"): + client.succeed( + "cat ${snakeOilPrivateKey} > privkey.snakeoil" + ) + client.succeed("chmod 600 privkey.snakeoil") + client.succeed( + "ssh -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no -i privkey.snakeoil server-no-pam true", timeout=30 ) ''; diff --git a/nixos/tests/paperless.nix b/nixos/tests/paperless.nix index 3d834b29958de..3ef291ba7e06f 100644 --- a/nixos/tests/paperless.nix +++ b/nixos/tests/paperless.nix @@ -23,6 +23,7 @@ import ./make-test-python.nix ({ lib, ... }: { }; services.paperless.settings = { PAPERLESS_DBHOST = "/run/postgresql"; + PAPERLESS_OCR_LANGUAGE = "deu"; }; }; }; in self; diff --git a/nixos/tests/phosh.nix b/nixos/tests/phosh.nix index 78d6da31beee1..d505f0ffc5245 100644 --- a/nixos/tests/phosh.nix +++ b/nixos/tests/phosh.nix @@ -25,6 +25,10 @@ in { }; }; + environment.systemPackages = [ + pkgs.phosh-mobile-settings + ]; + systemd.services.phosh = { environment = { # Accelerated graphics fail on phoc 0.20 (wlroots 0.15) @@ -63,8 +67,13 @@ in { phone.screenshot("03launcher") with subtest("Check the on-screen keyboard shows"): - phone.send_chars("setting", delay=0.2) + phone.send_chars("mobile setting", delay=0.2) phone.wait_for_text("123") # A button on the OSK phone.screenshot("04osk") + + with subtest("Check mobile-phosh-settings starts"): + phone.send_chars("\n") + phone.wait_for_text("Tweak advanced mobile settings"); + phone.screenshot("05settings") ''; }) diff --git a/nixos/tests/predictable-interface-names.nix b/nixos/tests/predictable-interface-names.nix index 51d5e8ae59b92..9ac4f8211e6b1 100644 --- a/nixos/tests/predictable-interface-names.nix +++ b/nixos/tests/predictable-interface-names.nix @@ -5,7 +5,7 @@ let inherit (import ../lib/testing-python.nix { inherit system pkgs; }) makeTest; - testCombinations = pkgs.lib.cartesianProductOfSets { + testCombinations = pkgs.lib.cartesianProduct { predictable = [true false]; withNetworkd = [true false]; systemdStage1 = [true false]; diff --git a/nixos/tests/prometheus-exporters.nix b/nixos/tests/prometheus-exporters.nix index 3dc368e320ff2..56569c4de2c85 100644 --- a/nixos/tests/prometheus-exporters.nix +++ b/nixos/tests/prometheus-exporters.nix @@ -227,6 +227,54 @@ let ''; }; + dnssec = { + exporterConfig = { + enable = true; + configuration = { + records = [ + { + zone = "example.com"; + record = "@"; + type = "SOA"; + } + ]; + }; + resolvers = [ "127.0.0.1:53" ]; + }; + metricProvider = { + services.knot = { + enable = true; + settingsFile = pkgs.writeText "knot.conf" '' + server: + listen: 127.0.0.1@53 + template: + - id: default + storage: ${pkgs.buildEnv { + name = "zones"; + paths = [(pkgs.writeTextDir "example.com.zone" '' + @ SOA ns1.example.com. noc.example.com. 2024032401 86400 7200 3600000 172800 + @ NS ns1 + ns1 A 192.168.0.1 + '')]; + }} + zonefile-load: difference + zonefile-sync: -1 + zone: + - domain: example.com + file: example.com.zone + dnssec-signing: on + ''; + }; + }; + exporterTest = '' + wait_for_unit("knot.service") + wait_for_open_port(53) + wait_for_unit("prometheus-dnssec-exporter.service") + wait_for_open_port(9204) + succeed("curl -sSf http://localhost:9204/metrics | grep 'example.com'") + ''; + }; + # Access to WHOIS server is required to properly test this exporter, so # just perform basic sanity check that the exporter is running and returns # a failure. @@ -859,7 +907,7 @@ let attrs = { objectClass = [ "olcDatabaseConfig" "olcMdbConfig" ]; olcDatabase = "{1}mdb"; - olcDbDirectory = "/var/db/openldap"; + olcDbDirectory = "/var/lib/openldap/db"; olcSuffix = "dc=example"; olcRootDN = { # cn=root,dc=example diff --git a/nixos/tests/promscale.nix b/nixos/tests/promscale.nix deleted file mode 100644 index da18628f2482c..0000000000000 --- a/nixos/tests/promscale.nix +++ /dev/null @@ -1,60 +0,0 @@ -# mostly copied from ./timescaledb.nix which was copied from ./postgresql.nix -# as it seemed unapproriate to test additional extensions for postgresql there. - -{ system ? builtins.currentSystem -, config ? { } -, pkgs ? import ../.. { inherit system config; } -}: - -with import ../lib/testing-python.nix { inherit system pkgs; }; -with pkgs.lib; - -let - postgresql-versions = import ../../pkgs/servers/sql/postgresql pkgs; - test-sql = pkgs.writeText "postgresql-test" '' - CREATE USER promscale SUPERUSER PASSWORD 'promscale'; - CREATE DATABASE promscale OWNER promscale; - ''; - - make-postgresql-test = postgresql-name: postgresql-package: makeTest { - name = postgresql-name; - meta = with pkgs.lib.maintainers; { - maintainers = [ anpin ]; - }; - - nodes.machine = { config, pkgs, ... }: - { - services.postgresql = { - enable = true; - package = postgresql-package; - extraPlugins = ps: with ps; [ - timescaledb - promscale_extension - ]; - settings = { shared_preload_libraries = "timescaledb, promscale"; }; - }; - environment.systemPackages = with pkgs; [ promscale ]; - }; - - testScript = '' - machine.start() - machine.wait_for_unit("postgresql") - with subtest("Postgresql with extensions timescaledb and promscale is available just after unit start"): - print(machine.succeed("sudo -u postgres psql -f ${test-sql}")) - machine.succeed("sudo -u postgres psql promscale -c 'SHOW shared_preload_libraries;' | grep promscale") - machine.succeed( - "promscale --db.name promscale --db.password promscale --db.user promscale --db.ssl-mode allow --startup.install-extensions --startup.only" - ) - machine.succeed("sudo -u postgres psql promscale -c 'SELECT ps_trace.get_trace_retention_period();' | grep '(1 row)'") - machine.shutdown() - ''; - }; - #version 15 is not supported yet - applicablePostgresqlVersions = filterAttrs (_: value: versionAtLeast value.version "12" && !(versionAtLeast value.version "15")) postgresql-versions; -in -mapAttrs' - (name: package: { - inherit name; - value = make-postgresql-test name package; - }) - applicablePostgresqlVersions diff --git a/nixos/tests/qtile.nix b/nixos/tests/qtile.nix new file mode 100644 index 0000000000000..b4d8f9d421144 --- /dev/null +++ b/nixos/tests/qtile.nix @@ -0,0 +1,34 @@ +import ./make-test-python.nix ({ lib, ...} : { + name = "qtile"; + + meta = { + maintainers = with lib.maintainers; [ sigmanificient ]; + }; + + nodes.machine = { pkgs, lib, ... }: { + imports = [ ./common/x11.nix ./common/user-account.nix ]; + test-support.displayManager.auto.user = "alice"; + + services.xserver.windowManager.qtile.enable = true; + services.displayManager.defaultSession = lib.mkForce "none+qtile"; + + environment.systemPackages = [ pkgs.kitty ]; + }; + + 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 client is available"): + machine.succeed("qtile --version") + + with subtest("ensure we can open a new terminal"): + machine.sleep(2) + machine.send_key("meta_l-ret") + machine.wait_for_window(r"alice.*?machine") + machine.sleep(2) + machine.screenshot("terminal") + ''; +}) diff --git a/nixos/tests/radicale.nix b/nixos/tests/radicale.nix index 66650dce4a008..868b28085a675 100644 --- a/nixos/tests/radicale.nix +++ b/nixos/tests/radicale.nix @@ -6,7 +6,7 @@ let port = "5232"; filesystem_folder = "/data/radicale"; - cli = "${pkgs.calendar-cli}/bin/calendar-cli --caldav-user ${user} --caldav-pass ${password}"; + cli = "${lib.getExe pkgs.calendar-cli} --caldav-user ${user} --caldav-pass ${password}"; in { name = "radicale3"; meta.maintainers = with lib.maintainers; [ dotlambda ]; diff --git a/nixos/tests/sunshine.nix b/nixos/tests/sunshine.nix new file mode 100644 index 0000000000000..7c7e86de203a0 --- /dev/null +++ b/nixos/tests/sunshine.nix @@ -0,0 +1,70 @@ +import ./make-test-python.nix ({ pkgs, lib, ... }: { + name = "sunshine"; + meta = { + # test is flaky on aarch64 + broken = pkgs.stdenv.isAarch64; + maintainers = [ lib.maintainers.devusb ]; + }; + + nodes.sunshine = { config, pkgs, ... }: { + imports = [ + ./common/x11.nix + ]; + + services.sunshine = { + enable = true; + openFirewall = true; + settings = { + capture = "x11"; + encoder = "software"; + output_name = 0; + }; + }; + + environment.systemPackages = with pkgs; [ + gxmessage + ]; + + }; + + nodes.moonlight = { config, pkgs, ... }: { + imports = [ + ./common/x11.nix + ]; + + environment.systemPackages = with pkgs; [ + moonlight-qt + ]; + + }; + + enableOCR = true; + + testScript = '' + # start the tests, wait for sunshine to be up + start_all() + sunshine.wait_for_open_port(48010,"localhost") + + # set the admin username/password, restart sunshine + sunshine.execute("sunshine --creds sunshine sunshine") + sunshine.systemctl("restart sunshine","root") + sunshine.wait_for_open_port(48010,"localhost") + + # initiate pairing from moonlight + moonlight.execute("moonlight pair sunshine --pin 1234 >&2 & disown") + moonlight.wait_for_console_text("Executing request") + + # respond to pairing request from sunshine + sunshine.succeed("curl --insecure -u sunshine:sunshine -d '{\"pin\": \"1234\"}' https://localhost:47990/api/pin") + + # close moonlight once pairing complete + moonlight.send_key("kp_enter") + + # put words on the sunshine screen for moonlight to see + sunshine.execute("gxmessage 'hello world' -center -font 'sans 75' >&2 & disown") + + # connect to sunshine from moonlight and look for the words + moonlight.execute("moonlight --video-decoder software stream sunshine 'Desktop' >&2 & disown") + moonlight.wait_for_text("hello world") + ''; +}) diff --git a/nixos/tests/swayfx.nix b/nixos/tests/swayfx.nix new file mode 100644 index 0000000000000..77844ec80ae1d --- /dev/null +++ b/nixos/tests/swayfx.nix @@ -0,0 +1,207 @@ +import ./make-test-python.nix ( + { pkgs, lib, ... }: + { + name = "swayfx"; + meta = { + maintainers = with lib.maintainers; [ eclairevoyant ]; + }; + + # testScriptWithTypes:49: error: Cannot call function of unknown type + # (machine.succeed if succeed else machine.execute)( + # ^ + # Found 1 error in 1 file (checked 1 source file) + skipTypeCheck = true; + + nodes.machine = + { config, ... }: + { + # Automatically login on tty1 as a normal user: + imports = [ ./common/user-account.nix ]; + services.getty.autologinUser = "alice"; + + environment = { + # For glinfo and wayland-info: + systemPackages = with pkgs; [ + mesa-demos + wayland-utils + alacritty + ]; + # Use a fixed SWAYSOCK path (for swaymsg): + variables = { + "SWAYSOCK" = "/tmp/sway-ipc.sock"; + # TODO: Investigate if we can get hardware acceleration to work (via + # virtio-gpu and Virgil). We currently have to use the Pixman software + # renderer since the GLES2 renderer doesn't work inside the VM (even + # with WLR_RENDERER_ALLOW_SOFTWARE): + # "WLR_RENDERER_ALLOW_SOFTWARE" = "1"; + "WLR_RENDERER" = "pixman"; + }; + # For convenience: + shellAliases = { + test-x11 = "glinfo | tee /tmp/test-x11.out && touch /tmp/test-x11-exit-ok"; + test-wayland = "wayland-info | tee /tmp/test-wayland.out && touch /tmp/test-wayland-exit-ok"; + }; + + # To help with OCR: + etc."xdg/foot/foot.ini".text = lib.generators.toINI { } { + main = { + font = "inconsolata:size=14"; + }; + colors = rec { + foreground = "000000"; + background = "ffffff"; + regular2 = foreground; + }; + }; + + etc."gpg-agent.conf".text = '' + pinentry-timeout 86400 + ''; + }; + + fonts.packages = [ pkgs.inconsolata ]; + + # Automatically configure and start Sway when logging in on tty1: + programs.bash.loginShellInit = '' + if [ "$(tty)" = "/dev/tty1" ]; then + set -e + + mkdir -p ~/.config/sway + sed s/Mod4/Mod1/ /etc/sway/config > ~/.config/sway/config + + sway --validate + sway && touch /tmp/sway-exit-ok + fi + ''; + + programs.sway = { + enable = true; + package = pkgs.swayfx.override { isNixOS = true; }; + }; + + # To test pinentry via gpg-agent: + programs.gnupg.agent.enable = true; + + # Need to switch to a different GPU driver than the default one (-vga std) so that Sway can launch: + virtualisation.qemu.options = [ "-vga none -device virtio-gpu-pci" ]; + }; + + testScript = + { nodes, ... }: + '' + import shlex + import json + + q = shlex.quote + NODE_GROUPS = ["nodes", "floating_nodes"] + + + def swaymsg(command: str = "", succeed=True, type="command"): + assert command != "" or type != "command", "Must specify command or type" + shell = q(f"swaymsg -t {q(type)} -- {q(command)}") + with machine.nested( + f"sending swaymsg {shell!r}" + " (allowed to fail)" * (not succeed) + ): + ret = (machine.succeed if succeed else machine.execute)( + f"su - alice -c {shell}" + ) + + # execute also returns a status code, but disregard. + if not succeed: + _, ret = ret + + if not succeed and not ret: + return None + + parsed = json.loads(ret) + return parsed + + + def walk(tree): + yield tree + for group in NODE_GROUPS: + for node in tree.get(group, []): + yield from walk(node) + + + def wait_for_window(pattern): + def func(last_chance): + nodes = (node["name"] for node in walk(swaymsg(type="get_tree"))) + + if last_chance: + nodes = list(nodes) + machine.log(f"Last call! Current list of windows: {nodes}") + + return any(pattern in name for name in nodes) + + retry(func) + + start_all() + machine.wait_for_unit("multi-user.target") + + # To check the version: + print(machine.succeed("sway --version")) + + # Wait for Sway to complete startup: + machine.wait_for_file("/run/user/1000/wayland-1") + machine.wait_for_file("/tmp/sway-ipc.sock") + + # Test XWayland (foot does not support X): + swaymsg("exec WINIT_UNIX_BACKEND=x11 WAYLAND_DISPLAY= alacritty") + wait_for_window("alice@machine") + machine.send_chars("test-x11\n") + machine.wait_for_file("/tmp/test-x11-exit-ok") + print(machine.succeed("cat /tmp/test-x11.out")) + machine.copy_from_vm("/tmp/test-x11.out") + machine.screenshot("alacritty_glinfo") + machine.succeed("pkill alacritty") + + # Start a terminal (foot) on workspace 3: + machine.send_key("alt-3") + machine.sleep(3) + machine.send_key("alt-ret") + wait_for_window("alice@machine") + machine.send_chars("test-wayland\n") + machine.wait_for_file("/tmp/test-wayland-exit-ok") + print(machine.succeed("cat /tmp/test-wayland.out")) + machine.copy_from_vm("/tmp/test-wayland.out") + machine.screenshot("foot_wayland_info") + machine.send_key("alt-shift-q") + machine.wait_until_fails("pgrep foot") + + # Test gpg-agent starting pinentry-gnome3 via D-Bus (tests if + # $WAYLAND_DISPLAY is correctly imported into the D-Bus user env): + swaymsg("exec mkdir -p ~/.gnupg") + swaymsg("exec cp /etc/gpg-agent.conf ~/.gnupg") + + swaymsg("exec DISPLAY=INVALID gpg --no-tty --yes --quick-generate-key test", succeed=False) + machine.wait_until_succeeds("pgrep --exact gpg") + wait_for_window("gpg") + machine.succeed("pgrep --exact gpg") + machine.screenshot("gpg_pinentry") + machine.send_key("alt-shift-q") + machine.wait_until_fails("pgrep --exact gpg") + + # Test swaynag: + def get_height(): + return [node['rect']['height'] for node in walk(swaymsg(type="get_tree")) if node['focused']][0] + + before = get_height() + machine.send_key("alt-shift-e") + retry(lambda _: get_height() < before) + machine.screenshot("sway_exit") + + swaymsg("exec swaylock") + machine.wait_until_succeeds("pgrep -x swaylock") + machine.sleep(3) + machine.send_chars("${nodes.machine.config.users.users.alice.password}") + machine.send_key("ret") + machine.wait_until_fails("pgrep -x swaylock") + + # Exit Sway and verify process exit status 0: + swaymsg("exit", succeed=False) + machine.wait_until_fails("pgrep -x sway") + machine.wait_for_file("/tmp/sway-exit-ok") + ''; + } +) diff --git a/nixos/tests/switch-test.nix b/nixos/tests/switch-test.nix index a57d66f82eac9..4a7bcd5a82264 100644 --- a/nixos/tests/switch-test.nix +++ b/nixos/tests/switch-test.nix @@ -610,6 +610,11 @@ in { # Returns a comma separated representation of the given list in sorted # order, that matches the output format of switch-to-configuration.pl sortedUnits = xs: lib.concatStringsSep ", " (builtins.sort builtins.lessThan xs); + + dbusService = { + "dbus" = "dbus.service"; + "broker" = "dbus-broker.service"; + }.${nodes.machine.services.dbus.implementation}; in /* python */ '' def switch_to_specialisation(system, name, action="test", fail=False): if name == "": @@ -691,9 +696,9 @@ in { with subtest("continuing from an aborted switch"): # An aborted switch will write into a file what it tried to start # and a second switch should continue from this - machine.succeed("echo dbus-broker.service > /run/nixos/start-list") + machine.succeed("echo ${dbusService} > /run/nixos/start-list") out = switch_to_specialisation("${machine}", "modifiedSystemConf") - assert_contains(out, "starting the following units: dbus-broker.service\n") + assert_contains(out, "starting the following units: ${dbusService}\n") with subtest("fstab mounts"): switch_to_specialisation("${machine}", "") @@ -732,7 +737,7 @@ in { out = switch_to_specialisation("${machine}", "") assert_contains(out, "stopping the following units: test.mount\n") assert_lacks(out, "NOT restarting the following changed units:") - assert_contains(out, "reloading the following units: dbus-broker.service\n") + assert_contains(out, "reloading the following units: ${dbusService}\n") assert_lacks(out, "\nrestarting the following units:") assert_lacks(out, "\nstarting the following units:") assert_lacks(out, "the following new units were started:") @@ -740,7 +745,7 @@ in { out = switch_to_specialisation("${machine}", "storeMountModified") assert_lacks(out, "stopping the following units:") assert_contains(out, "NOT restarting the following changed units: -.mount") - assert_contains(out, "reloading the following units: dbus-broker.service\n") + assert_contains(out, "reloading the following units: ${dbusService}\n") assert_lacks(out, "\nrestarting the following units:") assert_lacks(out, "\nstarting the following units:") assert_lacks(out, "the following new units were started:") @@ -751,7 +756,7 @@ in { out = switch_to_specialisation("${machine}", "swap") assert_lacks(out, "stopping the following units:") assert_lacks(out, "NOT restarting the following changed units:") - assert_contains(out, "reloading the following units: dbus-broker.service\n") + assert_contains(out, "reloading the following units: ${dbusService}\n") assert_lacks(out, "\nrestarting the following units:") assert_lacks(out, "\nstarting the following units:") assert_contains(out, "the following new units were started: swapfile.swap") @@ -760,7 +765,7 @@ in { assert_contains(out, "stopping swap device: /swapfile") assert_lacks(out, "stopping the following units:") assert_lacks(out, "NOT restarting the following changed units:") - assert_contains(out, "reloading the following units: dbus-broker.service\n") + assert_contains(out, "reloading the following units: ${dbusService}\n") assert_lacks(out, "\nrestarting the following units:") assert_lacks(out, "\nstarting the following units:") assert_lacks(out, "the following new units were started:") @@ -781,7 +786,7 @@ in { assert_lacks(out, "installing dummy bootloader") # test does not install a bootloader assert_lacks(out, "stopping the following units:") assert_lacks(out, "NOT restarting the following changed units:") - assert_contains(out, "reloading the following units: dbus-broker.service\n") # huh + assert_contains(out, "reloading the following units: ${dbusService}\n") # huh assert_lacks(out, "\nrestarting the following units:") assert_lacks(out, "\nstarting the following units:") assert_contains(out, "the following new units were started: test.service\n") @@ -858,7 +863,7 @@ in { assert_lacks(out, "installing dummy bootloader") # test does not install a bootloader assert_lacks(out, "stopping the following units:") assert_lacks(out, "NOT restarting the following changed units:") - assert_contains(out, "reloading the following units: dbus-broker.service\n") # huh + assert_contains(out, "reloading the following units: ${dbusService}\n") # huh assert_lacks(out, "\nrestarting the following units:") assert_lacks(out, "\nstarting the following units:") assert_contains(out, "the following new units were started: test.service\n") diff --git a/nixos/tests/syncthing-relay.nix b/nixos/tests/syncthing-relay.nix index 3d70b1eda7b2a..cab9bcafe9d5c 100644 --- a/nixos/tests/syncthing-relay.nix +++ b/nixos/tests/syncthing-relay.nix @@ -1,6 +1,6 @@ import ./make-test-python.nix ({ lib, pkgs, ... }: { name = "syncthing-relay"; - meta.maintainers = with pkgs.lib.maintainers; [ delroth ]; + meta.maintainers = with pkgs.lib.maintainers; [ ]; nodes.machine = { environment.systemPackages = [ pkgs.jq ]; diff --git a/nixos/tests/systemd-networkd-bridge.nix b/nixos/tests/systemd-networkd-bridge.nix new file mode 100644 index 0000000000000..f1f8823e84205 --- /dev/null +++ b/nixos/tests/systemd-networkd-bridge.nix @@ -0,0 +1,103 @@ +/* This test ensures that we can configure spanning-tree protocol + across bridges using systemd-networkd. + + Test topology: + + 1 2 3 + node1 --- sw1 --- sw2 --- node2 + \ / + 4 \ / 5 + sw3 + | + 6 | + | + node3 + + where switches 1, 2, and 3 bridge their links and use STP, + and each link is labeled with the VLAN we are assigning it in + virtualisation.vlans. +*/ +with builtins; +let + commonConf = { + systemd.services.systemd-networkd.environment.SYSTEMD_LOG_LEVEL = "debug"; + networking.useNetworkd = true; + networking.useDHCP = false; + networking.firewall.enable = false; + }; + + generateNodeConf = { octet, vlan }: + { lib, pkgs, config, ... }: { + imports = [ common/user-account.nix commonConf ]; + virtualisation.vlans = [ vlan ]; + systemd.network = { + enable = true; + networks = { + "30-eth" = { + matchConfig.Name = "eth1"; + address = [ "10.0.0.${toString octet}/24" ]; + }; + }; + }; + }; + + generateSwitchConf = vlans: + { lib, pkgs, config, ... }: { + imports = [ common/user-account.nix commonConf ]; + virtualisation.vlans = vlans; + systemd.network = { + enable = true; + netdevs = { + "40-br0" = { + netdevConfig = { + Kind = "bridge"; + Name = "br0"; + }; + bridgeConfig.STP = "yes"; + }; + }; + networks = { + "30-eth" = { + matchConfig.Name = "eth*"; + networkConfig.Bridge = "br0"; + }; + "40-br0" = { matchConfig.Name = "br0"; }; + }; + }; + }; +in import ./make-test-python.nix ({ pkgs, ... }: { + name = "networkd"; + meta = with pkgs.lib.maintainers; { maintainers = [ picnoir ]; }; + nodes = { + node1 = generateNodeConf { + octet = 1; + vlan = 1; + }; + node2 = generateNodeConf { + octet = 2; + vlan = 3; + }; + node3 = generateNodeConf { + octet = 3; + vlan = 6; + }; + sw1 = generateSwitchConf [ 1 2 4 ]; + sw2 = generateSwitchConf [ 2 3 5 ]; + sw3 = generateSwitchConf [ 4 5 6 ]; + }; + testScript = '' + network_nodes = [node1, node2, node3] + network_switches = [sw1, sw2, sw3] + start_all() + + for n in network_nodes + network_switches: + n.wait_for_unit("systemd-networkd-wait-online.service") + + node1.succeed("ping 10.0.0.2 -w 10 -c 1") + node1.succeed("ping 10.0.0.3 -w 10 -c 1") + node2.succeed("ping 10.0.0.1 -w 10 -c 1") + node2.succeed("ping 10.0.0.3 -w 10 -c 1") + node3.succeed("ping 10.0.0.1 -w 10 -c 1") + node3.succeed("ping 10.0.0.2 -w 10 -c 1") + ''; +}) diff --git a/nixos/tests/systemd.nix b/nixos/tests/systemd.nix index 1a39cc73c8868..4b087d403f37d 100644 --- a/nixos/tests/systemd.nix +++ b/nixos/tests/systemd.nix @@ -1,7 +1,7 @@ import ./make-test-python.nix ({ pkgs, ... }: { name = "systemd"; - nodes.machine = { lib, ... }: { + nodes.machine = { config, lib, ... }: { imports = [ common/user-account.nix common/x11.nix ]; virtualisation.emptyDiskImages = [ 512 512 ]; @@ -38,9 +38,18 @@ import ./make-test-python.nix ({ pkgs, ... }: { script = "true"; }; + systemd.services.testDependency1 = { + description = "Test Dependency 1"; + wantedBy = [ config.systemd.services."testservice1".name ]; + serviceConfig.Type = "oneshot"; + script = '' + true + ''; + }; + systemd.services.testservice1 = { description = "Test Service 1"; - wantedBy = [ "multi-user.target" ]; + wantedBy = [ config.systemd.targets.multi-user.name ]; serviceConfig.Type = "oneshot"; script = '' if [ "$XXX_SYSTEM" = foo ]; then diff --git a/nixos/tests/teleport.nix b/nixos/tests/teleport.nix index d68917c6c7acb..2fb347155759a 100644 --- a/nixos/tests/teleport.nix +++ b/nixos/tests/teleport.nix @@ -9,8 +9,8 @@ with import ../lib/testing-python.nix { inherit system pkgs; }; let packages = with pkgs; { "default" = teleport; - "12" = teleport_12; "13" = teleport_13; + "14" = teleport_14; }; minimal = package: { diff --git a/nixos/tests/web-apps/gotosocial.nix b/nixos/tests/web-apps/gotosocial.nix index 8c4e76b14e3bf..f9d28c2b8b998 100644 --- a/nixos/tests/web-apps/gotosocial.nix +++ b/nixos/tests/web-apps/gotosocial.nix @@ -1,7 +1,7 @@ { lib, ... }: { name = "gotosocial"; - meta.maintainers = with lib.maintainers; [ misuzu blakesmith ]; + meta.maintainers = with lib.maintainers; [ blakesmith ]; nodes.machine = { pkgs, ... }: { environment.systemPackages = [ pkgs.jq ]; diff --git a/nixos/tests/wpa_supplicant.nix b/nixos/tests/wpa_supplicant.nix index 8c701ca7d5f71..5e3b39f27ecf3 100644 --- a/nixos/tests/wpa_supplicant.nix +++ b/nixos/tests/wpa_supplicant.nix @@ -102,17 +102,34 @@ import ./make-test-python.nix ({ pkgs, lib, ...}: test2.psk = "@PSK_SPECIAL@"; # should be replaced test3.psk = "@PSK_MISSING@"; # should not be replaced test4.psk = "P@ssowrdWithSome@tSymbol"; # should not be replaced + test5.psk = "@PSK_AWK_REGEX@"; # should be replaced }; # secrets environmentFile = pkgs.writeText "wpa-secrets" '' PSK_VALID="S0m3BadP4ssw0rd"; # taken from https://github.com/minimaxir/big-list-of-naughty-strings - PSK_SPECIAL=",./;'[]\-= <>?:\"{}|_+ !@#$%^\&*()`~"; + PSK_SPECIAL=",./;'[]\/\-= <>?:\"{}|_+ !@#$%^&*()`~"; + PSK_AWK_REGEX="PassowrdWith&symbol"; ''; }; }; + imperative = { ... }: { + imports = [ ../modules/profiles/minimal.nix ]; + + # add a virtual wlan interface + boot.kernelModules = [ "mac80211_hwsim" ]; + + # wireless client + networking.wireless = { + enable = lib.mkOverride 0 true; + userControlled.enable = true; + allowAuxiliaryImperativeNetworks = true; + interfaces = [ "wlan1" ]; + }; + }; + # Test connecting to the SAE-only hotspot using SAE machineSae = machineWithHostapd { networking.wireless = { @@ -171,6 +188,7 @@ import ./make-test-python.nix ({ pkgs, lib, ...}: basic.fail(f"grep -q @PSK_SPECIAL@ {config_file}") basic.succeed(f"grep -q @PSK_MISSING@ {config_file}") basic.succeed(f"grep -q P@ssowrdWithSome@tSymbol {config_file}") + basic.succeed(f"grep -q 'PassowrdWith&symbol' {config_file}") with subtest("WPA2 fallbacks have been generated"): assert int(basic.succeed(f"grep -c sae-only {config_file}")) == 1 @@ -185,6 +203,15 @@ import ./make-test-python.nix ({ pkgs, lib, ...}: assert "Failed to connect" not in status, \ "Failed to connect to the daemon" + with subtest("Daemon can be configured imperatively"): + imperative.wait_for_unit("wpa_supplicant-wlan1.service") + imperative.wait_until_succeeds("wpa_cli -i wlan1 status") + imperative.succeed("wpa_cli -i wlan1 add_network") + imperative.succeed("wpa_cli -i wlan1 set_network 0 ssid '\"nixos-test\"'") + imperative.succeed("wpa_cli -i wlan1 set_network 0 psk '\"reproducibility\"'") + imperative.succeed("wpa_cli -i wlan1 save_config") + imperative.succeed("grep -q nixos-test /etc/wpa_supplicant.conf") + machineSae.wait_for_unit("hostapd.service") machineSae.copy_from_vm("/run/hostapd/wlan0.hostapd.conf") with subtest("Daemon can connect to the SAE access point using SAE"): |