generated from westfarn/web_django_template
CI / test (pull_request) Successful in 35s
Shoppers can register, save shipping details, and view order history while cards stay on Stripe. EasyPost tracker updates (including numbers from Pirate Ship) and 1–5 star reviews are limited to buyers. The contact form now only asks for email and a message.
344 lines
12 KiB
Python
344 lines
12 KiB
Python
from django.contrib.auth import get_user_model
|
|
from django.test import Client, TestCase, override_settings
|
|
from django.urls import reverse
|
|
from unittest.mock import patch
|
|
|
|
from contacts.models import Channel, ConsentRecord, Contact, Suppression
|
|
from directmail.models import Campaign, Message, MessageTemplate, ProviderEvent
|
|
from contacts.consent import channel_preferences
|
|
from directmail.services import (
|
|
contact_may_receive,
|
|
create_campaign_draft,
|
|
opted_in_contacts,
|
|
set_channel_consent,
|
|
)
|
|
|
|
|
|
class PcmPostcardWebhookTests(TestCase):
|
|
def setUp(self):
|
|
self.client = Client()
|
|
self.contact = Contact.objects.create(
|
|
email="mail@example.com",
|
|
first_name="Pat",
|
|
last_name="Lee",
|
|
postal_address=Contact.make_postal_address(
|
|
line1="123 Main St",
|
|
city="Naperville",
|
|
state="IL",
|
|
zip_code="60540",
|
|
),
|
|
)
|
|
set_channel_consent(
|
|
self.contact, Channel.POSTCARD, opted_in=True, reason="test"
|
|
)
|
|
template = MessageTemplate.objects.create(
|
|
name="PCM test design",
|
|
channel=Channel.POSTCARD,
|
|
body="Postcard",
|
|
postcard_front={"design_id": 99, "size": "46", "provider": "pcm"},
|
|
)
|
|
self.campaign = create_campaign_draft(
|
|
name="March mailer",
|
|
audience=Campaign.Audience.POSTCARD_OPT_IN,
|
|
body="Postcard mailing",
|
|
template=template,
|
|
)
|
|
self.message = self.campaign.messages.get()
|
|
self.message.status = Message.Status.SENT
|
|
self.message.provider = "pcm"
|
|
self.message.provider_message_id = "order-555"
|
|
self.message.save()
|
|
|
|
def test_delivered_by_ext_ref(self):
|
|
import json
|
|
|
|
url = reverse("directmail:postcard_webhook")
|
|
response = self.client.post(
|
|
url,
|
|
data=json.dumps(
|
|
{
|
|
"status": "Delivered",
|
|
"extRefNbr": str(self.message.pk),
|
|
"orderID": 555,
|
|
}
|
|
),
|
|
content_type="application/json",
|
|
)
|
|
self.assertEqual(response.status_code, 200)
|
|
self.message.refresh_from_db()
|
|
self.assertEqual(self.message.status, Message.Status.DELIVERED)
|
|
self.assertTrue(
|
|
ProviderEvent.objects.filter(
|
|
message=self.message, provider="pcm", event_type="Delivered"
|
|
).exists()
|
|
)
|
|
|
|
def test_requires_bearer_when_secret_set(self):
|
|
import json
|
|
|
|
url = reverse("directmail:postcard_webhook")
|
|
with self.settings(PCM_WEBHOOK_SECRET="pcm-secret", PCM_WEBHOOK_SECRETS=""):
|
|
denied = self.client.post(
|
|
url,
|
|
data=json.dumps({"status": "Delivered", "orderID": 555}),
|
|
content_type="application/json",
|
|
)
|
|
self.assertEqual(denied.status_code, 403)
|
|
ok = self.client.post(
|
|
url,
|
|
data=json.dumps(
|
|
{
|
|
"status": "Delivered",
|
|
"orderID": "order-555",
|
|
}
|
|
),
|
|
content_type="application/json",
|
|
HTTP_AUTHORIZATION="Bearer pcm-secret",
|
|
)
|
|
self.assertEqual(ok.status_code, 200)
|
|
|
|
def test_accepts_any_secret_from_list(self):
|
|
import json
|
|
|
|
url = reverse("directmail:postcard_webhook")
|
|
with self.settings(
|
|
PCM_WEBHOOK_SECRET="",
|
|
PCM_WEBHOOK_SECRETS="sec-a,sec-b",
|
|
):
|
|
denied = self.client.post(
|
|
url,
|
|
data=json.dumps({"status": "Delivered", "orderID": "order-555"}),
|
|
content_type="application/json",
|
|
HTTP_AUTHORIZATION="Bearer wrong",
|
|
)
|
|
self.assertEqual(denied.status_code, 403)
|
|
ok = self.client.post(
|
|
url,
|
|
data=json.dumps({"status": "Delivered", "orderID": "order-555"}),
|
|
content_type="application/json",
|
|
HTTP_AUTHORIZATION="Bearer sec-b",
|
|
)
|
|
self.assertEqual(ok.status_code, 200)
|
|
|
|
|
|
class PcmAuthTests(TestCase):
|
|
def setUp(self):
|
|
from directmail.providers.postcard import pcm as pcm_mod
|
|
|
|
pcm_mod.clear_token_cache()
|
|
|
|
def tearDown(self):
|
|
from directmail.providers.postcard import pcm as pcm_mod
|
|
|
|
pcm_mod.clear_token_cache()
|
|
|
|
def test_login_required_before_design_list(self):
|
|
from unittest.mock import MagicMock, patch
|
|
|
|
from directmail.providers.postcard import pcm as pcm_mod
|
|
|
|
login_resp = MagicMock()
|
|
login_resp.status_code = 200
|
|
login_resp.content = b'{"token":"session-tok","expires":"2099-01-01T00:00:00.000Z"}'
|
|
login_resp.json.return_value = {
|
|
"token": "session-tok",
|
|
"expires": "2099-01-01T00:00:00.000Z",
|
|
}
|
|
|
|
design_resp = MagicMock()
|
|
design_resp.status_code = 200
|
|
design_resp.content = b'{"results":[]}'
|
|
design_resp.json.return_value = {"results": []}
|
|
|
|
with self.settings(PCM_API_KEY="key", PCM_API_SECRET="secret"):
|
|
with patch("directmail.providers.postcard.pcm.requests.post") as post:
|
|
with patch(
|
|
"directmail.providers.postcard.pcm.requests.request"
|
|
) as request:
|
|
post.return_value = login_resp
|
|
request.return_value = design_resp
|
|
designs = pcm_mod.list_designs()
|
|
|
|
self.assertEqual(designs, [])
|
|
post.assert_called_once()
|
|
self.assertIn("/auth/login", post.call_args.args[0])
|
|
self.assertEqual(
|
|
post.call_args.kwargs["json"],
|
|
{"apiKey": "key", "apiSecret": "secret"},
|
|
)
|
|
auth = request.call_args.kwargs["headers"]["Authorization"]
|
|
self.assertEqual(auth, "Bearer session-tok")
|
|
|
|
def test_missing_secret_raises(self):
|
|
from directmail.providers.postcard import pcm as pcm_mod
|
|
|
|
with self.settings(PCM_API_KEY="key", PCM_API_SECRET=""):
|
|
with self.assertRaises(pcm_mod.PcmApiError) as ctx:
|
|
pcm_mod.login(force=True)
|
|
self.assertIn("PCM_API_SECRET", str(ctx.exception))
|
|
|
|
def test_return_address_requires_street_fields(self):
|
|
from directmail.providers.postcard import pcm as pcm_mod
|
|
|
|
with self.settings(
|
|
SITE_NAME="Monica Dhillon",
|
|
PCM_RETURN_ADDRESS="",
|
|
PCM_RETURN_LINE1="",
|
|
PCM_RETURN_CITY="",
|
|
PCM_RETURN_STATE="",
|
|
PCM_RETURN_ZIP="",
|
|
):
|
|
with self.assertRaises(pcm_mod.PcmApiError) as ctx:
|
|
pcm_mod.return_address_from_settings()
|
|
self.assertIn("PCM return address incomplete", str(ctx.exception))
|
|
|
|
def test_return_address_from_json(self):
|
|
from directmail.providers.postcard import pcm as pcm_mod
|
|
|
|
with self.settings(
|
|
PCM_RETURN_ADDRESS=(
|
|
'{"firstName":"Mo","lastName":"D","address":"1 Main",'
|
|
'"city":"Naperville","state":"IL","zipCode":"60540"}'
|
|
),
|
|
):
|
|
addr = pcm_mod.return_address_from_settings()
|
|
self.assertEqual(addr["address"], "1 Main")
|
|
self.assertEqual(addr["zipCode"], "60540")
|
|
|
|
|
|
class PostcardAddressDefaultConsentTests(TestCase):
|
|
def test_address_without_consent_is_postcard_eligible(self):
|
|
contact = Contact.objects.create(
|
|
email="addr@example.com",
|
|
first_name="Ann",
|
|
postal_address=Contact.make_postal_address(
|
|
line1="1 Oak St",
|
|
city="Naperville",
|
|
state="IL",
|
|
zip_code="60540",
|
|
),
|
|
)
|
|
self.assertTrue(contact_may_receive(contact, Channel.POSTCARD))
|
|
prefs = channel_preferences(contact)
|
|
self.assertTrue(prefs[Channel.POSTCARD])
|
|
self.assertEqual(opted_in_contacts(Channel.POSTCARD).count(), 1)
|
|
|
|
def test_explicit_postcard_opt_out_respected(self):
|
|
contact = Contact.objects.create(
|
|
email="out@example.com",
|
|
postal_address=Contact.make_postal_address(line1="2 Oak St"),
|
|
)
|
|
set_channel_consent(
|
|
contact, Channel.POSTCARD, opted_in=False, reason="opt_out"
|
|
)
|
|
self.assertFalse(contact_may_receive(contact, Channel.POSTCARD))
|
|
self.assertEqual(opted_in_contacts(Channel.POSTCARD).count(), 0)
|
|
|
|
|
|
class PostcardDesignPickTests(TestCase):
|
|
def setUp(self):
|
|
User = get_user_model()
|
|
self.user = User.objects.create_user(
|
|
username="pcm-pick", password="test-pass-123", is_staff=True
|
|
)
|
|
self.client = Client()
|
|
self.client.login(username="pcm-pick", password="test-pass-123")
|
|
self.contact = Contact.objects.create(
|
|
email="mail@example.com",
|
|
postal_address=Contact.make_postal_address(line1="9 Main"),
|
|
)
|
|
|
|
def test_compose_can_pick_pcm_design_id(self):
|
|
from unittest.mock import patch
|
|
|
|
with patch(
|
|
"directmail.views._fetch_pcm_designs",
|
|
return_value=(
|
|
[{"design_id": "42", "name": "Spring card", "size": "46"}],
|
|
"",
|
|
),
|
|
):
|
|
url = reverse("directmail:campaign_list")
|
|
response = self.client.post(
|
|
url,
|
|
{
|
|
"name": "Mailer",
|
|
"subject": "",
|
|
"body": "",
|
|
"audience": Campaign.Audience.POSTCARD_OPT_IN,
|
|
"template_id": "d:42",
|
|
},
|
|
)
|
|
campaign = Campaign.objects.get(name="Mailer")
|
|
self.assertEqual(response.status_code, 302)
|
|
self.assertEqual(campaign.channel, Channel.POSTCARD)
|
|
self.assertIsNotNone(campaign.template_id)
|
|
self.assertEqual(
|
|
campaign.template.postcard_front.get("design_id"), 42
|
|
)
|
|
self.assertEqual(campaign.messages.count(), 1)
|
|
|
|
|
|
class PostcardUtmPanelTests(TestCase):
|
|
def setUp(self):
|
|
User = get_user_model()
|
|
self.user = User.objects.create_user(
|
|
username="utm-mail", password="test-pass-123", is_staff=True
|
|
)
|
|
self.client = Client()
|
|
self.client.login(username="utm-mail", password="test-pass-123")
|
|
self.contact = Contact.objects.create(
|
|
email="mail@example.com",
|
|
first_name="Pat",
|
|
postal_address=Contact.make_postal_address(line1="9 Main"),
|
|
)
|
|
|
|
def test_composer_shows_tracked_link_panel(self):
|
|
with patch(
|
|
"directmail.views._fetch_pcm_designs",
|
|
return_value=([], ""),
|
|
):
|
|
url = reverse("directmail:campaign_list")
|
|
response = self.client.get(url)
|
|
self.assertEqual(response.status_code, 200)
|
|
self.assertContains(response, "Tracked site link")
|
|
self.assertContains(response, "campaign-utm.js")
|
|
self.assertContains(response, "qrcode.min.js")
|
|
self.assertContains(response, "data-utm-qr")
|
|
self.assertContains(response, reverse("campaign_short_link"))
|
|
|
|
def test_postcard_does_not_inject_into_body(self):
|
|
campaign = create_campaign_draft(
|
|
name="March mailer",
|
|
audience=Campaign.Audience.POSTCARD_OPT_IN,
|
|
body="Internal note only",
|
|
created_by=self.user,
|
|
)
|
|
self.assertEqual(campaign.body_override, "Internal note only")
|
|
self.assertNotIn("utm_source", campaign.body_override)
|
|
|
|
@override_settings(
|
|
PUBLIC_SITE_URL="https://acmehvac.com",
|
|
SITE_NAME="Acme HVAC",
|
|
UTM_SOURCE="",
|
|
SHORTENER_BASE_URL="",
|
|
SHORTENER_API_TOKEN="",
|
|
)
|
|
def test_detail_shows_tracked_url(self):
|
|
campaign = create_campaign_draft(
|
|
name="March mailer",
|
|
audience=Campaign.Audience.POSTCARD_OPT_IN,
|
|
body="Internal note only",
|
|
created_by=self.user,
|
|
)
|
|
url = reverse("directmail:campaign_detail", kwargs={"pk": campaign.pk})
|
|
response = self.client.get(url)
|
|
self.assertEqual(response.status_code, 200)
|
|
self.assertContains(response, "utm_campaign=march-mailer")
|
|
self.assertContains(response, "utm_medium=postcard")
|
|
self.assertContains(response, "qrcode.min.js")
|
|
self.assertNotContains(response, "Internal note onlyutm")
|
|
|
|
|
|
|