102 خطوط
6.4 KiB
PHP
102 خطوط
6.4 KiB
PHP
<?php
|
|
|
|
namespace Tests\Feature\Organizations;
|
|
|
|
use App\Models\User;
|
|
use App\Modules\Identity\Domain\Enums\UserRole;
|
|
use App\Modules\Organizations\Domain\Organization;
|
|
use App\Modules\Subscriptions\Domain\Subscription;
|
|
use App\Modules\System\Domain\DeploymentCapabilities;
|
|
use Illuminate\Foundation\Testing\RefreshDatabase;
|
|
use Illuminate\Support\Facades\DB;
|
|
use Laravel\Sanctum\Sanctum;
|
|
use Tests\TestCase;
|
|
|
|
class OrganizationAdministrationTest extends TestCase
|
|
{
|
|
use RefreshDatabase;
|
|
|
|
public function test_super_admin_can_manage_organizations_without_learning_analytics(): void
|
|
{
|
|
Sanctum::actingAs(User::factory()->create(['role' => UserRole::SuperAdmin]));
|
|
|
|
$id = $this->postJson('/api/v1/organizations', [
|
|
'name' => 'Acme Learning', 'slug' => 'acme-learning', 'defaultLocale' => 'fa', 'timezone' => 'Asia/Tehran',
|
|
])->assertCreated()->assertJsonMissingPath('data.learningAnalytics')->json('data.id');
|
|
|
|
$this->getJson('/api/v1/organizations')->assertOk()->assertJsonPath('meta.total', 2)->assertJsonFragment(['slug' => 'acme-learning']);
|
|
$this->patchJson("/api/v1/organizations/{$id}", ['status' => 'disabled'])->assertOk()->assertJsonPath('data.status', 'disabled');
|
|
}
|
|
|
|
public function test_tenant_roles_cannot_access_platform_organization_administration(): void
|
|
{
|
|
Sanctum::actingAs(User::factory()->create(['role' => UserRole::CourseDesigner]));
|
|
$this->getJson('/api/v1/organizations')->assertForbidden();
|
|
}
|
|
|
|
public function test_on_premise_mode_hides_multi_organization_endpoints(): void
|
|
{
|
|
config()->set('deployment.mode', 'on_premise');
|
|
$this->app->forgetInstance(DeploymentCapabilities::class);
|
|
Sanctum::actingAs(User::factory()->create(['role' => UserRole::SuperAdmin]));
|
|
$this->getJson('/api/v1/organizations')->assertNotFound();
|
|
}
|
|
|
|
public function test_organization_slug_is_unique(): void
|
|
{
|
|
Organization::factory()->create(['slug' => 'taken']);
|
|
Sanctum::actingAs(User::factory()->create(['role' => UserRole::SuperAdmin]));
|
|
$this->postJson('/api/v1/organizations', ['name' => 'Duplicate', 'slug' => 'taken', 'defaultLocale' => 'en', 'timezone' => 'UTC'])
|
|
->assertUnprocessable()->assertJsonValidationErrors('slug');
|
|
}
|
|
|
|
public function test_platform_list_supports_search_filters_sorting_and_complete_pagination_metadata(): void
|
|
{
|
|
$alpha = Organization::factory()->create(['name' => 'Alpha Learning', 'slug' => 'alpha', 'status' => 'active']);
|
|
Organization::factory()->create(['name' => 'Beta Academy', 'slug' => 'beta', 'status' => 'disabled']);
|
|
User::factory()->count(2)->for($alpha)->create();
|
|
Subscription::query()->create(['organization_id' => $alpha->getKey(), 'plan_key' => 'enterprise', 'status' => 'active', 'starts_at' => now()->subDay(), 'expires_at' => now()->addMonth(), 'seat_limit' => 50, 'storage_quota_bytes' => 1000, 'ai_credit_quota' => 500]);
|
|
DB::table('audit_logs')->insert(['id' => (string) str()->ulid(), 'organization_id' => $alpha->getKey(), 'actor_id' => null, 'action' => 'organization.activity', 'entity_type' => 'organization', 'entity_id' => $alpha->getKey(), 'metadata' => json_encode(['schemaVersion' => 1]), 'ip_address' => '127.0.0.1', 'created_at' => now()]);
|
|
Sanctum::actingAs(User::factory()->create(['organization_id' => null, 'role' => UserRole::SuperAdmin]));
|
|
|
|
$this->getJson('/api/v1/organizations?search=alpha&status=active&plan=enterprise&sort=users&direction=desc&perPage=1')
|
|
->assertOk()
|
|
->assertJsonPath('meta.currentPage', 1)
|
|
->assertJsonPath('meta.lastPage', 1)
|
|
->assertJsonPath('meta.perPage', 1)
|
|
->assertJsonPath('meta.total', 1)
|
|
->assertJsonPath('data.0.id', $alpha->getKey())
|
|
->assertJsonPath('data.0.planKey', 'enterprise')
|
|
->assertJsonPath('data.0.usersCount', 2)
|
|
->assertJsonPath('data.0.aiCreditsUsed', 0)
|
|
->assertJsonStructure(['data' => [['lastActivityAt', 'subscriptionExpiresAt', 'storageUsedBytes', 'storageQuotaBytes']]]);
|
|
}
|
|
|
|
public function test_organization_detail_is_platform_scoped_and_exposes_real_operational_metadata(): void
|
|
{
|
|
$organization = Organization::factory()->create(['name' => 'Detail Org']);
|
|
User::factory()->for($organization)->create(['name' => 'Member One']);
|
|
Subscription::query()->create(['organization_id' => $organization->getKey(), 'plan_key' => 'growth', 'status' => 'active', 'starts_at' => now()->subDay(), 'seat_limit' => 20, 'storage_quota_bytes' => 2048, 'storage_used_bytes' => 512, 'ai_credit_quota' => 100, 'ai_credits_used' => 12, 'enabled_features' => ['exports']]);
|
|
DB::table('audit_logs')->insert(['id' => (string) str()->ulid(), 'organization_id' => $organization->getKey(), 'actor_id' => null, 'action' => 'organization.seeded', 'entity_type' => 'organization', 'entity_id' => $organization->getKey(), 'metadata' => json_encode(['schemaVersion' => 1]), 'ip_address' => '127.0.0.1', 'created_at' => now()]);
|
|
Sanctum::actingAs(User::factory()->create(['organization_id' => null, 'role' => UserRole::SuperAdmin]));
|
|
|
|
$this->getJson('/api/v1/organizations/'.$organization->getKey())
|
|
->assertOk()
|
|
->assertJsonPath('data.subscription.planKey', 'growth')
|
|
->assertJsonPath('data.subscription.aiCreditsUsed', 12)
|
|
->assertJsonPath('data.recentUsers.0.name', 'Member One')
|
|
->assertJsonPath('data.recentActivity.0.action', 'organization.seeded')
|
|
->assertJsonStructure(['data' => ['storageUsedBytes', 'aiJobsCount', 'failedAiJobsCount', 'recentUsers', 'recentActivity']]);
|
|
}
|
|
|
|
public function test_organization_lifecycle_changes_are_audited(): void
|
|
{
|
|
Sanctum::actingAs(User::factory()->create(['organization_id' => null, 'role' => UserRole::SuperAdmin]));
|
|
$id = $this->postJson('/api/v1/organizations', ['name' => 'Audited Org', 'slug' => 'audited-org', 'defaultLocale' => 'fa', 'timezone' => 'Asia/Tehran'])->assertCreated()->json('data.id');
|
|
$this->patchJson('/api/v1/organizations/'.$id, ['status' => 'disabled'])->assertOk();
|
|
|
|
$this->assertDatabaseHas('audit_logs', ['organization_id' => $id, 'action' => 'organization.created', 'entity_id' => $id]);
|
|
$this->assertDatabaseHas('audit_logs', ['organization_id' => $id, 'action' => 'organization.updated', 'entity_id' => $id]);
|
|
}
|
|
}
|