{ pkgs, lib, self, }: pkgs.testers.runNixOSTest { name = "arr-init-edge-cases"; nodes.machine = { pkgs, lib, ... }: let mocks = import ./lib/mocks.nix { inherit pkgs; }; in { imports = [ self.nixosModules.default ]; system.stateVersion = "24.11"; virtualisation.memorySize = 4096; environment.systemPackages = with pkgs; [ curl jq gnugrep ]; systemd.services.mock-qbittorrent = mocks.mkMockQbittorrent { }; # Create directories including one with spaces systemd.tmpfiles.rules = [ "d /media/tv 0755 sonarr sonarr -" "d /media/anime 0755 sonarr sonarr -" "d '/media/my shows' 0755 sonarr sonarr -" ]; services.sonarr = { enable = true; dataDir = "/var/lib/sonarr/.config/NzbDrone"; settings.server.port = lib.mkDefault 8989; }; # Test 1: Empty lists (both downloadClients and rootFolders are empty) # Test 2: Multiple root folders # Test 3: Path with spaces services.arrInit.sonarr = { enable = true; serviceName = "sonarr"; dataDir = "/var/lib/sonarr/.config/NzbDrone"; port = 8989; # Empty downloadClients (explicitly set) downloadClients = [ ]; # Multiple root folders including one with spaces rootFolders = [ "/media/tv" "/media/anime" "/media/my shows" ]; }; }; testScript = '' start_all() # Wait for services machine.wait_for_unit("mock-qbittorrent.service") machine.wait_until_succeeds("curl -sf http://localhost:6011/api/v2/app/version", timeout=30) machine.wait_for_unit("sonarr.service") # Wait for Sonarr API machine.wait_until_succeeds( "API_KEY=$(grep -oP '(?<=)[^<]+' /var/lib/sonarr/.config/NzbDrone/config.xml) && " "curl -sf http://localhost:8989/api/v3/system/status -H \"X-Api-Key: $API_KEY\"", timeout=120, ) # Trigger init machine.succeed("systemctl restart sonarr-init.service") machine.wait_for_unit("sonarr-init.service") # Test 1: Empty downloadClients list should work result = machine.succeed( "API_KEY=$(grep -oP '(?<=)[^<]+' /var/lib/sonarr/.config/NzbDrone/config.xml) && " "curl -sf http://localhost:8989/api/v3/downloadclient -H \"X-Api-Key: $API_KEY\" | " "jq '. | length'" ).strip() assert result == "0", f"Expected 0 download clients, got {result}" # Test 2 & 3: Multiple root folders including one with spaces result = machine.succeed( "API_KEY=$(grep -oP '(?<=)[^<]+' /var/lib/sonarr/.config/NzbDrone/config.xml) && " "curl -sf http://localhost:8989/api/v3/rootfolder -H \"X-Api-Key: $API_KEY\" | " "jq '. | length'" ).strip() assert result == "3", f"Expected 3 root folders, got {result}" # Verify each root folder exists machine.succeed( "API_KEY=$(grep -oP '(?<=)[^<]+' /var/lib/sonarr/.config/NzbDrone/config.xml) && " "curl -sf http://localhost:8989/api/v3/rootfolder -H \"X-Api-Key: $API_KEY\" | " "jq -e '.[] | select(.path == \"/media/tv\")'" ) machine.succeed( "API_KEY=$(grep -oP '(?<=)[^<]+' /var/lib/sonarr/.config/NzbDrone/config.xml) && " "curl -sf http://localhost:8989/api/v3/rootfolder -H \"X-Api-Key: $API_KEY\" | " "jq -e '.[] | select(.path == \"/media/anime\")'" ) # Path with spaces machine.succeed( "API_KEY=$(grep -oP '(?<=)[^<]+' /var/lib/sonarr/.config/NzbDrone/config.xml) && " "curl -sf http://localhost:8989/api/v3/rootfolder -H \"X-Api-Key: $API_KEY\" | " "jq -e '.[] | select(.path == \"/media/my shows\")'" ) # Idempotency: restart and verify counts remain the same machine.succeed("systemctl restart sonarr-init.service") machine.wait_for_unit("sonarr-init.service") result = machine.succeed( "API_KEY=$(grep -oP '(?<=)[^<]+' /var/lib/sonarr/.config/NzbDrone/config.xml) && " "curl -sf http://localhost:8989/api/v3/rootfolder -H \"X-Api-Key: $API_KEY\" | " "jq '. | length'" ).strip() assert result == "3", f"Expected 3 root folders after idempotency test, got {result}" ''; }