diff --git a/src/adapter/etl-adapter-google-sheet/src/Flow/ETL/Adapter/GoogleSheet/GoogleSheetExtractor.php b/src/adapter/etl-adapter-google-sheet/src/Flow/ETL/Adapter/GoogleSheet/GoogleSheetExtractor.php index 24ed29eb0..dc9c8f3b8 100644 --- a/src/adapter/etl-adapter-google-sheet/src/Flow/ETL/Adapter/GoogleSheet/GoogleSheetExtractor.php +++ b/src/adapter/etl-adapter-google-sheet/src/Flow/ETL/Adapter/GoogleSheet/GoogleSheetExtractor.php @@ -37,48 +37,60 @@ public function __construct( public function extract(FlowContext $context) : \Generator { - $cellsRange = new SheetRange($this->columnRange, 1, $this->rowsPerPage); - $headers = []; - - $response = $this->service->spreadsheets_values->get( + $spreadsheet = $this->service->spreadsheets->get( $this->spreadsheetId, - $cellsRange->toString(), - $this->options + ['ranges' => [], 'includeGridData' => false] ); - /** - * @var array> $values - */ - $values = $response->getValues() ?? []; + $maxRows = 0; - $totalRows = 0; - - if ($this->withHeader && [] !== $values) { - foreach ($values as $index => $row) { - if ([] === $row) { - // Remove empty rows at the beginning of a sheet - unset($values[$index]); - - continue; - } - - /** @var array $headers */ - $headers = $row; - unset($values[$index]); - $totalRows = 1; + foreach ($spreadsheet->getSheets() as $sheet) { + if ($sheet->getProperties()->title === $this->columnRange->sheetName) { + $maxRows = $sheet->getProperties()->getGridProperties()->getRowCount(); break; } } - $headersCount = \count($headers); + $cellsRange = new SheetRange($this->columnRange, 1, $this->rowsPerPage, $maxRows); + + $ranges = []; + + for ($totalRows = 0; $totalRows < $cellsRange->endRow; $totalRows += $this->rowsPerPage) { + $ranges[] = $cellsRange->toString(); + + $cellsRange = $cellsRange->nextRows($this->rowsPerPage); + } $shouldPutInputIntoRows = $context->config->shouldPutInputIntoRows(); - while ([] !== $values) { - foreach ($values as $rowData) { + $headers = []; + $headersCount = 0; + + $response = $this->service->spreadsheets_values->batchGet($this->spreadsheetId, array_merge($this->options, ['ranges' => $ranges])); + + foreach ($response->getValueRanges() as $valueRange) { + foreach ($valueRange->getValues() as $rowData) { $rowDataCount = \count($rowData); + if ($this->withHeader) { + if ([] === $headers) { + // Skip empty rows at the beginning of a sheet + if ([] === $rowData) { + continue; + } + + /** @var array $headers */ + $headers = $rowData; + + $headersCount = $rowDataCount; + + continue; + } + } elseif (0 === $headersCount) { + $headersCount = $rowDataCount; + } + // Expand columns to the size of the previous row for ($i = $rowDataCount; $i < $headersCount; $i++) { $rowData[$i] = null; @@ -92,16 +104,16 @@ public function extract(FlowContext $context) : \Generator $rowData = \array_slice($rowData, 0, $headersCount); } - $row = \array_combine($headers, $rowData); + if ($this->withHeader) { + $rowData = \array_combine($headers, $rowData); + } if ($shouldPutInputIntoRows) { - $row['_spread_sheet_id'] = $this->spreadsheetId; - $row['_sheet_name'] = $this->columnRange->sheetName; + $rowData['_spread_sheet_id'] = $this->spreadsheetId; + $rowData['_sheet_name'] = $this->columnRange->sheetName; } - $totalRows++; - - $signal = yield array_to_rows($row, $context->entryFactory(), schema: $this->schema); + $signal = yield array_to_rows($rowData, $context->entryFactory(), schema: $this->schema); $this->incrementReturnedRows(); @@ -109,18 +121,6 @@ public function extract(FlowContext $context) : \Generator return; } } - - if ($totalRows < $cellsRange->endRow) { - return; - } - - $cellsRange = $cellsRange->nextRows($this->rowsPerPage); - - $response = $this->service->spreadsheets_values->get($this->spreadsheetId, $cellsRange->toString(), $this->options); - /** - * @var array> $values - */ - $values = $response->getValues() ?? []; } } diff --git a/src/adapter/etl-adapter-google-sheet/src/Flow/ETL/Adapter/GoogleSheet/SheetRange.php b/src/adapter/etl-adapter-google-sheet/src/Flow/ETL/Adapter/GoogleSheet/SheetRange.php index 462ecfbc4..ffeb18d96 100644 --- a/src/adapter/etl-adapter-google-sheet/src/Flow/ETL/Adapter/GoogleSheet/SheetRange.php +++ b/src/adapter/etl-adapter-google-sheet/src/Flow/ETL/Adapter/GoogleSheet/SheetRange.php @@ -8,22 +8,31 @@ final readonly class SheetRange { + public int $endRow; + public function __construct( public Columns $columnRange, public int $startRow, - public int $endRow, + int $endRow, + private int $maxRows, ) { if ($this->startRow < 1) { throw new InvalidArgumentException(\sprintf('Start row "%d" must be greater than 0', $this->startRow)); } - if ($this->endRow < 1) { - throw new InvalidArgumentException(\sprintf('End row "%d" must be greater than 0', $this->endRow)); + if ($endRow < 1) { + throw new InvalidArgumentException(\sprintf('End row "%d" must be greater than 0', $endRow)); + } + + if ($endRow < $this->startRow) { + throw new InvalidArgumentException(\sprintf('End row "%d" must be greater or equal to start row "%d"', $endRow, $this->startRow)); } - if ($this->endRow < $this->startRow) { - throw new InvalidArgumentException(\sprintf('End row "%d" must be greater or equal to start row "%d"', $this->endRow, $this->startRow)); + if ($this->maxRows < 1) { + throw new InvalidArgumentException(\sprintf('Max rows "%d" must be greater than 0', $this->maxRows)); } + + $this->endRow = min($endRow, $this->maxRows); } public function nextRows(int $count) : self @@ -34,8 +43,9 @@ public function nextRows(int $count) : self return new self( $this->columnRange, - $this->endRow + 1, - $this->endRow + $count, + min($this->endRow + 1, $this->maxRows), + min($this->endRow + $count, $this->maxRows), + $this->maxRows, ); } @@ -47,7 +57,7 @@ public function toString() : string $this->columnRange->startColumn, $this->startRow, $this->columnRange->endColumn, - $this->endRow + $this->endRow, ); } } diff --git a/src/adapter/etl-adapter-google-sheet/tests/Flow/ETL/Adapter/GoogleSheet/Tests/Fixtures/batch.json b/src/adapter/etl-adapter-google-sheet/tests/Flow/ETL/Adapter/GoogleSheet/Tests/Fixtures/batch.json new file mode 100644 index 000000000..89f42b14f --- /dev/null +++ b/src/adapter/etl-adapter-google-sheet/tests/Flow/ETL/Adapter/GoogleSheet/Tests/Fixtures/batch.json @@ -0,0 +1,37 @@ +{ + "spreadsheetId": "eae3913a86de8d5ccfe841fd3c7ec50dba3902045232", + "valueRanges": [ + { + "range": "Sheet!A1:C10", + "majorDimension": "ROWS", + "values": [ + ["Header 1", "Header 2", "Header 3"], + ["A2", "B2", "C2"], + ["A3", "B3", "C3"], + ["A4", "B4", "C4"], + ["A5", "B5", "C5"], + ["A6", "B6", "C6"], + ["A7", "B7", "C7"], + ["A8", "B8", "C8"], + ["A9", "B9", "C9"], + ["A10", "B10", "C10"] + ] + }, + { + "range": "Sheet!A11:C20", + "majorDimension": "ROWS", + "values": [ + ["A11", "B11", "C11"], + ["A12", "B12", "C12"], + ["A13", "B13", "C13"], + ["A14", "B14", "C14"], + ["A15", "B15", "C15"], + ["A16", "B16", "C16"], + ["A17", "B17", "C17"], + ["A18", "B18", "C18"], + ["A19", "B19", "C19"], + ["A20", "B20", "C20"] + ] + } + ] +} diff --git a/src/adapter/etl-adapter-google-sheet/tests/Flow/ETL/Adapter/GoogleSheet/Tests/Fixtures/extra-columns.json b/src/adapter/etl-adapter-google-sheet/tests/Flow/ETL/Adapter/GoogleSheet/Tests/Fixtures/extra-columns.json index 925006615..928a58625 100644 --- a/src/adapter/etl-adapter-google-sheet/tests/Flow/ETL/Adapter/GoogleSheet/Tests/Fixtures/extra-columns.json +++ b/src/adapter/etl-adapter-google-sheet/tests/Flow/ETL/Adapter/GoogleSheet/Tests/Fixtures/extra-columns.json @@ -1,17 +1,22 @@ { - "range": "Sheet!A1:D11", - "majorDimension": "ROWS", - "values": [ - ["Header 1", "Header 2", "Header 3"], - ["A2", "B2", "C2"], - ["A3", "B3", "C3"], - ["A4", "B4", "C4"], - ["A5", "B5", "C5"], - ["A6", "B6", "C6"], - ["A7", "B7", "C7"], - ["A8", "B8", "C8"], - ["A9", "B9", "C9"], - ["A10", "B10", "C10"], - ["A11", "B11", "C11", "D11"] + "spreadsheetId": "eae3913a86de8d5ccfe841fd3c7ec50dba3902045232", + "valueRanges": [ + { + "range": "Sheet!A1:D11", + "majorDimension": "ROWS", + "values": [ + ["Header 1", "Header 2", "Header 3"], + ["A2", "B2", "C2"], + ["A3", "B3", "C3"], + ["A4", "B4", "C4"], + ["A5", "B5", "C5"], + ["A6", "B6", "C6"], + ["A7", "B7", "C7"], + ["A8", "B8", "C8"], + ["A9", "B9", "C9"], + ["A10", "B10", "C10"], + ["A11", "B11", "C11", "D11"] + ] + } ] } diff --git a/src/adapter/etl-adapter-google-sheet/tests/Flow/ETL/Adapter/GoogleSheet/Tests/Fixtures/extra-empty-rows.json b/src/adapter/etl-adapter-google-sheet/tests/Flow/ETL/Adapter/GoogleSheet/Tests/Fixtures/extra-empty-rows.json index a04e924d8..8a030290b 100644 --- a/src/adapter/etl-adapter-google-sheet/tests/Flow/ETL/Adapter/GoogleSheet/Tests/Fixtures/extra-empty-rows.json +++ b/src/adapter/etl-adapter-google-sheet/tests/Flow/ETL/Adapter/GoogleSheet/Tests/Fixtures/extra-empty-rows.json @@ -1,18 +1,23 @@ { - "range": "Sheet!A1:D11", - "majorDimension": "ROWS", - "values": [ - [], - ["Header 1", "Header 2", "Header 3"], - ["A2", "B2", "C2"], - ["A3", "B3", "C3"], - ["A4", "B4", "C4"], - ["A5", "B5", "C5"], - ["A6", "B6", "C6"], - ["A7", "B7", "C7"], - ["A8", "B8", "C8"], - ["A9", "B9", "C9"], - ["A10", "B10", "C10"], - ["A11", "B11", "C11", "D11"] + "spreadsheetId": "eae3913a86de8d5ccfe841fd3c7ec50dba3902045232", + "valueRanges": [ + { + "range": "Sheet!A1:D11", + "majorDimension": "ROWS", + "values": [ + [], + ["Header 1", "Header 2", "Header 3"], + ["A2", "B2", "C2"], + ["A3", "B3", "C3"], + ["A4", "B4", "C4"], + ["A5", "B5", "C5"], + ["A6", "B6", "C6"], + ["A7", "B7", "C7"], + ["A8", "B8", "C8"], + ["A9", "B9", "C9"], + ["A10", "B10", "C10"], + ["A11", "B11", "C11", "D11"] + ] + } ] } diff --git a/src/adapter/etl-adapter-google-sheet/tests/Flow/ETL/Adapter/GoogleSheet/Tests/Fixtures/missing-columns.json b/src/adapter/etl-adapter-google-sheet/tests/Flow/ETL/Adapter/GoogleSheet/Tests/Fixtures/missing-columns.json index 26900ad28..7b7341cbf 100644 --- a/src/adapter/etl-adapter-google-sheet/tests/Flow/ETL/Adapter/GoogleSheet/Tests/Fixtures/missing-columns.json +++ b/src/adapter/etl-adapter-google-sheet/tests/Flow/ETL/Adapter/GoogleSheet/Tests/Fixtures/missing-columns.json @@ -1,11 +1,16 @@ { - "range": "Sheet!A1:C5", - "majorDimension": "ROWS", - "values": [ - ["Header 1", "Header 2", "Header 3"], - ["A2", "B2", "C2"], - ["A3", "B3"], - ["A4", "B4"], - ["A5", "B5", "C5"] + "spreadsheetId": "eae3913a86de8d5ccfe841fd3c7ec50dba3902045232", + "valueRanges": [ + { + "range": "Sheet!A1:C5", + "majorDimension": "ROWS", + "values": [ + ["Header 1", "Header 2", "Header 3"], + ["A2", "B2", "C2"], + ["A3", "B3"], + ["A4", "B4"], + ["A5", "B5", "C5"] + ] + } ] } diff --git a/src/adapter/etl-adapter-google-sheet/tests/Flow/ETL/Adapter/GoogleSheet/Tests/Fixtures/spreadsheet.json b/src/adapter/etl-adapter-google-sheet/tests/Flow/ETL/Adapter/GoogleSheet/Tests/Fixtures/spreadsheet.json new file mode 100644 index 000000000..79f3e7dff --- /dev/null +++ b/src/adapter/etl-adapter-google-sheet/tests/Flow/ETL/Adapter/GoogleSheet/Tests/Fixtures/spreadsheet.json @@ -0,0 +1,69 @@ +{ + "spreadsheetId": "eae3913a86de8d5ccfe841fd3c7ec50dba3902045232", + "properties": { + "title": "Spreadsheet 1", + "locale": "en_US", + "autoRecalc": "ON_CHANGE", + "timeZone": "Europe/Warsaw", + "defaultFormat": { + "backgroundColor": { + "red": 1, + "green": 1, + "blue": 1 + }, + "padding": { + "top": 2, + "right": 3, + "bottom": 2, + "left": 3 + }, + "verticalAlignment": "BOTTOM", + "wrapStrategy": "OVERFLOW_CELL", + "textFormat": { + "foregroundColor": {}, + "fontFamily": "arial,sans,sans-serif", + "fontSize": 10, + "bold": false, + "italic": false, + "strikethrough": false, + "underline": false, + "foregroundColorStyle": { + "rgbColor": {} + } + }, + "backgroundColorStyle": { + "rgbColor": { + "red": 1, + "green": 1, + "blue": 1 + } + } + }, + "spreadsheetTheme": { + "primaryFontFamily": "Arial", + "themeColors": [ + { + "colorType": "TEXT", + "color": { + "rgbColor": {} + } + } + ] + } + }, + "sheets": [ + { + "properties": { + "sheetId": 0, + "title": "Sheet", + "index": 0, + "sheetType": "GRID", + "gridProperties": { + "rowCount": 20, + "columnCount": 6 + } + } + } + ], + "spreadsheetUrl": "https://docs.google.com/spreadsheets/d/eae3913a86de8d5ccfe841fd3c7ec50dba3902045232/edit" +} diff --git a/src/adapter/etl-adapter-google-sheet/tests/Flow/ETL/Adapter/GoogleSheet/Tests/GoogleSheetsContext.php b/src/adapter/etl-adapter-google-sheet/tests/Flow/ETL/Adapter/GoogleSheet/Tests/GoogleSheetsContext.php index 52400b400..e9765191c 100644 --- a/src/adapter/etl-adapter-google-sheet/tests/Flow/ETL/Adapter/GoogleSheet/Tests/GoogleSheetsContext.php +++ b/src/adapter/etl-adapter-google-sheet/tests/Flow/ETL/Adapter/GoogleSheet/Tests/GoogleSheetsContext.php @@ -12,11 +12,9 @@ final readonly class GoogleSheetsContext { - private GoogleClient $client; - - public function __construct() - { - $this->client = new GoogleClient(); + public function __construct( + private GoogleClient $client = new GoogleClient(), + ) { } public function sheets(string $fixtureFile) : Sheets @@ -33,6 +31,11 @@ private function createHttpClient(string $fixtureFile) : HttpClient 'handler' => HandlerStack::create( new MockHandler( [ + new Response( + 200, + ['Content-Type' => 'application/json'], + file_get_contents(__DIR__ . '/Fixtures/spreadsheet.json') ?: throw new \RuntimeException('Failed to read the spreadsheet fixture'), + ), new Response( 200, ['Content-Type' => 'application/json'], diff --git a/src/adapter/etl-adapter-google-sheet/tests/Flow/ETL/Adapter/GoogleSheet/Tests/Integration/GoogleSheetExtractorTest.php b/src/adapter/etl-adapter-google-sheet/tests/Flow/ETL/Adapter/GoogleSheet/Tests/Integration/GoogleSheetExtractorTest.php index 105f3542f..75381f3d1 100644 --- a/src/adapter/etl-adapter-google-sheet/tests/Flow/ETL/Adapter/GoogleSheet/Tests/Integration/GoogleSheetExtractorTest.php +++ b/src/adapter/etl-adapter-google-sheet/tests/Flow/ETL/Adapter/GoogleSheet/Tests/Integration/GoogleSheetExtractorTest.php @@ -83,6 +83,41 @@ public function test_extract_skip_extra_empty_rows() : void } } + public function test_extract_with_batches() : void + { + $extractor = from_google_sheet( + $this->context->sheets(__DIR__ . '/../Fixtures/batch.json'), + '1234567890', + 'Sheet', + ); + $extractor->withRowsPerPage(10); + + $rows = df() + ->extract($extractor) + ->fetch() + ->toArray(); + + self::assertCount(19, $rows); + } + + public function test_extract_with_batches_without_header() : void + { + $extractor = from_google_sheet( + $this->context->sheets(__DIR__ . '/../Fixtures/batch.json'), + '1234567890', + 'Sheet', + ); + $extractor->withRowsPerPage(10); + $extractor->withHeader(false); + + $rows = df() + ->extract($extractor) + ->fetch() + ->toArray(); + + self::assertCount(20, $rows); + } + public function test_extract_with_cut_extra_columns() : void { $rows = df() diff --git a/src/adapter/etl-adapter-google-sheet/tests/Flow/ETL/Adapter/GoogleSheet/Tests/Unit/GoogleSheetExtractorTest.php b/src/adapter/etl-adapter-google-sheet/tests/Flow/ETL/Adapter/GoogleSheet/Tests/Unit/GoogleSheetExtractorTest.php index 2e83b85bf..fb3c9308f 100644 --- a/src/adapter/etl-adapter-google-sheet/tests/Flow/ETL/Adapter/GoogleSheet/Tests/Unit/GoogleSheetExtractorTest.php +++ b/src/adapter/etl-adapter-google-sheet/tests/Flow/ETL/Adapter/GoogleSheet/Tests/Unit/GoogleSheetExtractorTest.php @@ -15,32 +15,59 @@ final class GoogleSheetExtractorTest extends FlowTestCase { + public function test_its_fails_if_sheet_not_found() : void + { + $spreadsheet = $this->createMock(Sheets\Spreadsheet::class); + $spreadsheet->expects(self::once()) + ->method('getSheets') + ->willReturn([]); + + $resource = $this->createMock(Sheets\Resource\Spreadsheets::class); + $resource->expects(self::once()) + ->method('get') + ->with('spread-id', ['ranges' => [], 'includeGridData' => false]) + ->willReturn($spreadsheet); + + $service = $this->createMock(Sheets::class); + $service->spreadsheets = $resource; + + $extractor = from_google_sheet_columns($service, 'spread-id', 'sheet', 'A', 'B') + ->withHeader(true) + ->withRowsPerPage(2); + + $this->expectException(InvalidArgumentException::class); + $this->expectExceptionMessage('Max rows "0" must be greater than 0'); + + \iterator_to_array($extractor->extract(flow_context((new ConfigBuilder())->putInputIntoRows()->build()))); + } + public function test_its_stop_fetching_data_if_processed_row_count_is_less_then_last_range_end_row() : void { - $extractor = from_google_sheet_columns( - $service = $this->createMock(Sheets::class), - $spreadSheetId = 'spread-id', - $sheetName = 'sheet', - 'A', - 'B', - )->withHeader(true) + $sheetName = 'sheet'; + + $service = $this->createGoogleService($sheetName); + + $extractor = from_google_sheet_columns($service, $spreadSheetId = 'spread-id', $sheetName, 'A', 'B') + ->withHeader(true) ->withRowsPerPage(2); + + $firstValueRangeMock = new ValueRange(); + $firstValueRangeMock->setValues([['header'], ['row1']]); + $secondValueRangeMock = new ValueRange(); + $secondValueRangeMock->setValues([['row2']]); + + $response = new Sheets\BatchGetValuesResponse(); + $response->setValueRanges([$firstValueRangeMock, $secondValueRangeMock]); + + $spreadsheetsValues = $this->createMock(SpreadsheetsValues::class); + $spreadsheetsValues->expects(self::once()) + ->method('batchGet') + ->willReturn($response); + + $service->spreadsheets_values = $spreadsheetsValues; + $spreadSheetIdEntry = string_entry('_spread_sheet_id', $spreadSheetId); $sheetNameEntry = string_entry('_sheet_name', $sheetName); - $firstValueRangeMock = $this->createMock(ValueRange::class); - $firstValueRangeMock->method('getValues')->willReturn([ - ['header'], - ['row1'], - ]); - $secondValueRangeMock = $this->createMock(ValueRange::class); - $secondValueRangeMock->method('getValues')->willReturn([ - ['row2'], - ]); - $service->spreadsheets_values = ($spreadsheetsValues = $this->createMock(SpreadsheetsValues::class)); - - $spreadsheetsValues->expects(self::exactly(2)) - ->method('get') - ->willReturnOnConsecutiveCalls($firstValueRangeMock, $secondValueRangeMock); /** @var array $rowsArray */ $rowsArray = \iterator_to_array($extractor->extract(flow_context((new ConfigBuilder())->putInputIntoRows()->build()))); @@ -67,21 +94,56 @@ public function test_rows_in_batch_must_be_positive_integer() : void public function test_works_for_no_data() : void { - $extractor = from_google_sheet_columns( - $service = $this->createMock(Sheets::class), - 'spread-id', - 'sheet', - 'A', - 'B', - )->withHeader(true) + $service = $this->createGoogleService('sheet'); + + $extractor = from_google_sheet_columns($service, 'spread-id', 'sheet', 'A', 'B') + ->withHeader(true) ->withRowsPerPage(20); - $ValueRangeMock = $this->createMock(ValueRange::class); - $ValueRangeMock->method('getValues')->willReturn(null); - $service->spreadsheets_values = ($spreadsheetsValues = $this->createMock(SpreadsheetsValues::class)); - $spreadsheetsValues->method('get')->willReturn($ValueRangeMock); + $valueRangeMock = new ValueRange(); + $valueRangeMock->setValues([]); + + $response = new Sheets\BatchGetValuesResponse(); + $response->setValueRanges([$valueRangeMock]); + + $spreadsheetsValues = $this->createMock(SpreadsheetsValues::class); + $spreadsheetsValues->expects(self::once()) + ->method('batchGet') + ->willReturn($response); + + $service->spreadsheets_values = $spreadsheetsValues; + /** @var array $rowsArray */ $rowsArray = \iterator_to_array($extractor->extract(flow_context((new ConfigBuilder())->build()))); self::assertCount(0, $rowsArray); } + + private function createGoogleService(string $sheetName) : Sheets + { + $gridProperties = new Sheets\GridProperties(); + $gridProperties->setRowCount(100); + + $properties = new Sheets\SheetProperties(); + $properties->title = $sheetName; + $properties->setGridProperties($gridProperties); + + $sheet = new Sheets\Sheet(); + $sheet->setProperties($properties); + + $spreadsheet = $this->createMock(Sheets\Spreadsheet::class); + $spreadsheet->expects(self::once()) + ->method('getSheets') + ->willReturn([$sheet]); + + $resource = $this->createMock(Sheets\Resource\Spreadsheets::class); + $resource->expects(self::once()) + ->method('get') + ->with('spread-id', ['ranges' => [], 'includeGridData' => false]) + ->willReturn($spreadsheet); + + $service = new Sheets(); + $service->spreadsheets = $resource; + + return $service; + } } diff --git a/src/adapter/etl-adapter-google-sheet/tests/Flow/ETL/Adapter/GoogleSheet/Tests/Unit/SheetRangeTest.php b/src/adapter/etl-adapter-google-sheet/tests/Flow/ETL/Adapter/GoogleSheet/Tests/Unit/SheetRangeTest.php index 1f5ca9c31..bcaf3cd6b 100644 --- a/src/adapter/etl-adapter-google-sheet/tests/Flow/ETL/Adapter/GoogleSheet/Tests/Unit/SheetRangeTest.php +++ b/src/adapter/etl-adapter-google-sheet/tests/Flow/ETL/Adapter/GoogleSheet/Tests/Unit/SheetRangeTest.php @@ -14,55 +14,82 @@ final class SheetRangeTest extends FlowTestCase public static function example_string_ranges() : \Generator { yield 'one cell' => [ - new SheetRange(new Columns('Sheet2', 'B', 'B'), 2, 2), + new SheetRange(new Columns('Sheet2', 'B', 'B'), 2, 2, 10), 'Sheet2!B2:B2', ]; yield 'one line range' => [ - new SheetRange(new Columns('Sheet1', 'A', 'C'), 1, 1), + new SheetRange(new Columns('Sheet1', 'A', 'C'), 1, 1, 10), 'Sheet1!A1:C1', ]; yield 'multiple line range' => [ - new SheetRange(new Columns('Sheet1', 'B', 'D'), 2, 30), + new SheetRange(new Columns('Sheet1', 'B', 'D'), 2, 30, 100), 'Sheet1!B2:D30', ]; yield 'multi letter columns' => [ - new SheetRange(new Columns('Sheet1', 'ABC', 'CBA'), 101, 999), + new SheetRange(new Columns('Sheet1', 'ABC', 'CBA'), 101, 999, 1000), 'Sheet1!ABC101:CBA999', ]; + yield 'end row greater than max rows' => [ + new SheetRange(new Columns('Sheet1', 'ABC', 'CBA'), 101, 999, 950), + 'Sheet1!ABC101:CBA950', + ]; } public static function invalid_cases() : \Generator { yield 'start row under 0' => [ - 0, 1, + 0, + 1, + 100, 'Start row "0" must be greater than 0', ]; yield 'end row under 0' => [ - 1, 0, + 1, + 0, + 100, 'End row "0" must be greater than 0', ]; yield 'end row greater or equal to start row 0' => [ - 19, 10, + 19, + 10, + 100, 'End row "10" must be greater or equal to start row "19"', ]; + yield 'max row under 1' => [ + 1, + 1, + 0, + 'Max rows "0" must be greater than 0', + ]; } #[DataProvider('invalid_cases')] - public function test_assertions(int $startRow, int $endRow, string $expectedExceptionMessage) : void - { + public function test_assertions( + int $startRow, + int $endRow, + int $maxRows, + string $expectedExceptionMessage, + ) : void { $this->expectExceptionMessage($expectedExceptionMessage); $this->expectException(InvalidArgumentException::class); - new SheetRange(new Columns('Sheet2', 'A', 'B'), $startRow, $endRow); + new SheetRange(new Columns('Sheet2', 'A', 'B'), $startRow, $endRow, $maxRows); } public function test_next_rows_range() : void { - $range = new SheetRange(new Columns('Sheet2', 'A', 'B'), 1, 10); + $range = new SheetRange(new Columns('Sheet2', 'A', 'B'), 1, 10, 100); self::assertSame('Sheet2!A11:B20', $range->nextRows(10)->toString()); self::assertSame('Sheet2!A21:B40', $range->nextRows(10)->nextRows(20)->toString()); } + public function test_next_rows_range_with_amount_greater_than_max_rows() : void + { + $range = new SheetRange(new Columns('Sheet2', 'A', 'B'), 1, 10, 99); + self::assertSame('Sheet2!A11:B60', $range->nextRows(50)->toString()); + self::assertSame('Sheet2!A11:B99', $range->nextRows(100)->toString()); + } + #[DataProvider('example_string_ranges')] public function test_range_to_string(SheetRange $range, string $expectedStringRange) : void {