Phases 0–18: core meetings, webinar, breakouts, team chat, live streaming, town hall, billing, and Ladill Mail calendar wiring. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -0,0 +1,343 @@
|
||||
<?php
|
||||
|
||||
namespace Tests\Feature;
|
||||
|
||||
use App\Http\Middleware\EnsurePlatformSession;
|
||||
use App\Models\Member;
|
||||
use App\Models\Organization;
|
||||
use App\Models\Recording;
|
||||
use App\Models\Room;
|
||||
use App\Models\Session;
|
||||
use App\Models\User;
|
||||
use Illuminate\Foundation\Testing\RefreshDatabase;
|
||||
use Tests\TestCase;
|
||||
|
||||
class MeetWebTest extends TestCase
|
||||
{
|
||||
use RefreshDatabase;
|
||||
|
||||
protected User $user;
|
||||
|
||||
protected Organization $organization;
|
||||
|
||||
protected function setUp(): void
|
||||
{
|
||||
parent::setUp();
|
||||
$this->withoutMiddleware(EnsurePlatformSession::class);
|
||||
|
||||
$this->user = User::create([
|
||||
'public_id' => 'test-user-001',
|
||||
'name' => 'Test User',
|
||||
'email' => 'test@example.com',
|
||||
]);
|
||||
|
||||
$this->organization = Organization::create([
|
||||
'owner_ref' => $this->user->public_id,
|
||||
'name' => 'Test Org',
|
||||
'slug' => 'test-org',
|
||||
'timezone' => 'UTC',
|
||||
'settings' => ['onboarded' => true],
|
||||
]);
|
||||
|
||||
Member::create([
|
||||
'owner_ref' => $this->user->public_id,
|
||||
'organization_id' => $this->organization->id,
|
||||
'user_ref' => $this->user->public_id,
|
||||
'role' => 'owner',
|
||||
]);
|
||||
}
|
||||
|
||||
public function test_health_endpoint(): void
|
||||
{
|
||||
$this->getJson('/api/health')
|
||||
->assertOk()
|
||||
->assertJson(['status' => 'ok', 'app' => 'meet']);
|
||||
}
|
||||
|
||||
public function test_guest_redirected_from_dashboard(): void
|
||||
{
|
||||
$this->get('/dashboard')->assertRedirect();
|
||||
}
|
||||
|
||||
public function test_onboarded_user_can_view_dashboard(): void
|
||||
{
|
||||
$this->actingAs($this->user)
|
||||
->get('/dashboard')
|
||||
->assertOk();
|
||||
}
|
||||
|
||||
public function test_user_can_create_instant_meeting(): void
|
||||
{
|
||||
$this->actingAs($this->user)
|
||||
->get('/meetings/instant')
|
||||
->assertRedirect();
|
||||
|
||||
$this->assertDatabaseHas('meet_rooms', [
|
||||
'host_user_ref' => $this->user->public_id,
|
||||
'type' => 'instant',
|
||||
]);
|
||||
}
|
||||
|
||||
public function test_room_has_join_url(): void
|
||||
{
|
||||
$room = Room::create([
|
||||
'owner_ref' => $this->user->public_id,
|
||||
'organization_id' => $this->organization->id,
|
||||
'host_user_ref' => $this->user->public_id,
|
||||
'title' => 'Test Meeting',
|
||||
'type' => 'instant',
|
||||
'status' => 'scheduled',
|
||||
'scheduled_at' => now(),
|
||||
'timezone' => 'UTC',
|
||||
'settings' => config('meet.default_settings'),
|
||||
]);
|
||||
|
||||
$this->assertStringContainsString($room->uuid, $room->joinUrl());
|
||||
}
|
||||
|
||||
public function test_user_can_view_recordings_index(): void
|
||||
{
|
||||
$room = $this->createRoom();
|
||||
$session = Session::create([
|
||||
'owner_ref' => $this->user->public_id,
|
||||
'room_id' => $room->id,
|
||||
'media_room_name' => 'room-'.$room->uuid,
|
||||
'status' => 'ended',
|
||||
'started_at' => now()->subHour(),
|
||||
'ended_at' => now(),
|
||||
]);
|
||||
Recording::create([
|
||||
'owner_ref' => $this->user->public_id,
|
||||
'session_id' => $session->id,
|
||||
'status' => 'ready',
|
||||
'storage_path' => 'recordings/test.mp4',
|
||||
]);
|
||||
|
||||
$this->actingAs($this->user)
|
||||
->get('/recordings')
|
||||
->assertOk()
|
||||
->assertSee('Recordings');
|
||||
}
|
||||
|
||||
public function test_admin_can_create_template(): void
|
||||
{
|
||||
$this->actingAs($this->user)
|
||||
->post('/templates', [
|
||||
'name' => 'Weekly standup',
|
||||
'duration_minutes' => 30,
|
||||
'waiting_room' => '1',
|
||||
])
|
||||
->assertRedirect(route('meet.templates.index'));
|
||||
|
||||
$this->assertDatabaseHas('meet_templates', [
|
||||
'name' => 'Weekly standup',
|
||||
'organization_id' => $this->organization->id,
|
||||
]);
|
||||
}
|
||||
|
||||
public function test_admin_can_update_security_policy(): void
|
||||
{
|
||||
$this->actingAs($this->user)
|
||||
->put('/settings/security', [
|
||||
'org_only' => '1',
|
||||
'authenticated_only' => '1',
|
||||
'allowed_domains' => 'example.com, ladill.com',
|
||||
'recording_host_only' => '1',
|
||||
'session_idle_minutes' => 90,
|
||||
])
|
||||
->assertRedirect(route('meet.settings.security'));
|
||||
|
||||
$this->organization->refresh();
|
||||
$this->assertTrue($this->organization->securitySetting('org_only'));
|
||||
$this->assertContains('example.com', $this->organization->securitySetting('allowed_domains'));
|
||||
}
|
||||
|
||||
public function test_user_can_access_personal_room(): void
|
||||
{
|
||||
$this->actingAs($this->user)
|
||||
->get('/meetings/personal')
|
||||
->assertRedirect();
|
||||
|
||||
$this->assertDatabaseHas('meet_rooms', [
|
||||
'host_user_ref' => $this->user->public_id,
|
||||
'type' => 'personal',
|
||||
]);
|
||||
}
|
||||
|
||||
public function test_admin_can_view_reports(): void
|
||||
{
|
||||
$this->actingAs($this->user)
|
||||
->get('/reports')
|
||||
->assertOk()
|
||||
->assertSee('Reports');
|
||||
}
|
||||
|
||||
public function test_service_api_can_create_room(): void
|
||||
{
|
||||
config(['meet.service_api_keys.care' => 'test-care-key']);
|
||||
|
||||
$this->postJson('/api/service/v1/rooms', [
|
||||
'owner_ref' => $this->user->public_id,
|
||||
'organization_id' => $this->organization->id,
|
||||
'host_user_ref' => $this->user->public_id,
|
||||
'title' => 'Care consultation',
|
||||
'source' => ['app' => 'care', 'entity_type' => 'appointment', 'entity_id' => 'appt-123'],
|
||||
], ['Authorization' => 'Bearer test-care-key'])
|
||||
->assertCreated()
|
||||
->assertJsonPath('room.title', 'Care consultation');
|
||||
|
||||
$this->assertDatabaseHas('meet_rooms', [
|
||||
'title' => 'Care consultation',
|
||||
'host_user_ref' => $this->user->public_id,
|
||||
]);
|
||||
}
|
||||
|
||||
public function test_invitation_rsvp_accepts(): void
|
||||
{
|
||||
$room = $this->createRoom();
|
||||
$invitation = \App\Models\Invitation::create([
|
||||
'owner_ref' => $this->user->public_id,
|
||||
'room_id' => $room->id,
|
||||
'email' => 'guest@example.com',
|
||||
'status' => 'pending',
|
||||
'rsvp_token' => 'test-rsvp-token',
|
||||
]);
|
||||
|
||||
$this->post('/invite/'.$invitation->uuid.'/rsvp?token=test-rsvp-token', [
|
||||
'status' => 'accepted',
|
||||
])->assertOk();
|
||||
|
||||
$this->assertDatabaseHas('meet_invitations', [
|
||||
'id' => $invitation->id,
|
||||
'status' => 'accepted',
|
||||
]);
|
||||
}
|
||||
|
||||
public function test_admin_can_add_webhook_endpoint(): void
|
||||
{
|
||||
$this->actingAs($this->user)
|
||||
->post('/settings/webhooks', [
|
||||
'url' => 'https://care.example.com/webhooks/meet',
|
||||
'secret' => 'whsec_test',
|
||||
'events' => ['meeting.started', 'meeting.ended'],
|
||||
])
|
||||
->assertRedirect(route('meet.settings.webhooks'));
|
||||
|
||||
$this->assertDatabaseHas('meet_webhook_endpoints', [
|
||||
'organization_id' => $this->organization->id,
|
||||
'url' => 'https://care.example.com/webhooks/meet',
|
||||
]);
|
||||
}
|
||||
|
||||
public function test_webinar_registration_flow(): void
|
||||
{
|
||||
$room = $this->createRoom([
|
||||
'type' => 'webinar',
|
||||
'settings' => array_merge(config('meet.default_settings'), ['webinar_auto_approve' => true]),
|
||||
]);
|
||||
|
||||
$this->post('/w/'.$room->uuid.'/register', [
|
||||
'email' => 'attendee@example.com',
|
||||
'display_name' => 'Attendee One',
|
||||
])->assertOk();
|
||||
|
||||
$this->assertDatabaseHas('meet_webinar_registrations', [
|
||||
'room_id' => $room->id,
|
||||
'email' => 'attendee@example.com',
|
||||
'status' => 'approved',
|
||||
]);
|
||||
}
|
||||
|
||||
public function test_host_can_create_webinar_room(): void
|
||||
{
|
||||
$this->actingAs($this->user)
|
||||
->post('/meetings', [
|
||||
'title' => 'Product launch',
|
||||
'type' => 'webinar',
|
||||
'scheduled_at' => now()->addDay()->format('Y-m-d\TH:i'),
|
||||
'duration_minutes' => 60,
|
||||
])
|
||||
->assertRedirect();
|
||||
|
||||
$this->assertDatabaseHas('meet_rooms', [
|
||||
'title' => 'Product launch',
|
||||
'type' => 'webinar',
|
||||
]);
|
||||
}
|
||||
|
||||
public function test_user_can_create_channel_and_post_message(): void
|
||||
{
|
||||
$this->actingAs($this->user)
|
||||
->post('/channels', [
|
||||
'name' => 'General',
|
||||
'visibility' => 'public',
|
||||
])
|
||||
->assertRedirect();
|
||||
|
||||
$channel = \App\Models\Channel::first();
|
||||
$this->assertNotNull($channel);
|
||||
|
||||
$this->actingAs($this->user)
|
||||
->post('/channels/'.$channel->uuid.'/messages', ['body' => 'Hello team'])
|
||||
->assertRedirect();
|
||||
|
||||
$this->assertDatabaseHas('meet_channel_messages', [
|
||||
'channel_id' => $channel->id,
|
||||
'body' => 'Hello team',
|
||||
]);
|
||||
}
|
||||
|
||||
public function test_admin_can_view_usage_dashboard(): void
|
||||
{
|
||||
$this->actingAs($this->user)
|
||||
->get('/admin/usage')
|
||||
->assertOk()
|
||||
->assertSee('Usage');
|
||||
}
|
||||
|
||||
public function test_mail_calendar_can_be_connected(): void
|
||||
{
|
||||
config(['meet.calendar.mail.api_key' => 'test-mail-key']);
|
||||
|
||||
$this->actingAs($this->user)
|
||||
->post('/settings/calendar/mail')
|
||||
->assertRedirect(route('meet.settings.calendar'));
|
||||
|
||||
$this->assertDatabaseHas('meet_calendar_connections', [
|
||||
'user_ref' => $this->user->public_id,
|
||||
'provider' => 'mail',
|
||||
]);
|
||||
}
|
||||
|
||||
public function test_host_can_create_town_hall_room(): void
|
||||
{
|
||||
$this->actingAs($this->user)
|
||||
->post('/meetings', [
|
||||
'title' => 'All hands',
|
||||
'type' => 'town_hall',
|
||||
'scheduled_at' => now()->addDay()->format('Y-m-d\TH:i'),
|
||||
'duration_minutes' => 90,
|
||||
])
|
||||
->assertRedirect();
|
||||
|
||||
$this->assertDatabaseHas('meet_rooms', [
|
||||
'title' => 'All hands',
|
||||
'type' => 'town_hall',
|
||||
]);
|
||||
}
|
||||
|
||||
protected function createRoom(array $overrides = []): Room
|
||||
{
|
||||
return Room::create(array_merge([
|
||||
'owner_ref' => $this->user->public_id,
|
||||
'organization_id' => $this->organization->id,
|
||||
'host_user_ref' => $this->user->public_id,
|
||||
'title' => 'Test Meeting',
|
||||
'type' => 'scheduled',
|
||||
'status' => 'scheduled',
|
||||
'scheduled_at' => now()->addDay(),
|
||||
'timezone' => 'UTC',
|
||||
'settings' => config('meet.default_settings'),
|
||||
], $overrides));
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user