{ pkgs, lib, self, }: pkgs.testers.runNixOSTest { name = "arr-init-jellyseerr"; nodes = { # Test 1: Profile resolution, anime fallback (null -> profileName), idempotency, # and graceful skip when settings.json is missing. machine = { pkgs, lib, ... }: { imports = [ self.nixosModules.default ]; system.stateVersion = "24.11"; virtualisation.memorySize = 4096; environment.systemPackages = with pkgs; [ curl jq gnugrep ]; services.sonarr = { enable = true; dataDir = "/var/lib/sonarr/.config/NzbDrone"; settings.server.port = lib.mkDefault 8989; }; services.radarr = { enable = true; dataDir = "/var/lib/radarr/.config/Radarr"; settings.server.port = lib.mkDefault 7878; }; # Mock Jellyseerr (not in nixpkgs); the init script only needs the # systemd unit to exist so Requires/After are satisfied and # `systemctl restart jellyseerr.service` succeeds. systemd.services.jellyseerr = { description = "Mock Jellyseerr"; wantedBy = [ "multi-user.target" ]; serviceConfig = { Type = "simple"; ExecStart = "${pkgs.coreutils}/bin/sleep infinity"; }; }; systemd.tmpfiles.rules = [ "d /var/lib/jellyseerr 0755 root root -" ]; services.jellyseerrInit = { enable = true; configDir = "/var/lib/jellyseerr"; radarr = { profileName = "Any"; dataDir = "/var/lib/radarr/.config/Radarr"; port = 7878; serviceName = "radarr"; }; sonarr = { profileName = "Any"; # animeProfileName defaults to null -> Nix config falls back to profileName dataDir = "/var/lib/sonarr/.config/NzbDrone"; port = 8989; serviceName = "sonarr"; }; }; }; # Test 2: Explicit animeProfileName differs from profileName explicit_anime = { pkgs, lib, ... }: { imports = [ self.nixosModules.default ]; system.stateVersion = "24.11"; virtualisation.memorySize = 4096; environment.systemPackages = with pkgs; [ curl jq gnugrep ]; services.sonarr = { enable = true; dataDir = "/var/lib/sonarr/.config/NzbDrone"; settings.server.port = lib.mkDefault 8989; }; services.radarr = { enable = true; dataDir = "/var/lib/radarr/.config/Radarr"; settings.server.port = lib.mkDefault 7878; }; systemd.services.jellyseerr = { description = "Mock Jellyseerr"; wantedBy = [ "multi-user.target" ]; serviceConfig = { Type = "simple"; ExecStart = "${pkgs.coreutils}/bin/sleep infinity"; }; }; systemd.tmpfiles.rules = [ "d /var/lib/jellyseerr 0755 root root -" ]; services.jellyseerrInit = { enable = true; configDir = "/var/lib/jellyseerr"; radarr = { profileName = "Any"; dataDir = "/var/lib/radarr/.config/Radarr"; port = 7878; serviceName = "radarr"; }; sonarr = { profileName = "Any"; animeProfileName = "SD"; dataDir = "/var/lib/sonarr/.config/NzbDrone"; port = 8989; serviceName = "sonarr"; }; }; }; }; testScript = '' start_all() # ===== Test 1a: Basic profile resolution + anime fallback ===== machine.wait_for_unit("sonarr.service") machine.wait_for_unit("radarr.service") machine.wait_for_unit("jellyseerr.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, ) # Wait for Radarr API machine.wait_until_succeeds( "API_KEY=$(grep -oP '(?<=)[^<]+' /var/lib/radarr/.config/Radarr/config.xml) && " "curl -sf http://localhost:7878/api/v3/system/status -H \"X-Api-Key: $API_KEY\"", timeout=120, ) # Create settings.json with placeholder profile data import json settings = { "radarr": [ { "name": "Radarr Main", "activeProfileId": -1, "activeProfileName": "", } ], "sonarr": [ { "name": "Sonarr Main", "activeProfileId": -1, "activeProfileName": "", "activeAnimeProfileId": -1, "activeAnimeProfileName": "", } ], } machine.succeed( f"cat > /var/lib/jellyseerr/settings.json << 'SETTINGS'\n" f"{json.dumps(settings, indent=2)}\n" f"SETTINGS" ) # Trigger jellyseerr-init machine.succeed("systemctl restart jellyseerr-init.service") machine.wait_for_unit("jellyseerr-init.service") # Verify Radarr profile was resolved machine.succeed( "jq -e '.radarr[0].activeProfileName == \"Any\"' /var/lib/jellyseerr/settings.json" ) machine.succeed( "jq -e '.radarr[0].activeProfileId > 0' /var/lib/jellyseerr/settings.json" ) # Verify Sonarr profile was resolved machine.succeed( "jq -e '.sonarr[0].activeProfileName == \"Any\"' /var/lib/jellyseerr/settings.json" ) machine.succeed( "jq -e '.sonarr[0].activeProfileId > 0' /var/lib/jellyseerr/settings.json" ) # Verify anime profile defaulted to regular profile (animeProfileName was null) machine.succeed( "jq -e '.sonarr[0].activeAnimeProfileName == \"Any\"' /var/lib/jellyseerr/settings.json" ) machine.succeed( "jq -e '.sonarr[0].activeAnimeProfileId == .sonarr[0].activeProfileId' /var/lib/jellyseerr/settings.json" ) # ===== Test 1b: Idempotency — second run should not rewrite the file ===== machine.succeed("cp /var/lib/jellyseerr/settings.json /tmp/settings_before.json") machine.succeed("systemctl restart jellyseerr-init.service") machine.wait_for_unit("jellyseerr-init.service") machine.succeed("diff /tmp/settings_before.json /var/lib/jellyseerr/settings.json") # ===== Test 1c: Missing settings.json — graceful skip ===== machine.succeed("rm /var/lib/jellyseerr/settings.json") machine.succeed("systemctl restart jellyseerr-init.service") machine.wait_for_unit("jellyseerr-init.service") machine.succeed("test ! -f /var/lib/jellyseerr/settings.json") # ===== Test 2: Explicit anime profile (SD) differs from regular (Any) ===== explicit_anime.wait_for_unit("sonarr.service") explicit_anime.wait_for_unit("radarr.service") explicit_anime.wait_for_unit("jellyseerr.service") explicit_anime.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, ) explicit_anime.wait_until_succeeds( "API_KEY=$(grep -oP '(?<=)[^<]+' /var/lib/radarr/.config/Radarr/config.xml) && " "curl -sf http://localhost:7878/api/v3/system/status -H \"X-Api-Key: $API_KEY\"", timeout=120, ) explicit_anime.succeed( f"cat > /var/lib/jellyseerr/settings.json << 'SETTINGS'\n" f"{json.dumps(settings, indent=2)}\n" f"SETTINGS" ) explicit_anime.succeed("systemctl restart jellyseerr-init.service") explicit_anime.wait_for_unit("jellyseerr-init.service") # Regular profile is "Any" explicit_anime.succeed( "jq -e '.sonarr[0].activeProfileName == \"Any\"' /var/lib/jellyseerr/settings.json" ) # Anime profile is "SD" (explicitly set) explicit_anime.succeed( "jq -e '.sonarr[0].activeAnimeProfileName == \"SD\"' /var/lib/jellyseerr/settings.json" ) # Profile IDs must differ since the profiles are different explicit_anime.succeed( "jq -e '.sonarr[0].activeAnimeProfileId != .sonarr[0].activeProfileId' /var/lib/jellyseerr/settings.json" ) ''; }