Files
ladill-meet/tests/Feature/MeetBillingTest.php
T
isaaccladandCursor 5c1affcca5
Deploy Ladill Meet / deploy (push) Successful in 46s
Bill all meet sessions per participant instead of per hour.
Meetings and webinars now share GHS 0.30 per peak participant, with usage UI and plan quotas updated to match.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-07-01 18:28:35 +00:00

166 lines
5.2 KiB
PHP

<?php
namespace Tests\Feature;
use App\Models\Organization;
use App\Models\Room;
use App\Models\Session;
use App\Services\Meet\MeetBillingService;
use App\Services\Meet\UsageMeteringService;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Support\Facades\Http;
use Tests\TestCase;
class MeetBillingTest extends TestCase
{
use RefreshDatabase;
protected function fakeAffordableBilling(): void
{
$base = rtrim((string) config('billing.api_url'), '/');
Http::fake([
$base.'/can-afford*' => Http::response(['affordable' => true]),
$base.'/debit' => Http::response(['ok' => true]),
]);
}
public function test_session_end_charges_per_participant(): void
{
$this->fakeAffordableBilling();
$organization = Organization::create([
'owner_ref' => 'owner-001',
'name' => 'Billing Org',
'slug' => 'billing-org',
'timezone' => 'UTC',
]);
$room = Room::create([
'owner_ref' => 'owner-001',
'organization_id' => $organization->id,
'host_user_ref' => 'owner-001',
'title' => 'Billing Room',
'type' => 'instant',
'status' => 'live',
'settings' => config('meet.default_settings'),
]);
$session = Session::create([
'owner_ref' => 'owner-001',
'room_id' => $room->id,
'media_room_name' => 'room-test',
'status' => 'ended',
'started_at' => now()->subHour(),
'ended_at' => now(),
'peak_participants' => 4,
]);
app(UsageMeteringService::class)->recordSessionUsage($session);
$this->assertDatabaseHas('meet_usage_records', [
'organization_id' => $organization->id,
'metric' => 'participant_count',
'quantity' => 4,
]);
Http::assertSent(fn ($request) => str_contains($request->url(), '/debit')
&& ($request->data()['source'] ?? '') === 'meet_participants');
}
public function test_webinar_session_end_charges_per_participant(): void
{
$this->fakeAffordableBilling();
$organization = Organization::create([
'owner_ref' => 'owner-webinar',
'name' => 'Webinar Org',
'slug' => 'webinar-org',
'timezone' => 'UTC',
]);
$room = Room::create([
'owner_ref' => 'owner-webinar',
'organization_id' => $organization->id,
'host_user_ref' => 'owner-webinar',
'title' => 'Webinar Room',
'type' => 'webinar',
'status' => 'ended',
'settings' => config('meet.default_settings'),
]);
$session = Session::create([
'owner_ref' => 'owner-webinar',
'room_id' => $room->id,
'media_room_name' => 'webinar-test',
'status' => 'ended',
'started_at' => now()->subHour(),
'ended_at' => now(),
'peak_participants' => 5,
]);
app(UsageMeteringService::class)->recordSessionUsage($session);
$this->assertDatabaseHas('meet_usage_records', [
'organization_id' => $organization->id,
'metric' => 'participant_count',
'quantity' => 5,
]);
Http::assertSent(fn ($request) => str_contains($request->url(), '/debit')
&& ($request->data()['source'] ?? '') === 'meet_participants');
}
public function test_storage_renewal_enters_grace_when_wallet_is_empty(): void
{
$base = rtrim((string) config('billing.api_url'), '/');
Http::fake([
$base.'/can-afford*' => Http::response(['affordable' => false]),
]);
$organization = Organization::create([
'owner_ref' => 'owner-002',
'name' => 'Grace Org',
'slug' => 'grace-org',
'timezone' => 'UTC',
]);
$room = Room::create([
'owner_ref' => 'owner-002',
'organization_id' => $organization->id,
'host_user_ref' => 'owner-002',
'title' => 'Grace Room',
'type' => 'instant',
'status' => 'ended',
'settings' => config('meet.default_settings'),
]);
$session = Session::create([
'owner_ref' => 'owner-002',
'room_id' => $room->id,
'media_room_name' => 'room-grace',
'status' => 'ended',
'started_at' => now()->subDay(),
'ended_at' => now()->subDay(),
]);
$file = \App\Models\SessionFile::create([
'owner_ref' => 'owner-002',
'room_id' => $room->id,
'session_id' => $session->id,
'uploaded_by_ref' => 'owner-002',
'uploaded_by_name' => 'Host',
'original_name' => 'notes.pdf',
'storage_path' => 'meet-files/test/notes.pdf',
'file_size' => 1073741824,
'storage_paid_until' => now()->subMinute(),
]);
$result = app(MeetBillingService::class)->processDueStorageRenewals();
$file->refresh();
$this->assertSame(1, $result['graced']);
$this->assertNotNull($file->storage_grace_ends_at);
}
}