## Summary - move the Laravel application into `apps/platform` and keep the repository root for orchestration, docs, and tooling - update the local command model, Sail/Docker wiring, runtime paths, and ignore rules around the new platform location - add relocation quickstart/contracts plus focused smoke coverage for bootstrap, command model, routes, and runtime behavior ## Validation - `cd apps/platform && ./vendor/bin/sail artisan test --compact tests/Feature/PlatformRelocation` - integrated browser smoke validated `/up`, `/`, `/admin`, `/admin/choose-workspace`, and tenant route semantics for `200`, `403`, and `404` ## Remaining Rollout Checks - validate Dokploy build context and working-directory assumptions against the new `apps/platform` layout - confirm web, queue, and scheduler processes all start from the expected working directory in staging/production - verify no legacy volume mounts or asset-publish paths still point at the old root-level `public/` or `storage/` locations Co-authored-by: Ahmed Darrazi <ahmed.darrazi@live.de> Reviewed-on: #213
120 lines
4.5 KiB
PHP
120 lines
4.5 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
use App\Filament\Resources\AlertRuleResource;
|
|
use App\Filament\Resources\AlertRuleResource\Pages\CreateAlertRule;
|
|
use App\Filament\Resources\AlertRuleResource\Pages\EditAlertRule;
|
|
use App\Models\AlertDestination;
|
|
use App\Models\AlertRule;
|
|
use App\Support\Workspaces\WorkspaceContext;
|
|
use Livewire\Livewire;
|
|
|
|
it('creates and edits alert rules with attached destinations', function (): void {
|
|
[$user, $tenant] = createUserWithTenant(role: 'owner');
|
|
|
|
$workspaceId = (int) session()->get(\App\Support\Workspaces\WorkspaceContext::SESSION_KEY);
|
|
|
|
$destinationA = AlertDestination::factory()->create([
|
|
'workspace_id' => $workspaceId,
|
|
'name' => 'Teams destination',
|
|
]);
|
|
$destinationB = AlertDestination::factory()->email()->create([
|
|
'workspace_id' => $workspaceId,
|
|
'name' => 'Email destination',
|
|
]);
|
|
|
|
$this->actingAs($user);
|
|
|
|
Livewire::test(CreateAlertRule::class)
|
|
->fillForm([
|
|
'name' => 'Critical drift alerts',
|
|
'is_enabled' => true,
|
|
'event_type' => 'high_drift',
|
|
'minimum_severity' => 'high',
|
|
'tenant_scope_mode' => 'allowlist',
|
|
'tenant_allowlist' => [(int) $tenant->getKey()],
|
|
'cooldown_seconds' => 900,
|
|
'quiet_hours_enabled' => true,
|
|
'quiet_hours_start' => '22:00',
|
|
'quiet_hours_end' => '06:00',
|
|
'quiet_hours_timezone' => 'UTC',
|
|
'destination_ids' => [(int) $destinationA->getKey(), (int) $destinationB->getKey()],
|
|
])
|
|
->call('create')
|
|
->assertHasNoFormErrors();
|
|
|
|
$rule = AlertRule::query()->where('name', 'Critical drift alerts')->first();
|
|
expect($rule)->not->toBeNull();
|
|
expect($rule->tenant_allowlist)->toBe([(int) $tenant->getKey()]);
|
|
expect($rule->destinations()->count())->toBe(2);
|
|
|
|
Livewire::test(EditAlertRule::class, ['record' => $rule->getRouteKey()])
|
|
->fillForm([
|
|
'name' => 'Critical drift alerts updated',
|
|
'is_enabled' => false,
|
|
'destination_ids' => [(int) $destinationB->getKey()],
|
|
'tenant_allowlist' => [],
|
|
'tenant_scope_mode' => 'all',
|
|
])
|
|
->call('save')
|
|
->assertHasNoFormErrors();
|
|
|
|
$rule->refresh();
|
|
expect($rule->name)->toBe('Critical drift alerts updated');
|
|
expect((bool) $rule->is_enabled)->toBeFalse();
|
|
expect($rule->tenant_scope_mode)->toBe('all');
|
|
expect($rule->destinations()->pluck('alert_destinations.id')->all())->toBe([(int) $destinationB->getKey()]);
|
|
});
|
|
|
|
it('shows targeting semantics labels and hides old scope labels on alert rule edit form', function (): void {
|
|
[$user, $tenant] = createUserWithTenant(role: 'owner');
|
|
|
|
$workspaceId = (int) session()->get(WorkspaceContext::SESSION_KEY);
|
|
|
|
$destination = AlertDestination::factory()->create(['workspace_id' => $workspaceId]);
|
|
|
|
$rule = AlertRule::factory()->create([
|
|
'workspace_id' => $workspaceId,
|
|
'tenant_scope_mode' => 'allowlist',
|
|
'tenant_allowlist' => [(int) $tenant->getKey()],
|
|
]);
|
|
$rule->destinations()->attach((int) $destination->getKey(), ['workspace_id' => $workspaceId]);
|
|
|
|
$this->actingAs($user);
|
|
|
|
$this->withSession([
|
|
WorkspaceContext::SESSION_KEY => $workspaceId,
|
|
])->get(AlertRuleResource::getUrl('edit', ['record' => $rule], panel: 'admin'))
|
|
->assertOk()
|
|
->assertSee('Applies to tenants')
|
|
->assertSee('This rule is workspace-wide. Use this to limit where it applies.')
|
|
->assertSee('Selected tenants')
|
|
->assertSee('Only these tenants will trigger this rule.')
|
|
->assertDontSee('Tenant scope mode')
|
|
->assertDontSee('Tenant allowlist');
|
|
});
|
|
|
|
it('shows form section headings on alert rule edit form', function (): void {
|
|
[$user, $tenant] = createUserWithTenant(role: 'owner');
|
|
|
|
$workspaceId = (int) session()->get(WorkspaceContext::SESSION_KEY);
|
|
|
|
$destination = AlertDestination::factory()->create(['workspace_id' => $workspaceId]);
|
|
|
|
$rule = AlertRule::factory()->create([
|
|
'workspace_id' => $workspaceId,
|
|
]);
|
|
$rule->destinations()->attach((int) $destination->getKey(), ['workspace_id' => $workspaceId]);
|
|
|
|
$this->actingAs($user);
|
|
|
|
$this->withSession([
|
|
WorkspaceContext::SESSION_KEY => $workspaceId,
|
|
])->get(AlertRuleResource::getUrl('edit', ['record' => $rule], panel: 'admin'))
|
|
->assertOk()
|
|
->assertSee('Rule')
|
|
->assertSee('Applies to')
|
|
->assertSee('Delivery');
|
|
});
|