import importlib.util import pathlib import types import pytest _PATH = pathlib.Path(__file__).resolve().parent.parent / "scripts" / "integration-vm.py" _spec = importlib.util.spec_from_file_location("integration_vm", _PATH) ivm = importlib.util.module_from_spec(_spec) _spec.loader.exec_module(ivm) def test_valid_tiers(): assert ivm.VALID_TIERS == ("internal", "le-staging", "le-prod-wildcard") def test_vm_name_prefix_and_suffix(): assert ivm.vm_name("askari", "ab12cd34") == "boma-it-askari-ab12cd34" def test_vm_name_generates_suffix(): n = ivm.vm_name("askari") assert n.startswith("boma-it-askari-") and len(n.split("-")[-1]) == 8 def test_free_mib_parses_memavailable(): sample = "MemTotal: 16331156 kB\nMemAvailable: 8388608 kB\n" assert ivm.free_mib(sample) == 8192 def test_parse_lease_ip_extracts_ipv4(): out = (" Name MAC address Protocol Address\n" "-------------------------------------------------------------------\n" " vnet0 52:54:00:aa:bb:cc ipv4 192.168.150.42/24\n") assert ivm.parse_lease_ip(out) == "192.168.150.42" def test_parse_lease_ip_none_when_absent(): assert ivm.parse_lease_ip("no leases\n") is None def test_parse_lease_ip_format_is_source_agnostic(): # virsh domifaddr --source arp output format is identical to --source lease; # this test only proves the regex is format-agnostic (both sources produce the # same table). The behavioral arp-fallback in wait_for_ip is covered by # test_wait_for_ip_falls_back_to_arp below. out = (" Name MAC address Protocol Address\n" "-------------------------------------------------------------------\n" " vnet0 52:54:00:de:ad:be ipv4 192.168.150.73/24\n") assert ivm.parse_lease_ip(out) == "192.168.150.73" def test_wait_for_ip_falls_back_to_arp(monkeypatch): # wait_for_ip polls virsh domifaddr with --source lease first, then --source arp. # Simulate lease returning empty (no DHCP lease yet) and arp returning a real address. arp_out = (" Name MAC address Protocol Address\n" "-------------------------------------------------------------------\n" " vnet0 52:54:00:aa:bb:cc ipv4 192.168.150.142/24\n") def fake_sh(cmd, **kwargs): if "arp" in cmd: return types.SimpleNamespace(stdout=arp_out) return types.SimpleNamespace(stdout="") monkeypatch.setattr(ivm, "sh", fake_sh) monkeypatch.setattr(ivm.time, "sleep", lambda _: None) assert ivm.wait_for_ip("dummy") == "192.168.150.142" def test_meta_data_has_instance_and_hostname(): md = ivm.render_meta_data("iid-askari-x", "boma-it-askari-x") assert "instance-id: iid-askari-x" in md assert "local-hostname: boma-it-askari-x" in md def test_user_data_injects_key_and_ansible_user(): ud = ivm.render_user_data("ssh-ed25519 AAAA... claude@ubongo", "ansible") assert ud.startswith("#cloud-config") assert "name: ansible" in ud assert "ssh-ed25519 AAAA... claude@ubongo" in ud assert "NOPASSWD:ALL" in ud def test_cert_file_valid_tier(): p = ivm.cert_file("le-staging") assert p.name == "le-staging.yml" and p.parent.name == "certs" def test_cert_file_rejects_bad_tier(): with pytest.raises(ValueError): ivm.cert_file("bogus") def test_render_run_hosts_single_host_in_groups(): out = ivm.render_run_hosts("boma-it-askari-x", "192.168.150.42", "ansible", ["offsite_hosts"]) assert "offsite_hosts:" in out assert "boma-it-askari-x:" in out assert "ansible_host: 192.168.150.42" in out assert "ansible_user: ansible" in out assert "askari:" not in out.replace("boma-it-askari-x:", "") def test_free_mib_returns_zero_when_absent(): assert ivm.free_mib("MemTotal: 16384 kB\n") == 0 def test_render_run_hosts_multiple_groups(): out = ivm.render_run_hosts("boma-it-x-1", "192.168.150.5", "ansible", ["offsite_hosts", "docker_hosts"]) assert "offsite_hosts:" in out assert "docker_hosts:" in out def test_render_run_hosts_dedups_groups(): out = ivm.render_run_hosts("boma-it-x-1", "192.168.150.5", "ansible", ["docker_hosts", "docker_hosts"]) assert out.count("docker_hosts:") == 1