Subdomain-Cleanup-Commands
Neue Artisan-Commands DeleteAllKasSubdomains und DeletePartnerShopSubdomains zum Aufraeumen von KAS-/Partnershop-Subdomains, jeweils mit Feature-Tests. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
parent
9b7665c59a
commit
84238c7694
4 changed files with 677 additions and 0 deletions
214
app/Console/Commands/DeleteAllKasSubdomains.php
Normal file
214
app/Console/Commands/DeleteAllKasSubdomains.php
Normal file
|
|
@ -0,0 +1,214 @@
|
|||
<?php
|
||||
|
||||
namespace App\Console\Commands;
|
||||
|
||||
use App\Http\Controllers\Api\KasController;
|
||||
use Exception;
|
||||
use Illuminate\Console\Command;
|
||||
use Illuminate\Support\Collection;
|
||||
use Illuminate\Support\Facades\File;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
|
||||
class DeleteAllKasSubdomains extends Command
|
||||
{
|
||||
/**
|
||||
* The name and signature of the console command.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $signature = 'subdomains:delete-all-kas
|
||||
{--domain= : Optional nur Subdomains dieser Hauptdomain löschen}
|
||||
{--limit= : Maximale Anzahl zu löschender Subdomains}
|
||||
{--list-file= : Optionaler Pfad für die temporäre JSON-Liste}
|
||||
{--force : Subdomains wirklich löschen; ohne diese Option läuft nur ein Dry-Run}';
|
||||
|
||||
/**
|
||||
* The console command description.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $description = 'Löscht alle aktuell in KAS angelegten Subdomains anhand einer temporären KAS-Liste';
|
||||
|
||||
public function __construct(private KasController $kas)
|
||||
{
|
||||
parent::__construct();
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the console command.
|
||||
*/
|
||||
public function handle(): int
|
||||
{
|
||||
$domain = $this->option('domain') !== null
|
||||
? trim((string) $this->option('domain'), '.')
|
||||
: null;
|
||||
$limit = $this->option('limit') !== null ? (int) $this->option('limit') : null;
|
||||
$isForce = (bool) $this->option('force');
|
||||
|
||||
if ($domain === '') {
|
||||
$this->error('Die Option --domain darf nicht leer sein.');
|
||||
|
||||
return self::FAILURE;
|
||||
}
|
||||
|
||||
if ($limit !== null && $limit < 1) {
|
||||
$this->error('Die Option --limit muss größer als 0 sein.');
|
||||
|
||||
return self::FAILURE;
|
||||
}
|
||||
|
||||
try {
|
||||
$candidates = $this->getKasSubdomains($domain);
|
||||
|
||||
if ($limit !== null) {
|
||||
$candidates = $candidates->take($limit)->values();
|
||||
}
|
||||
|
||||
$listFilePath = $this->writeTemporaryList($candidates, $domain, $isForce);
|
||||
$this->printPreview($candidates, $domain, $listFilePath, $isForce);
|
||||
|
||||
if ($candidates->isEmpty()) {
|
||||
$this->info('Keine KAS-Subdomains gefunden.');
|
||||
|
||||
return self::SUCCESS;
|
||||
}
|
||||
|
||||
if (! $isForce) {
|
||||
$this->warn('Dry-Run: Es wurde nichts gelöscht. Mit --force werden die Subdomains aus der temporären Liste gelöscht.');
|
||||
|
||||
return self::SUCCESS;
|
||||
}
|
||||
|
||||
return $this->deleteSubdomains($candidates);
|
||||
} catch (Exception $e) {
|
||||
$this->error('Fehler beim Löschen der KAS-Subdomains: '.$e->getMessage());
|
||||
Log::error('KAS Komplett-Subdomain-Löschung fehlgeschlagen', [
|
||||
'error' => $e->getMessage(),
|
||||
'trace' => $e->getTraceAsString(),
|
||||
]);
|
||||
|
||||
return self::FAILURE;
|
||||
}
|
||||
}
|
||||
|
||||
private function getKasSubdomains(?string $domain): Collection
|
||||
{
|
||||
$this->info('Rufe Subdomains direkt von KAS ab...');
|
||||
|
||||
$subdomains = $this->kas->action('get_subdomains');
|
||||
|
||||
if (! is_iterable($subdomains)) {
|
||||
throw new Exception('KAS hat keine auswertbare Subdomain-Liste zurückgegeben: '.json_encode($subdomains));
|
||||
}
|
||||
|
||||
return collect($subdomains)
|
||||
->filter(function (array $subdomain) use ($domain): bool {
|
||||
$name = strtolower((string) ($subdomain['subdomain_name'] ?? ''));
|
||||
|
||||
if ($name === '') {
|
||||
return false;
|
||||
}
|
||||
|
||||
return $domain === null || str_ends_with($name, '.'.strtolower($domain));
|
||||
})
|
||||
->map(fn (array $subdomain): array => [
|
||||
'subdomain_name' => (string) $subdomain['subdomain_name'],
|
||||
'subdomain_path' => (string) ($subdomain['subdomain_path'] ?? ''),
|
||||
'php_version' => (string) ($subdomain['php_version'] ?? ''),
|
||||
'is_active' => (string) ($subdomain['is_active'] ?? ''),
|
||||
])
|
||||
->unique('subdomain_name')
|
||||
->sortBy('subdomain_name')
|
||||
->values();
|
||||
}
|
||||
|
||||
private function writeTemporaryList(Collection $candidates, ?string $domain, bool $isForce): string
|
||||
{
|
||||
$listFilePath = $this->resolveListFilePath();
|
||||
File::ensureDirectoryExists(dirname($listFilePath));
|
||||
|
||||
File::put($listFilePath, json_encode([
|
||||
'generated_at' => now()->toIso8601String(),
|
||||
'mode' => $isForce ? 'delete' : 'dry-run',
|
||||
'domain_filter' => $domain,
|
||||
'count' => $candidates->count(),
|
||||
'subdomains' => $candidates->values()->all(),
|
||||
], JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES));
|
||||
|
||||
return $listFilePath;
|
||||
}
|
||||
|
||||
private function resolveListFilePath(): string
|
||||
{
|
||||
$listFilePath = (string) ($this->option('list-file') ?? '');
|
||||
|
||||
if ($listFilePath !== '') {
|
||||
return str_starts_with($listFilePath, '/') ? $listFilePath : base_path($listFilePath);
|
||||
}
|
||||
|
||||
return storage_path('app/kas-subdomain-delete-lists/kas-subdomains-'.now()->format('Ymd-His').'.json');
|
||||
}
|
||||
|
||||
private function printPreview(Collection $candidates, ?string $domain, string $listFilePath, bool $isForce): void
|
||||
{
|
||||
$this->newLine();
|
||||
$this->info('Modus: '.($isForce ? 'Löschen' : 'Dry-Run'));
|
||||
$this->info('Domain-Filter: '.($domain ?: 'keiner'));
|
||||
$this->info('Temporäre Liste: '.$listFilePath);
|
||||
$this->info('Gefundene KAS-Subdomains: '.$candidates->count());
|
||||
|
||||
if ($candidates->isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->table(
|
||||
['Subdomain', 'Pfad', 'PHP', 'Aktiv'],
|
||||
$candidates->take(25)->map(fn (array $candidate): array => [
|
||||
$candidate['subdomain_name'],
|
||||
$candidate['subdomain_path'] ?: '-',
|
||||
$candidate['php_version'] ?: '-',
|
||||
$candidate['is_active'] ?: '-',
|
||||
])->all()
|
||||
);
|
||||
|
||||
if ($candidates->count() > 25) {
|
||||
$this->line('... weitere '.($candidates->count() - 25).' Subdomains ausgeblendet.');
|
||||
}
|
||||
}
|
||||
|
||||
private function deleteSubdomains(Collection $candidates): int
|
||||
{
|
||||
$deleted = 0;
|
||||
$failed = 0;
|
||||
|
||||
foreach ($candidates as $candidate) {
|
||||
$subdomainName = $candidate['subdomain_name'];
|
||||
$result = $this->kas->action('delete_subdomain', [
|
||||
'subdomain_name' => $subdomainName,
|
||||
]);
|
||||
|
||||
if ($this->isSuccessfulDeleteResult($result, $subdomainName)) {
|
||||
$deleted++;
|
||||
$this->info("Gelöscht: {$subdomainName}");
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
$failed++;
|
||||
$this->error("Fehler beim Löschen von {$subdomainName}: ".json_encode($result));
|
||||
}
|
||||
|
||||
$this->newLine();
|
||||
$this->info("Gelöscht: {$deleted}");
|
||||
$this->info("Fehler: {$failed}");
|
||||
|
||||
return $failed === 0 ? self::SUCCESS : self::FAILURE;
|
||||
}
|
||||
|
||||
private function isSuccessfulDeleteResult(mixed $result, string $subdomainName): bool
|
||||
{
|
||||
return $result === true
|
||||
|| $result === 'TRUE'
|
||||
|| $result === $subdomainName;
|
||||
}
|
||||
}
|
||||
241
app/Console/Commands/DeletePartnerShopSubdomains.php
Normal file
241
app/Console/Commands/DeletePartnerShopSubdomains.php
Normal file
|
|
@ -0,0 +1,241 @@
|
|||
<?php
|
||||
|
||||
namespace App\Console\Commands;
|
||||
|
||||
use App\Http\Controllers\Api\KasController;
|
||||
use App\Models\UserShop;
|
||||
use Exception;
|
||||
use Illuminate\Console\Command;
|
||||
use Illuminate\Support\Collection;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
|
||||
class DeletePartnerShopSubdomains extends Command
|
||||
{
|
||||
/**
|
||||
* The name and signature of the console command.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $signature = 'subdomains:delete-partnershops
|
||||
{--domain=mivita.care : Hauptdomain der Partnershop-Subdomains}
|
||||
{--start=1 : UserShop-ID, ab der Shops berücksichtigt werden}
|
||||
{--limit= : Maximale Anzahl zu löschender Subdomains}
|
||||
{--active-only : Nur aktive/nicht gelöschte UserShops berücksichtigen}
|
||||
{--include-path-matches : Zusätzlich KAS-Subdomains mit dem Partnershop-Pfad löschen}
|
||||
{--path=/mein.mivita.care/public/ : Zielpfad der alten Partnershop-Subdomains}
|
||||
{--force : Subdomains wirklich löschen; ohne diese Option läuft nur ein Dry-Run}';
|
||||
|
||||
/**
|
||||
* The console command description.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $description = 'Löscht alte KAS-Subdomains der Partnershops nach Umstellung auf Wildcard-Konfiguration';
|
||||
|
||||
/**
|
||||
* @var array<int, string>
|
||||
*/
|
||||
private array $protectedPrefixes = ['www.', 'api.', 'checkout.', 'preview.'];
|
||||
|
||||
public function __construct(private KasController $kas)
|
||||
{
|
||||
parent::__construct();
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the console command.
|
||||
*/
|
||||
public function handle(): int
|
||||
{
|
||||
$domain = trim((string) $this->option('domain'), '.');
|
||||
$startId = max(1, (int) $this->option('start'));
|
||||
$limit = $this->option('limit') !== null ? (int) $this->option('limit') : null;
|
||||
$isForce = (bool) $this->option('force');
|
||||
|
||||
if ($domain === '') {
|
||||
$this->error('Die Option --domain darf nicht leer sein.');
|
||||
|
||||
return self::FAILURE;
|
||||
}
|
||||
|
||||
if ($limit !== null && $limit < 1) {
|
||||
$this->error('Die Option --limit muss größer als 0 sein.');
|
||||
|
||||
return self::FAILURE;
|
||||
}
|
||||
|
||||
try {
|
||||
$subdomains = $this->getKasSubdomains();
|
||||
$shopHosts = $this->getPartnerShopHosts($domain, $startId);
|
||||
$candidates = $this->getDeletionCandidates($subdomains, $shopHosts, $domain);
|
||||
|
||||
if ($limit !== null) {
|
||||
$candidates = $candidates->take($limit)->values();
|
||||
}
|
||||
|
||||
$this->printPreview($candidates, $isForce);
|
||||
|
||||
if ($candidates->isEmpty()) {
|
||||
$this->info('Keine löschbaren Partnershop-Subdomains gefunden.');
|
||||
|
||||
return self::SUCCESS;
|
||||
}
|
||||
|
||||
if (! $isForce) {
|
||||
$this->warn('Dry-Run: Es wurde nichts gelöscht. Mit --force werden die gefundenen Subdomains gelöscht.');
|
||||
|
||||
return self::SUCCESS;
|
||||
}
|
||||
|
||||
return $this->deleteSubdomains($candidates);
|
||||
} catch (Exception $e) {
|
||||
$this->error('Fehler beim Löschen der Partnershop-Subdomains: '.$e->getMessage());
|
||||
Log::error('KAS Partnershop-Subdomain-Löschung fehlgeschlagen', [
|
||||
'error' => $e->getMessage(),
|
||||
'trace' => $e->getTraceAsString(),
|
||||
]);
|
||||
|
||||
return self::FAILURE;
|
||||
}
|
||||
}
|
||||
|
||||
private function getKasSubdomains(): Collection
|
||||
{
|
||||
$this->info('Rufe Subdomains von KAS ab...');
|
||||
|
||||
$subdomains = $this->kas->action('get_subdomains');
|
||||
|
||||
if (! is_iterable($subdomains)) {
|
||||
throw new Exception('KAS hat keine auswertbare Subdomain-Liste zurückgegeben: '.json_encode($subdomains));
|
||||
}
|
||||
|
||||
return collect($subdomains);
|
||||
}
|
||||
|
||||
private function getPartnerShopHosts(string $domain, int $startId): Collection
|
||||
{
|
||||
$query = UserShop::query()
|
||||
->where('id', '>=', $startId)
|
||||
->orderBy('id');
|
||||
|
||||
if ($this->option('active-only')) {
|
||||
$query->where('active', true);
|
||||
} else {
|
||||
$query->withTrashed();
|
||||
}
|
||||
|
||||
return $query
|
||||
->pluck('slug')
|
||||
->filter()
|
||||
->map(fn (string $slug): string => strtolower($slug.'.'.$domain))
|
||||
->unique()
|
||||
->values();
|
||||
}
|
||||
|
||||
private function getDeletionCandidates(Collection $subdomains, Collection $shopHosts, string $domain): Collection
|
||||
{
|
||||
$shopHostLookup = array_flip($shopHosts->all());
|
||||
$includePathMatches = (bool) $this->option('include-path-matches');
|
||||
$partnerShopPath = (string) $this->option('path');
|
||||
|
||||
return $subdomains
|
||||
->filter(function (array $subdomain) use ($domain, $includePathMatches, $partnerShopPath, $shopHostLookup): bool {
|
||||
$name = strtolower((string) ($subdomain['subdomain_name'] ?? ''));
|
||||
|
||||
if ($name === '' || ! $this->belongsToDomain($name, $domain) || $this->isProtectedSubdomain($name)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (isset($shopHostLookup[$name])) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return $includePathMatches
|
||||
&& trim((string) ($subdomain['subdomain_path'] ?? '')) === $partnerShopPath;
|
||||
})
|
||||
->map(fn (array $subdomain): array => [
|
||||
'subdomain_name' => (string) $subdomain['subdomain_name'],
|
||||
'subdomain_path' => (string) ($subdomain['subdomain_path'] ?? ''),
|
||||
'php_version' => (string) ($subdomain['php_version'] ?? ''),
|
||||
])
|
||||
->unique('subdomain_name')
|
||||
->sortBy('subdomain_name')
|
||||
->values();
|
||||
}
|
||||
|
||||
private function belongsToDomain(string $subdomainName, string $domain): bool
|
||||
{
|
||||
return str_ends_with($subdomainName, '.'.strtolower($domain));
|
||||
}
|
||||
|
||||
private function isProtectedSubdomain(string $subdomainName): bool
|
||||
{
|
||||
foreach ($this->protectedPrefixes as $prefix) {
|
||||
if (str_starts_with($subdomainName, $prefix)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private function printPreview(Collection $candidates, bool $isForce): void
|
||||
{
|
||||
$this->newLine();
|
||||
$this->info('Modus: '.($isForce ? 'Löschen' : 'Dry-Run'));
|
||||
$this->info('Gefundene Lösch-Kandidaten: '.$candidates->count());
|
||||
|
||||
if ($candidates->isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->table(
|
||||
['Subdomain', 'Pfad', 'PHP'],
|
||||
$candidates->take(25)->map(fn (array $candidate): array => [
|
||||
$candidate['subdomain_name'],
|
||||
$candidate['subdomain_path'] ?: '-',
|
||||
$candidate['php_version'] ?: '-',
|
||||
])->all()
|
||||
);
|
||||
|
||||
if ($candidates->count() > 25) {
|
||||
$this->line('... weitere '.($candidates->count() - 25).' Subdomains ausgeblendet.');
|
||||
}
|
||||
}
|
||||
|
||||
private function deleteSubdomains(Collection $candidates): int
|
||||
{
|
||||
$deleted = 0;
|
||||
$failed = 0;
|
||||
|
||||
foreach ($candidates as $candidate) {
|
||||
$subdomainName = $candidate['subdomain_name'];
|
||||
$result = $this->kas->action('delete_subdomain', [
|
||||
'subdomain_name' => $subdomainName,
|
||||
]);
|
||||
|
||||
if ($this->isSuccessfulDeleteResult($result, $subdomainName)) {
|
||||
$deleted++;
|
||||
$this->info("Gelöscht: {$subdomainName}");
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
$failed++;
|
||||
$this->error("Fehler beim Löschen von {$subdomainName}: ".json_encode($result));
|
||||
}
|
||||
|
||||
$this->newLine();
|
||||
$this->info("Gelöscht: {$deleted}");
|
||||
$this->info("Fehler: {$failed}");
|
||||
|
||||
return $failed === 0 ? self::SUCCESS : self::FAILURE;
|
||||
}
|
||||
|
||||
private function isSuccessfulDeleteResult(mixed $result, string $subdomainName): bool
|
||||
{
|
||||
return $result === true
|
||||
|| $result === 'TRUE'
|
||||
|| $result === $subdomainName;
|
||||
}
|
||||
}
|
||||
109
tests/Feature/DeleteAllKasSubdomainsCommandTest.php
Normal file
109
tests/Feature/DeleteAllKasSubdomainsCommandTest.php
Normal file
|
|
@ -0,0 +1,109 @@
|
|||
<?php
|
||||
|
||||
use App\Http\Controllers\Api\KasController;
|
||||
use Tests\TestCase;
|
||||
|
||||
uses(TestCase::class);
|
||||
|
||||
it('erstellt ohne force nur eine temporäre kas liste', function () {
|
||||
$listFile = sys_get_temp_dir().'/kas-subdomains-dry-run-test-'.uniqid().'.json';
|
||||
|
||||
$kas = \Mockery::mock(KasController::class);
|
||||
$kas->shouldReceive('action')
|
||||
->once()
|
||||
->with('get_subdomains')
|
||||
->andReturn([
|
||||
['subdomain_name' => 'api.mivita.care', 'subdomain_path' => '/api/public/', 'php_version' => '8.2'],
|
||||
['subdomain_name' => 'shop.mivita.care', 'subdomain_path' => '/mein.mivita.care/public/', 'php_version' => '8.2'],
|
||||
]);
|
||||
|
||||
$this->app->instance(KasController::class, $kas);
|
||||
|
||||
$this->artisan('subdomains:delete-all-kas', [
|
||||
'--list-file' => $listFile,
|
||||
])->assertSuccessful();
|
||||
|
||||
$data = json_decode(file_get_contents($listFile), true);
|
||||
|
||||
expect(file_exists($listFile))->toBeTrue();
|
||||
expect($data['count'])->toBe(2);
|
||||
expect($data['subdomains'])->toHaveCount(2);
|
||||
expect($data['mode'])->toBe('dry-run');
|
||||
|
||||
@unlink($listFile);
|
||||
});
|
||||
|
||||
it('löscht mit force alle subdomains aus der kas liste ohne datenbankabgleich', function () {
|
||||
$listFile = sys_get_temp_dir().'/kas-subdomains-force-test-'.uniqid().'.json';
|
||||
|
||||
$kas = \Mockery::mock(KasController::class);
|
||||
$kas->shouldReceive('action')
|
||||
->once()
|
||||
->with('get_subdomains')
|
||||
->andReturn([
|
||||
['subdomain_name' => 'api.mivita.care', 'subdomain_path' => '/api/public/'],
|
||||
['subdomain_name' => 'preview.mivita.care', 'subdomain_path' => '/preview/public/'],
|
||||
['subdomain_name' => 'partner.mivita.care', 'subdomain_path' => '/mein.mivita.care/public/'],
|
||||
]);
|
||||
$kas->shouldReceive('action')
|
||||
->once()
|
||||
->with('delete_subdomain', ['subdomain_name' => 'api.mivita.care'])
|
||||
->andReturn('TRUE');
|
||||
$kas->shouldReceive('action')
|
||||
->once()
|
||||
->with('delete_subdomain', ['subdomain_name' => 'partner.mivita.care'])
|
||||
->andReturn('TRUE');
|
||||
$kas->shouldReceive('action')
|
||||
->once()
|
||||
->with('delete_subdomain', ['subdomain_name' => 'preview.mivita.care'])
|
||||
->andReturn('TRUE');
|
||||
|
||||
$this->app->instance(KasController::class, $kas);
|
||||
|
||||
$this->artisan('subdomains:delete-all-kas', [
|
||||
'--force' => true,
|
||||
'--list-file' => $listFile,
|
||||
])->assertSuccessful();
|
||||
|
||||
$data = json_decode(file_get_contents($listFile), true);
|
||||
|
||||
expect($data['count'])->toBe(3);
|
||||
expect($data['mode'])->toBe('delete');
|
||||
|
||||
@unlink($listFile);
|
||||
});
|
||||
|
||||
it('kann optional auf eine hauptdomain und ein limit begrenzen', function () {
|
||||
$listFile = sys_get_temp_dir().'/kas-subdomains-filter-test-'.uniqid().'.json';
|
||||
|
||||
$kas = \Mockery::mock(KasController::class);
|
||||
$kas->shouldReceive('action')
|
||||
->once()
|
||||
->with('get_subdomains')
|
||||
->andReturn([
|
||||
['subdomain_name' => 'a.mivita.care'],
|
||||
['subdomain_name' => 'b.mivita.care'],
|
||||
['subdomain_name' => 'extern.example.com'],
|
||||
]);
|
||||
$kas->shouldReceive('action')
|
||||
->once()
|
||||
->with('delete_subdomain', ['subdomain_name' => 'a.mivita.care'])
|
||||
->andReturn('TRUE');
|
||||
|
||||
$this->app->instance(KasController::class, $kas);
|
||||
|
||||
$this->artisan('subdomains:delete-all-kas', [
|
||||
'--force' => true,
|
||||
'--domain' => 'mivita.care',
|
||||
'--limit' => 1,
|
||||
'--list-file' => $listFile,
|
||||
])->assertSuccessful();
|
||||
|
||||
$data = json_decode(file_get_contents($listFile), true);
|
||||
|
||||
expect($data['count'])->toBe(1);
|
||||
expect($data['domain_filter'])->toBe('mivita.care');
|
||||
expect($data['subdomains'])->toHaveCount(1);
|
||||
|
||||
@unlink($listFile);
|
||||
});
|
||||
113
tests/Feature/DeletePartnerShopSubdomainsCommandTest.php
Normal file
113
tests/Feature/DeletePartnerShopSubdomainsCommandTest.php
Normal file
|
|
@ -0,0 +1,113 @@
|
|||
<?php
|
||||
|
||||
use App\Http\Controllers\Api\KasController;
|
||||
use App\Models\UserShop;
|
||||
use App\User;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
use Tests\TestCase;
|
||||
|
||||
uses(TestCase::class);
|
||||
|
||||
beforeEach(function () {
|
||||
Schema::dropIfExists('user_shops');
|
||||
Schema::dropIfExists('users');
|
||||
|
||||
Schema::create('users', function (Blueprint $table) {
|
||||
$table->increments('id');
|
||||
$table->string('email')->unique();
|
||||
$table->string('password');
|
||||
$table->char('lang', 2)->default('de');
|
||||
$table->timestamps();
|
||||
$table->softDeletes();
|
||||
});
|
||||
|
||||
Schema::create('user_shops', function (Blueprint $table) {
|
||||
$table->increments('id');
|
||||
$table->unsignedInteger('user_id');
|
||||
$table->string('name', 20)->unique();
|
||||
$table->string('slug', 40)->unique();
|
||||
$table->boolean('active')->default(false);
|
||||
$table->timestamps();
|
||||
$table->softDeletes();
|
||||
});
|
||||
});
|
||||
|
||||
afterEach(function () {
|
||||
Schema::dropIfExists('user_shops');
|
||||
Schema::dropIfExists('users');
|
||||
});
|
||||
|
||||
it('führt ohne force nur einen dry run aus', function () {
|
||||
UserShop::forceCreate([
|
||||
'user_id' => User::forceCreate([
|
||||
'email' => 'partner@example.test',
|
||||
'password' => 'secret',
|
||||
'lang' => 'de',
|
||||
])->id,
|
||||
'name' => 'Partner Shop',
|
||||
'slug' => 'partner',
|
||||
'active' => true,
|
||||
]);
|
||||
|
||||
$kas = \Mockery::mock(KasController::class);
|
||||
$kas->shouldReceive('action')
|
||||
->once()
|
||||
->with('get_subdomains')
|
||||
->andReturn([
|
||||
['subdomain_name' => 'partner.mivita.care', 'subdomain_path' => '/mein.mivita.care/public/'],
|
||||
]);
|
||||
$this->app->instance(KasController::class, $kas);
|
||||
|
||||
$this->artisan('subdomains:delete-partnershops')->assertSuccessful();
|
||||
});
|
||||
|
||||
it('löscht passende partnershop subdomains mit force', function () {
|
||||
UserShop::forceCreate([
|
||||
'user_id' => User::forceCreate([
|
||||
'email' => 'shop@example.test',
|
||||
'password' => 'secret',
|
||||
'lang' => 'de',
|
||||
])->id,
|
||||
'name' => 'Shop Eins',
|
||||
'slug' => 'shop-eins',
|
||||
'active' => true,
|
||||
]);
|
||||
|
||||
$kas = \Mockery::mock(KasController::class);
|
||||
$kas->shouldReceive('action')
|
||||
->once()
|
||||
->with('get_subdomains')
|
||||
->andReturn([
|
||||
['subdomain_name' => 'shop-eins.mivita.care', 'subdomain_path' => '/mein.mivita.care/public/'],
|
||||
['subdomain_name' => 'api.mivita.care', 'subdomain_path' => '/mein.mivita.care/public/'],
|
||||
['subdomain_name' => 'extern.mivita.care', 'subdomain_path' => '/andere-app/public/'],
|
||||
]);
|
||||
$kas->shouldReceive('action')
|
||||
->once()
|
||||
->with('delete_subdomain', ['subdomain_name' => 'shop-eins.mivita.care'])
|
||||
->andReturn('TRUE');
|
||||
|
||||
$this->app->instance(KasController::class, $kas);
|
||||
|
||||
$this->artisan('subdomains:delete-partnershops --force')->assertSuccessful();
|
||||
});
|
||||
|
||||
it('kann verwaiste partnershop subdomains über den kas pfad löschen', function () {
|
||||
$kas = \Mockery::mock(KasController::class);
|
||||
$kas->shouldReceive('action')
|
||||
->once()
|
||||
->with('get_subdomains')
|
||||
->andReturn([
|
||||
['subdomain_name' => 'alte-partnerin.mivita.care', 'subdomain_path' => '/mein.mivita.care/public/'],
|
||||
['subdomain_name' => 'preview.mivita.care', 'subdomain_path' => '/mein.mivita.care/public/'],
|
||||
]);
|
||||
$kas->shouldReceive('action')
|
||||
->once()
|
||||
->with('delete_subdomain', ['subdomain_name' => 'alte-partnerin.mivita.care'])
|
||||
->andReturn('TRUE');
|
||||
|
||||
$this->app->instance(KasController::class, $kas);
|
||||
|
||||
$this->artisan('subdomains:delete-partnershops --force --include-path-matches')->assertSuccessful();
|
||||
});
|
||||
Loading…
Add table
Add a link
Reference in a new issue