|
20 | 20 | import static com.google.common.truth.Truth.assertThat; |
21 | 21 | import static org.junit.jupiter.api.Assertions.assertEquals; |
22 | 22 | import static org.junit.jupiter.api.Assertions.assertTrue; |
| 23 | +import static org.junit.jupiter.api.Assertions.fail; |
23 | 24 | import static org.mockito.ArgumentMatchers.any; |
24 | 25 | import static org.mockito.ArgumentMatchers.eq; |
25 | 26 | import static org.mockito.Mockito.doReturn; |
26 | 27 | import static org.mockito.Mockito.mock; |
27 | 28 | import static org.mockito.Mockito.verify; |
28 | 29 |
|
| 30 | +import com.google.api.gax.rpc.ApiException; |
| 31 | +import com.google.api.gax.rpc.StatusCode; |
29 | 32 | import com.google.cloud.ServiceOptions; |
30 | 33 | import com.google.cloud.bigquery.BigQuery; |
31 | 34 | import com.google.cloud.bigquery.BigQuery.QueryResultsOption; |
|
45 | 48 | import com.google.cloud.bigquery.StandardSQLTypeName; |
46 | 49 | import com.google.cloud.bigquery.TableId; |
47 | 50 | import com.google.cloud.bigquery.TableResult; |
| 51 | +import com.google.cloud.bigquery.exception.BigQueryJdbcException; |
48 | 52 | import com.google.cloud.bigquery.jdbc.BigQueryStatement.JobIdWrapper; |
49 | 53 | import com.google.cloud.bigquery.spi.BigQueryRpcFactory; |
50 | 54 | import com.google.cloud.bigquery.storage.v1.ArrowSchema; |
@@ -497,6 +501,81 @@ public void testGetStatementType(boolean isReadOnlyTokenUsed) throws Exception { |
497 | 501 | .create(any(JobInfo.class)); |
498 | 502 | } |
499 | 503 |
|
| 504 | + @Test |
| 505 | + public void testProcessQueryResponseFallbackToJsonOnReadApiFailure() throws SQLException { |
| 506 | + BigQueryStatement statementSpy = Mockito.spy(bigQueryStatement); |
| 507 | + TableResult tableResultMock = mockTableResultWithJob("job-id"); |
| 508 | + |
| 509 | + // Force useReadAPI to return true to enter the HTAPI block |
| 510 | + doReturn(true).when(statementSpy).useReadAPI(tableResultMock); |
| 511 | + |
| 512 | + // Mock a permission denied ApiException |
| 513 | + ApiException apiExceptionMock = mockApiException(StatusCode.Code.PERMISSION_DENIED); |
| 514 | + |
| 515 | + BigQueryJdbcException exceptionToThrow = |
| 516 | + new BigQueryJdbcException("Simulated permission denied", apiExceptionMock); |
| 517 | + |
| 518 | + // Force processArrowResultSet to throw the permission exception |
| 519 | + Mockito.doThrow(exceptionToThrow).when(statementSpy).processArrowResultSet(tableResultMock); |
| 520 | + |
| 521 | + BigQueryJsonResultSet jsonResultSetMock = mock(BigQueryJsonResultSet.class); |
| 522 | + // Mock processJsonResultSet to return our mock JSON result set |
| 523 | + doReturn(jsonResultSetMock).when(statementSpy).processJsonResultSet(tableResultMock); |
| 524 | + |
| 525 | + statementSpy.processQueryResponse("SELECT 1", tableResultMock); |
| 526 | + |
| 527 | + // Verify that processJsonResultSet was indeed called as a fallback |
| 528 | + verify(statementSpy).processJsonResultSet(tableResultMock); |
| 529 | + // Verify that currentResultSet is set to the mocked JSON result set |
| 530 | + assertThat(statementSpy.currentResultSet).isEqualTo(jsonResultSetMock); |
| 531 | + } |
| 532 | + |
| 533 | + @Test |
| 534 | + public void testProcessQueryResponseNoFallbackOnNonPermissionFailure() throws SQLException { |
| 535 | + BigQueryStatement statementSpy = Mockito.spy(bigQueryStatement); |
| 536 | + TableResult tableResultMock = mockTableResultWithJob("job-id"); |
| 537 | + |
| 538 | + // Force useReadAPI to return true to enter the HTAPI block |
| 539 | + doReturn(true).when(statementSpy).useReadAPI(tableResultMock); |
| 540 | + |
| 541 | + // Mock a non-permission ApiException (e.g., INTERNAL) |
| 542 | + ApiException apiExceptionMock = mockApiException(StatusCode.Code.INTERNAL); |
| 543 | + |
| 544 | + BigQueryJdbcException exceptionToThrow = |
| 545 | + new BigQueryJdbcException("Simulated internal error", apiExceptionMock); |
| 546 | + |
| 547 | + // Force processArrowResultSet to throw the non-permission exception |
| 548 | + Mockito.doThrow(exceptionToThrow).when(statementSpy).processArrowResultSet(tableResultMock); |
| 549 | + |
| 550 | + BigQueryJsonResultSet jsonResultSetMock = mock(BigQueryJsonResultSet.class); |
| 551 | + doReturn(jsonResultSetMock).when(statementSpy).processJsonResultSet(tableResultMock); |
| 552 | + |
| 553 | + // Assert that the exception is propagated |
| 554 | + try { |
| 555 | + statementSpy.processQueryResponse("SELECT 1", tableResultMock); |
| 556 | + fail("Expected SQLException to be thrown"); |
| 557 | + } catch (SQLException e) { |
| 558 | + assertEquals(exceptionToThrow, e); |
| 559 | + } |
| 560 | + |
| 561 | + // Verify that processJsonResultSet was NOT called |
| 562 | + verify(statementSpy, Mockito.never()).processJsonResultSet(tableResultMock); |
| 563 | + } |
| 564 | + |
| 565 | + private TableResult mockTableResultWithJob(String jobId) { |
| 566 | + TableResult tableResult = mock(TableResult.class); |
| 567 | + doReturn(JobId.of(jobId)).when(tableResult).getJobId(); |
| 568 | + return tableResult; |
| 569 | + } |
| 570 | + |
| 571 | + private ApiException mockApiException(StatusCode.Code code) { |
| 572 | + ApiException apiExceptionMock = mock(ApiException.class); |
| 573 | + StatusCode statusCodeMock = mock(StatusCode.class); |
| 574 | + doReturn(statusCodeMock).when(apiExceptionMock).getStatusCode(); |
| 575 | + doReturn(code).when(statusCodeMock).getCode(); |
| 576 | + return apiExceptionMock; |
| 577 | + } |
| 578 | + |
500 | 579 | @Test |
501 | 580 | public void testUseReadAPI_SafeguardSmallDataset() throws SQLException { |
502 | 581 | // Setup: totalRows < MinTableSize, so it should not activate the Read API |
|
0 commit comments