# The acceptance checks for `services.nxdns`. Neither runs in CI: the VM test # needs KVM, and both need a nixpkgs closure the shared runner should not # download on every push. AGENTS.md says when to run them. { self, pkgs }: let lib = pkgs.lib; # A local record answers without any upstream, which the test VM cannot reach. configWith = address: pkgs.writeText "nxdns-test.zon" '' .{ .groups = .{ .{ .name = "default" } }, .upstreams = .{ .{ .url = "tls://192.0.2.1:853", .tls_name = "dns.example" } }, .local_records = .{ .{ .name = "nas.lan", .rtype = .a, .value = "${address}" } }, } ''; rejected = pkgs.writeText "nxdns-rejected.zon" '' .{ .groups = .{ .{ .name = "default" } }, .upstreams = .{ .{ .url = "tls://192.0.2.1:853", .tls_name = "dns.example" } }, .rules = .{ .{ .group = "default", .pattern = "(", .kind = .regex, .action = .block } }, } ''; machineWith = configFile: { imports = [ self.nixosModules.default ]; services.nxdns = { enable = true; inherit configFile; }; boot.loader.grub.enable = false; fileSystems."/" = { device = "none"; fsType = "tmpfs"; }; system.stateVersion = lib.trivial.release; }; in { nixos-module = pkgs.testers.runNixOSTest { name = "nxdns-module"; nodes.machine = { pkgs, ... }: { imports = [ self.nixosModules.default ]; services.nxdns = { enable = true; configFile = configWith "192.0.2.5"; openFirewall = true; }; environment.systemPackages = [ pkgs.dnsutils ]; specialisation.changed.configuration.services.nxdns.configFile = lib.mkForce ( configWith "192.0.2.6" ); }; testScript = '' machine.wait_for_unit("nxdns.service") machine.wait_until_succeeds("dig +short @127.0.0.1 nas.lan A | grep -x 192.0.2.5") # The packaged unit runs, with the module's drop-in on top of it. unit = machine.succeed("systemctl cat nxdns.service") assert "/lib/systemd/system/nxdns.service" in unit, unit assert "ProtectSystem=strict" in unit, unit assert "run --config=/etc/nxdns/config.zon" in unit, unit machine.succeed("systemctl show -p User --value nxdns.service | grep -x nxdns") machine.succeed("test -z \"$(systemctl show -p ConfigurationDirectory --value nxdns.service)\"") machine.succeed("systemctl show -p ReadOnlyPaths --value nxdns.service | tr ' ' '\\n' | grep -Fx /etc/nxdns") machine.succeed("stat -c %U /etc/nxdns | grep -x root") machine.succeed("iptables -S nixos-fw | grep -- '-p udp .*--dport 53 '") machine.succeed("iptables -S nixos-fw | grep -- '-p tcp .*--dport 53 '") # A switch that changes only the configuration restarts the service. before = machine.succeed("systemctl show -p InvocationID --value nxdns.service").strip() machine.succeed("/run/current-system/specialisation/changed/bin/switch-to-configuration test") machine.wait_for_unit("nxdns.service") after = machine.succeed("systemctl show -p InvocationID --value nxdns.service").strip() assert before != after, "a configuration-only switch did not restart nxdns" machine.wait_until_succeeds("dig +short @127.0.0.1 nas.lan A | grep -x 192.0.2.6") ''; }; # A configuration the pinned binary rejects must fail the system build, and # the build log must carry the binary's own reason. nixos-module-rejects-config = let system = pkgs.nixos (machineWith rejected); failure = pkgs.testers.testBuildFailure system.config.environment.etc."nxdns/config.zon".source; in pkgs.runCommand "nxdns-module-rejects-config" { } '' grep -F 'FAIL rules[0].pattern' ${failure}/testBuildFailure.log touch "$out" ''; }