PM_Console/backend/tests/Feature/WorkspaceFeaturesTest.php

201 خطوط
7.9 KiB
PHP

<?php
namespace Tests\Feature;
use App\Models\Meeting;
use App\Models\MeetingType;
use App\Models\Notification;
use App\Models\Project;
use App\Models\Setting;
use App\Models\Sprint;
use App\Models\Task;
use App\Models\User;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Tests\TestCase;
class WorkspaceFeaturesTest extends TestCase
{
use RefreshDatabase;
private function authenticatedAdmin(): array
{
$user = User::factory()->create(['status' => 'active']);
$this->grantAdminRole($user);
return [$user, $user->createToken('workspace-test')->plainTextToken];
}
private function project(User $user): Project
{
return Project::create([
'title' => 'پروژه تست Workspace',
'project_manager_id' => $user->id,
'created_by' => $user->id,
'start_date' => now()->startOfMonth()->toDateString(),
'end_date' => now()->endOfMonth()->toDateString(),
'status' => 'in_progress',
]);
}
public function test_calendar_aggregates_real_project_sprint_task_and_meeting_events(): void
{
[$user, $token] = $this->authenticatedAdmin();
$project = $this->project($user);
$sprint = Sprint::create([
'title' => 'Sprint تقویم',
'project_id' => $project->id,
'start_date' => now()->startOfWeek()->toDateString(),
'end_date' => now()->endOfWeek()->toDateString(),
'status' => 'active',
'created_by' => $user->id,
]);
Task::create([
'title' => 'تسک سررسیددار',
'project_id' => $project->id,
'assignee_id' => $user->id,
'reporter_id' => $user->id,
'created_by' => $user->id,
'due_date' => now()->toDateString(),
'status' => 'todo',
]);
Meeting::create([
'title' => 'جلسه تقویم',
'project_id' => $project->id,
'sprint_id' => $sprint->id,
'meeting_type' => 'daily_standup',
'status' => 'scheduled',
'date' => now()->toDateString(),
'start_time' => '09:30',
'created_by' => $user->id,
]);
$response = $this->withToken($token)->getJson('/api/calendar/events?from='.now()->startOfMonth()->toDateString().'&to='.now()->endOfMonth()->toDateString());
$response->assertOk()->assertJsonPath('success', true);
$types = collect($response->json('data'))->pluck('event_type');
$this->assertTrue($types->contains('meeting'));
$this->assertTrue($types->contains('task'));
$this->assertTrue($types->contains('sprint'));
$this->assertTrue($types->contains('project'));
}
public function test_notification_can_be_previewed_archived_restored_and_soft_deleted(): void
{
[$user, $token] = $this->authenticatedAdmin();
$notification = Notification::create([
'user_id' => $user->id,
'type' => 'system',
'title' => 'اعلان تست',
'body' => 'جزئیات اعلان',
]);
$this->withToken($token)
->getJson("/api/notifications/{$notification->id}/preview")
->assertOk()
->assertJsonPath('data.preview.body', 'جزئیات اعلان');
$this->withToken($token)
->patchJson("/api/notifications/{$notification->id}/archive")
->assertOk()
->assertJsonPath('data.archived_at', fn ($value) => filled($value));
$this->withToken($token)
->patchJson("/api/notifications/{$notification->id}/restore")
->assertOk()
->assertJsonPath('data.archived_at', null);
$this->withToken($token)->deleteJson("/api/notifications/{$notification->id}")->assertOk();
$this->assertSoftDeleted('notifications', ['id' => $notification->id]);
}
public function test_sprint_creation_can_create_configured_meetings_and_workspace(): void
{
[$user, $token] = $this->authenticatedAdmin();
$project = $this->project($user);
$this->assertGreaterThan(0, MeetingType::count());
$response = $this->withToken($token)->postJson('/api/sprints', [
'title' => 'Sprint یکپارچه',
'project_id' => $project->id,
'start_date' => now()->addDay()->toDateString(),
'end_date' => now()->addDays(14)->toDateString(),
'member_ids' => [$user->id],
'meeting_setup' => [[
'type_key' => 'sprint_planning',
'date' => now()->addDay()->toDateString(),
'start_time' => '10:00',
]],
])->assertCreated();
$sprintId = $response->json('data.id');
$this->assertDatabaseHas('meetings', ['sprint_id' => $sprintId, 'meeting_type' => 'sprint_planning']);
$this->withToken($token)
->getJson("/api/sprints/{$sprintId}/workspace")
->assertOk()
->assertJsonPath('data.sprint.id', $sprintId)
->assertJsonCount(1, 'data.meetings');
}
public function test_meeting_lifecycle_and_structured_outputs_are_available_in_workspace(): void
{
[$user, $token] = $this->authenticatedAdmin();
$project = $this->project($user);
$meeting = Meeting::create([
'title' => 'جلسه چرخه عمر',
'project_id' => $project->id,
'meeting_type' => 'adhoc',
'status' => 'scheduled',
'date' => now()->toDateString(),
'created_by' => $user->id,
]);
$this->withToken($token)->postJson("/api/meetings/{$meeting->id}/start")->assertOk()->assertJsonPath('data.status', 'in_progress');
$this->withToken($token)->postJson("/api/meetings/{$meeting->id}/decisions", ['title' => 'تصمیم قطعی'])->assertCreated();
$this->withToken($token)->postJson("/api/meetings/{$meeting->id}/structured-action-items", [
'title' => 'اقدام بعدی',
'owner_id' => $user->id,
'due_date' => now()->addDay()->toDateString(),
])->assertCreated();
$this->withToken($token)->postJson("/api/meetings/{$meeting->id}/blockers", ['title' => 'مانع تست'])->assertCreated();
$this->withToken($token)->postJson("/api/meetings/{$meeting->id}/complete", ['summary' => 'خلاصه نهایی'])->assertOk()->assertJsonPath('data.status', 'completed');
$this->withToken($token)
->getJson("/api/meetings/{$meeting->id}/workspace")
->assertOk()
->assertJsonCount(1, 'data.structured_decisions')
->assertJsonCount(1, 'data.structured_action_items')
->assertJsonCount(1, 'data.blockers');
Setting::updateOrCreate(
['key' => 'meeting_effectiveness_enabled'],
['value' => true, 'group' => 'meeting', 'type' => 'boolean'],
);
$review = [
'objective_clarity' => 5,
'time_management' => 4,
'participation' => 4,
'outcome_quality' => 5,
'overall_rating' => 5,
'comment' => 'جلسه مفید و نتیجه‌محور بود.',
];
$this->withToken($token)
->postJson("/api/meetings/{$meeting->id}/effectiveness", $review)
->assertCreated()
->assertJsonPath('data.overall_rating', 5);
$this->withToken($token)
->postJson("/api/meetings/{$meeting->id}/effectiveness", [...$review, 'overall_rating' => 4])
->assertOk()
->assertJsonPath('data.overall_rating', 4);
$this->withToken($token)
->getJson("/api/meetings/{$meeting->id}/effectiveness")
->assertOk()
->assertJsonPath('data.summary.count', 1)
->assertJsonPath('data.summary.averages.overall_rating', 4)
->assertJsonPath('data.can_review', true);
}
}