# pxe-nic-check tests Tests for `playbook/pxe-server-helpers/pxe-nic-check`. Neither needs root or touches a real network. ## Unit tests python3 -m unittest discover -s tests/pxe-nic-check -v Synthesized LLDP, CDP, EAPOL, STP and ARP frames and DHCPOFFER/ACK packets (`frames.py`) fed to the parsers; the verdict table; own-server and cloned-PXE-server handling; networkd lease parsing; config fallbacks; the dnsmasq_bound logic; the status JSON shape and atomic 0644 write. ## Shell helpers `test_helpers.py` runs `pxe-dhcp-hook.sh` (add/del/old with PXEClient, MSFT 5.0 and no vendor class: only SMB 445/139 conntrack entries and sockets are cleared) and `pxe-nic-carrier` (loss stops dnsmasq, gain hands off once, GE NIC events touch nothing, bridge ports count) with stub `ss`, `conntrack`, `logger`, `systemctl` and `pxe-nic-check` on a fake PATH and a fake `/sys/class/net`. Part of the unit run above. ## Namespace lab python3 tests/pxe-nic-check/lab.py # all scenarios, ~5 min python3 tests/pxe-nic-check/lab.py rogue br-lldp # some Re-runs itself under `unshare --user --map-root-user --net --mount`: a private network namespace with veth pairs, so raw sockets work without sudo and every frame stays inside it. Each scenario runs the real script in check (`--json --probe`), `--guard` and `--watch` modes (watch with `--stop-cmd "touch "` instead of `systemctl stop dnsmasq`) and asserts the imaging verdict, the guard exit code, whether watch "stopped dnsmasq", the evidence text and the status JSON shape. Peers: `lab_peer.py` (a raw-socket DHCP responder and LLDP/EAPOL sender; refuses to run unless `PXE_NIC_LAB=1`, which only the lab sets) and real `dnsmasq` if installed (started with `--user= --group=`, since a user namespace cannot drop privileges; scenarios are skipped without it). | Scenario | Peer | Expect | Guard | |-----------------|-----------------------------------------------|---------------|-------| | isolated | none | isolated | 0 | | rogue | DHCP, no domain | foreign-dhcp | 1 | | fake-ge | DHCP domain test.geaerospace.net + LLDP | ge | 1 | | not-corp-domain | DHCP domain notgeaerospace.net | foreign-dhcp | 1 | | lldp-only | LLDP | ge | 1 | | eapol-only | 802.1X EAPOL | ge | 1 | | eapol-client | EAPOL-Start + EAP-Response, like a PXE-booting bay | isolated | 0 | | own-server | DHCP with server id 172.16.9.1 from a local MAC | isolated | 0 | | cloned-pxe | DHCP with server id 172.16.9.1, foreign MAC | foreign-dhcp | 1 | | no-link | peer end down | no-link | 0 | | dnsmasq-corp | real dnsmasq, domain lab.geaerospace.net | ge | 1 | | dnsmasq-rogue | real dnsmasq, no domain | foreign-dhcp | 1 | | own-dnsmasq | real dnsmasq serving the imaging NIC itself | isolated, dnsmasq_bound true | 0 | | br-* | same, imaging NIC is a bridge (like br-pxe), named in nic.conf by a port's MAC | | | `rogue`, `lldp-only` and `br-lldp` also run `--watch` as if dnsmasq had been restarted inside the window: it must log the re-check and still stop. Every watch gets `--state-cmd` (a printf standing in for `systemctl show`). The GE NIC (g0) is checked from a fake networkd lease with domain test.geaerospace.net and must read `ge` in every scenario.