1
0
mirror of synced 2026-01-28 10:01:55 -05:00
Files
airbyte/airbyte-integrations/connectors/source-sendgrid/unit_tests/unit_test.py

160 lines
5.6 KiB
Python

#
# Copyright (c) 2022 Airbyte, Inc., all rights reserved.
#
import unittest
from unittest.mock import MagicMock, Mock, patch
import pendulum
import pytest
import requests
from airbyte_cdk.logger import AirbyteLogger
from airbyte_cdk.models import SyncMode
from source_sendgrid.source import SourceSendgrid
from source_sendgrid.streams import (
Blocks,
Campaigns,
Contacts,
GlobalSuppressions,
Lists,
Messages,
Segments,
SendgridStream,
SendgridStreamIncrementalMixin,
SendgridStreamOffsetPagination,
SuppressionGroupMembers,
SuppressionGroups,
Templates,
)
FAKE_NOW = pendulum.DateTime(2022, 1, 1, tzinfo=pendulum.timezone("utc"))
@pytest.fixture(name="sendgrid_stream")
def sendgrid_stream_fixture(mocker) -> SendgridStream:
# Wipe the internal list of abstract methods to allow instantiating the abstract class without implementing its abstract methods
mocker.patch("source_sendgrid.streams.SendgridStream.__abstractmethods__", set())
# Mypy yells at us because we're init'ing an abstract class
return SendgridStream() # type: ignore
@pytest.fixture()
def mock_pendulum_now(monkeypatch):
pendulum_mock = unittest.mock.MagicMock(wraps=pendulum.now)
pendulum_mock.return_value = FAKE_NOW
monkeypatch.setattr(pendulum, "now", pendulum_mock)
def test_parse_response_gracefully_handles_nulls(mocker, sendgrid_stream: SendgridStream):
response = requests.Response()
mocker.patch.object(response, "json", return_value=None)
mocker.patch.object(response, "request", return_value=MagicMock())
assert [] == list(sendgrid_stream.parse_response(response))
def test_source_wrong_credentials():
source = SourceSendgrid()
status, error = source.check_connection(logger=AirbyteLogger(), config={"apikey": "wrong.api.key123"})
assert not status
def test_messages_stream_request_params(mock_pendulum_now):
start_time = 1558359830
stream = Messages(start_time)
state = {"last_event_time": 1558359000}
request_params = stream.request_params(state)
assert (
request_params
== "query=last_event_time%20BETWEEN%20TIMESTAMP%20%222019-05-20T13%3A30%3A00Z%22%20AND%20TIMESTAMP%20%222022-01-01T00%3A00%3A00Z%22&limit=1000"
)
def test_streams():
streams = SourceSendgrid().streams(config={"apikey": "wrong.api.key123", "start_time": FAKE_NOW})
assert len(streams) == 15
@patch.multiple(SendgridStreamOffsetPagination, __abstractmethods__=set())
def test_pagination(mocker):
stream = SendgridStreamOffsetPagination()
state = {}
response = requests.Response()
mocker.patch.object(response, "json", return_value={None: 1})
mocker.patch.object(response, "request", return_value=MagicMock())
next_page_token = stream.next_page_token(response)
request_params = stream.request_params(stream_state=state, next_page_token=next_page_token)
assert request_params == {"limit": 50}
@patch.multiple(SendgridStreamIncrementalMixin, __abstractmethods__=set())
def test_stream_state():
stream = SendgridStreamIncrementalMixin(start_time=FAKE_NOW)
state = {}
request_params = stream.request_params(stream_state=state)
assert request_params == {"end_time": pendulum.now().int_timestamp, "start_time": FAKE_NOW}
@pytest.mark.parametrize(
"stream_class, url , expected",
(
[Templates, "https://api.sendgrid.com/v3/templates", []],
[Lists, "https://api.sendgrid.com/v3/marketing/lists", []],
[Campaigns, "https://api.sendgrid.com/v3/marketing/campaigns", []],
[Contacts, "https://api.sendgrid.com/v3/marketing/contacts", []],
[Segments, "https://api.sendgrid.com/v3/marketing/segments", []],
[Blocks, "https://api.sendgrid.com/v3/suppression/blocks", ["name", "id", "contact_count", "_metadata"]],
[SuppressionGroupMembers, "https://api.sendgrid.com/v3/asm/suppressions", ["name", "id", "contact_count", "_metadata"]],
[SuppressionGroups, "https://api.sendgrid.com/v3/asm/groups", ["name", "id", "contact_count", "_metadata"]],
[GlobalSuppressions, "https://api.sendgrid.com/v3/suppression/unsubscribes", ["name", "id", "contact_count", "_metadata"]],
),
)
def test_read_records(
stream_class,
url,
expected,
requests_mock,
):
try:
stream = stream_class(start_time=FAKE_NOW)
except TypeError:
stream = stream_class()
requests_mock.get("https://api.sendgrid.com/v3/marketing", json={})
requests_mock.get(url, json={"name": "test", "id": "id", "contact_count": 20, "_metadata": {"self": "self"}})
records = list(stream.read_records(sync_mode=SyncMode))
assert records == expected
@pytest.mark.parametrize(
"stream_class, expected",
(
[Templates, "templates"],
[Lists, "marketing/lists"],
[Campaigns, "marketing/campaigns"],
[Contacts, "marketing/contacts"],
[Segments, "marketing/segments"],
[Blocks, "suppression/blocks"],
[SuppressionGroupMembers, "asm/suppressions"],
[SuppressionGroups, "asm/groups"],
[GlobalSuppressions, "suppression/unsubscribes"],
),
)
def test_path(stream_class, expected):
stream = stream_class(Mock())
assert stream.path() == expected
@pytest.mark.parametrize(
"stream_class, status, expected",
(
(Messages, 400, False),
(SuppressionGroupMembers, 401, False),
),
)
def test_should_retry_on_permission_error(requests_mock, stream_class, status, expected):
stream = stream_class(Mock())
response_mock = MagicMock()
response_mock.status_code = status
assert stream.should_retry(response_mock) == expected