170 خطوط
6.7 KiB
PHP
170 خطوط
6.7 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\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');
|
|
}
|
|
}
|