New_Micro_Learning/backend/tests/Feature/Manager/ManagerWorkspaceTest.php

127 خطوط
8.6 KiB
PHP

<?php
namespace Tests\Feature\Manager;
use App\Models\User;
use App\Modules\Assessments\Domain\Assessment;
use App\Modules\Assessments\Domain\AssessmentAttempt;
use App\Modules\Assignments\Domain\Assignment;
use App\Modules\Courses\Domain\Course;
use App\Modules\Courses\Domain\CourseVersion;
use App\Modules\Courses\Domain\Enums\CourseVersionStatus;
use App\Modules\Identity\Domain\Enums\UserRole;
use App\Modules\Learner\Domain\LearningEvent;
use App\Modules\Teams\Domain\Team;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Support\Str;
use Laravel\Sanctum\Sanctum;
use Tests\TestCase;
class ManagerWorkspaceTest extends TestCase
{
use RefreshDatabase;
public function test_manager_workspace_only_aggregates_managed_team_data(): void
{
$manager = User::factory()->create(['role' => UserRole::Manager]);
$learner = User::factory()->create(['organization_id' => $manager->organization_id, 'role' => UserRole::Learner, 'name' => 'عضو تیم من']);
$otherManager = User::factory()->create(['organization_id' => $manager->organization_id, 'role' => UserRole::Manager]);
$outsider = User::factory()->create(['organization_id' => $manager->organization_id, 'role' => UserRole::Learner, 'name' => 'عضو تیم دیگر']);
$team = Team::query()->create(['organization_id' => $manager->organization_id, 'name' => 'عملیات', 'status' => 'active', 'created_by' => $manager->getKey()]);
$team->managers()->attach($manager);
$team->members()->attach($learner);
$otherTeam = Team::query()->create(['organization_id' => $manager->organization_id, 'name' => 'فروش', 'status' => 'active', 'created_by' => $otherManager->getKey()]);
$otherTeam->managers()->attach($otherManager);
$otherTeam->members()->attach($outsider);
[$version, $assessment] = $this->content($manager);
$assignment = Assignment::query()->create(['organization_id' => $manager->organization_id, 'assignable_type' => 'course', 'assignable_id' => $version->getKey(), 'target_type' => 'team', 'target_id' => $team->getKey(), 'status' => 'active', 'mandatory' => true, 'assigned_by' => $manager->getKey()]);
$assignment->users()->attach($learner, ['status' => 'in_progress', 'assigned_at' => now(), 'due_at' => now()->addDays(2), 'progress' => 25, 'created_at' => now(), 'updated_at' => now()]);
$otherAssignment = Assignment::query()->create(['organization_id' => $manager->organization_id, 'assignable_type' => 'course', 'assignable_id' => $version->getKey(), 'target_type' => 'team', 'target_id' => $otherTeam->getKey(), 'status' => 'active', 'mandatory' => true, 'assigned_by' => $otherManager->getKey()]);
$otherAssignment->users()->attach($outsider, ['status' => 'completed', 'assigned_at' => now(), 'completed_at' => now(), 'progress' => 100, 'created_at' => now(), 'updated_at' => now()]);
LearningEvent::query()->create(['organization_id' => $manager->organization_id, 'learner_id' => $learner->getKey(), 'client_event_id' => Str::uuid(), 'event_type' => 'lesson.started', 'assignment_id' => $assignment->getKey(), 'course_version_id' => $version->getKey(), 'payload' => [], 'occurred_at' => now(), 'received_at' => now()]);
LearningEvent::query()->create(['organization_id' => $manager->organization_id, 'learner_id' => $outsider->getKey(), 'client_event_id' => Str::uuid(), 'event_type' => 'course.completed', 'assignment_id' => $otherAssignment->getKey(), 'course_version_id' => $version->getKey(), 'payload' => [], 'occurred_at' => now()->addSecond(), 'received_at' => now()]);
AssessmentAttempt::query()->create(['organization_id' => $manager->organization_id, 'learner_id' => $learner->getKey(), 'course_version_id' => $version->getKey(), 'assessment_id' => $assessment->getKey(), 'attempt_number' => 1, 'status' => 'completed', 'score' => .8, 'started_at' => now()->subMinute(), 'completed_at' => now()]);
AssessmentAttempt::query()->create(['organization_id' => $manager->organization_id, 'learner_id' => $outsider->getKey(), 'course_version_id' => $version->getKey(), 'assessment_id' => $assessment->getKey(), 'attempt_number' => 1, 'status' => 'completed', 'score' => .95, 'started_at' => now()->subMinute(), 'completed_at' => now()]);
Sanctum::actingAs($manager);
$this->getJson('/api/v1/manager/workspace')->assertOk()
->assertJsonPath('data.overview.teamCount', 1)->assertJsonPath('data.overview.memberCount', 1)
->assertJsonPath('data.overview.activeLearners', 1)->assertJsonPath('data.overview.dueSoon', 1)
->assertJsonPath('data.overview.engagement', 100)->assertJsonPath('data.overview.assessment', 80)
->assertJsonPath('data.overview.atRisk', 1)->assertJsonPath('data.members.0.name', 'عضو تیم من')
->assertJsonPath('data.recentActivity.0.learner', 'عضو تیم من')
->assertJsonCount(1, 'data.assessments')
->assertJsonPath('data.assessments.0.learnerId', $learner->getKey())
->assertJsonMissing(['learner' => 'عضو تیم دیگر'])
->assertJsonMissing(['name' => 'عضو تیم دیگر']);
}
public function test_manager_endpoint_rejects_other_roles_and_manager_mutations_remain_forbidden(): void
{
$designer = User::factory()->create(['role' => UserRole::CourseDesigner]);
Sanctum::actingAs($designer);
$this->getJson('/api/v1/manager/workspace')->assertForbidden();
$manager = User::factory()->create(['organization_id' => $designer->organization_id, 'role' => UserRole::Manager]);
Sanctum::actingAs($manager);
$this->postJson('/api/v1/assignments', [])->assertForbidden();
$this->postJson('/api/v1/courses', [])->assertForbidden();
}
public function test_manager_can_view_only_learning_assigned_to_their_own_account(): void
{
$manager = User::factory()->create(['role' => UserRole::Manager]);
$teamMember = User::factory()->create(['organization_id' => $manager->organization_id, 'role' => UserRole::Learner]);
[$version] = $this->content($manager);
$managerAssignment = Assignment::query()->create([
'organization_id' => $manager->organization_id,
'assignable_type' => 'course',
'assignable_id' => $version->getKey(),
'target_type' => 'individual',
'target_id' => $manager->getKey(),
'status' => 'active',
'mandatory' => true,
'assigned_by' => $manager->getKey(),
]);
$managerAssignment->users()->attach($manager, [
'status' => 'assigned', 'assigned_at' => now(), 'progress' => 0,
'created_at' => now(), 'updated_at' => now(),
]);
$memberAssignment = Assignment::query()->create([
'organization_id' => $manager->organization_id,
'assignable_type' => 'course',
'assignable_id' => $version->getKey(),
'target_type' => 'individual',
'target_id' => $teamMember->getKey(),
'status' => 'active',
'mandatory' => true,
'assigned_by' => $manager->getKey(),
]);
$memberAssignment->users()->attach($teamMember, [
'status' => 'assigned', 'assigned_at' => now(), 'progress' => 0,
'created_at' => now(), 'updated_at' => now(),
]);
Sanctum::actingAs($manager);
$this->getJson('/api/v1/auth/me')
->assertOk()
->assertJsonFragment(['learning.personal.view']);
$this->getJson('/api/v1/learner/home')
->assertOk()
->assertJsonPath('data.summary.total', 1)
->assertJsonPath('data.assigned.0.id', $managerAssignment->getKey())
->assertJsonMissing(['id' => $memberAssignment->getKey()]);
$this->getJson('/api/v1/learner/assignments/'.$memberAssignment->getKey())->assertNotFound();
}
private function content(User $owner): array
{
$course = Course::query()->create(['organization_id' => $owner->organization_id, 'title' => 'ایمنی', 'slug' => 'manager-safety', 'status' => 'published', 'created_by' => $owner->getKey()]);
$version = CourseVersion::query()->create(['organization_id' => $owner->organization_id, 'course_id' => $course->getKey(), 'version_number' => 1, 'status' => CourseVersionStatus::Published, 'title' => 'ایمنی محیط کار', 'settings' => [], 'completion_rules' => [], 'published_at' => now()]);
$assessment = Assessment::query()->create(['organization_id' => $owner->organization_id, 'course_version_id' => $version->getKey(), 'title' => 'ارزیابی ایمنی', 'settings' => []]);
return [$version, $assessment];
}
}