create(); $package = Package::factory()->endcustomer()->create([ 'features' => ['ai_styling'], ]); EventPackage::query()->create([ 'event_id' => $event->id, 'package_id' => $package->id, 'purchased_price' => $package->price, 'purchased_at' => now(), 'gallery_expires_at' => now()->addDays(14), ]); $style = AiStyle::query()->create([ 'key' => 'ledger-style-package', 'name' => 'Ledger Package', 'provider' => 'runware', 'provider_model' => 'runware-default', 'is_active' => true, ]); $photo = Photo::factory()->for($event)->create([ 'tenant_id' => $event->tenant_id, 'status' => 'approved', ]); $request = AiEditRequest::query()->create([ 'tenant_id' => $event->tenant_id, 'event_id' => $event->id, 'photo_id' => $photo->id, 'style_id' => $style->id, 'provider' => 'runware', 'provider_model' => 'runware-default', 'status' => AiEditRequest::STATUS_SUCCEEDED, 'safety_state' => 'passed', 'prompt' => 'Package context.', 'idempotency_key' => 'ledger-package-context-1', 'queued_at' => now(), 'completed_at' => now(), ]); $ledger = app(AiUsageLedgerService::class)->recordDebitForRequest($request, 0.01234); $this->assertSame('package_included', $ledger->package_context); $this->assertSame('0.01234', $ledger->amount_usd); $this->assertSame('runware', $ledger->metadata['provider'] ?? null); } public function test_it_records_addon_context_and_is_idempotent_per_request(): void { $event = Event::factory()->create(); $package = Package::factory()->endcustomer()->create([ 'features' => ['basic_uploads'], ]); $eventPackage = EventPackage::query()->create([ 'event_id' => $event->id, 'package_id' => $package->id, 'purchased_price' => $package->price, 'purchased_at' => now(), 'gallery_expires_at' => now()->addDays(14), ]); EventPackageAddon::query()->create([ 'event_package_id' => $eventPackage->id, 'event_id' => $event->id, 'tenant_id' => $event->tenant_id, 'addon_key' => 'ai_styling_unlock', 'quantity' => 1, 'status' => 'completed', 'purchased_at' => now(), ]); $style = AiStyle::query()->create([ 'key' => 'ledger-style-addon', 'name' => 'Ledger Addon', 'provider' => 'runware', 'provider_model' => 'runware-default', 'is_active' => true, ]); $photo = Photo::factory()->for($event)->create([ 'tenant_id' => $event->tenant_id, 'status' => 'approved', ]); $request = AiEditRequest::query()->create([ 'tenant_id' => $event->tenant_id, 'event_id' => $event->id, 'photo_id' => $photo->id, 'style_id' => $style->id, 'provider' => 'runware', 'provider_model' => 'runware-default', 'status' => AiEditRequest::STATUS_SUCCEEDED, 'safety_state' => 'passed', 'prompt' => 'Addon context.', 'idempotency_key' => 'ledger-addon-context-1', 'queued_at' => now(), 'completed_at' => now(), ]); $service = app(AiUsageLedgerService::class); $first = $service->recordDebitForRequest($request, 0.01); $second = $service->recordDebitForRequest($request, 0.99); $this->assertSame($first->id, $second->id); $this->assertSame('addon_unlock', $first->package_context); $this->assertSame(1, $request->usageLedgers()->count()); } }