151 lines
5.7 KiB
PHP
151 lines
5.7 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
use App\Models\EnhanceJob;
|
|
use App\Models\User;
|
|
use App\Services\Enhance\Processors\ExternalWorkerEnhanceProcessor;
|
|
use Illuminate\Http\Client\Request;
|
|
use Illuminate\Http\UploadedFile;
|
|
use Illuminate\Support\Facades\Http;
|
|
use Illuminate\Support\Facades\Storage;
|
|
|
|
beforeEach(function (): void {
|
|
config()->set('app.url', 'http://skinbase.test');
|
|
config()->set('enhance.disk', 'public');
|
|
config()->set('enhance.external_worker.url', 'http://127.0.0.1:8095');
|
|
config()->set('enhance.external_worker.token', 'worker-secret');
|
|
config()->set('enhance.external_worker.timeout', 15);
|
|
config()->set('enhance.external_worker.max_download_mb', 2);
|
|
Storage::fake('public');
|
|
});
|
|
|
|
function makeEnhanceJob(): EnhanceJob {
|
|
$user = User::factory()->create();
|
|
Storage::disk('public')->put('enhance/sources/10/source.png', UploadedFile::fake()->image('source.png', 40, 40)->get());
|
|
|
|
return EnhanceJob::query()->create([
|
|
'user_id' => $user->id,
|
|
'status' => EnhanceJob::STATUS_QUEUED,
|
|
'engine' => EnhanceJob::ENGINE_EXTERNAL_WORKER,
|
|
'mode' => 'artwork',
|
|
'scale' => 2,
|
|
'source_disk' => 'public',
|
|
'source_path' => 'enhance/sources/10/source.png',
|
|
'input_mime' => 'image/png',
|
|
]);
|
|
}
|
|
|
|
it('requires a configured worker url', function (): void {
|
|
config()->set('enhance.external_worker.url', '');
|
|
|
|
app(ExternalWorkerEnhanceProcessor::class)->process(makeEnhanceJob());
|
|
})->throws(RuntimeException::class, 'Worker URL is missing.');
|
|
|
|
it('requires a configured worker token', function (): void {
|
|
config()->set('enhance.external_worker.token', '');
|
|
|
|
app(ExternalWorkerEnhanceProcessor::class)->process(makeEnhanceJob());
|
|
})->throws(RuntimeException::class, 'Worker token is missing.');
|
|
|
|
it('sends bearer token and stores successful worker output', function (): void {
|
|
$job = makeEnhanceJob();
|
|
$outputBinary = UploadedFile::fake()->image('output.png', 80, 80)->get();
|
|
|
|
Http::fake([
|
|
'http://127.0.0.1:8095/v1/upscale' => Http::response([
|
|
'success' => true,
|
|
'job_id' => $job->id,
|
|
'output_url' => 'http://127.0.0.1:8095/v1/results/result-output.png',
|
|
'width' => 80,
|
|
'height' => 80,
|
|
'filesize' => strlen($outputBinary),
|
|
'mime' => 'image/png',
|
|
'metadata' => ['engine' => 'pillow'],
|
|
], 200),
|
|
'http://127.0.0.1:8095/v1/results/result-output.png' => Http::response($outputBinary, 200, ['Content-Type' => 'image/png']),
|
|
'http://127.0.0.1:8095/v1/results/result-output.png*' => Http::response(['success' => true, 'deleted' => true], 200),
|
|
]);
|
|
|
|
$result = app(ExternalWorkerEnhanceProcessor::class)->process($job);
|
|
|
|
expect($result->width)->toBe(80);
|
|
expect($result->height)->toBe(80);
|
|
expect($result->mime)->toBe('image/png');
|
|
expect($result->metadata['engine'])->toBe('pillow');
|
|
Storage::disk('public')->assertExists($result->path);
|
|
|
|
Http::assertSent(function (Request $request): bool {
|
|
if ($request->url() !== 'http://127.0.0.1:8095/v1/upscale') {
|
|
return false;
|
|
}
|
|
|
|
$data = $request->data();
|
|
$sourceUrl = (string) ($data['source_url'] ?? '');
|
|
|
|
return $request->hasHeader('Authorization')
|
|
&& ($data['mode'] ?? null) === 'artwork'
|
|
&& (int) ($data['scale'] ?? 0) === 2
|
|
&& (int) ($data['job_id'] ?? 0) > 0
|
|
&& ($sourceUrl !== '')
|
|
&& (str_contains($sourceUrl, '/internal/enhance/source/') || str_contains($sourceUrl, '/enhance/sources/'));
|
|
});
|
|
});
|
|
|
|
it('handles a failed worker response', function (): void {
|
|
$job = makeEnhanceJob();
|
|
|
|
Http::fake([
|
|
'http://127.0.0.1:8095/v1/upscale' => Http::response(['success' => false, 'error' => 'Worker rejected the image.'], 422),
|
|
]);
|
|
|
|
app(ExternalWorkerEnhanceProcessor::class)->process($job);
|
|
})->throws(RuntimeException::class, 'Worker rejected the image.');
|
|
|
|
it('handles an invalid json worker response', function (): void {
|
|
$job = makeEnhanceJob();
|
|
|
|
Http::fake([
|
|
'http://127.0.0.1:8095/v1/upscale' => Http::response('not-json', 200, ['Content-Type' => 'text/plain']),
|
|
]);
|
|
|
|
app(ExternalWorkerEnhanceProcessor::class)->process($job);
|
|
})->throws(RuntimeException::class, 'Worker returned an invalid response.');
|
|
|
|
it('rejects oversized downloaded output', function (): void {
|
|
$job = makeEnhanceJob();
|
|
config()->set('enhance.external_worker.max_download_mb', 1);
|
|
|
|
Http::fake([
|
|
'http://127.0.0.1:8095/v1/upscale' => Http::response([
|
|
'success' => true,
|
|
'job_id' => $job->id,
|
|
'output_url' => 'http://127.0.0.1:8095/v1/results/too-large.webp',
|
|
'mime' => 'image/webp',
|
|
], 200),
|
|
'http://127.0.0.1:8095/v1/results/too-large.webp' => Http::response(str_repeat('a', (1024 * 1024) + 1), 200, ['Content-Type' => 'image/webp']),
|
|
]);
|
|
|
|
app(ExternalWorkerEnhanceProcessor::class)->process($job);
|
|
})->throws(RuntimeException::class, 'The upscaled output exceeded the maximum allowed size.');
|
|
|
|
it('accepts base64 worker output', function (): void {
|
|
$job = makeEnhanceJob();
|
|
$outputBinary = UploadedFile::fake()->image('output.png', 90, 60)->get();
|
|
|
|
Http::fake([
|
|
'http://127.0.0.1:8095/v1/upscale' => Http::response([
|
|
'success' => true,
|
|
'job_id' => $job->id,
|
|
'output_base64' => base64_encode($outputBinary),
|
|
'mime' => 'image/png',
|
|
'metadata' => ['engine' => 'pillow', 'real_ai_upscale' => false],
|
|
], 200),
|
|
]);
|
|
|
|
$result = app(ExternalWorkerEnhanceProcessor::class)->process($job);
|
|
|
|
expect($result->width)->toBe(90);
|
|
expect($result->height)->toBe(60);
|
|
expect($result->metadata['real_ai_upscale'])->toBeFalse();
|
|
}); |