code: /*
   * Copyright (c) 2022 Airbyte, Inc., all rights reserved.
   */
  
  package io.airbyte.scheduler.persistence.job_error_reporter;
  
  import static io.airbyte.scheduler.persistence.job_error_reporter.SentryJobErrorReportingClient.STACKTRACE_PARSE_ERROR_TAG_KEY;
  import static org.junit.jupiter.api.Assertions.assertEquals;
  import static org.junit.jupiter.api.Assertions.assertFalse;
  import static org.junit.jupiter.api.Assertions.assertNotNull;
  import static org.junit.jupiter.api.Assertions.assertNull;
  import static org.mockito.Mockito.mock;
  import static org.mockito.Mockito.verify;
  import static org.mockito.Mockito.when;
  
  import io.airbyte.config.FailureReason;
  import io.airbyte.config.FailureReason.FailureOrigin;
  import io.airbyte.config.FailureReason.FailureType;
  import io.airbyte.config.StandardWorkspace;
  import io.sentry.IHub;
  import io.sentry.NoOpHub;
  import io.sentry.SentryEvent;
  import io.sentry.protocol.Message;
  import io.sentry.protocol.SentryException;
  import io.sentry.protocol.User;
  import java.util.ArrayList;
  import java.util.List;
  import java.util.Map;
  import java.util.Optional;
  import java.util.UUID;
  import org.junit.jupiter.api.BeforeEach;
  import org.junit.jupiter.api.Test;
  import org.mockito.ArgumentCaptor;
  
  class SentryJobErrorReportingClientTest {
  
    private static final UUID WORKSPACE_ID = UUID.randomUUID();
    private static final String WORKSPACE_NAME = "My Workspace";
    private static final String DOCKER_IMAGE = "airbyte/source-stripe:1.2.3";
    private static final String ERROR_MESSAGE = "RuntimeError: Something went wrong";
  
    private final StandardWorkspace workspace = new StandardWorkspace().withWorkspaceId(WORKSPACE_ID).withName(WORKSPACE_NAME);
    private SentryJobErrorReportingClient sentryErrorReportingClient;
    private IHub mockSentryHub;
    private SentryExceptionHelper mockSentryExceptionHelper;
  
    @BeforeEach
    void setup() {
      mockSentryHub = mock(IHub.class);
      mockSentryExceptionHelper = mock(SentryExceptionHelper.class);
      sentryErrorReportingClient = new SentryJobErrorReportingClient(mockSentryHub, mockSentryExceptionHelper);
    }
  
    @Test
    void testCreateSentryHubWithBlankDSN() {
      final String sentryDSN = "";
      final IHub sentryHub = SentryJobErrorReportingClient.createSentryHubWithDSN(sentryDSN);
      assertEquals(NoOpHub.getInstance(), sentryHub);
    }
  
    @Test
    void testCreateSentryHubWithNullDSN() {
      final IHub sentryHub = SentryJobErrorReportingClient.createSentryHubWithDSN(null);
      assertEquals(NoOpHub.getInstance(), sentryHub);
    }
  
    @Test
    void testCreateSentryHubWithDSN() {
      final String sentryDSN = "https://public@sentry.example.com/1";
      final IHub sentryHub = SentryJobErrorReportingClient.createSentryHubWithDSN(sentryDSN);
      assertNotNull(sentryHub);
      assertEquals(sentryDSN, sentryHub.getOptions().getDsn());
      assertFalse(sentryHub.getOptions().isAttachStacktrace());
      assertFalse(sentryHub.getOptions().isEnableUncaughtExceptionHandler());
    }
  
    @Test
    void testReportJobFailureReason() {
      final ArgumentCaptor<SentryEvent> eventCaptor = ArgumentCaptor.forClass(SentryEvent.class);
  
      final FailureReason failureReason = new FailureReason()
          .withFailureOrigin(FailureOrigin.SOURCE)
          .withFailureType(FailureType.SYSTEM_ERROR)
          .withInternalMessage(ERROR_MESSAGE);
      final Map<String, String> metadata = Map.of("some_metadata", "some_metadata_value");
  
      sentryErrorReportingClient.reportJobFailureReason(workspace, failureReason, DOCKER_IMAGE, metadata);
  
      verify(mockSentryHub).captureEvent(eventCaptor.capture());
      final SentryEvent actualEvent = eventCaptor.getValue();
      assertEquals("other", actualEvent.getPlatform());
      assertEquals("airbyte-source-stripe@1.2.3", actualEvent.getRelease());
      assertEquals(List.of("{{ default }}", "airbyte-source-stripe"), actualEvent.getFingerprints());
      assertEquals("some_metadata_value", actualEvent.getTag("some_metadata"));
      assertNull(actualEvent.getTag(STACKTRACE_PARSE_ERROR_TAG_KEY));
      assertNull(actualEvent.getExceptions());
  
      final User sentryUser = actualEvent.getUser();
      assertNotNull(sentryUser);
      assertEquals(WORKSPACE_ID.toString(), sentryUser.getId());
      assertEquals(WORKSPACE_NAME, sentryUser.getUsername());
  
      final Message message = actualEvent.getMessage();
      assertNotNull(message);
      assertEquals(ERROR_MESSAGE, message.getFormatted());
    }
  
    @Test
    void testReportJobFailureReasonWithNoWorkspace() {
      final ArgumentCaptor<SentryEvent> eventCaptor = ArgumentCaptor.forClass(SentryEvent.class);
  
      final FailureReason failureReason = new FailureReason()
          .withFailureOrigin(FailureOrigin.SOURCE)
          .withFailureType(FailureType.SYSTEM_ERROR)
          .withInternalMessage(ERROR_MESSAGE);
  
      sentryErrorReportingClient.reportJobFailureReason(null, failureReason, DOCKER_IMAGE, Map.of());
  
      verify(mockSentryHub).captureEvent(eventCaptor.capture());
      final SentryEvent actualEvent = eventCaptor.getValue();
      final User sentryUser = actualEvent.getUser();
      assertNull(sentryUser);
  
      final Message message = actualEvent.getMessage();
      assertNotNull(message);
      assertEquals(ERROR_MESSAGE, message.getFormatted());
    }
  
    @Test
    void testReportJobFailureReasonWithStacktrace() {
      final ArgumentCaptor<SentryEvent> eventCaptor = ArgumentCaptor.forClass(SentryEvent.class);
  
      final List<SentryException> exceptions = new ArrayList<>();
      final SentryException exception = new SentryException();
      exception.setType("RuntimeError");
      exception.setValue("Something went wrong");
      exceptions.add(exception);
  
      when(mockSentryExceptionHelper.buildSentryExceptions("Some valid stacktrace")).thenReturn(Optional.of(exceptions));
  
      final FailureReason failureReason = new FailureReason()
          .withInternalMessage(ERROR_MESSAGE)
          .withStacktrace("Some valid stacktrace");
  
      sentryErrorReportingClient.reportJobFailureReason(workspace, failureReason, DOCKER_IMAGE, Map.of());
  
      verify(mockSentryHub).captureEvent(eventCaptor.capture());
      final SentryEvent actualEvent = eventCaptor.getValue();
      assertEquals(exceptions, actualEvent.getExceptions());
      assertNull(actualEvent.getTag(STACKTRACE_PARSE_ERROR_TAG_KEY));
    }
  
    @Test
    void testReportJobFailureReasonWithInvalidStacktrace() {
      final ArgumentCaptor<SentryEvent> eventCaptor = ArgumentCaptor.forClass(SentryEvent.class);
      final String invalidStacktrace = "Invalid stacktrace\nRuntimeError: Something went wrong";
  
      when(mockSentryExceptionHelper.buildSentryExceptions(invalidStacktrace)).thenReturn(Optional.empty());
  
      final FailureReason failureReason = new FailureReason()
          .withInternalMessage("Something went wrong")
          .withStacktrace(invalidStacktrace);
  
      sentryErrorReportingClient.reportJobFailureReason(workspace, failureReason, DOCKER_IMAGE, Map.of());
  
      verify(mockSentryHub).captureEvent(eventCaptor.capture());
      final SentryEvent actualEvent = eventCaptor.getValue();
      assertEquals("1", actualEvent.getTag(STACKTRACE_PARSE_ERROR_TAG_KEY));
      final List<SentryException> exceptions = actualEvent.getExceptions();
      assertNotNull(exceptions);
      assertEquals(1, exceptions.size());
      assertEquals("Invalid stacktrace, RuntimeError: ", exceptions.get(0).getValue());
    }
  
  }
  