dir = sys_get_temp_dir().'/clupilot-hosts-'.bin2hex(random_bytes(6)); config()->set('provisioning.dns.hosts_dir', $this->dir); config()->set('provisioning.wireguard.hub_address', '10.66.0.1'); config()->set('admin_access.vpn_internal_host', 'admin.clupilot.test'); }); afterEach(function () { foreach (glob($this->dir.'/*') ?: [] as $file) { @unlink($file); } @rmdir($this->dir); }); it('veröffentlicht Portal, Website und Statusseite auf der Hub-Adresse', function () { config()->set('admin_access.app_host', 'app.clupilot.test'); config()->set('admin_access.site_hosts', ['www.clupilot.test', 'clupilot.test']); config()->set('admin_access.status_host', 'status.clupilot.test'); $this->artisan('clupilot:publish-tunnel-names')->assertSuccessful(); $written = file_get_contents($this->dir.'/platform.hosts'); expect($written)->toContain('10.66.0.1 app.clupilot.test') ->and($written)->toContain('10.66.0.1 www.clupilot.test') ->and($written)->toContain('10.66.0.1 clupilot.test') ->and($written)->toContain('10.66.0.1 status.clupilot.test'); }); it('lässt den Dateihost draußen', function () { // Ein Server im Rettungssystem holt dort sein Archiv — der ist per // Definition nicht im Tunnel. Bögen wir den Namen um, holte er es nie. config()->set('admin_access.app_host', 'app.clupilot.test'); config()->set('admin_access.files_host', 'files.clupilot.test'); $this->artisan('clupilot:publish-tunnel-names')->assertSuccessful(); expect(file_get_contents($this->dir.'/platform.hosts')) ->not->toContain('files.clupilot.test'); }); it('räumt die Datei weg, wenn kein Name mehr konfiguriert ist', function () { config()->set('admin_access.app_host', 'app.clupilot.test'); $this->artisan('clupilot:publish-tunnel-names')->assertSuccessful(); expect(file_exists($this->dir.'/platform.hosts'))->toBeTrue(); // Eine Datei, nicht eine je Name: sonst bliebe beim Wechsel von zwei // Website-Namen auf einen die verwaiste Zeile im Resolver stehen, und der // alte Name zeigte weiter in den Tunnel. config()->set('admin_access.app_host', ''); $this->artisan('clupilot:publish-tunnel-names')->assertSuccessful(); expect(file_exists($this->dir.'/platform.hosts'))->toBeFalse(); }); it('tut nichts, solange kein Tunnel konfiguriert ist', function () { // Ohne VPN_INTERNAL_HOST gibt es den Gateway nicht, und eine hosts-Datei // für einen Resolver, den niemand startet, ist nur eine Datei, die beim // nächsten Einschalten falsch sein kann. config()->set('admin_access.vpn_internal_host', ''); config()->set('admin_access.app_host', 'app.clupilot.test'); $this->artisan('clupilot:publish-tunnel-names')->assertSuccessful(); expect(file_exists($this->dir.'/platform.hosts'))->toBeFalse(); }); it('schreibt genau eine Zeile je Name, im hosts-Format', function () { config()->set('admin_access.app_host', 'app.clupilot.test'); config()->set('admin_access.site_hosts', ['www.clupilot.test']); app(HostDnsDirectory::class)->writeMany('probe', ['a.test', 'b.test'], '10.66.0.1'); expect(file_get_contents($this->dir.'/probe.hosts')) ->toBe("10.66.0.1 a.test\n10.66.0.1 b.test\n"); }); it('ist dieselbe Datei-Umsetzung wie für die Host-Namen', function () { // Nicht Fake gegen Fake geprüft: die Rechte und das Anlegen des // Verzeichnisses sind der Teil, der in Produktion schiefgeht. expect(app(HostDnsDirectory::class))->toBeInstanceOf(FileHostDnsDirectory::class); })->skip(fn () => ! app()->environment('testing'), 'nur im Testlauf');