Files
ladill-events/tests/Feature/EventMailCalendarSyncTest.php
T
isaaccladandCursor 9cdfc331b0 Sync Ladill Events to the Mail utility calendar.
Events upsert calendar entries by stable external_ref keys, prefer the
owner's linked mailbox, and Meet skips mail sync for events-sourced rooms
so linked virtual sessions appear only once on the calendar.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-07-04 11:48:37 +00:00

127 lines
4.5 KiB
PHP

<?php
namespace Tests\Feature;
use App\Models\QrCode;
use App\Models\User;
use App\Services\Events\EventMailCalendarSyncService;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Support\Facades\Http;
use Tests\TestCase;
class EventMailCalendarSyncTest extends TestCase
{
use RefreshDatabase;
protected function setUp(): void
{
parent::setUp();
config([
'mail_calendar.api_url' => 'https://mail.ladill.com/api/service/v1',
'mail_calendar.api_key' => 'test-events-mail-key',
'identity.api_url' => 'https://ladill.com/api',
'identity.api_key' => 'test-identity-key',
]);
Http::fake([
'https://ladill.com/api/identity/mailbox-link*' => Http::response(['data' => []]),
'https://mail.ladill.com/*' => Http::response(['data' => ['id' => 55]], 201),
]);
}
public function test_in_person_event_syncs_to_mail_calendar(): void
{
$owner = User::factory()->create(['public_id' => 'usr-cal-1', 'email' => 'host@example.com']);
$event = $this->makeEvent($owner, [
'format' => 'in_person',
'name' => 'Town hall',
'starts_at' => now()->addWeek()->toDateString(),
'ends_at' => now()->addWeek()->addHours(3)->toDateString(),
]);
app(EventMailCalendarSyncService::class)->sync($event, $owner);
Http::assertSent(function ($request) use ($event) {
return str_contains($request->url(), '/calendar/events')
&& $request['mailbox_email'] === 'host@example.com'
&& $request['external_ref'] === 'events:'.$event->id
&& $request['title'] === 'Town hall';
});
$this->assertSame(
['events:'.$event->id => 55],
data_get($event->fresh()->payload, 'calendar_sync.items'),
);
}
public function test_virtual_session_syncs_one_entry_with_meet_join_url(): void
{
$owner = User::factory()->create(['public_id' => 'usr-cal-3', 'email' => 'host@example.com']);
$event = $this->makeEvent($owner, [
'format' => 'virtual',
'name' => 'Product launch',
'starts_at' => now()->addWeek()->toDateString(),
'virtual_sessions' => [[
'id' => 'vs-main',
'title' => 'Main stage',
'scheduled_at' => now()->addWeek()->toIso8601String(),
'duration_minutes' => 90,
'join_url' => 'https://meet.ladill.com/join/demo',
]],
]);
app(EventMailCalendarSyncService::class)->sync($event, $owner);
Http::assertSent(function ($request) use ($event) {
return str_contains($request->url(), '/calendar/events')
&& $request['external_ref'] === 'events:'.$event->id.':session:vs-main'
&& $request['join_url'] === 'https://meet.ladill.com/join/demo';
});
$this->assertSame(1, collect(Http::recorded())->filter(
fn (array $pair) => str_contains($pair[0]->url(), '/calendar/events'),
)->count());
}
public function test_uses_linked_mailbox_when_available(): void
{
Http::fake([
'https://mail.ladill.com/*' => Http::response(['data' => ['id' => 77]], 201),
]);
$this->mock(\App\Services\Identity\IdentityClient::class, function ($mock) {
$mock->shouldReceive('mailboxLinkStatus')
->once()
->andReturn(['linked_mailbox' => 'owner@customdomain.com']);
});
$owner = User::factory()->create(['public_id' => 'usr-cal-2', 'email' => 'sso@ladill.com']);
$event = $this->makeEvent($owner, [
'format' => 'in_person',
'name' => 'Gala',
'starts_at' => now()->addWeek()->toDateString(),
]);
app(EventMailCalendarSyncService::class)->sync($event, $owner);
Http::assertSent(function ($request) {
return str_contains($request->url(), '/calendar/events')
&& ($request['mailbox_email'] ?? '') === 'owner@customdomain.com';
});
}
/** @param array<string, mixed> $content */
private function makeEvent(User $owner, array $content): QrCode
{
return QrCode::create([
'user_id' => $owner->id,
'short_code' => 'evt-cal-'.uniqid(),
'type' => QrCode::TYPE_EVENT,
'label' => (string) ($content['name'] ?? 'Event'),
'payload' => ['content' => $content, 'style' => []],
'is_active' => true,
]);
}
}