sentBatches[] = $draft_lines; return (object)['lines' => $draft_lines]; } } } if (!class_exists('EconomicInvoiceDraftFailingBatchingProbe')) { class EconomicInvoiceDraftFailingBatchingProbe extends EconomicInvoiceDraftBatchingProbe { public int $failOnBatch = 1; protected function sendDraftLines(array $draft_lines): object { if (count($this->sentBatches) + 1 === $this->failOnBatch) { throw new RuntimeException('Simulated e-conomic line batch failure'); } return parent::sendDraftLines($draft_lines); } } } it('does not call e-conomic when flushing an empty draft line buffer', function (): void { $draft = new EconomicInvoiceDraftBatchingProbe(123, 'DKK', true); $metrics = $draft->flushLinesInBatches(); expect($metrics)->toBe([ 'line_count' => 0, 'batch_count' => 0, 'batch_sizes' => [], ])->and($draft->sentBatches)->toBe([]); }); it('flushes a small draft line buffer in one request and clears pending lines', function (): void { $draft = new EconomicInvoiceDraftBatchingProbe(123, 'DKK', true); $draft->addTextLine('line-0'); $draft->addTextLine('line-1'); $draft->addTextLine('line-2'); $metrics = $draft->flushLinesInBatches(500); expect($metrics)->toBe([ 'line_count' => 3, 'batch_count' => 1, 'batch_sizes' => [3], ])->and($draft->sentBatches)->toHaveCount(1) ->and($draft->sentBatches[0][0]['description'])->toBe('line-0') ->and($draft->sentBatches[0][2]['description'])->toBe('line-2') ->and($draft->pendingLineCount())->toBe(0); }); it('chunks large draft line buffers while preserving line order', function (): void { $draft = new EconomicInvoiceDraftBatchingProbe(123, 'DKK', true); for ($i = 0; $i < 1201; $i++) { $draft->addTextLine('line-' . $i); } $metrics = $draft->flushLinesInBatches(500); expect($metrics)->toBe([ 'line_count' => 1201, 'batch_count' => 3, 'batch_sizes' => [500, 500, 201], ])->and($draft->sentBatches)->toHaveCount(3) ->and($draft->sentBatches[0][0]['description'])->toBe('line-0') ->and($draft->sentBatches[1][0]['description'])->toBe('line-500') ->and($draft->sentBatches[2][200]['description'])->toBe('line-1200') ->and($draft->pendingLineCount())->toBe(0); }); it('bubbles line batch failures and keeps pending lines available', function (): void { $draft = new EconomicInvoiceDraftFailingBatchingProbe(123, 'DKK', true); $draft->addTextLine('line-0'); expect(fn () => $draft->flushLinesInBatches(500)) ->toThrow(RuntimeException::class, 'Simulated e-conomic line batch failure'); expect($draft->sentBatches)->toBe([]) ->and($draft->pendingLineCount())->toBe(1); });