createMock(ImportAuditRunRepository::class); $repository->expects($this->once()) ->method('createRunning') ->with($this->callback(function (array $payload) use (&$captured): bool { $captured = $payload; return true; })) ->willReturn(77); $service = new ImportAuditService($repository); $runId = $service->startRun('users', ['email', 'first_name', 'email'], '/tmp/../users.csv', 12, 5); $this->assertSame(77, $runId); $this->assertSame('users.csv', (string) ($captured['source_filename'] ?? '')); $this->assertSame('email,first_name', (string) ($captured['mapped_targets_csv'] ?? '')); $this->assertSame(12, (int) ($captured['user_id'] ?? 0)); $this->assertSame(5, (int) ($captured['current_tenant_id'] ?? 0)); } public function testFinishRunDerivesPartialStatusAndAggregatesErrors(): void { $captured = null; $repository = $this->createMock(ImportAuditRunRepository::class); $repository->expects($this->once()) ->method('finishById') ->with( 88, $this->callback(function (array $payload) use (&$captured): bool { $captured = $payload; return true; }) ) ->willReturn(true); $service = new ImportAuditService($repository); $service->finishRun(88, [ 'ok' => true, 'processed' => 3, 'created' => 1, 'skipped' => 1, 'failed' => 1, 'errors' => [ ['code' => 'email_exists'], ['code' => 'email_exists'], ['code' => 'invalid_email'], ], ]); $this->assertSame('partial', (string) ($captured['status'] ?? '')); $this->assertSame(3, (int) ($captured['rows_total'] ?? 0)); $this->assertSame(1, (int) ($captured['created_count'] ?? 0)); $this->assertSame(1, (int) ($captured['skipped_count'] ?? 0)); $this->assertSame(1, (int) ($captured['failed_count'] ?? 0)); $errorJson = (string) ($captured['error_codes_json'] ?? ''); $this->assertStringContainsString('email_exists', $errorJson); $this->assertStringContainsString('invalid_email', $errorJson); } }