<?php namespace Tests\Integration\Services; use App\Exceptions\FailedToActivateLicenseException; use App\Http\Integrations\LemonSqueezy\Requests\ActivateLicenseRequest; use App\Http\Integrations\LemonSqueezy\Requests\DeactivateLicenseRequest; use App\Http\Integrations\LemonSqueezy\Requests\ValidateLicenseRequest; use App\Models\License; use App\Services\LicenseService; use App\Values\LicenseStatus; use Illuminate\Http\Response; use Illuminate\Support\Facades\Cache; use Illuminate\Support\Facades\File; use Saloon\Http\Faking\MockResponse; use Saloon\Laravel\Facades\Saloon; use Tests\TestCase; use function Tests\test_path; class LicenseServiceTest extends TestCase { private LicenseService $service; public function setUp(): void { parent::setUp(); $this->service = app(LicenseService::class); } public function testActivateLicense(): void { config(['lemonsqueezy.store_id' => 42]); $key = '38b1460a-5104-4067-a91d-77b872934d51'; Saloon::fake([ ActivateLicenseRequest::class => MockResponse::make( body: File::get(test_path('blobs/lemonsqueezy/license-activated-successful.json')), ), ]); $license = $this->service->activate($key); self::assertSame($key, $license->key); self::assertNotNull($license->instance); self::assertSame('Luke Skywalker', $license->meta->customerName); self::assertSame('luke@skywalker.com', $license->meta->customerEmail); /** @var LicenseStatus $cachedLicenseStatus */ $cachedLicenseStatus = Cache::get('license_status'); self::assertSame($license->key, $cachedLicenseStatus->license->key); self::assertTrue($cachedLicenseStatus->isValid()); Saloon::assertSent(static function (ActivateLicenseRequest $request) use ($key): bool { self::assertSame([ 'license_key' => $key, 'instance_name' => 'Koel Plus', ], $request->body()->all()); return true; }); } public function testActivateLicenseFailsBecauseOfIncorrectStoreId(): void { $this->expectException(FailedToActivateLicenseException::class); $this->expectExceptionMessage('This license key is not from Koel’s official store.'); config(['lemonsqueezy.store_id' => 43]); $key = '38b1460a-5104-4067-a91d-77b872934d51'; Saloon::fake([ ActivateLicenseRequest::class => MockResponse::make( body: File::get(test_path('blobs/lemonsqueezy/license-activated-successful.json')), ), ]); $this->service->activate($key); } public function testActivateLicenseFailsForInvalidLicenseKey(): void { $this->expectException(FailedToActivateLicenseException::class); $this->expectExceptionMessage('license_key not found'); Saloon::fake([ ActivateLicenseRequest::class => MockResponse::make( body: File::get(test_path('blobs/lemonsqueezy/license-invalid.json')), status: Response::HTTP_NOT_FOUND, ), ]); $this->service->activate('invalid-key'); } public function testDeactivateLicense(): void { /** @var License $license */ $license = License::factory()->create(); Saloon::fake([ DeactivateLicenseRequest::class => MockResponse::make( body: File::get(test_path('blobs/lemonsqueezy/license-deactivated-successful.json')), status: Response::HTTP_NOT_FOUND, ), ]); $this->service->deactivate($license); self::assertModelMissing($license); self::assertFalse(Cache::has('license_status')); Saloon::assertSent(static function (DeactivateLicenseRequest $request) use ($license): bool { self::assertSame([ 'license_key' => $license->key, 'instance_id' => $license->instance->id, ], $request->body()->all()); return true; }); } public function testDeactivateLicenseHandlesLeftoverRecords(): void { /** @var License $license */ $license = License::factory()->create(); Saloon::fake([DeactivateLicenseRequest::class => MockResponse::make(status: Response::HTTP_NOT_FOUND)]); $this->service->deactivate($license); self::assertModelMissing($license); } public function testDeactivateLicenseFails(): void { $this->expectExceptionMessage('Unprocessable Entity (422) Response: Something went horrible wrong'); /** @var License $license */ $license = License::factory()->create(); Saloon::fake([ DeactivateLicenseRequest::class => MockResponse::make( body: 'Something went horrible wrong', status: Response::HTTP_UNPROCESSABLE_ENTITY ), ]); $this->service->deactivate($license); } public function testGetLicenseStatusFromCache(): void { Saloon::fake([]); /** @var License $license */ $license = License::factory()->create(); Cache::put('license_status', LicenseStatus::valid($license)); self::assertTrue($this->service->getStatus()->license->is($license)); self::assertTrue($this->service->getStatus()->isValid()); Saloon::assertNothingSent(); } public function testGetLicenseStatusWithNoLicenses(): void { Saloon::fake([]); License::query()->delete(); self::assertTrue($this->service->getStatus()->hasNoLicense()); Saloon::assertNothingSent(); } public function testGetLicenseStatusValidatesWithApi(): void { /** @var License $license */ $license = License::factory()->create(); self::assertFalse(Cache::has('license_status')); Saloon::fake([ ValidateLicenseRequest::class => MockResponse::make( body: File::get(test_path('blobs/lemonsqueezy/license-validated-successful.json')), ), ]); self::assertTrue($this->service->getStatus()->isValid()); self::assertTrue(Cache::has('license_status')); Saloon::assertSent(static function (ValidateLicenseRequest $request) use ($license): bool { self::assertSame([ 'license_key' => $license->key, 'instance_id' => $license->instance->id, ], $request->body()->all()); return true; }); } public function testGetLicenseStatusValidatesWithApiWithInvalidLicense(): void { License::factory()->create(); self::assertFalse(Cache::has('license_status')); Saloon::fake([ValidateLicenseRequest::class => MockResponse::make(status: Response::HTTP_NOT_FOUND)]); self::assertFalse($this->service->getStatus()->isValid()); self::assertTrue(Cache::has('license_status')); } }