fqdn => rrset id */ public array $records = []; /** @var array fqdn => published value — without this a test * cannot tell which address ended up in DNS, which is the whole point for * host records. */ public array $values = []; public bool $failUpsert = false; public bool $failDelete = false; public function upsertRecord(string $fqdn, string $type, string $value): string { if ($this->failUpsert) { throw new RuntimeException('hetzner dns 5xx'); } // Derived, not counted: the id a caller stores has to be the one the // real client would store, or the round trip through deleteRecord() // below proves nothing about the round trip in production. $id = RrsetId::for($fqdn, $type); $this->values[$fqdn] = $value; return $this->records[$fqdn] = $id; } public function deleteRecord(string $recordId): void { if ($this->failDelete) { throw new RuntimeException('hetzner dns 5xx'); } // Lets a legacy id pass exactly like the real client does — a host // carrying one has to stay purgeable, and a fake that threw here would // prove the opposite of what the real client does. if (! RrsetId::isRrsetId($recordId)) { return; } $this->records = array_filter($this->records, fn ($id) => $id !== $recordId); } }