Skip to content

Commit

Permalink
Fix #814: add timestamps to newsletters and waitlists in contact resp…
Browse files Browse the repository at this point in the history
…onses (#816)

* Fix #814: add timestamps to newsletters and waitlists in contact responses

* Rewrite datetime comparisons in tests

* Add docstring to test helper

* Rename Whatever to FuzzyAssert
  • Loading branch information
leplatrem authored Oct 3, 2023
1 parent 04a9932 commit ec34e7c
Show file tree
Hide file tree
Showing 8 changed files with 154 additions and 21 deletions.
12 changes: 9 additions & 3 deletions ctms/schemas/contact.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,12 @@
)
from .fxa import FirefoxAccountsInSchema, FirefoxAccountsSchema
from .mofo import MozillaFoundationInSchema, MozillaFoundationSchema
from .newsletter import NewsletterInSchema, NewsletterSchema, NewsletterTableSchema
from .newsletter import (
NewsletterInSchema,
NewsletterSchema,
NewsletterTableSchema,
NewsletterTimestampedSchema,
)
from .product import ProductBaseSchema, ProductSegmentEnum
from .waitlist import (
RelayWaitlistInSchema,
Expand All @@ -26,6 +31,7 @@
WaitlistInSchema,
WaitlistSchema,
WaitlistTableSchema,
WaitlistTimestampedSchema,
validate_waitlist_newsletters,
)

Expand Down Expand Up @@ -360,8 +366,8 @@ class CTMSResponse(BaseModel):
email: EmailSchema
fxa: FirefoxAccountsSchema
mofo: MozillaFoundationSchema
newsletters: List[NewsletterSchema]
waitlists: List[WaitlistSchema]
newsletters: List[NewsletterTimestampedSchema]
waitlists: List[WaitlistTimestampedSchema]
# Retro-compat fields
vpn_waitlist: VpnWaitlistSchema
relay_waitlist: RelayWaitlistSchema
Expand Down
13 changes: 8 additions & 5 deletions ctms/schemas/newsletter.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,11 +50,7 @@ class Config:
NewsletterSchema = NewsletterBase


class NewsletterTableSchema(NewsletterBase):
email_id: UUID4 = Field(
description=EMAIL_ID_DESCRIPTION,
example=EMAIL_ID_EXAMPLE,
)
class NewsletterTimestampedSchema(NewsletterBase):
create_timestamp: datetime = Field(
description="Newsletter data creation timestamp",
example="2020-12-05T19:21:50.908000+00:00",
Expand All @@ -64,5 +60,12 @@ class NewsletterTableSchema(NewsletterBase):
example="2021-02-04T15:36:57.511000+00:00",
)


class NewsletterTableSchema(NewsletterTimestampedSchema):
email_id: UUID4 = Field(
description=EMAIL_ID_DESCRIPTION,
example=EMAIL_ID_EXAMPLE,
)

class Config:
extra = "forbid"
13 changes: 8 additions & 5 deletions ctms/schemas/waitlist.py
Original file line number Diff line number Diff line change
Expand Up @@ -60,11 +60,7 @@ class Config:
WaitlistInSchema = WaitlistBase


class WaitlistTableSchema(WaitlistBase):
email_id: UUID4 = Field(
description=EMAIL_ID_DESCRIPTION,
example=EMAIL_ID_EXAMPLE,
)
class WaitlistTimestampedSchema(WaitlistBase):
create_timestamp: datetime = Field(
description="Waitlist data creation timestamp",
example="2020-12-05T19:21:50.908000+00:00",
Expand All @@ -74,6 +70,13 @@ class WaitlistTableSchema(WaitlistBase):
example="2021-02-04T15:36:57.511000+00:00",
)


class WaitlistTableSchema(WaitlistTimestampedSchema):
email_id: UUID4 = Field(
description=EMAIL_ID_DESCRIPTION,
example=EMAIL_ID_EXAMPLE,
)

class Config:
extra = "forbid"

Expand Down
52 changes: 52 additions & 0 deletions tests/conftest.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
"""
Common test configuration both unit and integration tests.
"""

from datetime import datetime


class FuzzyAssert:
"""
This class is a testing helper that provides flexible equality
of values.
.. code-block::
>>> FuzzyAssert(lambda x: x.startswith("a")) == "abc"
True
>>> FuzzyAssert(lambda x: x % 2 == 0) == 11
False
It is mainly used to make sure fields contain valid dates
without having to hardcode values:
.. code-block::
>>> FuzzyAssert.iso8601() == "2020-01-01"
True
>>> FuzzyAssert.iso8601() == None
False
"""

def __init__(self, test=lambda x: True, name="unnamed"):
self.test = test
self.name = name

def __eq__(self, other):
return self.test(other)

def __repr__(self):
return f"<{self.__class__.__name__}.{self.name}>"

@classmethod
def iso8601(cls):
def is_iso8601_date(sdate):
if not isinstance(sdate, str):
return False
try:
datetime.fromisoformat(sdate)
return True
except ValueError:
return False

return cls(is_iso8601_date, name="datetime")
16 changes: 16 additions & 0 deletions tests/integration/test_basket_waitlist_subscription.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@
import requests
from pydantic import BaseSettings

from tests.conftest import FuzzyAssert

TEST_FOLDER = os.path.dirname(os.path.realpath(__file__))


Expand Down Expand Up @@ -137,6 +139,8 @@ def fetch_created():
},
"subscribed": True,
"unsub_reason": None,
"create_timestamp": FuzzyAssert.iso8601(),
"update_timestamp": FuzzyAssert.iso8601(),
}
]
# Legacy (read-only) fields.
Expand Down Expand Up @@ -173,6 +177,8 @@ def fetch_created():
},
"subscribed": True,
"unsub_reason": None,
"create_timestamp": FuzzyAssert.iso8601(),
"update_timestamp": FuzzyAssert.iso8601(),
}
]
# Legacy (read-only) fields.
Expand Down Expand Up @@ -231,6 +237,8 @@ def fetch_created():
},
"subscribed": True,
"unsub_reason": None,
"create_timestamp": FuzzyAssert.iso8601(),
"update_timestamp": FuzzyAssert.iso8601(),
}
]
# Legacy (read-only) fields.
Expand Down Expand Up @@ -265,6 +273,8 @@ def check_subscribed():
},
"subscribed": True,
"unsub_reason": None,
"create_timestamp": FuzzyAssert.iso8601(),
"update_timestamp": FuzzyAssert.iso8601(),
},
{
"name": "relay-vpn-bundle",
Expand All @@ -274,6 +284,8 @@ def check_subscribed():
},
"subscribed": True,
"unsub_reason": None,
"create_timestamp": FuzzyAssert.iso8601(),
"update_timestamp": FuzzyAssert.iso8601(),
},
]
# Legacy (read-only) fields.
Expand Down Expand Up @@ -305,6 +317,8 @@ def check_unsubscribed():
"source": "https://relay.firefox.com/",
"subscribed": False,
"unsub_reason": None,
"create_timestamp": FuzzyAssert.iso8601(),
"update_timestamp": FuzzyAssert.iso8601(),
},
{
"name": "relay-vpn-bundle",
Expand All @@ -314,6 +328,8 @@ def check_unsubscribed():
},
"subscribed": True,
"unsub_reason": None,
"create_timestamp": FuzzyAssert.iso8601(),
"update_timestamp": FuzzyAssert.iso8601(),
},
]
# Legacy (read-only) fields.
Expand Down
34 changes: 34 additions & 0 deletions tests/unit/routers/contacts/test_api_get.py
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,8 @@ def test_get_ctms_for_minimal_contact(client, dbsession, email_factory):
"source": newsletter.source,
"subscribed": newsletter.subscribed,
"unsub_reason": newsletter.unsub_reason,
"create_timestamp": newsletter.create_timestamp.isoformat(),
"update_timestamp": newsletter.update_timestamp.isoformat(),
}
],
"status": "ok",
Expand Down Expand Up @@ -133,6 +135,8 @@ def test_get_ctms_for_maximal_contact(client, maximal_contact):
"source": "https://www.mozilla.org/en-US/contribute/studentambassadors/",
"subscribed": False,
"unsub_reason": "Graduated, don't have time for FSA",
"create_timestamp": "2010-01-01T08:04:00+00:00",
"update_timestamp": "2020-01-28T14:50:00+00:00",
},
{
"format": "T",
Expand All @@ -141,6 +145,8 @@ def test_get_ctms_for_maximal_contact(client, maximal_contact):
"source": "https://commonvoice.mozilla.org/fr",
"subscribed": True,
"unsub_reason": None,
"create_timestamp": "2010-01-01T08:04:00+00:00",
"update_timestamp": "2020-01-28T14:50:00+00:00",
},
{
"format": "H",
Expand All @@ -149,6 +155,8 @@ def test_get_ctms_for_maximal_contact(client, maximal_contact):
"source": "https://www.mozilla.org/fr/firefox/accounts/",
"subscribed": False,
"unsub_reason": "done with this mailing list",
"create_timestamp": "2010-01-01T08:04:00+00:00",
"update_timestamp": "2020-01-28T14:50:00+00:00",
},
{
"format": "H",
Expand All @@ -157,6 +165,8 @@ def test_get_ctms_for_maximal_contact(client, maximal_contact):
"source": None,
"subscribed": True,
"unsub_reason": None,
"create_timestamp": "2010-01-01T08:04:00+00:00",
"update_timestamp": "2020-01-28T14:50:00+00:00",
},
{
"format": "H",
Expand All @@ -165,6 +175,8 @@ def test_get_ctms_for_maximal_contact(client, maximal_contact):
"source": None,
"subscribed": True,
"unsub_reason": None,
"create_timestamp": "2010-01-01T08:04:00+00:00",
"update_timestamp": "2020-01-28T14:50:00+00:00",
},
{
"format": "H",
Expand All @@ -173,6 +185,8 @@ def test_get_ctms_for_maximal_contact(client, maximal_contact):
"source": None,
"subscribed": True,
"unsub_reason": None,
"create_timestamp": "2010-01-01T08:04:00+00:00",
"update_timestamp": "2020-01-28T14:50:00+00:00",
},
{
"format": "H",
Expand All @@ -181,6 +195,8 @@ def test_get_ctms_for_maximal_contact(client, maximal_contact):
"source": None,
"subscribed": True,
"unsub_reason": None,
"create_timestamp": "2010-01-01T08:04:00+00:00",
"update_timestamp": "2020-01-28T14:50:00+00:00",
},
],
"status": "ok",
Expand All @@ -193,27 +209,35 @@ def test_get_ctms_for_maximal_contact(client, maximal_contact):
"source": "https://a-software.mozilla.org/",
"subscribed": True,
"unsub_reason": None,
"create_timestamp": "2010-01-01T08:04:00+00:00",
"update_timestamp": "2020-01-28T14:50:00+00:00",
},
{
"fields": {"geo": "cn"},
"name": "relay",
"source": None,
"subscribed": True,
"unsub_reason": None,
"create_timestamp": "2010-01-01T08:04:00+00:00",
"update_timestamp": "2020-01-28T14:50:00+00:00",
},
{
"fields": {"geo": "fr", "platform": "win64"},
"name": "super-product",
"source": "https://super-product.mozilla.org/",
"subscribed": True,
"unsub_reason": None,
"create_timestamp": "2010-01-01T08:04:00+00:00",
"update_timestamp": "2020-01-28T14:50:00+00:00",
},
{
"fields": {"geo": "ca", "platform": "windows,android"},
"name": "vpn",
"source": None,
"subscribed": True,
"unsub_reason": None,
"create_timestamp": "2010-01-01T08:04:00+00:00",
"update_timestamp": "2020-01-28T14:50:00+00:00",
},
],
}
Expand Down Expand Up @@ -277,6 +301,8 @@ def test_get_ctms_for_api_example(client, example_contact):
"source": None,
"subscribed": True,
"unsub_reason": None,
"create_timestamp": "2020-12-05T19:21:50.908000+00:00",
"update_timestamp": "2021-02-04T15:36:57.511000+00:00",
},
{
"format": "H",
Expand All @@ -285,6 +311,8 @@ def test_get_ctms_for_api_example(client, example_contact):
"source": None,
"subscribed": True,
"unsub_reason": None,
"create_timestamp": "2020-12-05T19:21:50.908000+00:00",
"update_timestamp": "2021-02-04T15:36:57.511000+00:00",
},
],
"status": "ok",
Expand All @@ -297,20 +325,26 @@ def test_get_ctms_for_api_example(client, example_contact):
"source": None,
"subscribed": True,
"unsub_reason": None,
"create_timestamp": "2020-12-05T19:21:50.908000+00:00",
"update_timestamp": "2021-02-04T15:36:57.511000+00:00",
},
{
"fields": {"geo": "fr"},
"name": "relay",
"source": None,
"subscribed": True,
"unsub_reason": None,
"create_timestamp": "2020-12-05T19:21:50.908000+00:00",
"update_timestamp": "2021-02-04T15:36:57.511000+00:00",
},
{
"fields": {"geo": "fr", "platform": "ios,mac"},
"name": "vpn",
"source": None,
"subscribed": True,
"unsub_reason": None,
"create_timestamp": "2020-12-05T19:21:50.908000+00:00",
"update_timestamp": "2021-02-04T15:36:57.511000+00:00",
},
],
}
Expand Down
Loading

0 comments on commit ec34e7c

Please sign in to comment.