Skip to content

Commit

Permalink
Update tests to cover all cases of save_to_airtable
Browse files Browse the repository at this point in the history
  • Loading branch information
gasman committed Dec 13, 2024
1 parent 5c99a29 commit e7dded4
Show file tree
Hide file tree
Showing 2 changed files with 157 additions and 108 deletions.
113 changes: 72 additions & 41 deletions tests/mock_airtable.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
"""A mocked Airtable API wrapper."""
from unittest import mock
from requests.exceptions import HTTPError

def get_mock_airtable():
"""
Expand All @@ -14,21 +15,29 @@ def get_iter(self):
MockAirtable.table_name = "app_airtable_advert_base_key"

MockAirtable.get = mock.MagicMock("get")
MockAirtable.get.return_value = {
"id": "recNewRecordId",
"fields": {
"title": "Red! It's the new blue!",
"description": "Red is a scientifically proven color that moves faster than all other colors.",
"external_link": "https://example.com/",
"is_active": True,
"rating": "1.5",
"long_description": "<p>Lorem ipsum dolor sit amet, consectetur adipisicing elit. Veniam laboriosam consequatur saepe. Repellat itaque dolores neque, impedit reprehenderit eum culpa voluptates harum sapiente nesciunt ratione.</p>",
"points": 95,
"slug": "red-its-new-blue",
},
}

def get_fn(record_id):
if record_id == "recNewRecordId":
return {
"id": "recNewRecordId",
"fields": {
"title": "Red! It's the new blue!",
"description": "Red is a scientifically proven color that moves faster than all other colors.",
"external_link": "https://example.com/",
"is_active": True,
"rating": "1.5",
"long_description": "<p>Lorem ipsum dolor sit amet, consectetur adipisicing elit. Veniam laboriosam consequatur saepe. Repellat itaque dolores neque, impedit reprehenderit eum culpa voluptates harum sapiente nesciunt ratione.</p>",
"points": 95,
"slug": "red-its-new-blue",
},
}
else:
raise HTTPError("404 Client Error: Not Found")

MockAirtable.get.side_effect = get_fn

MockAirtable.insert = mock.MagicMock("insert")

MockAirtable.insert.return_value = {
"id": "recNewRecordId",
"fields": {
Expand Down Expand Up @@ -62,34 +71,56 @@ def get_iter(self):
MockAirtable.delete.return_value = {"deleted": True, "record": "recNewRecordId"}

MockAirtable.search = mock.MagicMock("search")
MockAirtable.search.return_value = [
{
"id": "recNewRecordId",
"fields": {
"title": "Red! It's the new blue!",
"description": "Red is a scientifically proven color that moves faster than all other colors.",
"external_link": "https://example.com/",
"is_active": True,
"rating": "1.5",
"long_description": "<p>Lorem ipsum dolor sit amet, consectetur adipisicing elit. Veniam laboriosam consequatur saepe. Repellat itaque dolores neque, impedit reprehenderit eum culpa voluptates harum sapiente nesciunt ratione.</p>",
"points": 95,
"slug": "red-its-new-blue",
},
},
{
"id": "Different record",
"fields": {
"title": "Not the used record.",
"description": "This is only used for multiple responses from MockAirtable",
"external_link": "https://example.com/",
"is_active": False,
"rating": "5.5",
"long_description": "",
"points": 1,
"slug": "not-the-used-record",
},
},
]
def search_fn(field, value):
if field == "slug" and value == "red-its-new-blue":
return [
{
"id": "recNewRecordId",
"fields": {
"title": "Red! It's the new blue!",
"description": "Red is a scientifically proven color that moves faster than all other colors.",
"external_link": "https://example.com/",
"is_active": True,
"rating": "1.5",
"long_description": "<p>Lorem ipsum dolor sit amet, consectetur adipisicing elit. Veniam laboriosam consequatur saepe. Repellat itaque dolores neque, impedit reprehenderit eum culpa voluptates harum sapiente nesciunt ratione.</p>",
"points": 95,
"slug": "red-its-new-blue",
},
},
{
"id": "Different record",
"fields": {
"title": "Not the used record.",
"description": "This is only used for multiple responses from MockAirtable",
"external_link": "https://example.com/",
"is_active": False,
"rating": "5.5",
"long_description": "",
"points": 1,
"slug": "not-the-used-record",
},
},
]
elif field == "slug" and value == "a-matching-slug":
return [
{
"id": "recMatchedRecordId",
"fields": {
"title": "Red! It's the new blue!",
"description": "Red is a scientifically proven color that moves faster than all other colors.",
"external_link": "https://example.com/",
"is_active": True,
"rating": "1.5",
"long_description": "<p>Lorem ipsum dolor sit amet, consectetur adipisicing elit. Veniam laboriosam consequatur saepe. Repellat itaque dolores neque, impedit reprehenderit eum culpa voluptates harum sapiente nesciunt ratione.</p>",
"points": 95,
"slug": "a-matching-slug",
},
},
]
else:
return []

MockAirtable.search.side_effect = search_fn

MockAirtable.get_all = mock.MagicMock("get_all")
MockAirtable.get_all.return_value = [
Expand Down
152 changes: 85 additions & 67 deletions tests/test_models.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

from tests.models import Advert
from wagtail_airtable.mixins import AirtableMixin
from unittest.mock import patch
from unittest.mock import ANY, patch
from .mock_airtable import get_mock_airtable


Expand Down Expand Up @@ -75,33 +75,97 @@ def test_create_object_from_url(self):
self.assertTrue(advert._push_to_airtable)
self.assertTrue(hasattr(advert, 'airtable_client'))

def test_edit_object(self):
def test_create_object_with_existing_airtable_record_id(self):
advert = Advert.objects.create(
title='Throw away advert',
title='Testing creation',
description='Lorem ipsum dolor sit amet, consectetur adipisicing elit.',
rating="2.5",
slug='edit-advert',
airtable_record_id='recCustomEditId',
slug='testing-creation',
airtable_record_id='recNewRecordId',
)
# Get the new advert without the instantiated airtable properties and api client
advert = Advert.objects.get(slug='edit-advert')
self.assertFalse(advert._ran_airtable_setup)
self.assertFalse(advert._is_enabled)
self.assertFalse(advert._push_to_airtable)
# save_to_airtable will confirm that a record with the given ID exists
# and update that record
advert.airtable_client.get.assert_called_once_with('recNewRecordId')
advert.airtable_client.update.assert_called_once_with('recNewRecordId', ANY)
call_args = advert.airtable_client.update.call_args.args
self.assertEqual(call_args[1]['title'], 'Testing creation')
advert.airtable_client.insert.assert_not_called()

def test_create_object_with_missing_id_and_matching_airtable_record(self):
advert = Advert.objects.create(
title='Testing creation',
description='Lorem ipsum dolor sit amet, consectetur adipisicing elit.',
rating="2.5",
slug='a-matching-slug',
airtable_record_id='recMissingRecordId',
)
# save_to_airtable will find that a record with the given ID does not exist,
# but one matching the slug does, and update that record
advert.airtable_client.get.assert_called_once_with('recMissingRecordId')
advert.airtable_client.search.assert_called_once_with('slug', 'a-matching-slug')
advert.airtable_client.update.assert_called_once_with('recMatchedRecordId', ANY)
call_args = advert.airtable_client.update.call_args.args
self.assertEqual(call_args[1]['title'], 'Testing creation')
advert.airtable_client.insert.assert_not_called()
advert.refresh_from_db()
self.assertEqual(advert.airtable_record_id, 'recMatchedRecordId')

def test_create_object_with_no_id_and_matching_airtable_record(self):
advert = Advert.objects.create(
title='Testing creation',
description='Lorem ipsum dolor sit amet, consectetur adipisicing elit.',
rating="2.5",
slug='a-matching-slug',
)
# save_to_airtable will skip the lookup by ID, but find a record matching the slug,
# and update that record
advert.airtable_client.get.assert_not_called()
advert.airtable_client.search.assert_called_once_with('slug', 'a-matching-slug')
advert.airtable_client.update.assert_called_once_with('recMatchedRecordId', ANY)
call_args = advert.airtable_client.update.call_args.args
self.assertEqual(call_args[1]['title'], 'Testing creation')
advert.airtable_client.insert.assert_not_called()
advert.refresh_from_db()
self.assertEqual(advert.airtable_record_id, 'recMatchedRecordId')

def test_create_object_with_missing_id_and_non_matching_airtable_record(self):
advert = Advert.objects.create(
title='Testing creation',
description='Lorem ipsum dolor sit amet, consectetur adipisicing elit.',
rating="2.5",
slug='a-non-matching-slug',
airtable_record_id='recMissingRecordId',
)
# save_to_airtable will find that a record with the given ID does not exist,
# and neither does one matching the slug - so it will create a new one
# and update the model with the new record ID
advert.airtable_client.get.assert_called_once_with('recMissingRecordId')
advert.airtable_client.search.assert_called_once_with('slug', 'a-non-matching-slug')
advert.airtable_client.insert.assert_called_once()
call_args = advert.airtable_client.insert.call_args.args
self.assertEqual(call_args[0]['title'], 'Testing creation')
advert.airtable_client.update.assert_not_called()
advert.refresh_from_db()
self.assertEqual(advert.airtable_record_id, 'recNewRecordId')

def test_edit_object(self):
advert = Advert.objects.get(airtable_record_id='recNewRecordId')
advert.title = "Edited title"
advert.description = "Edited description"
advert.save()
advert.airtable_client.update.assert_called()
self.assertTrue(advert._ran_airtable_setup)
self.assertTrue(advert._is_enabled)
self.assertTrue(advert._push_to_airtable)
# save_to_airtable will confirm that a record with the given ID exists and update it
advert.airtable_client.get.assert_called_once_with('recNewRecordId')
advert.airtable_client.update.assert_called_once_with('recNewRecordId', ANY)
call_args = advert.airtable_client.update.call_args.args
advert.airtable_client.insert.assert_not_called()
self.assertEqual(call_args[1]['title'], 'Edited title')
self.assertEqual(advert.title, "Edited title")

def test_delete_object(self):
advert = Advert.objects.get(slug='delete-me')
# If we werent mocking the Airtable.update() method, we'd assert advert.airtable_client.insert
self.assertEqual(advert.airtable_record_id, 'recNewRecordId')
advert.delete()
advert.airtable_client.delete.assert_called_once_with('recNewRecordId')
find_deleted_advert = Advert.objects.filter(slug='delete-me').count()
self.assertEqual(find_deleted_advert, 0)

Expand All @@ -118,32 +182,21 @@ def setUp(self):

def test_setup_airtable(self):
advert = copy(self.advert)
self.assertEqual(advert._ran_airtable_setup, False)
self.assertEqual(advert._is_enabled, False)
self.assertEqual(advert._push_to_airtable, False)
self.assertFalse(advert._ran_airtable_setup)
self.assertFalse(advert._is_enabled)
self.assertFalse(advert._push_to_airtable)
self.assertFalse(hasattr(advert, 'airtable_client'))

advert.setup_airtable()

self.assertEqual(advert.AIRTABLE_BASE_KEY, 'app_airtable_advert_base_key')
self.assertEqual(advert.AIRTABLE_TABLE_NAME, 'Advert Table Name')
self.assertEqual(advert.AIRTABLE_UNIQUE_IDENTIFIER, 'slug')
self.assertEqual(advert._ran_airtable_setup, True)
self.assertEqual(advert._is_enabled, True)
self.assertEqual(advert._push_to_airtable, True)
self.assertTrue(advert._ran_airtable_setup)
self.assertTrue(advert._is_enabled)
self.assertTrue(advert._push_to_airtable)
self.assertTrue(hasattr(advert, 'airtable_client'))

def test_create_and_attach_airtable_record(self):
advert = copy(self.advert)
self.assertEqual(advert.airtable_record_id, '')

advert.setup_airtable()
advert.save()

advert.airtable_client.update.assert_called()
advert.airtable_client.insert.assert_not_called()
self.assertEqual(advert.airtable_record_id, 'recNewRecordId')

def test_delete_record(self):
advert = copy(self.advert)
advert.setup_airtable()
Expand Down Expand Up @@ -183,38 +236,3 @@ def test_check_record_exists(self):
record_exists = advert.check_record_exists('recNewRecordId')
self.assertTrue(record_exists)
advert.airtable_client.get.assert_called()

def test_is_airtable_enabled(self):
advert = copy(self.advert)
self.assertFalse(advert._ran_airtable_setup)
self.assertFalse(advert._is_enabled)
advert.setup_airtable()
self.assertTrue(advert._ran_airtable_setup)
self.assertTrue(advert._is_enabled)

def test_save(self):
advert = copy(self.advert)
self.assertFalse(advert._ran_airtable_setup)
self.assertFalse(advert._push_to_airtable)
self.assertFalse(advert._is_enabled)
self.assertEqual(advert.airtable_record_id, '')

advert.setup_airtable()
self.assertTrue(advert._ran_airtable_setup)
self.assertTrue(advert._push_to_airtable)
self.assertTrue(advert._is_enabled)

self.assertEqual(advert.airtable_record_id, '')

advert.save()
self.assertEqual(advert.airtable_record_id, 'recNewRecordId')
advert.airtable_client.update.assert_called()
advert.airtable_client.insert.assert_not_called()

def test_delete(self):
advert = copy(self.advert)
advert.setup_airtable()

deleted = advert.delete_record()
self.assertTrue(deleted)
advert.airtable_client.delete.assert_called()

0 comments on commit e7dded4

Please sign in to comment.