TenantAtlas/tests/Feature/WorkspaceIsolation/TenantOwnedWorkspaceInvariantTest.php
ahmido ef5c223172 fix(onboarding): preserve workspace scope and consent flow (#117)
## Summary
<!-- Kurz: Was ändert sich und warum? -->

## Spec-Driven Development (SDD)
- [ ] Es gibt eine Spec unter `specs/<NNN>-<feature>/`
- [ ] Enthaltene Dateien: `plan.md`, `tasks.md`, `spec.md`
- [ ] Spec beschreibt Verhalten/Acceptance Criteria (nicht nur Implementation)
- [ ] Wenn sich Anforderungen während der Umsetzung geändert haben: Spec/Plan/Tasks wurden aktualisiert

## Implementation
- [ ] Implementierung entspricht der Spec
- [ ] Edge cases / Fehlerfälle berücksichtigt
- [ ] Keine unbeabsichtigten Änderungen außerhalb des Scopes

## Tests
- [ ] Tests ergänzt/aktualisiert (Pest/PHPUnit)
- [ ] Relevante Tests lokal ausgeführt (`./vendor/bin/sail artisan test` oder `php artisan test`)

## Migration / Config / Ops (falls relevant)
- [ ] Migration(en) enthalten und getestet
- [ ] Rollback bedacht (rückwärts kompatibel, sichere Migration)
- [ ] Neue Env Vars dokumentiert (`.env.example` / Doku)
- [ ] Queue/cron/storage Auswirkungen geprüft

## UI (Filament/Livewire) (falls relevant)
- [ ] UI-Flows geprüft
- [ ] Screenshots/Notizen hinzugefügt

## Notes
<!-- Links, Screenshots, Follow-ups, offene Punkte -->

Co-authored-by: Ahmed Darrazi <ahmed.darrazi@live.de>
Reviewed-on: #117
2026-02-15 22:27:55 +00:00

124 lines
4.5 KiB
PHP

<?php
use App\Models\BackupItem;
use App\Models\BackupSchedule;
use App\Models\BackupSet;
use App\Models\EntraGroup;
use App\Models\EntraRoleDefinition;
use App\Models\Finding;
use App\Models\InventoryItem;
use App\Models\InventoryLink;
use App\Models\Policy;
use App\Models\PolicyVersion;
use App\Models\RestoreRun;
use App\Models\Tenant;
use App\Models\TenantPermission;
use App\Models\Workspace;
use App\Support\WorkspaceIsolation\WorkspaceIsolationViolation;
use Illuminate\Foundation\Testing\RefreshDatabase;
uses(RefreshDatabase::class);
it('enforces tenant workspace binding for tenant-owned models even when events are disabled', function (): void {
$workspaceA = Workspace::factory()->create();
$workspaceB = Workspace::factory()->create();
$tenant = Tenant::factory()->create([
'workspace_id' => $workspaceA->getKey(),
]);
$policy = Policy::factory()->create([
'tenant_id' => $tenant->getKey(),
'workspace_id' => $workspaceA->getKey(),
]);
$backupSet = BackupSet::factory()->create([
'tenant_id' => $tenant->getKey(),
'workspace_id' => $workspaceA->getKey(),
]);
$cases = [
'policies' => fn (int $workspaceId) => Policy::factory()->make([
'tenant_id' => $tenant->getKey(),
'workspace_id' => $workspaceId,
]),
'policy_versions' => fn (int $workspaceId) => PolicyVersion::factory()->make([
'tenant_id' => $tenant->getKey(),
'workspace_id' => $workspaceId,
'policy_id' => $policy->getKey(),
]),
'backup_sets' => fn (int $workspaceId) => BackupSet::factory()->make([
'tenant_id' => $tenant->getKey(),
'workspace_id' => $workspaceId,
]),
'backup_items' => fn (int $workspaceId) => BackupItem::factory()->make([
'tenant_id' => $tenant->getKey(),
'workspace_id' => $workspaceId,
'backup_set_id' => $backupSet->getKey(),
'policy_id' => $policy->getKey(),
]),
'restore_runs' => fn (int $workspaceId) => RestoreRun::factory()->make([
'tenant_id' => $tenant->getKey(),
'workspace_id' => $workspaceId,
'backup_set_id' => $backupSet->getKey(),
]),
'backup_schedules' => fn (int $workspaceId) => BackupSchedule::make([
'tenant_id' => $tenant->getKey(),
'workspace_id' => $workspaceId,
'name' => 'Weekly backup',
'is_enabled' => true,
'timezone' => 'UTC',
'frequency' => 'daily',
'time_of_day' => '00:00:00',
'days_of_week' => null,
'policy_types' => ['settingsCatalogPolicy'],
'include_foundations' => true,
'retention_keep_last' => 30,
]),
'inventory_items' => fn (int $workspaceId) => InventoryItem::factory()->make([
'tenant_id' => $tenant->getKey(),
'workspace_id' => $workspaceId,
]),
'inventory_links' => fn (int $workspaceId) => InventoryLink::factory()->make([
'tenant_id' => $tenant->getKey(),
'workspace_id' => $workspaceId,
]),
'entra_groups' => fn (int $workspaceId) => EntraGroup::factory()->make([
'tenant_id' => $tenant->getKey(),
'workspace_id' => $workspaceId,
]),
'findings' => fn (int $workspaceId) => Finding::factory()->make([
'tenant_id' => $tenant->getKey(),
'workspace_id' => $workspaceId,
]),
'entra_role_definitions' => fn (int $workspaceId) => EntraRoleDefinition::factory()->make([
'tenant_id' => $tenant->getKey(),
'workspace_id' => $workspaceId,
]),
'tenant_permissions' => fn (int $workspaceId) => TenantPermission::make([
'tenant_id' => $tenant->getKey(),
'workspace_id' => $workspaceId,
'permission_key' => 'test.permission.'.uniqid(),
'status' => 'missing',
]),
];
foreach ($cases as $table => $makeModel) {
$model = $makeModel((int) $workspaceA->getKey());
($model::class)::withoutEvents(function () use ($model): void {
$model->save();
});
$this->assertDatabaseHas($table, [
'tenant_id' => $tenant->getKey(),
'workspace_id' => $workspaceA->getKey(),
]);
$mismatched = $makeModel((int) $workspaceB->getKey());
expect(fn () => $mismatched->save())
->toThrow(WorkspaceIsolationViolation::class);
}
});