Files
portal/app/tests/Unit/Supplier/PlaywrightBridgeTest.php
T
Дмитрий c52d42d447 fix(supplier): Plan 3 final review C-1 + I-2 — TestCase autowire + lock TTL
C-1: PlaywrightBridgeTest + RefreshSupplierSessionJobTest swap line order
- uses(TestCase::class) was BEFORE use Tests\TestCase → Pest resolved to
  non-existent \TestCase → file-load fatal → 6 tests NEVER ran since 71c999b.
- Fix: import BEFORE uses() call (pattern matches 3 working files в same dir).
- Misdiagnosis correction: ранее "pao stream_filter" symptoms attributed to
  laravel/pao env quirk были фактически C-1. Full suite parallel:
  617 tests / 614 passed / 3 skipped / 0 failed.

I-2: RefreshSupplierSessionJob lock TTL bump 30s → 90s
- PlaywrightBridge::TIMEOUT_SECONDS=75s. Lock TTL 30s → auto-expiry while
  real chromium boot+login still в процессе → concurrent refresh race.
- Fix: lock(name, 90)->block(95, ...) — exceeds PlaywrightBridge timeout.

Baseline regenerated после C-1 fix (stale entries removed, fresh Mockery
type errors captured).

Verification:
- tests/Unit/Supplier/: 30/30 pass (was 22/30 — 6 не запускались, 2 skip)
- Full parallel suite: 617/614+3 skipped/0 failed
- PHPStan: 0 errors. Pint: clean.

Final code-review (subagent verdict pre-C-1): "Ready to merge, with C-1 fix".
После этого commit'а — verdict satisfied.

Issues defer-to-post-merge per reviewer (follow-up tracking в memory):
- I-1 onOneServer (multi-server scaling; gated на Б-1 production)
- I-3 save_orphan recovery path (rare DB-write fail during HTTP success)
- I-4 region_mode='exclude' regions_reverse passthrough
- I-5 #4 acceptance criterion deferred until Task 1 closes
2026-05-11 06:51:22 +03:00

93 lines
3.1 KiB
PHP

<?php
declare(strict_types=1);
use App\Exceptions\Supplier\SupplierAuthException;
use App\Services\Supplier\PlaywrightBridge;
use App\Services\Supplier\ProcessFactory;
use Tests\TestCase;
use Tests\Unit\Supplier\Stubs\StubPlaywrightProcessHandle;
uses(TestCase::class);
test('PlaywrightBridge passes credentials via stdin not argv', function () {
$stubHandle = new StubPlaywrightProcessHandle(
successful: true,
output: json_encode([
'phpsessid' => 'abc123',
'csrf' => 'xyz789',
'refreshed_at' => '2026-05-11T10:00:00Z',
]),
);
$factoryMock = Mockery::mock(ProcessFactory::class);
/** @var ProcessFactory $factoryMock */
$factoryMock->shouldReceive('create')->andReturn($stubHandle);
$bridge = new PlaywrightBridge($factoryMock);
$result = $bridge->refreshSession(
login: 'test_login',
password: 'test_password',
url: 'https://crm.bp-gr.ru'
);
// Credentials must come through stdin, not argv (avoid leak in ps output)
$capturedInput = json_decode($stubHandle->capturedInput, true);
expect($capturedInput)->toBeArray()
->and($capturedInput['login'])->toBe('test_login')
->and($capturedInput['password'])->toBe('test_password')
->and($capturedInput['url'])->toBe('https://crm.bp-gr.ru');
expect($stubHandle->capturedTimeout)->toBe(75);
expect($result)->toHaveKeys(['phpsessid', 'csrf', 'refreshed_at'])
->and($result['phpsessid'])->toBe('abc123')
->and($result['csrf'])->toBe('xyz789');
});
test('PlaywrightBridge throws SupplierAuthException on non-zero exit', function () {
$stubHandle = new StubPlaywrightProcessHandle(
successful: false,
errorOutput: '{"error":"login rejected"}',
exitCode: 1,
);
$factoryMock = Mockery::mock(ProcessFactory::class);
$factoryMock->shouldReceive('create')->andReturn($stubHandle);
$bridge = new PlaywrightBridge($factoryMock);
expect(fn () => $bridge->refreshSession('bad', 'bad', 'https://crm.bp-gr.ru'))
->toThrow(SupplierAuthException::class);
});
test('PlaywrightBridge throws SupplierAuthException on invalid stdout JSON', function () {
$stubHandle = new StubPlaywrightProcessHandle(
successful: true,
output: 'not valid json{',
);
$factoryMock = Mockery::mock(ProcessFactory::class);
$factoryMock->shouldReceive('create')->andReturn($stubHandle);
$bridge = new PlaywrightBridge($factoryMock);
expect(fn () => $bridge->refreshSession('a', 'b', 'https://crm.bp-gr.ru'))
->toThrow(SupplierAuthException::class);
});
test('PlaywrightBridge throws SupplierAuthException on missing keys in stdout', function () {
$stubHandle = new StubPlaywrightProcessHandle(
successful: true,
output: json_encode(['phpsessid' => 'a']), // no csrf
);
$factoryMock = Mockery::mock(ProcessFactory::class);
$factoryMock->shouldReceive('create')->andReturn($stubHandle);
$bridge = new PlaywrightBridge($factoryMock);
expect(fn () => $bridge->refreshSession('a', 'b', 'https://crm.bp-gr.ru'))
->toThrow(SupplierAuthException::class);
});