diff options
Diffstat (limited to 'nixos/tests')
34 files changed, 1070 insertions, 175 deletions
diff --git a/nixos/tests/all-tests.nix b/nixos/tests/all-tests.nix index 3f3a99a83fee0..035c288c22e5c 100644 --- a/nixos/tests/all-tests.nix +++ b/nixos/tests/all-tests.nix @@ -130,6 +130,7 @@ in { apparmor = handleTest ./apparmor.nix {}; archi = handleTest ./archi.nix {}; armagetronad = handleTest ./armagetronad.nix {}; + artalk = handleTest ./artalk.nix {}; atd = handleTest ./atd.nix {}; atop = handleTest ./atop.nix {}; atuin = handleTest ./atuin.nix {}; @@ -144,6 +145,7 @@ in { bcachefs = handleTestOn ["x86_64-linux" "aarch64-linux"] ./bcachefs.nix {}; beanstalkd = handleTest ./beanstalkd.nix {}; bees = handleTest ./bees.nix {}; + benchexec = handleTest ./benchexec.nix {}; binary-cache = handleTest ./binary-cache.nix {}; bind = handleTest ./bind.nix {}; bird = handleTest ./bird.nix {}; @@ -204,6 +206,7 @@ in { code-server = handleTest ./code-server.nix {}; coder = handleTest ./coder.nix {}; collectd = handleTest ./collectd.nix {}; + commafeed = handleTest ./commafeed.nix {}; connman = handleTest ./connman.nix {}; consul = handleTest ./consul.nix {}; consul-template = handleTest ./consul-template.nix {}; @@ -309,6 +312,7 @@ in { fenics = handleTest ./fenics.nix {}; ferm = handleTest ./ferm.nix {}; ferretdb = handleTest ./ferretdb.nix {}; + filesender = handleTest ./filesender.nix {}; filesystems-overlayfs = runTest ./filesystems-overlayfs.nix; firefly-iii = handleTest ./firefly-iii.nix {}; firefox = handleTest ./firefox.nix { firefoxPackage = pkgs.firefox; }; @@ -587,7 +591,7 @@ in { mysql-backup = handleTest ./mysql/mysql-backup.nix {}; mysql-replication = handleTest ./mysql/mysql-replication.nix {}; n8n = handleTest ./n8n.nix {}; - nagios = handleTest ./nagios.nix {}; + nagios = handleTestOn [ "x86_64-linux" "aarch64-linux" ] ./nagios.nix {}; nar-serve = handleTest ./nar-serve.nix {}; nat.firewall = handleTest ./nat.nix { withFirewall = true; }; nat.standalone = handleTest ./nat.nix { withFirewall = false; }; @@ -612,6 +616,7 @@ in { # TODO: put in networking.nix after the test becomes more complete networkingProxy = handleTest ./networking-proxy.nix {}; nextcloud = handleTest ./nextcloud {}; + nextjs-ollama-llm-ui = runTest ./web-apps/nextjs-ollama-llm-ui.nix; nexus = handleTest ./nexus.nix {}; # TODO: Test nfsv3 + Kerberos nfs3 = handleTest ./nfs { version = 3; }; @@ -926,6 +931,7 @@ in { systemd-oomd = handleTest ./systemd-oomd.nix {}; systemd-portabled = handleTest ./systemd-portabled.nix {}; systemd-repart = handleTest ./systemd-repart.nix {}; + systemd-resolved = handleTest ./systemd-resolved.nix {}; systemd-shutdown = handleTest ./systemd-shutdown.nix {}; systemd-sysupdate = runTest ./systemd-sysupdate.nix; systemd-sysusers-mutable = runTest ./systemd-sysusers-mutable.nix; @@ -1004,7 +1010,7 @@ in { vault-dev = handleTest ./vault-dev.nix {}; vault-postgresql = handleTest ./vault-postgresql.nix {}; vaultwarden = handleTest ./vaultwarden.nix {}; - vector = handleTest ./vector.nix {}; + vector = handleTest ./vector {}; vengi-tools = handleTest ./vengi-tools.nix {}; victoriametrics = handleTest ./victoriametrics.nix {}; vikunja = handleTest ./vikunja.nix {}; @@ -1042,6 +1048,7 @@ in { yabar = handleTest ./yabar.nix {}; ydotool = handleTest ./ydotool.nix {}; yggdrasil = handleTest ./yggdrasil.nix {}; + your_spotify = handleTest ./your_spotify.nix {}; zammad = handleTest ./zammad.nix {}; zeronet-conservancy = handleTest ./zeronet-conservancy.nix {}; zfs = handleTest ./zfs.nix {}; diff --git a/nixos/tests/artalk.nix b/nixos/tests/artalk.nix new file mode 100644 index 0000000000000..1338e5cd380c6 --- /dev/null +++ b/nixos/tests/artalk.nix @@ -0,0 +1,28 @@ +import ./make-test-python.nix ( + { lib, pkgs, ... }: + { + + name = "artalk"; + + meta = { + maintainers = with lib.maintainers; [ moraxyc ]; + }; + + nodes.machine = + { pkgs, ... }: + { + environment.systemPackages = [ pkgs.curl ]; + services.artalk = { + enable = true; + }; + }; + + testScript = '' + machine.wait_for_unit("artalk.service") + + machine.wait_for_open_port(23366) + + machine.succeed("curl --fail --max-time 10 http://127.0.0.1:23366/") + ''; + } +) diff --git a/nixos/tests/ayatana-indicators.nix b/nixos/tests/ayatana-indicators.nix index 5709ad2a1af69..cfd4d8099d112 100644 --- a/nixos/tests/ayatana-indicators.nix +++ b/nixos/tests/ayatana-indicators.nix @@ -28,8 +28,11 @@ in { enable = true; packages = with pkgs; [ ayatana-indicator-datetime + ayatana-indicator-display ayatana-indicator-messages + ayatana-indicator-power ayatana-indicator-session + ayatana-indicator-sound ] ++ (with pkgs.lomiri; [ lomiri-indicator-network telephony-service @@ -40,6 +43,8 @@ in { services.accounts-daemon.enable = true; # messages + hardware.pulseaudio.enable = true; # sound + # Lomiri-ish setup for Lomiri indicators # TODO move into a Lomiri module, once the package set is far enough for the DE to start @@ -91,7 +96,7 @@ in { # Now check if all indicators were brought up successfully, and kill them for later '' + (runCommandOverAyatanaIndicators (service: let serviceExec = builtins.replaceStrings [ "." ] [ "-" ] service; in '' - machine.succeed("pgrep -u ${user} -f ${serviceExec}") + machine.wait_until_succeeds("pgrep -u ${user} -f ${serviceExec}") machine.succeed("pkill -f ${serviceExec}") '')) + '' diff --git a/nixos/tests/benchexec.nix b/nixos/tests/benchexec.nix new file mode 100644 index 0000000000000..3fc9ebc2c35f5 --- /dev/null +++ b/nixos/tests/benchexec.nix @@ -0,0 +1,54 @@ +import ./make-test-python.nix ({ pkgs, lib, ... }: +let + user = "alice"; +in +{ + name = "benchexec"; + + nodes.benchexec = { + imports = [ ./common/user-account.nix ]; + + programs.benchexec = { + enable = true; + users = [ user ]; + }; + }; + + testScript = { ... }: + let + runexec = lib.getExe' pkgs.benchexec "runexec"; + echo = builtins.toString pkgs.benchexec; + test = lib.getExe (pkgs.writeShellApplication rec { + name = "test"; + meta.mainProgram = name; + text = "echo '${echo}'"; + }); + wd = "/tmp"; + stdout = "${wd}/runexec.out"; + stderr = "${wd}/runexec.err"; + in + '' + start_all() + machine.wait_for_unit("multi-user.target") + benchexec.succeed(''''\ + systemd-run \ + --property='StandardOutput=file:${stdout}' \ + --property='StandardError=file:${stderr}' \ + --unit=runexec --wait --user --machine='${user}@' \ + --working-directory ${wd} \ + '${runexec}' \ + --debug \ + --read-only-dir / \ + --hidden-dir /home \ + '${test}' \ + '''') + benchexec.succeed("grep -s '${echo}' ${wd}/output.log") + benchexec.succeed("test \"$(grep -Ec '((start|wall|cpu)time|memory)=' ${stdout})\" = 4") + benchexec.succeed("! grep -E '(WARNING|ERROR)' ${stderr}") + ''; + + interactive.nodes.benchexec.services.kmscon = { + enable = true; + fonts = [{ name = "Fira Code"; package = pkgs.fira-code; }]; + }; +}) diff --git a/nixos/tests/castopod.nix b/nixos/tests/castopod.nix index 3257cd3d363c7..57e035354d23e 100644 --- a/nixos/tests/castopod.nix +++ b/nixos/tests/castopod.nix @@ -98,6 +98,7 @@ import ./make-test-python.nix ({ pkgs, lib, ... }: driver = Firefox(options=options, service=service) driver = Firefox(options=options) driver.implicitly_wait(30) + driver.set_page_load_timeout(60) # install ########################################################## @@ -207,7 +208,7 @@ import ./make-test-python.nix ({ pkgs, lib, ... }: text = '' out=/tmp/podcast.mp3 sox -n -r 48000 -t wav - synth ${targetPodcastDuration} sine 440 ` - `| lame --noreplaygain -cbr -q 9 -b 320 - $out + `| lame --noreplaygain --cbr -q 9 -b 320 - $out FILESIZE="$(stat -c%s $out)" [ "$FILESIZE" -gt 0 ] [ "$FILESIZE" -le "${toString targetPodcastSize}" ] diff --git a/nixos/tests/commafeed.nix b/nixos/tests/commafeed.nix new file mode 100644 index 0000000000000..7b65720818a9b --- /dev/null +++ b/nixos/tests/commafeed.nix @@ -0,0 +1,21 @@ +import ./make-test-python.nix ( + { lib, ... }: + { + name = "commafeed"; + + nodes.server = { + services.commafeed = { + enable = true; + }; + }; + + testScript = '' + server.start() + server.wait_for_unit("commafeed.service") + server.wait_for_open_port(8082) + server.succeed("curl --fail --silent http://localhost:8082") + ''; + + meta.maintainers = [ lib.maintainers.raroh73 ]; + } +) diff --git a/nixos/tests/filesender.nix b/nixos/tests/filesender.nix new file mode 100644 index 0000000000000..9274ddbf7e90e --- /dev/null +++ b/nixos/tests/filesender.nix @@ -0,0 +1,137 @@ +import ./make-test-python.nix ({ pkgs, lib, ... }: { + name = "filesender"; + meta = { + maintainers = with lib.maintainers; [ nhnn ]; + broken = pkgs.stdenv.isAarch64; # selenium.common.exceptions.WebDriverException: Message: Unsupported platform/architecture combination: linux/aarch64 + }; + + nodes.filesender = { ... }: let + format = pkgs.formats.php { }; + in { + networking.firewall.allowedTCPPorts = [ 80 ]; + + services.filesender.enable = true; + services.filesender.localDomain = "filesender"; + services.filesender.settings = { + auth_sp_saml_authentication_source = "default"; + auth_sp_saml_uid_attribute = "uid"; + storage_filesystem_path = "/tmp"; + site_url = "http://filesender"; + force_ssl = false; + admin = ""; + admin_email = "admin@localhost"; + email_reply_to = "noreply@localhost"; + }; + services.simplesamlphp.filesender = { + settings = { + baseurlpath = "http://filesender/saml"; + "module.enable".exampleauth = true; + }; + authSources = { + admin = [ "core:AdminPassword" ]; + default = format.lib.mkMixedArray [ "exampleauth:UserPass" ] { + "user:password" = { + uid = [ "user" ]; + cn = [ "user" ]; + mail = [ "user@nixos.org" ]; + }; + }; + }; + }; + }; + + nodes.client = + { pkgs + , nodes + , ... + }: + let + filesenderIP = (builtins.head (nodes.filesender.networking.interfaces.eth1.ipv4.addresses)).address; + in + { + networking.hosts.${filesenderIP} = [ "filesender" ]; + + environment.systemPackages = + let + username = "user"; + password = "password"; + browser-test = + pkgs.writers.writePython3Bin "browser-test" + { + libraries = [ pkgs.python3Packages.selenium ]; + flakeIgnore = [ "E124" "E501" ]; + } '' + from selenium.webdriver.common.by import By + from selenium.webdriver import Firefox + from selenium.webdriver.firefox.options import Options + from selenium.webdriver.firefox.firefox_profile import FirefoxProfile + from selenium.webdriver.firefox.service import Service + from selenium.webdriver.support.ui import WebDriverWait + from selenium.webdriver.support import expected_conditions as EC + from subprocess import STDOUT + import string + import random + import logging + import time + selenium_logger = logging.getLogger("selenium") + selenium_logger.setLevel(logging.DEBUG) + selenium_logger.addHandler(logging.StreamHandler()) + profile = FirefoxProfile() + profile.set_preference("browser.download.folderList", 2) + profile.set_preference("browser.download.manager.showWhenStarting", False) + profile.set_preference("browser.download.dir", "/tmp/firefox") + profile.set_preference("browser.helperApps.neverAsk.saveToDisk", "text/plain;text/txt") + options = Options() + options.profile = profile + options.add_argument('--headless') + service = Service(log_output=STDOUT) + driver = Firefox(options=options) + driver.set_window_size(1024, 768) + driver.implicitly_wait(30) + driver.get('http://filesender/') + wait = WebDriverWait(driver, 20) + wait.until(EC.title_contains("FileSender")) + driver.find_element(By.ID, "btn_logon").click() + wait.until(EC.title_contains("Enter your username and password")) + driver.find_element(By.ID, 'username').send_keys( + '${username}' + ) + driver.find_element(By.ID, 'password').send_keys( + '${password}' + ) + driver.find_element(By.ID, "submit_button").click() + wait.until(EC.title_contains("FileSender")) + wait.until(EC.presence_of_element_located((By.ID, "topmenu_logoff"))) + test_string = "".join(random.choices(string.ascii_uppercase + string.digits, k=20)) + with open("/tmp/test_file.txt", "w") as file: + file.write(test_string) + driver.find_element(By.ID, "files").send_keys("/tmp/test_file.txt") + time.sleep(2) + driver.find_element(By.CSS_SELECTOR, '.start').click() + wait.until(EC.presence_of_element_located((By.CSS_SELECTOR, ".download_link"))) + download_link = driver.find_element(By.CSS_SELECTOR, '.download_link > textarea').get_attribute('value').strip() + driver.get(download_link) + wait.until(EC.presence_of_element_located((By.CSS_SELECTOR, ".download"))) + driver.find_element(By.CSS_SELECTOR, '.download').click() + wait.until(EC.presence_of_element_located((By.CSS_SELECTOR, ".ui-dialog-buttonset > button:nth-child(2)"))) + driver.find_element(By.CSS_SELECTOR, ".ui-dialog-buttonset > button:nth-child(2)").click() + driver.close() + driver.quit() + ''; + in + [ + pkgs.firefox-unwrapped + pkgs.geckodriver + browser-test + ]; + }; + + testScript = '' + start_all() + filesender.wait_for_file("/run/phpfpm/filesender.sock") + filesender.wait_for_open_port(80) + if "If you have received an invitation to access this site as a guest" not in client.wait_until_succeeds("curl -sS -f http://filesender"): + raise Exception("filesender returned invalid html") + client.succeed("browser-test") + ''; +}) diff --git a/nixos/tests/firefly-iii.nix b/nixos/tests/firefly-iii.nix index c93d799320a48..2373ba8360264 100644 --- a/nixos/tests/firefly-iii.nix +++ b/nixos/tests/firefly-iii.nix @@ -1,14 +1,19 @@ -import ./make-test-python.nix ({ lib, pkgs, ... }: { +import ./make-test-python.nix ({ lib, ... }: + +let + db-pass = "Test2Test2"; + app-key = "TestTestTestTestTestTestTestTest"; +in +{ name = "firefly-iii"; meta.maintainers = [ lib.maintainers.savyajha ]; - nodes.machine = { config, ... }: { + nodes.fireflySqlite = { config, ... }: { environment.etc = { - "firefly-iii-appkey".text = "TestTestTestTestTestTestTestTest"; + "firefly-iii-appkey".text = app-key; }; services.firefly-iii = { enable = true; - virtualHost = "http://localhost"; enableNginx = true; settings = { APP_KEY_FILE = "/etc/firefly-iii-appkey"; @@ -18,9 +23,87 @@ import ./make-test-python.nix ({ lib, pkgs, ... }: { }; }; + nodes.fireflyPostgresql = { config, pkgs, ... }: { + environment.etc = { + "firefly-iii-appkey".text = app-key; + "postgres-pass".text = db-pass; + }; + services.firefly-iii = { + enable = true; + enableNginx = true; + settings = { + APP_KEY_FILE = "/etc/firefly-iii-appkey"; + LOG_CHANNEL = "stdout"; + SITE_OWNER = "mail@example.com"; + DB_CONNECTION = "pgsql"; + DB_DATABASE = "firefly"; + DB_USERNAME = "firefly"; + DB_PASSWORD_FILE = "/etc/postgres-pass"; + }; + }; + + services.postgresql = { + enable = true; + package = pkgs.postgresql_15; + authentication = '' + local all postgres peer + local firefly firefly password + ''; + initialScript = pkgs.writeText "firefly-init.sql" '' + CREATE USER "firefly" WITH LOGIN PASSWORD '${db-pass}'; + CREATE DATABASE "firefly" WITH OWNER "firefly"; + CREATE SCHEMA AUTHORIZATION firefly; + ''; + }; + }; + + nodes.fireflyMysql = { config, pkgs, ... }: { + environment.etc = { + "firefly-iii-appkey".text = app-key; + "mysql-pass".text = db-pass; + }; + services.firefly-iii = { + enable = true; + enableNginx = true; + settings = { + APP_KEY_FILE = "/etc/firefly-iii-appkey"; + LOG_CHANNEL = "stdout"; + SITE_OWNER = "mail@example.com"; + DB_CONNECTION = "mysql"; + DB_DATABASE = "firefly"; + DB_USERNAME = "firefly"; + DB_PASSWORD_FILE = "/etc/mysql-pass"; + DB_SOCKET = "/run/mysqld/mysqld.sock"; + }; + }; + + services.mysql = { + enable = true; + package = pkgs.mariadb; + initialScript = pkgs.writeText "firefly-init.sql" '' + create database firefly DEFAULT CHARACTER SET utf8mb4; + create user 'firefly'@'localhost' identified by '${db-pass}'; + grant all on firefly.* to 'firefly'@'localhost'; + ''; + settings.mysqld.character-set-server = "utf8mb4"; + }; + }; + 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'") + fireflySqlite.wait_for_unit("phpfpm-firefly-iii.service") + fireflySqlite.wait_for_unit("nginx.service") + fireflySqlite.succeed("curl -fvvv -Ls http://localhost/ | grep 'Firefly III'") + fireflySqlite.succeed("curl -fvvv -Ls http://localhost/v1/js/app.js") + fireflySqlite.succeed("systemctl start firefly-iii-cron.service") + fireflyPostgresql.wait_for_unit("phpfpm-firefly-iii.service") + fireflyPostgresql.wait_for_unit("nginx.service") + fireflyPostgresql.wait_for_unit("postgresql.service") + fireflyPostgresql.succeed("curl -fvvv -Ls http://localhost/ | grep 'Firefly III'") + fireflyPostgresql.succeed("systemctl start firefly-iii-cron.service") + fireflyMysql.wait_for_unit("phpfpm-firefly-iii.service") + fireflyMysql.wait_for_unit("nginx.service") + fireflyMysql.wait_for_unit("mysql.service") + fireflyMysql.succeed("curl -fvvv -Ls http://localhost/ | grep 'Firefly III'") + fireflyMysql.succeed("systemctl start firefly-iii-cron.service") ''; }) diff --git a/nixos/tests/garage/default.nix b/nixos/tests/garage/default.nix index a42236e9a5bbe..b7f9bb4b865bd 100644 --- a/nixos/tests/garage/default.nix +++ b/nixos/tests/garage/default.nix @@ -51,4 +51,5 @@ in [ "0_8" "0_9" + "1_x" ] diff --git a/nixos/tests/garage/with-3node-replication.nix b/nixos/tests/garage/with-3node-replication.nix index d4387b198d976..266a1082893f7 100644 --- a/nixos/tests/garage/with-3node-replication.nix +++ b/nixos/tests/garage/with-3node-replication.nix @@ -7,10 +7,10 @@ args@{ mkNode, ver, ... }: }; nodes = { - node1 = mkNode { replicationMode = 3; publicV6Address = "fc00:1::1"; }; - node2 = mkNode { replicationMode = 3; publicV6Address = "fc00:1::2"; }; - node3 = mkNode { replicationMode = 3; publicV6Address = "fc00:1::3"; }; - node4 = mkNode { replicationMode = 3; publicV6Address = "fc00:1::4"; }; + node1 = mkNode { replicationMode = "3"; publicV6Address = "fc00:1::1"; }; + node2 = mkNode { replicationMode = "3"; publicV6Address = "fc00:1::2"; }; + node3 = mkNode { replicationMode = "3"; publicV6Address = "fc00:1::3"; }; + node4 = mkNode { replicationMode = "3"; publicV6Address = "fc00:1::4"; }; }; testScript = '' diff --git a/nixos/tests/installer-systemd-stage-1.nix b/nixos/tests/installer-systemd-stage-1.nix index 1dd55dada042a..00205f9417718 100644 --- a/nixos/tests/installer-systemd-stage-1.nix +++ b/nixos/tests/installer-systemd-stage-1.nix @@ -19,7 +19,7 @@ luksroot luksroot-format1 luksroot-format2 - # lvm + lvm separateBoot separateBootFat separateBootZfs diff --git a/nixos/tests/installer.nix b/nixos/tests/installer.nix index 7e835041eb39f..3f57a64333dda 100644 --- a/nixos/tests/installer.nix +++ b/nixos/tests/installer.nix @@ -249,12 +249,11 @@ let with subtest("Check whether nixos-rebuild works"): target.succeed("nixos-rebuild switch >&2") - # FIXME: Nix 2.4 broke nixos-option, someone has to fix it. - # with subtest("Test nixos-option"): - # kernel_modules = target.succeed("nixos-option boot.initrd.kernelModules") - # assert "virtio_console" in kernel_modules - # assert "List of modules" in kernel_modules - # assert "qemu-guest.nix" in kernel_modules + with subtest("Test nixos-option"): + kernel_modules = target.succeed("nixos-option boot.initrd.kernelModules") + assert "virtio_console" in kernel_modules + assert "List of modules" in kernel_modules + assert "qemu-guest.nix" in kernel_modules target.shutdown() @@ -975,6 +974,9 @@ in { "mount LABEL=nixos /mnt", ) ''; + extraConfig = optionalString systemdStage1 '' + boot.initrd.services.lvm.enable = true; + ''; }; # Boot off an encrypted root partition with the default LUKS header format diff --git a/nixos/tests/keepalived.nix b/nixos/tests/keepalived.nix index 16564511d85dc..052b36266d037 100644 --- a/nixos/tests/keepalived.nix +++ b/nixos/tests/keepalived.nix @@ -4,8 +4,8 @@ import ./make-test-python.nix ({ pkgs, lib, ... }: { nodes = { node1 = { pkgs, ... }: { - networking.firewall.extraCommands = "iptables -A INPUT -p vrrp -j ACCEPT"; services.keepalived.enable = true; + services.keepalived.openFirewall = true; services.keepalived.vrrpInstances.test = { interface = "eth1"; state = "MASTER"; @@ -16,8 +16,8 @@ import ./make-test-python.nix ({ pkgs, lib, ... }: { environment.systemPackages = [ pkgs.tcpdump ]; }; node2 = { pkgs, ... }: { - networking.firewall.extraCommands = "iptables -A INPUT -p vrrp -j ACCEPT"; services.keepalived.enable = true; + services.keepalived.openFirewall = true; services.keepalived.vrrpInstances.test = { interface = "eth1"; state = "MASTER"; diff --git a/nixos/tests/kernel-generic.nix b/nixos/tests/kernel-generic.nix index 5f0e7b3e37cd7..11a3bf80c15e4 100644 --- a/nixos/tests/kernel-generic.nix +++ b/nixos/tests/kernel-generic.nix @@ -31,6 +31,8 @@ let linux_5_15_hardened linux_6_1_hardened linux_6_6_hardened + linux_6_8_hardened + linux_6_9_hardened linux_rt_5_4 linux_rt_5_10 linux_rt_5_15 diff --git a/nixos/tests/lomiri.nix b/nixos/tests/lomiri.nix index 9d6337e9977cb..99f04a303be31 100644 --- a/nixos/tests/lomiri.nix +++ b/nixos/tests/lomiri.nix @@ -19,9 +19,13 @@ in { inherit description password; }; + # To control mouse via scripting + programs.ydotool.enable = true; + services.desktopManager.lomiri.enable = lib.mkForce true; services.displayManager.defaultSession = lib.mkForce "lomiri"; + # Help with OCR fonts.packages = [ pkgs.inconsolata ]; environment = { @@ -114,17 +118,9 @@ in { enableOCR = true; testScript = { nodes, ... }: '' - def open_starter(): - """ - Open the starter, and ensure it's opened. - """ - machine.send_key("meta_l-a") - # Look for any of the default apps - machine.wait_for_text(r"(Search|System|Settings|Morph|Browser|Terminal|Alacritty)") - def toggle_maximise(): """ - Send the keybind to maximise the current window. + Maximise the current window. """ machine.send_key("ctrl-meta_l-up") @@ -135,13 +131,43 @@ in { machine.send_key("esc") machine.sleep(5) + def mouse_click(xpos, ypos): + """ + Move the mouse to a screen location and hit left-click. + """ + + # Need to reset to top-left, --absolute doesn't work? + machine.execute("ydotool mousemove -- -10000 -10000") + machine.sleep(2) + + # Move + machine.execute(f"ydotool mousemove -- {xpos} {ypos}") + machine.sleep(2) + + # Click (C0 - left button: down & up) + machine.execute("ydotool click 0xC0") + machine.sleep(2) + + def open_starter(): + """ + Open the starter, and ensure it's opened. + """ + + # Using the keybind has a chance of instantly closing the menu again? Just click the button + mouse_click(20, 30) + + # Look for Search box & GUI-less content-hub examples, highest chances of avoiding false positives + machine.wait_for_text(r"(Search|Export|Import|Share)") + start_all() machine.wait_for_unit("multi-user.target") # Lomiri in greeter mode should work & be able to start a session with subtest("lomiri greeter works"): machine.wait_for_unit("display-manager.service") - # Start page shows current tie + machine.wait_until_succeeds("pgrep -u lightdm -f 'lomiri --mode=greeter'") + + # Start page shows current time machine.wait_for_text(r"(AM|PM)") machine.screenshot("lomiri_greeter_launched") @@ -152,7 +178,6 @@ in { # Login machine.send_chars("${password}\n") - # Best way I can think of to differenciate "Lomiri in LightDM greeter mode" from "Lomiri in user shell mode" machine.wait_until_succeeds("pgrep -u ${user} -f 'lomiri --mode=full-shell'") # The session should start, and not be stuck in i.e. a crash loop @@ -196,6 +221,17 @@ in { machine.screenshot("alacritty_opens") machine.send_key("alt-f4") + # Morph is how we go online + with subtest("morph browser works"): + open_starter() + machine.send_chars("Morph\n") + machine.wait_for_text(r"(Bookmarks|address|site|visited any)") + machine.screenshot("morph_open") + + # morph-browser has a separate VM test, there isn't anything new we could test here + + # Keep it running, we're using it to check content-hub communication from LSS + # LSS provides DE settings with subtest("system settings open"): open_starter() @@ -231,64 +267,75 @@ in { machine.wait_for_text("Morph") # or Gallery, but Morph is already packaged machine.screenshot("settings_content-hub_peers") - # Sadly, it doesn't seem possible to actually select a peer and attempt a content-hub data exchange with just the keyboard + # Select Morph as content source + mouse_click(300, 100) - machine.send_key("alt-f4") + # Expect Morph to be brought into the foreground, with its Downloads page open + machine.wait_for_text("No downloads") - # Morph is how we go online - with subtest("morph browser works"): - open_starter() - machine.send_chars("Morph\n") - machine.wait_for_text(r"(Bookmarks|address|site|visited any)") - machine.screenshot("morph_open") + # If content-hub encounters a problem, it may have crashed the original application issuing the request. + # Check that it's still alive + machine.succeed("pgrep -u ${user} -f lomiri-system-settings") - # morph-browser has a separate VM test, there isn't anything new we could test here + machine.screenshot("content-hub_exchange") - machine.send_key("alt-f4") + # Testing any more would require more applications & setup, the fact that it's already being attempted is a good sign + machine.send_key("esc") + + machine.send_key("alt-f4") # LSS + machine.sleep(2) # focus is slow to switch to second window, closing it *really* helps with OCR afterwards + machine.send_key("alt-f4") # Morph # The ayatana indicators are an important part of the experience, and they hold the only graphical way of exiting the session. - # Reaching them via the intended way requires wayland mouse control, but ydotool lacks a module for its daemon: - # https://github.com/NixOS/nixpkgs/issues/183659 - # Luckily, there's a test app that also displays their contents, but it's abit inconsistent. Hopefully this is *good-enough*. + # There's a test app we could use that also displays their contents, but it's abit inconsistent. with subtest("ayatana indicators work"): - open_starter() - machine.send_chars("Indicators\n") - machine.wait_for_text(r"(Indicators|Client|List|network|datetime|session)") + mouse_click(735, 0) # the cog in the top-right, for the session indicator + machine.wait_for_text(r"(Notifications|Rotation|Battery|Sound|Time|Date|System)") 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 + # Indicator order within the menus *should* be fixed based on per-indicator order setting + # Session is the one we clicked, but the last we should test (logout). Go as far left as we can test. + machine.send_key("left") + machine.send_key("left") + machine.send_key("left") + machine.send_key("left") + machine.send_key("left") + # Notifications are usually empty, nothing to check there + + with subtest("ayatana indicator display works"): + # We start on this, don't go right + machine.wait_for_text("Lock") + machine.screenshot("indicators_display") 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.send_key("right") 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 sound works"): + machine.send_key("right") + machine.wait_for_text(r"(Silent|Volume)") + machine.screenshot("indicators_sound") + + with subtest("ayatana indicator power works"): + machine.send_key("right") + machine.wait_for_text(r"(Charge|Battery settings)") + machine.screenshot("indicators_power") with subtest("ayatana indicator datetime works"): - # Select ayatana-indicator-datetime - machine.send_key("tab") - machine.send_key("down") - machine.send_key("ret") + machine.send_key("right") 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|network|datetime|session)") - with subtest("ayatana indicator session works"): - # Select ayatana-indicator-session - machine.send_key("tab") - machine.send_key("down") - machine.send_key("ret") + machine.send_key("right") machine.wait_for_text("Log Out") machine.screenshot("indicators_session") + + # We should be able to log out and return to the greeter + mouse_click(720, 280) # "Log Out" + mouse_click(400, 240) # confirm logout + machine.wait_until_fails("pgrep -u ${user} -f 'lomiri --mode=full-shell'") + machine.wait_until_succeeds("pgrep -u lightdm -f 'lomiri --mode=greeter'") ''; }) diff --git a/nixos/tests/mediamtx.nix b/nixos/tests/mediamtx.nix index 8cacd02631d95..f40c4a8cb5832 100644 --- a/nixos/tests/mediamtx.nix +++ b/nixos/tests/mediamtx.nix @@ -1,57 +1,60 @@ -import ./make-test-python.nix ({ pkgs, lib, ...} : +import ./make-test-python.nix ( + { pkgs, lib, ... }: -{ - name = "mediamtx"; - meta.maintainers = with lib.maintainers; [ fpletz ]; + { + name = "mediamtx"; + meta.maintainers = with lib.maintainers; [ fpletz ]; - nodes = { - machine = { config, ... }: { - services.mediamtx = { - enable = true; - settings = { - metrics = true; - paths.all.source = "publisher"; + nodes = { + machine = { + services.mediamtx = { + enable = true; + settings = { + metrics = true; + paths.all.source = "publisher"; + }; }; - }; - systemd.services.rtmp-publish = { - description = "Publish an RTMP stream to mediamtx"; - after = [ "mediamtx.service" ]; - bindsTo = [ "mediamtx.service" ]; - wantedBy = [ "multi-user.target" ]; - serviceConfig = { - DynamicUser = true; - Restart = "on-failure"; - RestartSec = "1s"; - TimeoutStartSec = "10s"; - ExecStart = "${lib.getBin pkgs.ffmpeg-headless}/bin/ffmpeg -re -f lavfi -i smptebars=size=800x600:rate=10 -c libx264 -f flv rtmp://localhost:1935/test"; + systemd.services.rtmp-publish = { + description = "Publish an RTMP stream to mediamtx"; + after = [ "mediamtx.service" ]; + bindsTo = [ "mediamtx.service" ]; + wantedBy = [ "multi-user.target" ]; + serviceConfig = { + DynamicUser = true; + Restart = "on-failure"; + RestartSec = "1s"; + TimeoutStartSec = "10s"; + ExecStart = "${lib.getBin pkgs.ffmpeg-headless}/bin/ffmpeg -re -f lavfi -i smptebars=size=800x600:rate=10 -c libx264 -f flv rtmp://localhost:1935/test"; + }; }; - }; - systemd.services.rtmp-receive = { - description = "Receive an RTMP stream from mediamtx"; - after = [ "rtmp-publish.service" ]; - bindsTo = [ "rtmp-publish.service" ]; - wantedBy = [ "multi-user.target" ]; - serviceConfig = { - DynamicUser = true; - Restart = "on-failure"; - RestartSec = "1s"; - TimeoutStartSec = "10s"; - ExecStart = "${lib.getBin pkgs.ffmpeg-headless}/bin/ffmpeg -y -re -i rtmp://localhost:1935/test -f flv /dev/null"; + systemd.services.rtmp-receive = { + description = "Receive an RTMP stream from mediamtx"; + after = [ "rtmp-publish.service" ]; + bindsTo = [ "rtmp-publish.service" ]; + wantedBy = [ "multi-user.target" ]; + serviceConfig = { + DynamicUser = true; + Restart = "on-failure"; + RestartSec = "1s"; + TimeoutStartSec = "10s"; + ExecStart = "${lib.getBin pkgs.ffmpeg-headless}/bin/ffmpeg -y -re -i rtmp://localhost:1935/test -f flv /dev/null"; + }; }; }; }; - }; - testScript = '' - start_all() + testScript = '' + start_all() - machine.wait_for_unit("mediamtx.service") - machine.wait_for_unit("rtmp-publish.service") - machine.wait_for_unit("rtmp-receive.service") - machine.wait_for_open_port(9998) - machine.succeed("curl http://localhost:9998/metrics | grep '^rtmp_conns.*state=\"publish\".*1$'") - machine.succeed("curl http://localhost:9998/metrics | grep '^rtmp_conns.*state=\"read\".*1$'") - ''; -}) + machine.wait_for_unit("mediamtx.service") + machine.wait_for_unit("rtmp-publish.service") + machine.sleep(10) + machine.wait_for_unit("rtmp-receive.service") + machine.wait_for_open_port(9998) + machine.succeed("curl http://localhost:9998/metrics | grep '^rtmp_conns.*state=\"publish\".*1$'") + machine.succeed("curl http://localhost:9998/metrics | grep '^rtmp_conns.*state=\"read\".*1$'") + ''; + } +) diff --git a/nixos/tests/mysql/common.nix b/nixos/tests/mysql/common.nix index 1cf52347f4c74..ad54b0e00c1b3 100644 --- a/nixos/tests/mysql/common.nix +++ b/nixos/tests/mysql/common.nix @@ -4,7 +4,7 @@ inherit (pkgs) mysql80; }; perconaPackages = { - inherit (pkgs) percona-server_8_0; + inherit (pkgs) percona-server_lts percona-server_innovation; }; mkTestName = pkg: "mariadb_${builtins.replaceStrings ["."] [""] (lib.versions.majorMinor pkg.version)}"; } diff --git a/nixos/tests/mysql/mysql.nix b/nixos/tests/mysql/mysql.nix index 0a61f9d38fe2e..093da4f46aa10 100644 --- a/nixos/tests/mysql/mysql.nix +++ b/nixos/tests/mysql/mysql.nix @@ -146,6 +146,6 @@ in }) mariadbPackages) // (lib.mapAttrs (_: package: makeMySQLTest { inherit package; - name = "percona_8_0"; + name = builtins.replaceStrings ["-"] ["_"] package.pname; hasMroonga = false; useSocketAuth = false; }) perconaPackages) diff --git a/nixos/tests/stalwart-mail.nix b/nixos/tests/stalwart-mail.nix index 634c0e2e39261..173b4fce4ad5d 100644 --- a/nixos/tests/stalwart-mail.nix +++ b/nixos/tests/stalwart-mail.nix @@ -18,8 +18,8 @@ in import ./make-test-python.nix ({ lib, ... }: { server.hostname = domain; certificate."snakeoil" = { - cert = "file://${certs.${domain}.cert}"; - private-key = "file://${certs.${domain}.key}"; + cert = "%{file:${certs.${domain}.cert}}%"; + private-key = "%{file:${certs.${domain}.key}}%"; }; server.tls = { @@ -40,24 +40,24 @@ in import ./make-test-python.nix ({ lib, ... }: { }; }; - session.auth.mechanisms = [ "PLAIN" ]; - session.auth.directory = "in-memory"; - storage.directory = "in-memory"; # shared with imap + session.auth.mechanisms = "[plain]"; + session.auth.directory = "'in-memory'"; + storage.directory = "in-memory"; - session.rcpt.directory = "in-memory"; - queue.outbound.next-hop = [ "local" ]; + session.rcpt.directory = "'in-memory'"; + queue.outbound.next-hop = "'local'"; directory."in-memory" = { type = "memory"; principals = [ { - type = "individual"; + class = "individual"; name = "alice"; secret = "foobar"; email = [ "alice@${domain}" ]; } { - type = "individual"; + class = "individual"; name = "bob"; secret = "foobar"; email = [ "bob@${domain}" ]; @@ -115,6 +115,6 @@ in import ./make-test-python.nix ({ lib, ... }: { ''; meta = { - maintainers = with lib.maintainers; [ happysalada pacien ]; + maintainers = with lib.maintainers; [ happysalada pacien onny ]; }; }) diff --git a/nixos/tests/step-ca.nix b/nixos/tests/step-ca.nix index a855b590232dd..184c35f6b85cc 100644 --- a/nixos/tests/step-ca.nix +++ b/nixos/tests/step-ca.nix @@ -62,6 +62,24 @@ import ./make-test-python.nix ({ pkgs, ... }: }; }; + caclientcaddy = + { config, pkgs, ... }: { + security.pki.certificateFiles = [ "${test-certificates}/root_ca.crt" ]; + + networking.firewall.allowedTCPPorts = [ 80 443 ]; + + services.caddy = { + enable = true; + virtualHosts."caclientcaddy".extraConfig = '' + respond "Welcome to Caddy!" + + tls caddy@example.org { + ca https://caserver:8443/acme/acme/directory + } + ''; + }; + }; + catester = { config, pkgs, ... }: { security.pki.certificateFiles = [ "${test-certificates}/root_ca.crt" ]; }; @@ -71,7 +89,12 @@ import ./make-test-python.nix ({ pkgs, ... }: '' catester.start() caserver.wait_for_unit("step-ca.service") + caserver.wait_until_succeeds("journalctl -o cat -u step-ca.service | grep '${pkgs.step-ca.version}'") + caclient.wait_for_unit("acme-finished-caclient.target") catester.succeed("curl https://caclient/ | grep \"Welcome to nginx!\"") + + caclientcaddy.wait_for_unit("caddy.service") + catester.succeed("curl https://caclientcaddy/ | grep \"Welcome to Caddy!\"") ''; }) diff --git a/nixos/tests/stub-ld.nix b/nixos/tests/stub-ld.nix index 25161301741b7..72b0aebf3e6ce 100644 --- a/nixos/tests/stub-ld.nix +++ b/nixos/tests/stub-ld.nix @@ -45,10 +45,10 @@ import ./make-test-python.nix ({ lib, pkgs, ... }: { ${if32 "machine.succeed('test -L /${libDir32}/${ldsoBasename32}')"} with subtest("Try FHS executable"): - machine.copy_from_host('${test-exec.${pkgs.system}}','test-exec') + machine.copy_from_host('${test-exec.${pkgs.stdenv.hostPlatform.system}}','test-exec') machine.succeed('if test-exec/${exec-name} 2>outfile; then false; else [ $? -eq 127 ];fi') machine.succeed('grep -qi nixos outfile') - ${if32 "machine.copy_from_host('${test-exec.${pkgs32.system}}','test-exec32')"} + ${if32 "machine.copy_from_host('${test-exec.${pkgs32.stdenv.hostPlatform.system}}','test-exec32')"} ${if32 "machine.succeed('if test-exec32/${exec-name} 2>outfile32; then false; else [ $? -eq 127 ];fi')"} ${if32 "machine.succeed('grep -qi nixos outfile32')"} diff --git a/nixos/tests/systemd-initrd-luks-fido2.nix b/nixos/tests/systemd-initrd-luks-fido2.nix index f9f75ab7f301c..207f51f4dd9b4 100644 --- a/nixos/tests/systemd-initrd-luks-fido2.nix +++ b/nixos/tests/systemd-initrd-luks-fido2.nix @@ -9,7 +9,6 @@ import ./make-test-python.nix ({ lib, pkgs, ... }: { # Booting off the encrypted disk requires having a Nix store available for the init script mountHostNixStore = true; useEFIBoot = true; - qemu.package = lib.mkForce (pkgs.qemu_test.override { canokeySupport = true; }); qemu.options = [ "-device canokey,file=/tmp/canokey-file" ]; }; boot.loader.systemd-boot.enable = true; diff --git a/nixos/tests/systemd-resolved.nix b/nixos/tests/systemd-resolved.nix new file mode 100644 index 0000000000000..3eedc17f4b34f --- /dev/null +++ b/nixos/tests/systemd-resolved.nix @@ -0,0 +1,75 @@ +import ./make-test-python.nix ({ pkgs, lib, ... }: { + name = "systemd-resolved"; + meta.maintainers = [ lib.maintainers.elvishjerricco ]; + + nodes.server = { lib, config, ... }: let + exampleZone = pkgs.writeTextDir "example.com.zone" '' + @ SOA ns.example.com. noc.example.com. 2019031301 86400 7200 3600000 172800 + @ A ${(lib.head config.networking.interfaces.eth1.ipv4.addresses).address} + @ AAAA ${(lib.head config.networking.interfaces.eth1.ipv6.addresses).address} + ''; + in { + networking.firewall.enable = false; + networking.useDHCP = false; + + networking.interfaces.eth1.ipv6.addresses = lib.mkForce [ + { address = "fd00::1"; prefixLength = 64; } + ]; + + services.knot = { + enable = true; + settings = { + server.listen = [ + "0.0.0.0@53" + "::@53" + ]; + template.default.storage = exampleZone; + zone."example.com".file = "example.com.zone"; + }; + }; + }; + + nodes.client = { nodes, ... }: let + inherit (lib.head nodes.server.networking.interfaces.eth1.ipv4.addresses) address; + in { + networking.nameservers = [ address ]; + networking.interfaces.eth1.ipv6.addresses = lib.mkForce [ + { address = "fd00::2"; prefixLength = 64; } + ]; + services.resolved.enable = true; + services.resolved.fallbackDns = [ ]; + networking.useNetworkd = true; + networking.useDHCP = false; + systemd.network.networks."40-eth0".enable = false; + + testing.initrdBackdoor = true; + boot.initrd = { + systemd.enable = true; + systemd.initrdBin = [ pkgs.iputils ]; + network.enable = true; + services.resolved.enable = true; + }; + }; + + testScript = { nodes, ... }: let + address4 = (lib.head nodes.server.networking.interfaces.eth1.ipv4.addresses).address; + address6 = (lib.head nodes.server.networking.interfaces.eth1.ipv6.addresses).address; + in '' + start_all() + server.wait_for_unit("multi-user.target") + + def test_client(): + query = client.succeed("resolvectl query example.com") + assert "${address4}" in query + assert "${address6}" in query + client.succeed("ping -4 -c 1 example.com") + client.succeed("ping -6 -c 1 example.com") + + client.wait_for_unit("initrd.target") + test_client() + client.switch_root() + + client.wait_for_unit("multi-user.target") + test_client() + ''; +}) diff --git a/nixos/tests/tigervnc.nix b/nixos/tests/tigervnc.nix index ed575682d9338..b80cb49519c45 100644 --- a/nixos/tests/tigervnc.nix +++ b/nixos/tests/tigervnc.nix @@ -38,16 +38,18 @@ makeTest { server.succeed("Xvnc -geometry 720x576 :1 -PasswordFile vncpasswd >&2 &") server.wait_until_succeeds("nc -z localhost 5901", timeout=10) server.succeed("DISPLAY=:1 xwininfo -root | grep 720x576") - server.execute("DISPLAY=:1 display -size 360x200 -font sans -gravity south label:'HELLO VNC WORLD' >&2 &") + server.execute("DISPLAY=:1 display -size 360x200 -font sans -gravity south label:'HELLO VNC' >&2 &") client.wait_for_x() client.execute("vncviewer server:1 -PasswordFile vncpasswd >&2 &") client.wait_for_window(r"VNC") client.screenshot("screenshot") text = client.get_screen_text() + # Displayed text - assert 'HELLO VNC WORLD' in text + assert 'HELLO VNC' in text # Client window title - assert 'TigerVNC' in text + # get_screen_text can't get correct string from screenshot + # assert 'TigerVNC' in text ''; } diff --git a/nixos/tests/vector.nix b/nixos/tests/vector.nix deleted file mode 100644 index a55eb4e012c5b..0000000000000 --- a/nixos/tests/vector.nix +++ /dev/null @@ -1,37 +0,0 @@ -{ system ? builtins.currentSystem, config ? { } -, pkgs ? import ../.. { inherit system config; } }: - -with import ../lib/testing-python.nix { inherit system pkgs; }; -with pkgs.lib; - -{ - test1 = makeTest { - name = "vector-test1"; - meta.maintainers = [ pkgs.lib.maintainers.happysalada ]; - - nodes.machine = { config, pkgs, ... }: { - services.vector = { - enable = true; - journaldAccess = true; - settings = { - sources.journald.type = "journald"; - - sinks = { - file = { - type = "file"; - inputs = [ "journald" ]; - path = "/var/lib/vector/logs.log"; - encoding = { codec = "json"; }; - }; - }; - }; - }; - }; - - # ensure vector is forwarding the messages appropriately - testScript = '' - machine.wait_for_unit("vector.service") - machine.wait_for_file("/var/lib/vector/logs.log") - ''; - }; -} diff --git a/nixos/tests/vector/api.nix b/nixos/tests/vector/api.nix new file mode 100644 index 0000000000000..8aa3a0c1b771f --- /dev/null +++ b/nixos/tests/vector/api.nix @@ -0,0 +1,39 @@ +import ../make-test-python.nix ({ lib, pkgs, ... }: + +{ + name = "vector-api"; + meta.maintainers = [ pkgs.lib.maintainers.happysalada ]; + + nodes.machineapi = { config, pkgs, ... }: { + services.vector = { + enable = true; + journaldAccess = false; + settings = { + api.enabled = true; + + sources = { + demo_logs = { + type = "demo_logs"; + format = "json"; + }; + }; + + sinks = { + file = { + type = "file"; + inputs = [ "demo_logs" ]; + path = "/var/lib/vector/logs.log"; + encoding = { codec = "json"; }; + }; + }; + }; + }; + }; + + testScript = '' + machineapi.wait_for_unit("vector") + machineapi.wait_for_open_port(8686) + machineapi.succeed("journalctl -o cat -u vector.service | grep 'API server running'") + machineapi.wait_until_succeeds("curl -sSf http://localhost:8686/health") + ''; +}) diff --git a/nixos/tests/vector/default.nix b/nixos/tests/vector/default.nix new file mode 100644 index 0000000000000..990b067e81774 --- /dev/null +++ b/nixos/tests/vector/default.nix @@ -0,0 +1,11 @@ +{ system ? builtins.currentSystem +, config ? { } +, pkgs ? import ../../.. { inherit system config; } +}: + +{ + file-sink = import ./file-sink.nix { inherit system pkgs; }; + api = import ./api.nix { inherit system pkgs; }; + dnstap = import ./dnstap.nix { inherit system pkgs; }; + nginx-clickhouse = import ./nginx-clickhouse.nix { inherit system pkgs; }; +} diff --git a/nixos/tests/vector/dnstap.nix b/nixos/tests/vector/dnstap.nix new file mode 100644 index 0000000000000..15d643311b604 --- /dev/null +++ b/nixos/tests/vector/dnstap.nix @@ -0,0 +1,118 @@ +import ../make-test-python.nix ({ lib, pkgs, ... }: + +let + dnstapSocket = "/var/run/vector/dnstap.sock"; +in +{ + name = "vector-dnstap"; + meta.maintainers = [ pkgs.lib.maintainers.happysalada ]; + + nodes = { + unbound = { config, pkgs, ... }: { + networking.firewall.allowedUDPPorts = [ 53 ]; + + services.vector = { + enable = true; + + settings = { + sources = { + dnstap = { + type = "dnstap"; + multithreaded = true; + mode = "unix"; + lowercase_hostnames = true; + socket_file_mode = 504; + socket_path = "${dnstapSocket}"; + }; + }; + + sinks = { + file = { + type = "file"; + inputs = [ "dnstap" ]; + path = "/var/lib/vector/logs.log"; + encoding = { codec = "json"; }; + }; + }; + }; + }; + + systemd.services.vector.serviceConfig = { + RuntimeDirectory = "vector"; + RuntimeDirectoryMode = "0770"; + }; + + services.unbound = { + enable = true; + enableRootTrustAnchor = false; + package = pkgs.unbound-full; + settings = { + server = { + interface = [ "0.0.0.0" "::" ]; + access-control = [ "192.168.1.0/24 allow" ]; + + domain-insecure = "local"; + private-domain = "local"; + + local-zone = "local. static"; + local-data = [ + ''"test.local. 10800 IN A 192.168.123.5"'' + ]; + }; + + dnstap = { + dnstap-enable = "yes"; + dnstap-socket-path = "${dnstapSocket}"; + dnstap-send-identity = "yes"; + dnstap-send-version = "yes"; + dnstap-log-client-query-messages = "yes"; + dnstap-log-client-response-messages = "yes"; + }; + }; + }; + + systemd.services.unbound = { + after = [ "vector.service" ]; + wants = [ "vector.service" ]; + serviceConfig = { + # DNSTAP access + ReadWritePaths = [ "/var/run/vector" ]; + SupplementaryGroups = [ "vector" ]; + }; + }; + }; + + dnsclient = { config, pkgs, ... }: { + environment.systemPackages = [ pkgs.dig ]; + }; + }; + + testScript = '' + unbound.wait_for_unit("unbound") + unbound.wait_for_unit("vector") + + unbound.wait_until_succeeds( + "journalctl -o cat -u vector.service | grep 'Socket permissions updated to 0o770'" + ) + unbound.wait_until_succeeds( + "journalctl -o cat -u vector.service | grep 'component_type=dnstap' | grep 'Listening... path=\"${dnstapSocket}\"'" + ) + + unbound.wait_for_file("${dnstapSocket}") + unbound.succeed("test 770 -eq $(stat -c '%a' ${dnstapSocket})") + + dnsclient.wait_for_unit("network-online.target") + dnsclient.succeed( + "dig @unbound test.local" + ) + + unbound.wait_for_file("/var/lib/vector/logs.log") + + unbound.wait_until_succeeds( + "grep ClientQuery /var/lib/vector/logs.log | grep '\"domainName\":\"test.local.\"' | grep '\"rcodeName\":\"NoError\"'" + ) + unbound.wait_until_succeeds( + "grep ClientResponse /var/lib/vector/logs.log | grep '\"domainName\":\"test.local.\"' | grep '\"rData\":\"192.168.123.5\"'" + ) + ''; +}) diff --git a/nixos/tests/vector/file-sink.nix b/nixos/tests/vector/file-sink.nix new file mode 100644 index 0000000000000..2220d20ac55c3 --- /dev/null +++ b/nixos/tests/vector/file-sink.nix @@ -0,0 +1,49 @@ +import ../make-test-python.nix ({ lib, pkgs, ... }: + +{ + name = "vector-test1"; + meta.maintainers = [ pkgs.lib.maintainers.happysalada ]; + + nodes.machine = { config, pkgs, ... }: { + services.vector = { + enable = true; + journaldAccess = true; + settings = { + sources = { + journald.type = "journald"; + + vector_metrics.type = "internal_metrics"; + + vector_logs.type = "internal_logs"; + }; + + sinks = { + file = { + type = "file"; + inputs = [ "journald" "vector_logs" ]; + path = "/var/lib/vector/logs.log"; + encoding = { codec = "json"; }; + }; + + prometheus_exporter = { + type = "prometheus_exporter"; + inputs = [ "vector_metrics" ]; + address = "[::]:9598"; + }; + }; + }; + }; + }; + + # ensure vector is forwarding the messages appropriately + testScript = '' + machine.wait_for_unit("vector.service") + machine.wait_for_open_port(9598) + machine.wait_until_succeeds("journalctl -o cat -u vector.service | grep 'version=\"${pkgs.vector.version}\"'") + machine.wait_until_succeeds("journalctl -o cat -u vector.service | grep 'API is disabled'") + machine.wait_until_succeeds("curl -sSf http://localhost:9598/metrics | grep vector_build_info") + machine.wait_until_succeeds("curl -sSf http://localhost:9598/metrics | grep vector_component_received_bytes_total | grep journald") + machine.wait_until_succeeds("curl -sSf http://localhost:9598/metrics | grep vector_utilization | grep prometheus_exporter") + machine.wait_for_file("/var/lib/vector/logs.log") + ''; +}) diff --git a/nixos/tests/vector/nginx-clickhouse.nix b/nixos/tests/vector/nginx-clickhouse.nix new file mode 100644 index 0000000000000..3d99bac6ac161 --- /dev/null +++ b/nixos/tests/vector/nginx-clickhouse.nix @@ -0,0 +1,168 @@ +import ../make-test-python.nix ({ lib, pkgs, ... }: + +{ + name = "vector-nginx-clickhouse"; + meta.maintainers = [ pkgs.lib.maintainers.happysalada ]; + + nodes = { + clickhouse = { config, pkgs, ... }: { + virtualisation.memorySize = 4096; + + # Clickhouse module can't listen on a non-loopback IP. + networking.firewall.allowedTCPPorts = [ 6000 ]; + services.clickhouse.enable = true; + + # Exercise Vector sink->source for now. + services.vector = { + enable = true; + + settings = { + sources = { + vector_source = { + type = "vector"; + address = "[::]:6000"; + }; + }; + + sinks = { + clickhouse = { + type = "clickhouse"; + inputs = [ "vector_source" ]; + endpoint = "http://localhost:8123"; + database = "nginxdb"; + table = "access_logs"; + skip_unknown_fields = true; + }; + }; + }; + }; + }; + + nginx = { config, pkgs, ... }: { + services.nginx = { + enable = true; + virtualHosts.localhost = {}; + }; + + services.vector = { + enable = true; + + settings = { + sources = { + nginx_logs = { + type = "file"; + include = [ "/var/log/nginx/access.log" ]; + read_from = "end"; + }; + }; + + sinks = { + vector_sink = { + type = "vector"; + inputs = [ "nginx_logs" ]; + address = "clickhouse:6000"; + }; + }; + }; + }; + + systemd.services.vector.serviceConfig = { + SupplementaryGroups = [ "nginx" ]; + }; + }; + }; + + testScript = + let + # work around quote/substitution complexity by Nix, Perl, bash and SQL. + databaseDDL = pkgs.writeText "database.sql" "CREATE DATABASE IF NOT EXISTS nginxdb"; + + tableDDL = pkgs.writeText "table.sql" '' + CREATE TABLE IF NOT EXISTS nginxdb.access_logs ( + message String + ) + ENGINE = MergeTree() + ORDER BY tuple() + ''; + + # Graciously taken from https://clickhouse.com/docs/en/integrations/vector + tableView = pkgs.writeText "table-view.sql" '' + CREATE MATERIALIZED VIEW nginxdb.access_logs_view + ( + RemoteAddr String, + Client String, + RemoteUser String, + TimeLocal DateTime, + RequestMethod String, + Request String, + HttpVersion String, + Status Int32, + BytesSent Int64, + UserAgent String + ) + ENGINE = MergeTree() + ORDER BY RemoteAddr + POPULATE AS + WITH + splitByWhitespace(message) as split, + splitByRegexp('\S \d+ "([^"]*)"', message) as referer + SELECT + split[1] AS RemoteAddr, + split[2] AS Client, + split[3] AS RemoteUser, + parseDateTimeBestEffort(replaceOne(trim(LEADING '[' FROM split[4]), ':', ' ')) AS TimeLocal, + trim(LEADING '"' FROM split[6]) AS RequestMethod, + split[7] AS Request, + trim(TRAILING '"' FROM split[8]) AS HttpVersion, + split[9] AS Status, + split[10] AS BytesSent, + trim(BOTH '"' from referer[2]) AS UserAgent + FROM + (SELECT message FROM nginxdb.access_logs) + ''; + + selectQuery = pkgs.writeText "select.sql" "SELECT * from nginxdb.access_logs_view"; + in + '' + clickhouse.wait_for_unit("clickhouse") + clickhouse.wait_for_open_port(8123) + + clickhouse.wait_until_succeeds( + "journalctl -o cat -u clickhouse.service | grep 'Started ClickHouse server'" + ) + + clickhouse.wait_for_unit("vector") + clickhouse.wait_for_open_port(6000) + + clickhouse.succeed( + "cat ${databaseDDL} | clickhouse-client" + ) + + clickhouse.succeed( + "cat ${tableDDL} | clickhouse-client" + ) + + clickhouse.succeed( + "cat ${tableView} | clickhouse-client" + ) + + nginx.wait_for_unit("nginx") + nginx.wait_for_open_port(80) + nginx.wait_for_unit("vector") + nginx.wait_until_succeeds( + "journalctl -o cat -u vector.service | grep 'Starting file server'" + ) + + nginx.succeed("curl http://localhost/") + nginx.succeed("curl http://localhost/") + + nginx.wait_for_file("/var/log/nginx/access.log") + nginx.wait_until_succeeds( + "journalctl -o cat -u vector.service | grep 'Found new file to watch. file=/var/log/nginx/access.log'" + ) + + clickhouse.wait_until_succeeds( + "cat ${selectQuery} | clickhouse-client | grep 'curl'" + ) + ''; +}) diff --git a/nixos/tests/virtualbox.nix b/nixos/tests/virtualbox.nix index 3c2a391233dbd..5fce3ba548123 100644 --- a/nixos/tests/virtualbox.nix +++ b/nixos/tests/virtualbox.nix @@ -98,7 +98,6 @@ let cfg = (import ../lib/eval-config.nix { system = if use64bitGuest then "x86_64-linux" else "i686-linux"; modules = [ - ../modules/profiles/minimal.nix (testVMConfig vmName vmScript) ]; }).config; diff --git a/nixos/tests/web-apps/nextjs-ollama-llm-ui.nix b/nixos/tests/web-apps/nextjs-ollama-llm-ui.nix new file mode 100644 index 0000000000000..3bb9d1e62aefe --- /dev/null +++ b/nixos/tests/web-apps/nextjs-ollama-llm-ui.nix @@ -0,0 +1,22 @@ +{ lib, ... }: + +{ + name = "nextjs-ollama-llm-ui"; + meta.maintainers = with lib.maintainers; [ malteneuss ]; + + nodes.machine = + { pkgs, ... }: + { + services.nextjs-ollama-llm-ui = { + enable = true; + port = 8080; + }; + }; + + testScript = '' + # Ensure the service is started and reachable + machine.wait_for_unit("nextjs-ollama-llm-ui.service") + machine.wait_for_open_port(8080) + machine.succeed("curl --fail http://127.0.0.1:8080") + ''; +} diff --git a/nixos/tests/web-apps/pretalx.nix b/nixos/tests/web-apps/pretalx.nix index 76e261b2207ec..cbb6580aa0515 100644 --- a/nixos/tests/web-apps/pretalx.nix +++ b/nixos/tests/web-apps/pretalx.nix @@ -5,13 +5,16 @@ meta.maintainers = lib.teams.c3d2.members; nodes = { - pretalx = { + pretalx = { config, ... }: { networking.extraHosts = '' 127.0.0.1 talks.local ''; services.pretalx = { enable = true; + plugins = with config.services.pretalx.package.plugins; [ + pages + ]; nginx.domain = "talks.local"; settings = { site.url = "http://talks.local"; diff --git a/nixos/tests/your_spotify.nix b/nixos/tests/your_spotify.nix new file mode 100644 index 0000000000000..a1fa0e459a8e1 --- /dev/null +++ b/nixos/tests/your_spotify.nix @@ -0,0 +1,33 @@ +import ./make-test-python.nix ({pkgs, ...}: { + name = "your_spotify"; + meta = with pkgs.lib.maintainers; { + maintainers = [patrickdag]; + }; + + nodes.machine = { + services.your_spotify = { + enable = true; + spotifySecretFile = pkgs.writeText "spotifySecretFile" "deadbeef"; + settings = { + CLIENT_ENDPOINT = "http://localhost"; + API_ENDPOINT = "http://localhost:3000"; + SPOTIFY_PUBLIC = "beefdead"; + }; + enableLocalDB = true; + nginxVirtualHost = "localhost"; + }; + }; + + testScript = '' + machine.wait_for_unit("your_spotify.service") + + machine.wait_for_open_port(3000) + machine.wait_for_open_port(80) + + out = machine.succeed("curl --fail -X GET 'http://localhost:3000/'") + assert "Hello !" in out + + out = machine.succeed("curl --fail -X GET 'http://localhost:80/'") + assert "<title>Your Spotify</title>" in out + ''; +}) |