getJson('/api/v1/support/tenants'); $response->assertStatus(401); } public function test_support_resources_allow_super_admin_tokens(): void { $user = User::factory()->create([ 'role' => 'super_admin', ]); Tenant::factory()->create(); Sanctum::actingAs($user, ['support-admin', 'support:read']); $response = $this->getJson('/api/v1/support/tenants'); $response->assertOk() ->assertJsonStructure(['data', 'meta']); $this->assertSupportListSchema($response->json()); } public function test_support_token_endpoint_issues_bearer_token_and_allows_api_access(): void { $user = User::factory()->create([ 'role' => 'super_admin', 'password' => Hash::make('secret-password'), ]); Tenant::factory()->create(); $response = $this->postJson('/api/v1/support/auth/token', [ 'login' => $user->email, 'password' => 'secret-password', ]); $response->assertOk() ->assertJsonStructure(['token', 'token_type', 'abilities', 'user']); $token = $response->json('token'); $this->assertIsString($token); $this->withHeader('Authorization', 'Bearer '.$token) ->getJson('/api/v1/support/tenants') ->assertOk() ->assertJsonStructure(['data', 'meta']); } public function test_support_requests_require_support_admin_ability(): void { $user = User::factory()->create([ 'role' => 'super_admin', ]); $token = $user->createToken('support-api', ['support:read'])->plainTextToken; $response = $this->withHeader('Authorization', 'Bearer '.$token) ->getJson('/api/v1/support/tenants'); $response->assertStatus(403) ->assertJsonPath('error.code', 'support_forbidden'); } public function test_support_write_requires_write_ability(): void { $user = User::factory()->create([ 'role' => 'super_admin', ]); $tenant = Tenant::factory()->create(); $token = $user->createToken('support-api', ['support-admin', 'support:read'])->plainTextToken; $response = $this->withHeader('Authorization', 'Bearer '.$token) ->patchJson('/api/v1/support/tenants/'.$tenant->id, [ 'data' => [ 'slug' => 'not-allowed', ], ]); $response->assertStatus(403) ->assertJsonPath('error.code', 'forbidden'); } public function test_support_read_only_resource_rejects_deletes(): void { $user = User::factory()->create([ 'role' => 'super_admin', ]); $eventType = EventType::factory()->create(); $token = $user->createToken('support-api', ['support-admin', 'support:write'])->plainTextToken; $response = $this->withHeader('Authorization', 'Bearer '.$token) ->deleteJson('/api/v1/support/event-types/'.$eventType->id); $response->assertStatus(403) ->assertJsonPath('error.code', 'support_mutation_not_allowed'); } public function test_expired_support_token_is_rejected(): void { $user = User::factory()->create([ 'role' => 'super_admin', ]); $token = $user->createToken('support-api', ['support-admin', 'support:read'], now()->subMinute())->plainTextToken; $response = $this->withHeader('Authorization', 'Bearer '.$token) ->getJson('/api/v1/support/tenants'); $response->assertStatus(401); } public function test_support_resource_update_rejects_invalid_fields(): void { $user = User::factory()->create([ 'role' => 'super_admin', ]); $tenant = Tenant::factory()->create(); Sanctum::actingAs($user, ['support-admin', 'support:write']); $response = $this->patchJson('/api/v1/support/tenants/'.$tenant->id, [ 'data' => [ 'name' => 'Unauthorized', ], ]); $response->assertStatus(422) ->assertJsonPath('error.code', 'support_invalid_fields'); } public function test_support_list_supports_search_and_sorting(): void { $user = User::factory()->create([ 'role' => 'super_admin', ]); Tenant::factory()->create([ 'name' => 'Alpha Tenant', ]); Tenant::factory()->create([ 'name' => 'Beta Tenant', ]); Sanctum::actingAs($user, ['support-admin', 'support:read']); $response = $this->getJson('/api/v1/support/tenants?search=Alpha&sort=name&direction=asc'); $response->assertOk() ->assertJsonStructure(['data', 'meta']); $payload = $response->json(); $this->assertSupportListSchema($payload); $this->assertNotEmpty($payload['data']); } public function test_support_data_export_create_sets_user_and_dispatches_job(): void { $user = User::factory()->create([ 'role' => 'super_admin', ]); $tenant = Tenant::factory()->create(); Bus::fake(); Sanctum::actingAs($user, ['support-admin', 'support:ops']); $response = $this->postJson('/api/v1/support/data-exports', [ 'data' => [ 'scope' => 'tenant', 'tenant_id' => $tenant->id, 'include_media' => true, ], ]); $response->assertCreated() ->assertJsonPath('data.status', 'pending') ->assertJsonPath('data.user_id', $user->id) ->assertJsonPath('data.event_id', null); Bus::assertDispatched(\App\Jobs\GenerateDataExport::class); } public function test_support_tenant_actions_require_actions_ability(): void { $user = User::factory()->create([ 'role' => 'super_admin', ]); $tenant = Tenant::factory()->create([ 'is_active' => false, ]); Sanctum::actingAs($user, ['support-admin', 'support:write']); $response = $this->postJson('/api/v1/support/tenants/'.$tenant->id.'/actions/activate'); $response->assertStatus(403) ->assertJsonPath('error.code', 'forbidden'); } public function test_support_tenant_actions_update_state(): void { $user = User::factory()->create([ 'role' => 'super_admin', ]); $tenant = Tenant::factory()->create([ 'is_active' => false, ]); Sanctum::actingAs($user, ['support-admin', 'support:actions']); $response = $this->postJson('/api/v1/support/tenants/'.$tenant->id.'/actions/activate'); $response->assertOk() ->assertJsonPath('ok', true); $tenant->refresh(); $this->assertTrue((bool) $tenant->is_active); } public function test_support_photo_reject_requires_moderation_notes(): void { $user = User::factory()->create([ 'role' => 'super_admin', ]); $photo = Photo::factory()->create(); Sanctum::actingAs($user, ['support-admin', 'support:write']); $response = $this->patchJson('/api/v1/support/photos/'.$photo->id, [ 'data' => [ 'status' => 'rejected', ], ]); $response->assertStatus(422) ->assertJsonValidationErrors(['moderation_notes']); } public function test_support_blog_post_create_requires_title_and_content(): void { $user = User::factory()->create([ 'role' => 'super_admin', ]); $category = BlogCategory::create([ 'slug' => 'news', 'name' => ['de' => 'News', 'en' => 'News'], 'is_visible' => true, ]); Sanctum::actingAs($user, ['support-admin', 'support:content']); $response = $this->postJson('/api/v1/support/blog-posts', [ 'data' => [ 'blog_category_id' => $category->id, 'slug' => 'missing-title', 'is_published' => false, ], ]); $response->assertStatus(422) ->assertJsonValidationErrors(['title', 'content']); } public function test_support_update_logs_audit_entry(): void { $user = User::factory()->create([ 'role' => 'super_admin', ]); $tenant = Tenant::factory()->create(); Sanctum::actingAs($user, ['support-admin', 'support:write']); $response = $this->patchJson('/api/v1/support/tenants/'.$tenant->id, [ 'data' => [ 'slug' => 'support-updated', ], ]); $response->assertOk(); $this->assertTrue(SuperAdminActionLog::query()->where('action', 'tenants.updated')->exists()); } public function test_support_show_returns_item_schema(): void { $user = User::factory()->create([ 'role' => 'super_admin', ]); $tenant = Tenant::factory()->create(); Sanctum::actingAs($user, ['support-admin', 'support:read']); $response = $this->getJson('/api/v1/support/tenants/'.$tenant->id); $response->assertOk(); $payload = $response->json(); $this->assertSupportItemSchema($payload); } public function test_support_guest_policy_settings_schema(): void { $user = User::factory()->create([ 'role' => 'super_admin', ]); Sanctum::actingAs($user, ['support-admin', 'support:settings']); $response = $this->getJson('/api/v1/support/settings/guest-policy'); $response->assertOk() ->assertJson(fn (AssertableJson $json) => $json ->has('data') ->whereType('data', 'array') ->has('data.join_token_failure_limit') ->has('data.join_token_access_limit') ->has('data.join_token_download_limit') ->has('data.join_token_ttl_hours') ); } /** * @param array $payload */ private function assertSupportListSchema(array $payload): void { $this->assertArrayHasKey('data', $payload); $this->assertIsArray($payload['data']); $this->assertArrayHasKey('meta', $payload); $this->assertIsArray($payload['meta']); $this->assertArrayHasKey('page', $payload['meta']); $this->assertArrayHasKey('per_page', $payload['meta']); $this->assertArrayHasKey('total', $payload['meta']); $this->assertArrayHasKey('last_page', $payload['meta']); } /** * @param array $payload */ private function assertSupportItemSchema(array $payload): void { $this->assertArrayHasKey('data', $payload); $this->assertNotNull($payload['data']); } }