|
31 | 31 | import com.google.cloud.bigquery.BigQuery; |
32 | 32 | import com.google.cloud.bigquery.BigQuery.QueryResultsOption; |
33 | 33 | import com.google.cloud.bigquery.BigQuery.TableDataListOption; |
| 34 | +import com.google.cloud.bigquery.BigQueryException; |
34 | 35 | import com.google.cloud.bigquery.BigQueryOptions; |
35 | 36 | import com.google.cloud.bigquery.Field; |
36 | 37 | import com.google.cloud.bigquery.FieldList; |
|
47 | 48 | import com.google.cloud.bigquery.StandardSQLTypeName; |
48 | 49 | import com.google.cloud.bigquery.TableId; |
49 | 50 | import com.google.cloud.bigquery.TableResult; |
| 51 | +import com.google.cloud.bigquery.exception.BigQueryJdbcException; |
50 | 52 | import com.google.cloud.bigquery.jdbc.BigQueryStatement.JobIdWrapper; |
51 | 53 | import com.google.cloud.bigquery.spi.BigQueryRpcFactory; |
52 | 54 | import com.google.cloud.bigquery.storage.v1.ArrowSchema; |
|
55 | 57 | import com.google.cloud.bigquery.storage.v1.ReadSession; |
56 | 58 | import com.google.common.collect.ImmutableList; |
57 | 59 | import com.google.common.collect.Maps; |
| 60 | +import io.opentelemetry.api.baggage.Baggage; |
58 | 61 | import io.opentelemetry.api.common.AttributeKey; |
59 | 62 | import io.opentelemetry.api.trace.Span; |
60 | 63 | import io.opentelemetry.api.trace.StatusCode; |
@@ -753,4 +756,70 @@ public void testUseReadAPI_ZeroPageSizeDivisionByZeroSafeguard() throws SQLExcep |
753 | 756 | boolean useReadApi = statement.useReadAPI(tableResult); |
754 | 757 | assertThat(useReadApi).isTrue(); // ratio = 500 / 1 = 500 > 2 -> true |
755 | 758 | } |
| 759 | + |
| 760 | + @Test |
| 761 | + public void testExecute_registersException() throws Exception { |
| 762 | + // Mock bigquery to throw a backend exception |
| 763 | + BigQueryException expectedException = new BigQueryException(500, "Backend Error"); |
| 764 | + Mockito.doThrow(expectedException) |
| 765 | + .when(bigquery) |
| 766 | + .queryWithTimeout(Mockito.any(QueryJobConfiguration.class), Mockito.any(), Mockito.any()); |
| 767 | + |
| 768 | + BigQueryStatement spiedStatement = Mockito.spy(bigQueryStatement); |
| 769 | + |
| 770 | + // Execute and expect the exception to be propagated to JDBC |
| 771 | + Assertions.assertThrows(SQLException.class, () -> spiedStatement.executeQuery("SELECT 1")); |
| 772 | + |
| 773 | + // Retrieve the exported span from OTel extension |
| 774 | + List<SpanData> spans = otelTesting.getSpans(); |
| 775 | + SpanData span = |
| 776 | + OpenTelemetryTestUtility.findSpanByName(spans, "BigQueryStatement.executeQuery"); |
| 777 | + |
| 778 | + // Assert that the span recorded the error correctly |
| 779 | + OpenTelemetryTestUtility.assertSpanStatus(span, StatusCode.ERROR); |
| 780 | + OpenTelemetryTestUtility.assertSpanHasException(span, BigQueryJdbcException.class); |
| 781 | + } |
| 782 | + |
| 783 | + @Test |
| 784 | + public void testExecute_propagatesContextAndBaggage() throws Exception { |
| 785 | + // Mock bigquery using thenAnswer to hook into the call and assert Context/Baggage |
| 786 | + Mockito.doAnswer( |
| 787 | + invocation -> { |
| 788 | + // This code runs on the execution thread during the SDK call |
| 789 | + String connectionIdBaggage = |
| 790 | + Baggage.current() |
| 791 | + .getEntryValue(BigQueryJdbcOpenTelemetry.CONNECTION_ID_BAGGAGE_KEY); |
| 792 | + assertEquals("test-connection-id", connectionIdBaggage); |
| 793 | + |
| 794 | + Span currentSpan = Span.current(); |
| 795 | + assertTrue(currentSpan.getSpanContext().isValid()); |
| 796 | + |
| 797 | + // Return a mock TableResult to allow the execution to proceed |
| 798 | + TableResult tableResultMock = mock(TableResult.class); |
| 799 | + doReturn(jobId).when(tableResultMock).getJobId(); |
| 800 | + doReturn(Schema.of()).when(tableResultMock).getSchema(); |
| 801 | + return tableResultMock; |
| 802 | + }) |
| 803 | + .when(bigquery) |
| 804 | + .queryWithTimeout(Mockito.any(QueryJobConfiguration.class), Mockito.any(), Mockito.any()); |
| 805 | + |
| 806 | + BigQueryStatement spiedStatement = Mockito.spy(bigQueryStatement); |
| 807 | + |
| 808 | + // Setup connection mocks to allow the statement to execute successfully |
| 809 | + doReturn(true).when(bigQueryConnection).getUseStatelessQueryMode(); |
| 810 | + Job dryRunJobMock = getJobMock(null, null, StatementType.SELECT); |
| 811 | + doReturn(dryRunJobMock).when(bigquery).create(Mockito.any(JobInfo.class)); |
| 812 | + |
| 813 | + BigQueryJsonResultSet resultSetMock = mock(BigQueryJsonResultSet.class); |
| 814 | + doReturn(resultSetMock) |
| 815 | + .when(spiedStatement) |
| 816 | + .processJsonResultSet(Mockito.any(TableResult.class)); |
| 817 | + |
| 818 | + // Execute query |
| 819 | + spiedStatement.executeQuery("SELECT 1"); |
| 820 | + |
| 821 | + // Verify the SDK call actually occurred |
| 822 | + verify(bigquery) |
| 823 | + .queryWithTimeout(Mockito.any(QueryJobConfiguration.class), Mockito.any(), Mockito.any()); |
| 824 | + } |
756 | 825 | } |
0 commit comments