diff options
Diffstat (limited to 'nixpkgs/nixos/tests/web-apps')
18 files changed, 1301 insertions, 0 deletions
diff --git a/nixpkgs/nixos/tests/web-apps/gotosocial.nix b/nixpkgs/nixos/tests/web-apps/gotosocial.nix new file mode 100644 index 000000000000..6d279ab63a79 --- /dev/null +++ b/nixpkgs/nixos/tests/web-apps/gotosocial.nix @@ -0,0 +1,28 @@ +{ lib, ... }: +{ + name = "gotosocial"; + meta.maintainers = with lib.maintainers; [ misuzu ]; + + nodes.machine = { pkgs, ... }: { + environment.systemPackages = [ pkgs.jq ]; + services.gotosocial = { + enable = true; + setupPostgresqlDB = true; + settings = { + host = "localhost:8081"; + port = 8081; + }; + }; + }; + + testScript = '' + machine.wait_for_unit("gotosocial.service") + machine.wait_for_unit("postgresql.service") + machine.wait_for_open_port(8081) + + # check user registration via cli + machine.succeed("curl -sS -f http://localhost:8081/nodeinfo/2.0 | jq '.usage.users.total' | grep -q '^0$'") + machine.succeed("gotosocial-admin account create --username nickname --email email@example.com --password kurtz575VPeBgjVm") + machine.succeed("curl -sS -f http://localhost:8081/nodeinfo/2.0 | jq '.usage.users.total' | grep -q '^1$'") + ''; +} diff --git a/nixpkgs/nixos/tests/web-apps/healthchecks.nix b/nixpkgs/nixos/tests/web-apps/healthchecks.nix new file mode 100644 index 000000000000..41c40cd5dd8d --- /dev/null +++ b/nixpkgs/nixos/tests/web-apps/healthchecks.nix @@ -0,0 +1,42 @@ +import ../make-test-python.nix ({ lib, pkgs, ... }: { + name = "healthchecks"; + + meta = with lib.maintainers; { + maintainers = [ phaer ]; + }; + + nodes.machine = { ... }: { + services.healthchecks = { + enable = true; + settings = { + SITE_NAME = "MyUniqueInstance"; + COMPRESS_ENABLED = "True"; + SECRET_KEY_FILE = pkgs.writeText "secret" + "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"; + }; + }; + }; + + testScript = '' + machine.start() + machine.wait_for_unit("healthchecks.target") + machine.wait_until_succeeds("journalctl --since -1m --unit healthchecks --grep Listening") + + with subtest("Home screen loads"): + machine.succeed( + "curl -sSfL http://localhost:8000 | grep '<title>Sign In'" + ) + + with subtest("Setting SITE_NAME via freeform option works"): + machine.succeed( + "curl -sSfL http://localhost:8000 | grep 'MyUniqueInstance</title>'" + ) + + with subtest("Manage script works"): + # "shell" sucommand should succeed, needs python in PATH. + assert "foo\n" == machine.succeed("echo 'print(\"foo\")' | sudo -u healthchecks healthchecks-manage shell") + + # Shouldn't fail if not called by healthchecks user + assert "foo\n" == machine.succeed("echo 'print(\"foo\")' | healthchecks-manage shell") + ''; +}) diff --git a/nixpkgs/nixos/tests/web-apps/mastodon/default.nix b/nixpkgs/nixos/tests/web-apps/mastodon/default.nix new file mode 100644 index 000000000000..178590d13b63 --- /dev/null +++ b/nixpkgs/nixos/tests/web-apps/mastodon/default.nix @@ -0,0 +1,9 @@ +{ system ? builtins.currentSystem, handleTestOn }: +let + supportedSystems = [ "x86_64-linux" "i686-linux" "aarch64-linux" ]; + +in +{ + standard = handleTestOn supportedSystems ./standard.nix { inherit system; }; + remote-databases = handleTestOn supportedSystems ./remote-databases.nix { inherit system; }; +} diff --git a/nixpkgs/nixos/tests/web-apps/mastodon/remote-databases.nix b/nixpkgs/nixos/tests/web-apps/mastodon/remote-databases.nix new file mode 100644 index 000000000000..fa6430a99353 --- /dev/null +++ b/nixpkgs/nixos/tests/web-apps/mastodon/remote-databases.nix @@ -0,0 +1,190 @@ +import ../../make-test-python.nix ({pkgs, ...}: +let + cert = pkgs: pkgs.runCommand "selfSignedCerts" { buildInputs = [ pkgs.openssl ]; } '' + openssl req -x509 -newkey rsa:4096 -keyout key.pem -out cert.pem -nodes -subj '/CN=mastodon.local' -days 36500 + mkdir -p $out + cp key.pem cert.pem $out + ''; + + hosts = '' + 192.168.2.103 mastodon.local + ''; + +in +{ + name = "mastodon-remote-postgresql"; + meta.maintainers = with pkgs.lib.maintainers; [ erictapen izorkin ]; + + nodes = { + databases = { config, ... }: { + environment = { + etc = { + "redis/password-redis-db".text = '' + ogjhJL8ynrP7MazjYOF6 + ''; + }; + }; + networking = { + interfaces.eth1 = { + ipv4.addresses = [ + { address = "192.168.2.102"; prefixLength = 24; } + ]; + }; + extraHosts = hosts; + firewall.allowedTCPPorts = [ + config.services.redis.servers.mastodon.port + config.services.postgresql.port + ]; + }; + + services.redis.servers.mastodon = { + enable = true; + bind = "0.0.0.0"; + port = 31637; + requirePassFile = "/etc/redis/password-redis-db"; + }; + + services.postgresql = { + enable = true; + # TODO remove once https://github.com/NixOS/nixpkgs/pull/266270 is resolved. + package = pkgs.postgresql_14; + enableTCPIP = true; + authentication = '' + hostnossl mastodon_local mastodon_test 192.168.2.201/32 md5 + ''; + initialScript = pkgs.writeText "postgresql_init.sql" '' + CREATE ROLE mastodon_test LOGIN PASSWORD 'SoDTZcISc3f1M1LJsRLT'; + CREATE DATABASE mastodon_local TEMPLATE template0 ENCODING UTF8; + GRANT ALL PRIVILEGES ON DATABASE mastodon_local TO mastodon_test; + ''; + }; + }; + + nginx = { nodes, ... }: { + networking = { + interfaces.eth1 = { + ipv4.addresses = [ + { address = "192.168.2.103"; prefixLength = 24; } + ]; + }; + extraHosts = hosts; + firewall.allowedTCPPorts = [ 80 443 ]; + }; + + security = { + pki.certificateFiles = [ "${cert pkgs}/cert.pem" ]; + }; + + services.nginx = { + enable = true; + recommendedProxySettings = true; + virtualHosts."mastodon.local" = { + root = "/var/empty"; + forceSSL = true; + enableACME = pkgs.lib.mkForce false; + sslCertificate = "${cert pkgs}/cert.pem"; + sslCertificateKey = "${cert pkgs}/key.pem"; + locations."/" = { + tryFiles = "$uri @proxy"; + }; + locations."@proxy" = { + proxyPass = "http://192.168.2.201:${toString nodes.server.services.mastodon.webPort}"; + proxyWebsockets = true; + }; + }; + }; + }; + + server = { config, pkgs, ... }: { + virtualisation.memorySize = 2048; + + environment = { + etc = { + "mastodon/password-redis-db".text = '' + ogjhJL8ynrP7MazjYOF6 + ''; + "mastodon/password-posgressql-db".text = '' + SoDTZcISc3f1M1LJsRLT + ''; + }; + }; + + networking = { + interfaces.eth1 = { + ipv4.addresses = [ + { address = "192.168.2.201"; prefixLength = 24; } + ]; + }; + extraHosts = hosts; + firewall.allowedTCPPorts = [ + config.services.mastodon.webPort + config.services.mastodon.sidekiqPort + ]; + }; + + services.mastodon = { + enable = true; + configureNginx = false; + localDomain = "mastodon.local"; + enableUnixSocket = false; + streamingProcesses = 2; + redis = { + createLocally = false; + host = "192.168.2.102"; + port = 31637; + passwordFile = "/etc/mastodon/password-redis-db"; + }; + database = { + createLocally = false; + host = "192.168.2.102"; + port = 5432; + name = "mastodon_local"; + user = "mastodon_test"; + passwordFile = "/etc/mastodon/password-posgressql-db"; + }; + smtp = { + createLocally = false; + fromAddress = "mastodon@mastodon.local"; + }; + extraConfig = { + BIND = "0.0.0.0"; + EMAIL_DOMAIN_ALLOWLIST = "example.com"; + RAILS_SERVE_STATIC_FILES = "true"; + TRUSTED_PROXY_IP = "192.168.2.103"; + }; + }; + }; + + client = { pkgs, ... }: { + environment.systemPackages = [ pkgs.jq ]; + networking = { + interfaces.eth1 = { + ipv4.addresses = [ + { address = "192.168.2.202"; prefixLength = 24; } + ]; + }; + extraHosts = hosts; + }; + + security = { + pki.certificateFiles = [ "${cert pkgs}/cert.pem" ]; + }; + }; + }; + + testScript = import ./script.nix { + inherit pkgs; + extraInit = '' + nginx.wait_for_unit("nginx.service") + nginx.wait_for_open_port(443) + databases.wait_for_unit("redis-mastodon.service") + databases.wait_for_unit("postgresql.service") + databases.wait_for_open_port(31637) + databases.wait_for_open_port(5432) + ''; + extraShutdown = '' + nginx.shutdown() + databases.shutdown() + ''; + }; +}) diff --git a/nixpkgs/nixos/tests/web-apps/mastodon/script.nix b/nixpkgs/nixos/tests/web-apps/mastodon/script.nix new file mode 100644 index 000000000000..9184c63c8941 --- /dev/null +++ b/nixpkgs/nixos/tests/web-apps/mastodon/script.nix @@ -0,0 +1,52 @@ +{ pkgs +, extraInit ? "" +, extraShutdown ? "" +}: + +'' + start_all() + + ${extraInit} + + server.wait_for_unit("mastodon-sidekiq-all.service") + server.wait_for_unit("mastodon-streaming.target") + server.wait_for_unit("mastodon-web.service") + server.wait_for_open_port(55001) + + # Check that mastodon-media-auto-remove is scheduled + server.succeed("systemctl status mastodon-media-auto-remove.timer") + + # Check Mastodon version from remote client + client.succeed("curl --fail https://mastodon.local/api/v1/instance | jq -r '.version' | grep '${pkgs.mastodon.version}'") + + # Check access from remote client + client.succeed("curl --fail https://mastodon.local/about | grep 'Mastodon hosted on mastodon.local'") + client.succeed("curl --fail $(curl https://mastodon.local/api/v1/instance 2> /dev/null | jq -r .thumbnail) --output /dev/null") + + # Simple check tootctl commands + # Check Mastodon version + server.succeed("mastodon-tootctl version | grep '${pkgs.mastodon.version}'") + + # Manage accounts + server.succeed("mastodon-tootctl email_domain_blocks add example.com") + server.succeed("mastodon-tootctl email_domain_blocks list | grep example.com") + server.fail("mastodon-tootctl email_domain_blocks list | grep mastodon.local") + server.fail("mastodon-tootctl accounts create alice --email=alice@example.com") + server.succeed("mastodon-tootctl email_domain_blocks remove example.com") + server.succeed("mastodon-tootctl accounts create bob --email=bob@example.com") + server.succeed("mastodon-tootctl accounts approve bob") + server.succeed("mastodon-tootctl accounts delete bob") + + # Manage IP access + server.succeed("mastodon-tootctl ip_blocks add 192.168.0.0/16 --severity=no_access") + server.succeed("mastodon-tootctl ip_blocks export | grep 192.168.0.0/16") + server.fail("mastodon-tootctl ip_blocks export | grep 172.16.0.0/16") + client.fail("curl --fail https://mastodon.local/about") + server.succeed("mastodon-tootctl ip_blocks remove 192.168.0.0/16") + client.succeed("curl --fail https://mastodon.local/about") + + server.shutdown() + client.shutdown() + + ${extraShutdown} +'' diff --git a/nixpkgs/nixos/tests/web-apps/mastodon/standard.nix b/nixpkgs/nixos/tests/web-apps/mastodon/standard.nix new file mode 100644 index 000000000000..ddc764e2168c --- /dev/null +++ b/nixpkgs/nixos/tests/web-apps/mastodon/standard.nix @@ -0,0 +1,91 @@ +import ../../make-test-python.nix ({pkgs, ...}: +let + cert = pkgs: pkgs.runCommand "selfSignedCerts" { buildInputs = [ pkgs.openssl ]; } '' + openssl req -x509 -newkey rsa:4096 -keyout key.pem -out cert.pem -nodes -subj '/CN=mastodon.local' -days 36500 + mkdir -p $out + cp key.pem cert.pem $out + ''; + + hosts = '' + 192.168.2.101 mastodon.local + ''; + +in +{ + name = "mastodon-standard"; + meta.maintainers = with pkgs.lib.maintainers; [ erictapen izorkin turion ]; + + nodes = { + server = { pkgs, ... }: { + + virtualisation.memorySize = 2048; + + networking = { + interfaces.eth1 = { + ipv4.addresses = [ + { address = "192.168.2.101"; prefixLength = 24; } + ]; + }; + extraHosts = hosts; + firewall.allowedTCPPorts = [ 80 443 ]; + }; + + security = { + pki.certificateFiles = [ "${cert pkgs}/cert.pem" ]; + }; + + # TODO remove once https://github.com/NixOS/nixpkgs/pull/266270 is resolved. + services.postgresql.package = pkgs.postgresql_14; + + services.mastodon = { + enable = true; + configureNginx = true; + localDomain = "mastodon.local"; + enableUnixSocket = false; + streamingProcesses = 2; + smtp = { + createLocally = false; + fromAddress = "mastodon@mastodon.local"; + }; + extraConfig = { + EMAIL_DOMAIN_ALLOWLIST = "example.com"; + }; + }; + + services.nginx = { + virtualHosts."mastodon.local" = { + enableACME = pkgs.lib.mkForce false; + sslCertificate = "${cert pkgs}/cert.pem"; + sslCertificateKey = "${cert pkgs}/key.pem"; + }; + }; + }; + + client = { pkgs, ... }: { + environment.systemPackages = [ pkgs.jq ]; + networking = { + interfaces.eth1 = { + ipv4.addresses = [ + { address = "192.168.2.102"; prefixLength = 24; } + ]; + }; + extraHosts = hosts; + }; + + security = { + pki.certificateFiles = [ "${cert pkgs}/cert.pem" ]; + }; + }; + }; + + testScript = import ./script.nix { + inherit pkgs; + extraInit = '' + server.wait_for_unit("nginx.service") + server.wait_for_open_port(443) + server.wait_for_unit("redis-mastodon.service") + server.wait_for_unit("postgresql.service") + server.wait_for_open_port(5432) + ''; + }; +}) diff --git a/nixpkgs/nixos/tests/web-apps/monica.nix b/nixpkgs/nixos/tests/web-apps/monica.nix new file mode 100644 index 000000000000..29f5cb85bb13 --- /dev/null +++ b/nixpkgs/nixos/tests/web-apps/monica.nix @@ -0,0 +1,33 @@ +import ../make-test-python.nix ({pkgs, ...}: +let + cert = pkgs.runCommand "selfSignedCerts" { nativeBuildInputs = [ pkgs.openssl ]; } '' + openssl req -x509 -newkey rsa:4096 -keyout key.pem -out cert.pem -nodes -subj '/CN=localhost' -days 36500 + mkdir -p $out + cp key.pem cert.pem $out + ''; +in +{ + name = "monica"; + + nodes = { + machine = {pkgs, ...}: { + services.monica = { + enable = true; + hostname = "localhost"; + appKeyFile = "${pkgs.writeText "keyfile" "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"}"; + nginx = { + forceSSL = true; + sslCertificate = "${cert}/cert.pem"; + sslCertificateKey = "${cert}/key.pem"; + }; + }; + }; + }; + + testScript = '' + start_all() + machine.wait_for_unit("monica-setup.service") + machine.wait_for_open_port(443) + machine.succeed("curl -k --fail https://localhost", timeout=10) + ''; +}) diff --git a/nixpkgs/nixos/tests/web-apps/netbox-upgrade.nix b/nixpkgs/nixos/tests/web-apps/netbox-upgrade.nix new file mode 100644 index 000000000000..4c554e7ae613 --- /dev/null +++ b/nixpkgs/nixos/tests/web-apps/netbox-upgrade.nix @@ -0,0 +1,87 @@ +import ../make-test-python.nix ({ lib, pkgs, ... }: let + oldNetbox = pkgs.netbox_3_6; + newNetbox = pkgs.netbox_3_7; +in { + name = "netbox-upgrade"; + + meta = with lib.maintainers; { + maintainers = [ minijackson raitobezarius ]; + }; + + nodes.machine = { config, ... }: { + virtualisation.memorySize = 2048; + services.netbox = { + enable = true; + package = oldNetbox; + secretKeyFile = pkgs.writeText "secret" '' + abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789 + ''; + }; + + services.nginx = { + enable = true; + + recommendedProxySettings = true; + + virtualHosts.netbox = { + default = true; + locations."/".proxyPass = "http://localhost:${toString config.services.netbox.port}"; + locations."/static/".alias = "/var/lib/netbox/static/"; + }; + }; + + users.users.nginx.extraGroups = [ "netbox" ]; + + networking.firewall.allowedTCPPorts = [ 80 ]; + + specialisation.upgrade.configuration.services.netbox.package = lib.mkForce newNetbox; + }; + + testScript = { nodes, ... }: + let + apiVersion = version: lib.pipe version [ + (lib.splitString ".") + (lib.take 2) + (lib.concatStringsSep ".") + ]; + oldApiVersion = apiVersion oldNetbox.version; + newApiVersion = apiVersion newNetbox.version; + in + '' + start_all() + machine.wait_for_unit("netbox.target") + machine.wait_for_unit("nginx.service") + machine.wait_until_succeeds("journalctl --since -1m --unit netbox --grep Listening") + + def api_version(headers): + header = [header for header in headers.splitlines() if header.startswith("API-Version:")][0] + return header.split()[1] + + def check_api_version(version): + headers = machine.succeed( + "curl -sSfL http://localhost/api/ --head -H 'Content-Type: application/json'" + ) + assert api_version(headers) == version + + with subtest("NetBox version is the old one"): + check_api_version("${oldApiVersion}") + + # Somehow, even though netbox-housekeeping.service has After=netbox.service, + # netbox-housekeeping.service and netbox.service still get started at the + # same time, making netbox-housekeeping fail (can't really do some house + # keeping job if the database is not correctly formed). + # + # So we don't check that the upgrade went well, we just check that + # netbox.service is active, and that netbox-housekeeping can be run + # successfully afterwards. + # + # This is not good UX, but the system should be working nonetheless. + machine.execute("${nodes.machine.system.build.toplevel}/specialisation/upgrade/bin/switch-to-configuration test >&2") + + machine.wait_for_unit("netbox.service") + machine.succeed("systemctl start netbox-housekeeping.service") + + with subtest("NetBox version is the new one"): + check_api_version("${newApiVersion}") + ''; +}) diff --git a/nixpkgs/nixos/tests/web-apps/netbox.nix b/nixpkgs/nixos/tests/web-apps/netbox.nix new file mode 100644 index 000000000000..233f16a8fe0d --- /dev/null +++ b/nixpkgs/nixos/tests/web-apps/netbox.nix @@ -0,0 +1,318 @@ +let + ldapDomain = "example.org"; + ldapSuffix = "dc=example,dc=org"; + + ldapRootUser = "admin"; + ldapRootPassword = "foobar"; + + testUser = "alice"; + testPassword = "verySecure"; + testGroup = "netbox-users"; +in import ../make-test-python.nix ({ lib, pkgs, netbox, ... }: { + name = "netbox"; + + meta = with lib.maintainers; { + maintainers = [ minijackson n0emis ]; + }; + + nodes.machine = { config, ... }: { + virtualisation.memorySize = 2048; + services.netbox = { + enable = true; + package = netbox; + secretKeyFile = pkgs.writeText "secret" '' + abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789 + ''; + + enableLdap = true; + ldapConfigPath = pkgs.writeText "ldap_config.py" '' + import ldap + from django_auth_ldap.config import LDAPSearch, PosixGroupType + + AUTH_LDAP_SERVER_URI = "ldap://localhost/" + + AUTH_LDAP_USER_SEARCH = LDAPSearch( + "ou=accounts,ou=posix,${ldapSuffix}", + ldap.SCOPE_SUBTREE, + "(uid=%(user)s)", + ) + + AUTH_LDAP_GROUP_SEARCH = LDAPSearch( + "ou=groups,ou=posix,${ldapSuffix}", + ldap.SCOPE_SUBTREE, + "(objectClass=posixGroup)", + ) + AUTH_LDAP_GROUP_TYPE = PosixGroupType() + + # Mirror LDAP group assignments. + AUTH_LDAP_MIRROR_GROUPS = True + + # For more granular permissions, we can map LDAP groups to Django groups. + AUTH_LDAP_FIND_GROUP_PERMS = True + ''; + }; + + services.nginx = { + enable = true; + + recommendedProxySettings = true; + + virtualHosts.netbox = { + default = true; + locations."/".proxyPass = "http://localhost:${toString config.services.netbox.port}"; + locations."/static/".alias = "/var/lib/netbox/static/"; + }; + }; + + # Adapted from the sssd-ldap NixOS test + services.openldap = { + enable = true; + settings = { + children = { + "cn=schema".includes = [ + "${pkgs.openldap}/etc/schema/core.ldif" + "${pkgs.openldap}/etc/schema/cosine.ldif" + "${pkgs.openldap}/etc/schema/inetorgperson.ldif" + "${pkgs.openldap}/etc/schema/nis.ldif" + ]; + "olcDatabase={1}mdb" = { + attrs = { + objectClass = [ "olcDatabaseConfig" "olcMdbConfig" ]; + olcDatabase = "{1}mdb"; + olcDbDirectory = "/var/lib/openldap/db"; + olcSuffix = ldapSuffix; + olcRootDN = "cn=${ldapRootUser},${ldapSuffix}"; + olcRootPW = ldapRootPassword; + }; + }; + }; + }; + declarativeContents = { + ${ldapSuffix} = '' + dn: ${ldapSuffix} + objectClass: top + objectClass: dcObject + objectClass: organization + o: ${ldapDomain} + + dn: ou=posix,${ldapSuffix} + objectClass: top + objectClass: organizationalUnit + + dn: ou=accounts,ou=posix,${ldapSuffix} + objectClass: top + objectClass: organizationalUnit + + dn: uid=${testUser},ou=accounts,ou=posix,${ldapSuffix} + objectClass: person + objectClass: posixAccount + userPassword: ${testPassword} + homeDirectory: /home/${testUser} + uidNumber: 1234 + gidNumber: 1234 + cn: "" + sn: "" + + dn: ou=groups,ou=posix,${ldapSuffix} + objectClass: top + objectClass: organizationalUnit + + dn: cn=${testGroup},ou=groups,ou=posix,${ldapSuffix} + objectClass: posixGroup + gidNumber: 2345 + memberUid: ${testUser} + ''; + }; + }; + + users.users.nginx.extraGroups = [ "netbox" ]; + + networking.firewall.allowedTCPPorts = [ 80 ]; + }; + + testScript = let + changePassword = pkgs.writeText "change-password.py" '' + from django.contrib.auth.models import User + u = User.objects.get(username='netbox') + u.set_password('netbox') + u.save() + ''; + in '' + from typing import Any, Dict + import json + + start_all() + machine.wait_for_unit("netbox.target") + machine.wait_until_succeeds("journalctl --since -1m --unit netbox --grep Listening") + + with subtest("Home screen loads"): + machine.succeed( + "curl -sSfL http://[::1]:8001 | grep '<title>Home | NetBox</title>'" + ) + + with subtest("Staticfiles are generated"): + machine.succeed("test -e /var/lib/netbox/static/netbox.js") + + with subtest("Superuser can be created"): + machine.succeed( + "netbox-manage createsuperuser --noinput --username netbox --email netbox@example.com" + ) + # Django doesn't have a "clean" way of inputting the password from the command line + machine.succeed("cat '${changePassword}' | netbox-manage shell") + + machine.wait_for_unit("network.target") + + with subtest("Home screen loads from nginx"): + machine.succeed( + "curl -sSfL http://localhost | grep '<title>Home | NetBox</title>'" + ) + + with subtest("Staticfiles can be fetched"): + machine.succeed("curl -sSfL http://localhost/static/netbox.js") + machine.succeed("curl -sSfL http://localhost/static/docs/") + + with subtest("Can interact with API"): + json.loads( + machine.succeed("curl -sSfL -H 'Accept: application/json' 'http://localhost/api/'") + ) + + def login(username: str, password: str): + encoded_data = json.dumps({"username": username, "password": password}) + uri = "/users/tokens/provision/" + result = json.loads( + machine.succeed( + "curl -sSfL " + "-X POST " + "-H 'Accept: application/json' " + "-H 'Content-Type: application/json' " + f"'http://localhost/api{uri}' " + f"--data '{encoded_data}'" + ) + ) + return result["key"] + + with subtest("Can login"): + auth_token = login("netbox", "netbox") + + def get(uri: str): + return json.loads( + machine.succeed( + "curl -sSfL " + "-H 'Accept: application/json' " + f"-H 'Authorization: Token {auth_token}' " + f"'http://localhost/api{uri}'" + ) + ) + + def delete(uri: str): + return machine.succeed( + "curl -sSfL " + f"-X DELETE " + "-H 'Accept: application/json' " + f"-H 'Authorization: Token {auth_token}' " + f"'http://localhost/api{uri}'" + ) + + + def data_request(uri: str, method: str, data: Dict[str, Any]): + encoded_data = json.dumps(data) + return json.loads( + machine.succeed( + "curl -sSfL " + f"-X {method} " + "-H 'Accept: application/json' " + "-H 'Content-Type: application/json' " + f"-H 'Authorization: Token {auth_token}' " + f"'http://localhost/api{uri}' " + f"--data '{encoded_data}'" + ) + ) + + def post(uri: str, data: Dict[str, Any]): + return data_request(uri, "POST", data) + + def patch(uri: str, data: Dict[str, Any]): + return data_request(uri, "PATCH", data) + + with subtest("Can create objects"): + result = post("/dcim/sites/", {"name": "Test site", "slug": "test-site"}) + site_id = result["id"] + + # Example from: + # http://netbox.extra.cea.fr/static/docs/integrations/rest-api/#creating-a-new-object + post("/ipam/prefixes/", {"prefix": "192.0.2.0/24", "site": site_id}) + + result = post( + "/dcim/manufacturers/", + {"name": "Test manufacturer", "slug": "test-manufacturer"} + ) + manufacturer_id = result["id"] + + # Had an issue with device-types before NetBox 3.4.0 + result = post( + "/dcim/device-types/", + { + "model": "Test device type", + "manufacturer": manufacturer_id, + "slug": "test-device-type", + }, + ) + device_type_id = result["id"] + + with subtest("Can list objects"): + result = get("/dcim/sites/") + + assert result["count"] == 1 + assert result["results"][0]["id"] == site_id + assert result["results"][0]["name"] == "Test site" + assert result["results"][0]["description"] == "" + + result = get("/dcim/device-types/") + assert result["count"] == 1 + assert result["results"][0]["id"] == device_type_id + assert result["results"][0]["model"] == "Test device type" + + with subtest("Can update objects"): + new_description = "Test site description" + patch(f"/dcim/sites/{site_id}/", {"description": new_description}) + result = get(f"/dcim/sites/{site_id}/") + assert result["description"] == new_description + + with subtest("Can delete objects"): + # Delete a device-type since no object depends on it + delete(f"/dcim/device-types/{device_type_id}/") + + result = get("/dcim/device-types/") + assert result["count"] == 0 + + with subtest("Can use the GraphQL API"): + encoded_data = json.dumps({ + "query": "query { prefix_list { prefix, site { id, description } } }", + }) + result = json.loads( + machine.succeed( + "curl -sSfL " + "-H 'Accept: application/json' " + "-H 'Content-Type: application/json' " + f"-H 'Authorization: Token {auth_token}' " + "'http://localhost/graphql/' " + f"--data '{encoded_data}'" + ) + ) + + assert len(result["data"]["prefix_list"]) == 1 + assert result["data"]["prefix_list"][0]["prefix"] == "192.0.2.0/24" + assert result["data"]["prefix_list"][0]["site"]["id"] == str(site_id) + assert result["data"]["prefix_list"][0]["site"]["description"] == new_description + + with subtest("Can login with LDAP"): + machine.wait_for_unit("openldap.service") + login("alice", "${testPassword}") + + with subtest("Can associate LDAP groups"): + result = get("/users/users/?username=${testUser}") + + assert result["count"] == 1 + assert any(group["name"] == "${testGroup}" for group in result["results"][0]["groups"]) + ''; +}) diff --git a/nixpkgs/nixos/tests/web-apps/nifi.nix b/nixpkgs/nixos/tests/web-apps/nifi.nix new file mode 100644 index 000000000000..92f7fa231df3 --- /dev/null +++ b/nixpkgs/nixos/tests/web-apps/nifi.nix @@ -0,0 +1,30 @@ +import ../make-test-python.nix ({pkgs, ...}: +{ + name = "nifi"; + meta.maintainers = with pkgs.lib.maintainers; [ izorkin ]; + + nodes = { + nifi = { pkgs, ... }: { + virtualisation = { + memorySize = 2048; + diskSize = 4096; + }; + services.nifi = { + enable = true; + enableHTTPS = false; + }; + }; + }; + + testScript = '' + nifi.start() + + nifi.wait_for_unit("nifi.service") + nifi.wait_for_open_port(8080) + + # Check if NiFi is running + nifi.succeed("curl --fail http://127.0.0.1:8080/nifi/login 2> /dev/null | grep 'NiFi Login'") + + nifi.shutdown() + ''; +}) diff --git a/nixpkgs/nixos/tests/web-apps/peering-manager.nix b/nixpkgs/nixos/tests/web-apps/peering-manager.nix new file mode 100644 index 000000000000..3f0acd560d13 --- /dev/null +++ b/nixpkgs/nixos/tests/web-apps/peering-manager.nix @@ -0,0 +1,40 @@ +import ../make-test-python.nix ({ lib, pkgs, ... }: { + name = "peering-manager"; + + meta = with lib.maintainers; { + maintainers = [ yuka ]; + }; + + nodes.machine = { ... }: { + services.peering-manager = { + enable = true; + secretKeyFile = pkgs.writeText "secret" '' + abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789 + ''; + }; + }; + + testScript = { nodes }: '' + machine.start() + machine.wait_for_unit("peering-manager.target") + machine.wait_until_succeeds("journalctl --since -1m --unit peering-manager --grep Listening") + + print(machine.succeed( + "curl -sSfL http://[::1]:8001" + )) + with subtest("Home screen loads"): + machine.succeed( + "curl -sSfL http://[::1]:8001 | grep '<title>Home - Peering Manager</title>'" + ) + with subtest("checks succeed"): + machine.succeed( + "systemctl stop peering-manager peering-manager-rq" + ) + machine.succeed( + "sudo -u postgres psql -c 'ALTER USER \"peering-manager\" WITH SUPERUSER;'" + ) + machine.succeed( + "cd ${nodes.machine.system.build.peeringManagerPkg}/opt/peering-manager ; peering-manager-manage test --no-input" + ) + ''; +}) diff --git a/nixpkgs/nixos/tests/web-apps/peertube.nix b/nixpkgs/nixos/tests/web-apps/peertube.nix new file mode 100644 index 000000000000..0e5f39c08a02 --- /dev/null +++ b/nixpkgs/nixos/tests/web-apps/peertube.nix @@ -0,0 +1,139 @@ +import ../make-test-python.nix ({pkgs, ...}: +{ + name = "peertube"; + meta.maintainers = with pkgs.lib.maintainers; [ izorkin ]; + + nodes = { + database = { + networking = { + interfaces.eth1 = { + ipv4.addresses = [ + { address = "192.168.2.10"; prefixLength = 24; } + ]; + }; + firewall.allowedTCPPorts = [ 5432 31638 ]; + }; + + services.postgresql = { + enable = true; + enableTCPIP = true; + authentication = '' + hostnossl peertube_local peertube_test 192.168.2.11/32 md5 + ''; + initialScript = pkgs.writeText "postgresql_init.sql" '' + CREATE ROLE peertube_test LOGIN PASSWORD '0gUN0C1mgST6czvjZ8T9'; + CREATE DATABASE peertube_local TEMPLATE template0 ENCODING UTF8; + GRANT ALL PRIVILEGES ON DATABASE peertube_local TO peertube_test; + \connect peertube_local + CREATE EXTENSION IF NOT EXISTS pg_trgm; + CREATE EXTENSION IF NOT EXISTS unaccent; + ''; + }; + + services.redis.servers.peertube = { + enable = true; + bind = "0.0.0.0"; + requirePass = "turrQfaQwnanGbcsdhxy"; + port = 31638; + }; + }; + + server = { pkgs, ... }: { + environment = { + etc = { + "peertube/secrets-peertube".text = '' + 063d9c60d519597acef26003d5ecc32729083965d09181ef3949200cbe5f09ee + ''; + "peertube/password-posgressql-db".text = '' + 0gUN0C1mgST6czvjZ8T9 + ''; + "peertube/password-redis-db".text = '' + turrQfaQwnanGbcsdhxy + ''; + }; + }; + + networking = { + interfaces.eth1 = { + ipv4.addresses = [ + { address = "192.168.2.11"; prefixLength = 24; } + ]; + }; + extraHosts = '' + 192.168.2.11 peertube.local + ''; + firewall.allowedTCPPorts = [ 9000 ]; + }; + + services.peertube = { + enable = true; + localDomain = "peertube.local"; + enableWebHttps = false; + + secrets = { + secretsFile = "/etc/peertube/secrets-peertube"; + }; + + database = { + host = "192.168.2.10"; + name = "peertube_local"; + user = "peertube_test"; + passwordFile = "/etc/peertube/password-posgressql-db"; + }; + + redis = { + host = "192.168.2.10"; + port = 31638; + passwordFile = "/etc/peertube/password-redis-db"; + }; + + settings = { + listen = { + hostname = "0.0.0.0"; + }; + instance = { + name = "PeerTube Test Server"; + }; + }; + }; + }; + + client = { + environment.systemPackages = [ pkgs.jq ]; + networking = { + interfaces.eth1 = { + ipv4.addresses = [ + { address = "192.168.2.12"; prefixLength = 24; } + ]; + }; + extraHosts = '' + 192.168.2.11 peertube.local + ''; + }; + }; + + }; + + testScript = '' + start_all() + + database.wait_for_unit("postgresql.service") + database.wait_for_unit("redis-peertube.service") + + database.wait_for_open_port(5432) + database.wait_for_open_port(31638) + + server.wait_for_unit("peertube.service") + server.wait_for_open_port(9000) + + # Check if PeerTube is running + client.succeed("curl --fail http://peertube.local:9000/api/v1/config/about | jq -r '.instance.name' | grep 'PeerTube\ Test\ Server'") + + # Check PeerTube CLI version + assert "${pkgs.peertube.version}" in server.succeed('su - peertube -s /bin/sh -c "peertube --version"') + + client.shutdown() + server.shutdown() + database.shutdown() + ''; +}) diff --git a/nixpkgs/nixos/tests/web-apps/phylactery.nix b/nixpkgs/nixos/tests/web-apps/phylactery.nix new file mode 100644 index 000000000000..cf2689d2300d --- /dev/null +++ b/nixpkgs/nixos/tests/web-apps/phylactery.nix @@ -0,0 +1,20 @@ +import ../make-test-python.nix ({ pkgs, lib, ... }: { + name = "phylactery"; + + nodes.machine = { ... }: { + services.phylactery = rec { + enable = true; + port = 8080; + library = "/tmp"; + }; + }; + + testScript = '' + start_all() + machine.wait_for_unit('phylactery') + machine.wait_for_open_port(8080) + machine.wait_until_succeeds('curl localhost:8080') + ''; + + meta.maintainers = with lib.maintainers; [ McSinyx ]; +}) diff --git a/nixpkgs/nixos/tests/web-apps/pixelfed/default.nix b/nixpkgs/nixos/tests/web-apps/pixelfed/default.nix new file mode 100644 index 000000000000..4464ebe43486 --- /dev/null +++ b/nixpkgs/nixos/tests/web-apps/pixelfed/default.nix @@ -0,0 +1,8 @@ +{ system ? builtins.currentSystem, handleTestOn }: +let + supportedSystems = [ "x86_64-linux" "i686-linux" ]; + +in +{ + standard = handleTestOn supportedSystems ./standard.nix { inherit system; }; +} diff --git a/nixpkgs/nixos/tests/web-apps/pixelfed/standard.nix b/nixpkgs/nixos/tests/web-apps/pixelfed/standard.nix new file mode 100644 index 000000000000..9260e27af960 --- /dev/null +++ b/nixpkgs/nixos/tests/web-apps/pixelfed/standard.nix @@ -0,0 +1,38 @@ +import ../../make-test-python.nix ({pkgs, ...}: +{ + name = "pixelfed-standard"; + meta.maintainers = with pkgs.lib.maintainers; [ raitobezarius ]; + + nodes = { + server = { pkgs, ... }: { + services.pixelfed = { + enable = true; + domain = "pixelfed.local"; + # Configure NGINX. + nginx = {}; + secretFile = (pkgs.writeText "secrets.env" '' + # Snakeoil secret, can be any random 32-chars secret via CSPRNG. + APP_KEY=adKK9EcY8Hcj3PLU7rzG9rJ6KKTOtYfA + ''); + settings."FORCE_HTTPS_URLS" = false; + }; + }; + }; + + testScript = '' + # Wait for Pixelfed PHP pool + server.wait_for_unit("phpfpm-pixelfed.service") + # Wait for NGINX + server.wait_for_unit("nginx.service") + # Wait for HTTP port + server.wait_for_open_port(80) + # Access the homepage. + server.succeed("curl -H 'Host: pixelfed.local' http://localhost") + # Create an account + server.succeed("pixelfed-manage user:create --name=test --username=test --email=test@test.com --password=test") + # Create a OAuth token. + # TODO: figure out how to use it to send a image/toot + # server.succeed("pixelfed-manage passport:client --personal") + # server.succeed("curl -H 'Host: pixefed.local' -H 'Accept: application/json' -H 'Authorization: Bearer secret' -F'status'='test' http://localhost/api/v1/statuses") + ''; +}) diff --git a/nixpkgs/nixos/tests/web-apps/pretalx.nix b/nixpkgs/nixos/tests/web-apps/pretalx.nix new file mode 100644 index 000000000000..a226639b076b --- /dev/null +++ b/nixpkgs/nixos/tests/web-apps/pretalx.nix @@ -0,0 +1,31 @@ +{ lib, ... }: + +{ + name = "pretalx"; + meta.maintainers = lib.teams.c3d2.members; + + nodes = { + pretalx = { + networking.extraHosts = '' + 127.0.0.1 talks.local + ''; + + services.pretalx = { + enable = true; + nginx.domain = "talks.local"; + settings = { + site.url = "http://talks.local"; + }; + }; + }; + }; + + testScript = '' + start_all() + + pretalx.wait_for_unit("pretalx-web.service") + pretalx.wait_for_unit("pretalx-worker.service") + + pretalx.wait_until_succeeds("curl -q --fail http://talks.local/orga/") + ''; +} diff --git a/nixpkgs/nixos/tests/web-apps/snipe-it.nix b/nixpkgs/nixos/tests/web-apps/snipe-it.nix new file mode 100644 index 000000000000..123d7742056b --- /dev/null +++ b/nixpkgs/nixos/tests/web-apps/snipe-it.nix @@ -0,0 +1,101 @@ +/* +Snipe-IT NixOS test + +It covers the following scenario: +- Installation +- Backup and restore + +Scenarios NOT covered by this test (but perhaps in the future): +- Sending and receiving emails +*/ +{ pkgs, ... }: let + siteName = "NixOS Snipe-IT Test Instance"; +in { + name = "snipe-it"; + + meta.maintainers = with pkgs.lib.maintainers; [ yayayayaka ]; + + nodes = { + snipeit = { ... }: { + services.snipe-it = { + enable = true; + appKeyFile = toString (pkgs.writeText "snipe-it-app-key" "uTqGUN5GUmUrh/zSAYmhyzRk62pnpXICyXv9eeITI8k="); + hostName = "localhost"; + database.createLocally = true; + mail = { + driver = "smtp"; + encryption = "tls"; + host = "localhost"; + port = 1025; + from.name = "Snipe-IT NixOS test"; + from.address = "snipe-it@localhost"; + replyTo.address = "snipe-it@localhost"; + user = "snipe-it@localhost"; + passwordFile = toString (pkgs.writeText "snipe-it-mail-pass" "a-secure-mail-password"); + }; + }; + }; + }; + + testScript = { nodes }: let + backupPath = "${nodes.snipeit.services.snipe-it.dataDir}/storage/app/backups"; + + # Snipe-IT has been installed successfully if the site name shows up on the login page + checkLoginPage = { shouldSucceed ? true }: '' + snipeit.${if shouldSucceed then "succeed" else "fail"}("""curl http://localhost/login | grep '${siteName}'""") + ''; + in '' + start_all() + + snipeit.wait_for_unit("nginx.service") + snipeit.wait_for_unit("snipe-it-setup.service") + + # Create an admin user + snipeit.succeed( + """ + snipe-it snipeit:create-admin \ + --username="admin" \ + --email="janedoe@localhost" \ + --password="extremesecurepassword" \ + --first_name="Jane" \ + --last_name="Doe" + """ + ) + + with subtest("Circumvent the pre-flight setup by just writing some settings into the database ourself"): + snipeit.succeed( + """ + mysql -D ${nodes.snipeit.services.snipe-it.database.name} -e "INSERT INTO settings (id, user_id, site_name) VALUES ('1', '1', '${siteName}');" + """ + ) + + # Usually these are generated during the pre-flight setup + snipeit.succeed("snipe-it passport:keys") + + + # Login page should now contain the configured site name + ${checkLoginPage {}} + + with subtest("Test Backup and restore"): + snipeit.succeed("snipe-it snipeit:backup") + + # One zip file should have been created + snipeit.succeed("""[ "$(ls -1 "${backupPath}" | wc -l)" -eq 1 ]""") + + # Purge the state + snipeit.succeed("snipe-it migrate:fresh --force") + + # Login page should disappear + ${checkLoginPage { shouldSucceed = false; }} + + # Restore the state + snipeit.succeed( + """ + snipe-it snipeit:restore --force $(find "${backupPath}/" -type f -name "*.zip") + """ + ) + + # Login page should be back again + ${checkLoginPage {}} + ''; +} diff --git a/nixpkgs/nixos/tests/web-apps/writefreely.nix b/nixpkgs/nixos/tests/web-apps/writefreely.nix new file mode 100644 index 000000000000..ce614909706b --- /dev/null +++ b/nixpkgs/nixos/tests/web-apps/writefreely.nix @@ -0,0 +1,44 @@ +{ system ? builtins.currentSystem, config ? { } +, pkgs ? import ../../.. { inherit system config; } }: + +with import ../../lib/testing-python.nix { inherit system pkgs; }; +with pkgs.lib; + +let + writefreelyTest = { name, type }: + makeTest { + name = "writefreely-${name}"; + + nodes.machine = { config, pkgs, ... }: { + services.writefreely = { + enable = true; + host = "localhost:3000"; + admin.name = "nixos"; + + database = { + inherit type; + createLocally = type == "mysql"; + passwordFile = pkgs.writeText "db-pass" "pass"; + }; + + settings.server.port = 3000; + }; + }; + + testScript = '' + start_all() + machine.wait_for_unit("writefreely.service") + machine.wait_for_open_port(3000) + machine.succeed("curl --fail http://localhost:3000") + ''; + }; +in { + sqlite = writefreelyTest { + name = "sqlite"; + type = "sqlite3"; + }; + mysql = writefreelyTest { + name = "mysql"; + type = "mysql"; + }; +} |