From de4a7ea96d15f53003e0e876327d04f718d8e342 Mon Sep 17 00:00:00 2001 From: Kial Jinnah Date: Mon, 29 Jun 2026 10:24:49 -0400 Subject: [PATCH 1/7] 32958 Business Emailer - updates for reg and cor Signed-off-by: Kial Jinnah --- .../email_processors/__init__.py | 36 +- .../change_of_registration_notification.py | 183 ----- .../email_processors/filing_notification.py | 7 +- .../registration_notification.py | 176 ----- .../business_emailer/email_processors/util.py | 10 + .../email_templates/CHGREG-COMPLETED.html | 39 -- .../email_templates/CHGREG-PAID.html | 43 -- .../email_templates/REG-COMPLETED.html | 59 -- .../email_templates/REG-PAID.html | 46 -- .../email_templates/changeOfRegistration.md | 13 + .../common/business-tombstone.md | 2 +- .../email_templates/registration.md | 24 + .../resources/business_emailer.py | 8 - .../business-emailer/tests/unit/__init__.py | 4 +- ...est_change_of_registration_notification.py | 144 ---- .../test_email_processors_init.py | 218 ++++++ .../test_filing_notification.py | 634 +++++++++--------- .../test_registration_notification.py | 112 ---- .../tests/unit/test_worker_dispatch.py | 40 +- 19 files changed, 613 insertions(+), 1185 deletions(-) delete mode 100644 queue_services/business-emailer/src/business_emailer/email_processors/change_of_registration_notification.py delete mode 100644 queue_services/business-emailer/src/business_emailer/email_processors/registration_notification.py delete mode 100644 queue_services/business-emailer/src/business_emailer/email_templates/CHGREG-COMPLETED.html delete mode 100644 queue_services/business-emailer/src/business_emailer/email_templates/CHGREG-PAID.html delete mode 100644 queue_services/business-emailer/src/business_emailer/email_templates/REG-COMPLETED.html delete mode 100644 queue_services/business-emailer/src/business_emailer/email_templates/REG-PAID.html create mode 100644 queue_services/business-emailer/src/business_emailer/email_templates/changeOfRegistration.md create mode 100644 queue_services/business-emailer/src/business_emailer/email_templates/registration.md delete mode 100644 queue_services/business-emailer/tests/unit/email_processors/test_change_of_registration_notification.py delete mode 100644 queue_services/business-emailer/tests/unit/email_processors/test_registration_notification.py diff --git a/queue_services/business-emailer/src/business_emailer/email_processors/__init__.py b/queue_services/business-emailer/src/business_emailer/email_processors/__init__.py index bd03cab30a..ff28b0ab1b 100644 --- a/queue_services/business-emailer/src/business_emailer/email_processors/__init__.py +++ b/queue_services/business-emailer/src/business_emailer/email_processors/__init__.py @@ -48,26 +48,36 @@ def get_filing_info(filing_id: str) -> tuple[Filing, dict, dict, str, str]: return filing, business_json, leg_tmz_filing_date, leg_tmz_effective_date +def get_party_emails(parties: list[dict], roles: list[str]) -> str: + """Return the emails for the specified party types.""" + recipients = [] + for party in parties: + for role in party["roles"]: + if role["roleType"] in roles: + recipients.append(party["officer"].get("email")) + break + + return recipients + + def get_recipients(option: str, filing_json: dict, token: str | None = None, filing_type: str | None = None) -> str: """Get the recipients for the email output.""" recipients = "" filing_type = filing_type if filing_type else "incorporationApplication" if filing_json["filing"].get(filing_type): - recipients = filing_json["filing"][filing_type].get("contactPoint", {}).get("email", "") - if option in [Filing.Status.PAID.value, "bn"] and \ - filing_json["filing"]["header"]["name"] == filing_type: - parties = filing_json["filing"][filing_type].get("parties") - comp_party_email = None - for party in parties: - for role in party["roles"]: - if role["roleType"] == "Completing Party" and \ - (comp_party_email := party["officer"].get("email")): - recipients = f"{recipients}, {comp_party_email}" - break + # add filing contact point email + recipients = filing_json["filing"][filing_type].get("contactPoint", {}).get("email", "") or "" + # add relevant party emails + if (option != "mras" + and filing_json["filing"]["header"]["name"] == filing_type + and (parties := filing_json["filing"][filing_type].get("parties")) + and (party_emails := get_party_emails(parties, ["Completing Party", "Partner", "Proprietor"])) + ): + recipients = f"{recipients}, {', '.join(party_emails)}" else: identifier = filing_json["filing"]["business"]["identifier"] if identifier[:2] != "CP": - # only add recipients if not coop + # only add business email recipient for non-coop recipients = get_recipient_from_auth(identifier, token) return recipients @@ -298,6 +308,8 @@ def _add_filing_document_pdf( # noqa: PLR0913 file_name = "Address Change" elif document_type == "changeOfDirectors": file_name = "Director Change" + elif document_type == "registration": + file_name = "Statement of Registration" # Get pdf and add it to the list filing_pdf_encoded = get_filing_document(business["identifier"], filing.id, document_type, token) diff --git a/queue_services/business-emailer/src/business_emailer/email_processors/change_of_registration_notification.py b/queue_services/business-emailer/src/business_emailer/email_processors/change_of_registration_notification.py deleted file mode 100644 index 834d017543..0000000000 --- a/queue_services/business-emailer/src/business_emailer/email_processors/change_of_registration_notification.py +++ /dev/null @@ -1,183 +0,0 @@ -# Copyright © 2022 Province of British Columbia -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -"""Email processing rules and actions for Change of Registration notifications.""" -from __future__ import annotations - -import base64 -import re -from http import HTTPStatus -from pathlib import Path - -import requests -from flask import current_app -from jinja2 import Template - -from business_emailer.email_processors import ( - get_filing_document, - get_filing_info, - get_user_email_from_auth, - substitute_template_parts, -) -from business_model.models import Business, Filing, UserRoles - - -def _get_pdfs( # noqa: PLR0913 - status: str, - token: str, - business: dict, - filing: Filing, - filing_date_time: str, - effective_date: str -) -> list: - # pylint: disable=too-many-locals, too-many-branches, too-many-statements, too-many-arguments - """Get the outputs for the change of registration notification.""" - pdfs = [] - attach_order = 1 - headers = { - "Accept": "application/pdf", - "Authorization": f"Bearer {token}" - } - - if status == Filing.Status.PAID.value: - # add filing pdf - filing_pdf_type = "changeOfRegistration" - filing_pdf_encoded = get_filing_document(business["identifier"], filing.id, filing_pdf_type, token) - if filing_pdf_encoded: - pdfs.append( - { - "fileName": "Change of Registration.pdf", - "fileBytes": filing_pdf_encoded.decode("utf-8"), - "fileUrl": "", - "attachOrder": str(attach_order) - } - ) - attach_order += 1 - - corp_name = business.get("legalName") - business_data = Business.find_by_internal_id(filing.business_id) - receipt = requests.post( - f'{current_app.config.get("PAY_API_URL")}/{filing.payment_token}/receipts', - json={ - "corpName": corp_name, - "filingDateTime": filing_date_time, - "effectiveDateTime": effective_date if effective_date != filing_date_time else "", - "filingIdentifier": str(filing.id), - "businessNumber": business_data.tax_id if business_data and business_data.tax_id else "" - }, - headers=headers - ) - if receipt.status_code != HTTPStatus.CREATED: - current_app.logger.error("Failed to get receipt pdf for filing: %s", filing.id) - else: - receipt_encoded = base64.b64encode(receipt.content) - pdfs.append( - { - "fileName": "Receipt.pdf", - "fileBytes": receipt_encoded.decode("utf-8"), - "fileUrl": "", - "attachOrder": str(attach_order) - } - ) - attach_order += 1 - elif status == Filing.Status.COMPLETED.value: - # add amended registration statement - certificate_pdf_type = "amendedRegistrationStatement" - certificate_encoded = get_filing_document(business["identifier"], filing.id, certificate_pdf_type, token) - if certificate_encoded: - pdfs.append( - { - "fileName": "AmendedRegistrationStatement.pdf", - "fileBytes": certificate_encoded.decode("utf-8"), - "fileUrl": "", - "attachOrder": str(attach_order) - } - ) - attach_order += 1 - return pdfs - - -def process(email_info: dict, token: str) -> dict: # pylint: disable=too-many-locals, , too-many-branches - """Build the email for Change of Registration notification.""" - current_app.logger.debug("change_of_registration_notification: %s", email_info) - # get template and fill in parts - filing_type, status = email_info["type"], email_info["option"] - # get template vars from filing - filing, business, leg_tmz_filing_date, leg_tmz_effective_date = get_filing_info(email_info["filingId"]) - filing_name = filing.filing_type[0].upper() + " ".join(re.findall("[a-zA-Z][^A-Z]*", filing.filing_type[1:])) - - template = Path( - f'{current_app.config.get("TEMPLATE_PATH")}/CHGREG-{status}.html' - ).read_text() - filled_template = substitute_template_parts(template) - - # render template with vars - jnja_template = Template(filled_template, autoescape=True) - filing_data = (filing.json)["filing"][f"{filing_type}"] - html_out = jnja_template.render( - business=business, - filing=filing_data, - header=(filing.json)["filing"]["header"], - filing_date_time=leg_tmz_filing_date, - effective_date_time=leg_tmz_effective_date, - entity_dashboard_url=current_app.config.get("DASHBOARD_URL") + - (filing.json)["filing"]["business"].get("identifier", ""), - email_header=filing_name.upper(), - filing_type=filing_type - ) - - # get attachments - pdfs = _get_pdfs(status, token, business, filing, leg_tmz_filing_date, leg_tmz_effective_date) - - # get recipients - recipients = [] - - for party in filing.filing_json["filing"]["changeOfRegistration"]["parties"]: - for role in party["roles"]: - if role["roleType"] in ("Partner", "Proprietor", "Completing Party"): - recipients.append(party["officer"].get("email")) - break - - if filing.filing_json["filing"]["changeOfRegistration"].get("contactPoint"): - recipients.append(filing.filing_json["filing"]["changeOfRegistration"]["contactPoint"]["email"]) - - if filing.submitter_roles and UserRoles.staff in filing.submitter_roles: - # when staff do filing documentOptionalEmail may contain completing party email - recipients.append(filing.filing_json["filing"]["header"].get("documentOptionalEmail")) - else: - recipients.append(get_user_email_from_auth(filing.filing_submitter.username, token)) - - recipients = list(set(recipients)) - recipients = ", ".join(filter(None, recipients)).strip() - - # assign subject - if status == Filing.Status.PAID.value: - subject = "Confirmation of Filing from the Business Registry" - - elif status == Filing.Status.COMPLETED.value: - subject = "Change of Registration Documents from the Business Registry" - - if not subject: # fallback case - should never happen - subject = "Notification from the BC Business Registry" - - legal_name = business.get("businessName") or business.get("legalName") - subject = f"{legal_name} - {subject}" if legal_name else subject - return { - "recipients": recipients, - "requestBy": "BCRegistries@gov.bc.ca", - "content": { - "subject": subject, - "body": f"{html_out}", - "attachments": pdfs - } - } diff --git a/queue_services/business-emailer/src/business_emailer/email_processors/filing_notification.py b/queue_services/business-emailer/src/business_emailer/email_processors/filing_notification.py index 5e283415ce..88691bdc95 100644 --- a/queue_services/business-emailer/src/business_emailer/email_processors/filing_notification.py +++ b/queue_services/business-emailer/src/business_emailer/email_processors/filing_notification.py @@ -33,7 +33,7 @@ OFFICE_NAME, get_legal_type_key, ) -from business_model.models import Business, Filing, UserRoles +from business_model.models import Business, CorpType, Filing, UserRoles def _get_additional_info(filing: Filing) -> dict: @@ -118,6 +118,9 @@ def process(email_info: dict, token: str) -> dict: business_name = business.get("legalName") or NOT_AVAILABLE filing_name_short = FILING_TITLE_SHORT.get(filing_type) legal_type_key = get_legal_type_key(legal_type) + business_description = "Business" + if corp_type := CorpType.find_by_id(legal_type): + business_description: str = corp_type.full_desc.replace("BC ", "") business_number = None if len(business.get("taxId", "")) > 9: # noqa: PLR2004 @@ -145,6 +148,7 @@ def process(email_info: dict, token: str) -> dict: entity_dashboard_url=dashboard_url, filing_type=filing_type, attachments_list=attachments_list, + business_description=business_description, business_name=business_name, business_identifier=business_identifier, business_number=business_number, @@ -152,6 +156,7 @@ def process(email_info: dict, token: str) -> dict: filing_name_short=filing_name_short, future_attachments_list=future_attachments, office_name=OFFICE_NAME.get(legal_type_key), + number_description="Registration" if legal_type_key == "FIRM" else "Incorporation", show_effective_date=show_effective_date, ) diff --git a/queue_services/business-emailer/src/business_emailer/email_processors/registration_notification.py b/queue_services/business-emailer/src/business_emailer/email_processors/registration_notification.py deleted file mode 100644 index e8162cb986..0000000000 --- a/queue_services/business-emailer/src/business_emailer/email_processors/registration_notification.py +++ /dev/null @@ -1,176 +0,0 @@ -# Copyright © 2022 Province of British Columbia -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -"""Email processing rules and actions for Registration Application notifications.""" -from __future__ import annotations - -import base64 -import re -from http import HTTPStatus -from pathlib import Path - -import requests -from flask import current_app -from jinja2 import Template - -from business_emailer.email_processors import ( - get_entity_dashboard_url, - get_filing_document, - get_filing_info, - substitute_template_parts, -) -from business_model.models import Business, CorpType, Filing - - -def _get_pdfs( # noqa: PLR0913 - status: str, - token: str, - business: dict, - filing: Filing, - filing_date_time: str, - effective_date: str) -> list: - # pylint: disable=too-many-locals, too-many-branches, too-many-statements, too-many-arguments - """Get the pdfs for the registration output.""" - pdfs = [] - attach_order = 1 - headers = { - "Accept": "application/pdf", - "Authorization": f"Bearer {token}" - } - - if status == Filing.Status.PAID.value: - name_request = filing.json["filing"]["registration"]["nameRequest"] - corp_name = name_request.get("legalName") - business_data = Business.find_by_internal_id(filing.business_id) - receipt = requests.post( - f'{current_app.config.get("PAY_API_URL")}/{filing.payment_token}/receipts', - json={ - "corpName": corp_name, - "filingDateTime": filing_date_time, - "effectiveDateTime": effective_date if effective_date != filing_date_time else "", - "filingIdentifier": str(filing.id), - "businessNumber": business_data.tax_id if business_data and business_data.tax_id else "" - }, - headers=headers - ) - if receipt.status_code != HTTPStatus.CREATED: - current_app.logger.error("Failed to get receipt pdf for filing: %s", filing.id) - else: - receipt_encoded = base64.b64encode(receipt.content) - pdfs.append( - { - "fileName": "Receipt.pdf", - "fileBytes": receipt_encoded.decode("utf-8"), - "fileUrl": "", - "attachOrder": str(attach_order) - } - ) - attach_order += 1 - elif status == Filing.Status.COMPLETED.value: - filing_pdf_type = "registration" - filing_pdf_encoded = get_filing_document(business["identifier"], filing.id, filing_pdf_type, token) - if filing_pdf_encoded: - pdfs.append( - { - "fileName": "Statement of Registration.pdf", - "fileBytes": filing_pdf_encoded.decode("utf-8"), - "fileUrl": "", - "attachOrder": str(attach_order) - } - ) - attach_order += 1 - - return pdfs - - -def process(email_info: dict, token: str) -> dict: # pylint: disable=too-many-locals, , too-many-branches - """Build the email for Registration notification.""" - current_app.logger.debug("registration_notification: %s", email_info) - # get template and fill in parts - filing_type, status = email_info["type"], email_info["option"] - # get template vars from filing - filing, business, leg_tmz_filing_date, leg_tmz_effective_date = get_filing_info(email_info["filingId"]) - filing_name = filing.filing_type[0].upper() + " ".join(re.findall("[a-zA-Z][^A-Z]*", filing.filing_type[1:])) - filing_data = (filing.json)["filing"][filing_type] - if not business: # if filing status PAID - business = filing_data["nameRequest"] - business["identifier"] = filing.temp_reg - identifier = business.get("identifier") - name_request = filing.json["filing"]["registration"]["nameRequest"] - corp_type = CorpType.find_by_id(name_request.get("legalType")) - - template = Path( - f'{current_app.config.get("TEMPLATE_PATH")}/REG-{status}.html' - ).read_text() - filled_template = substitute_template_parts(template) - # render template with vars - jnja_template = Template(filled_template, autoescape=True) - html_out = jnja_template.render( - business=business, - filing=filing_data, - header=(filing.json)["filing"]["header"], - filing_date_time=leg_tmz_filing_date, - effective_date_time=leg_tmz_effective_date, - entity_dashboard_url=get_entity_dashboard_url(identifier, token), - email_header=filing_name.upper(), - filing_type=filing_type, - status=status, - entityDescription=corp_type.full_desc if corp_type else "" - ) - - # get attachments - pdfs = _get_pdfs(status, token, business, filing, leg_tmz_filing_date, leg_tmz_effective_date) - - # get recipients - recipients = [] - - for party in filing.filing_json["filing"]["registration"]["parties"]: - for role in party["roles"]: - if role["roleType"] == "Completing Party": - recipients.append(party["officer"].get("email")) - break - - if status == Filing.Status.COMPLETED.value: - recipients.append(filing.filing_json["filing"]["registration"]["contactPoint"]["email"]) - - for party in filing.filing_json["filing"]["registration"]["parties"]: - for role in party["roles"]: - if role["roleType"] in ("Partner", "Proprietor"): - recipients.append(party["officer"].get("email")) - break - - recipients = list(set(recipients)) - recipients = ", ".join(filter(None, recipients)).strip() - - # assign subject - if status == Filing.Status.PAID.value: - subject = "Confirmation of Filing from the Business Registry" - - elif status == Filing.Status.COMPLETED.value: - subject = "Registration Documents from the Business Registry" - - if not subject: # fallback case - should never happen - subject = "Notification from the BC Business Registry" - - legal_name = name_request.get("legalName", None) - subject = f"{legal_name} - {subject}" if legal_name else subject - - return { - "recipients": recipients, - "requestBy": "BCRegistries@gov.bc.ca", - "content": { - "subject": subject, - "body": f"{html_out}", - "attachments": pdfs - } - } diff --git a/queue_services/business-emailer/src/business_emailer/email_processors/util.py b/queue_services/business-emailer/src/business_emailer/email_processors/util.py index e2ba19e699..2e8a728421 100644 --- a/queue_services/business-emailer/src/business_emailer/email_processors/util.py +++ b/queue_services/business-emailer/src/business_emailer/email_processors/util.py @@ -32,7 +32,9 @@ "annualReport": "Annual Report", "changeOfDirectors": "Director Change", "changeOfAddress": "Address Change", + "changeOfRegistration": "Change of Registration", "incorporationApplication": "Incorporation Application", + "registration": "Registration", } FILING_TITLE_SHORT = { @@ -85,6 +87,14 @@ } }, "FIRM": { + "changeOfRegistration": { + "attachments": ["Change of Registration", "Amended Registration Statement", "Receipt"], + "extraPdfTypes": ["amendedRegistrationStatement"], + }, + "registration": { + "attachments": ["Statement of Registration","Receipt"], + "extraPdfTypes": [], + } } } diff --git a/queue_services/business-emailer/src/business_emailer/email_templates/CHGREG-COMPLETED.html b/queue_services/business-emailer/src/business_emailer/email_templates/CHGREG-COMPLETED.html deleted file mode 100644 index f632a39fa8..0000000000 --- a/queue_services/business-emailer/src/business_emailer/email_templates/CHGREG-COMPLETED.html +++ /dev/null @@ -1,39 +0,0 @@ - - - - - - - - - Confirmation of Change of Registration - [[style.html]] - - - - - - - - - - diff --git a/queue_services/business-emailer/src/business_emailer/email_templates/CHGREG-PAID.html b/queue_services/business-emailer/src/business_emailer/email_templates/CHGREG-PAID.html deleted file mode 100644 index b27e4cbaf8..0000000000 --- a/queue_services/business-emailer/src/business_emailer/email_templates/CHGREG-PAID.html +++ /dev/null @@ -1,43 +0,0 @@ - - - - - - - - - You have successfully submitted your change of registration with the BC Business Registry. - [[style.html]] - - - - - - - - - - diff --git a/queue_services/business-emailer/src/business_emailer/email_templates/REG-COMPLETED.html b/queue_services/business-emailer/src/business_emailer/email_templates/REG-COMPLETED.html deleted file mode 100644 index 5fcba59df4..0000000000 --- a/queue_services/business-emailer/src/business_emailer/email_templates/REG-COMPLETED.html +++ /dev/null @@ -1,59 +0,0 @@ - - - - - - - - - You have successfully registered your business with BC Business Registry - [[style.html]] - - - - - - - - - - diff --git a/queue_services/business-emailer/src/business_emailer/email_templates/REG-PAID.html b/queue_services/business-emailer/src/business_emailer/email_templates/REG-PAID.html deleted file mode 100644 index 9b23034568..0000000000 --- a/queue_services/business-emailer/src/business_emailer/email_templates/REG-PAID.html +++ /dev/null @@ -1,46 +0,0 @@ - - - - - - - - - We have received your application to register a {{ entityDescription }} - [[style.html]] - - - - - - - - - - diff --git a/queue_services/business-emailer/src/business_emailer/email_templates/changeOfRegistration.md b/queue_services/business-emailer/src/business_emailer/email_templates/changeOfRegistration.md new file mode 100644 index 0000000000..a837c147ce --- /dev/null +++ b/queue_services/business-emailer/src/business_emailer/email_templates/changeOfRegistration.md @@ -0,0 +1,13 @@ +# You have successfully completed your change of registration with the BC Business Registry + +--- + +[[business-tombstone.md]] + +--- + +[[attachments.md]] + +--- + +[[business-registry-footer.md]] \ No newline at end of file diff --git a/queue_services/business-emailer/src/business_emailer/email_templates/common/business-tombstone.md b/queue_services/business-emailer/src/business_emailer/email_templates/common/business-tombstone.md index ae6de6a0e5..3bd24a85e8 100644 --- a/queue_services/business-emailer/src/business_emailer/email_templates/common/business-tombstone.md +++ b/queue_services/business-emailer/src/business_emailer/email_templates/common/business-tombstone.md @@ -1,5 +1,5 @@ **Business Name:** {{ business_name }} -**Incorporation Number:** {{ business_identifier }} +**{{number_description}} Number:** {{ business_identifier }} {% if business_number -%} **Business Number:** {{ business_number }} {% endif -%} diff --git a/queue_services/business-emailer/src/business_emailer/email_templates/registration.md b/queue_services/business-emailer/src/business_emailer/email_templates/registration.md new file mode 100644 index 0000000000..c418965403 --- /dev/null +++ b/queue_services/business-emailer/src/business_emailer/email_templates/registration.md @@ -0,0 +1,24 @@ +# You have successfully registered your {{business_description}} with the BC Business Registry + +--- + +[[business-tombstone.md]] + +--- + +[[attachments.md]] + +--- + +## About these documents +The Statement of Registration is an important document that can be used to open a business account with your bank or credit union. It should be kept in safe space. + +--- +{% if business_number == 'Not Available' -%} + +[[business-number.md]] + +--- +{% endif -%} + +[[business-registry-footer.md]] \ No newline at end of file diff --git a/queue_services/business-emailer/src/business_emailer/resources/business_emailer.py b/queue_services/business-emailer/src/business_emailer/resources/business_emailer.py index 1147e1510a..a6d390b82f 100644 --- a/queue_services/business-emailer/src/business_emailer/resources/business_emailer.py +++ b/queue_services/business-emailer/src/business_emailer/resources/business_emailer.py @@ -51,7 +51,6 @@ ar_reminder_notification, bn_notification, cease_receiver_notification, - change_of_registration_notification, consent_amalgamation_out_notification, consent_continuation_out_notification, continuation_in_notification, @@ -65,7 +64,6 @@ name_request, notice_of_withdrawal_notification, nr_notification, - registration_notification, restoration_notification, special_resolution_notification, ) @@ -241,15 +239,9 @@ def process_email(ce: SimpleCloudEvent): # pylint: disable=too-many-branches, t elif etype == "dissolution": email = dissolution_notification.process(email_msg["email"], token) send_email(email, token) - elif etype == "registration": - email = registration_notification.process(email_msg["email"], token) - send_email(email, token) elif etype == "restoration": email_object = restoration_notification.process(email_msg["email"], token) send_email(email_object, token) - elif etype == "changeOfRegistration": - email = change_of_registration_notification.process(email_msg["email"], token) - send_email(email, token) elif etype == "correction": email = correction_notification.process(email_msg["email"], token) send_email(email, token) diff --git a/queue_services/business-emailer/tests/unit/__init__.py b/queue_services/business-emailer/tests/unit/__init__.py index 67582641ac..4f21f314a6 100644 --- a/queue_services/business-emailer/tests/unit/__init__.py +++ b/queue_services/business-emailer/tests/unit/__init__.py @@ -142,7 +142,7 @@ def prep_incorp_filing(session, identifier, payment_id, option, legal_type=None) return filing -def prep_registration_filing(session, identifier, payment_id, option, legal_type, legal_name): +def prep_registration_filing(session, identifier, payment_id, option, legal_type, legal_name, parties=None): """Return a new registration filing prepped for email notification.""" now = datetime.now().strftime('%Y-%m-%d') REGISTRATION['business']['naics'] = { @@ -187,7 +187,7 @@ def prep_registration_filing(session, identifier, payment_id, option, legal_type if option == 'PAID': del filing_template['filing']['business'] elif option == 'COMPLETED': - business = create_business(identifier, legal_type) + business = create_business(identifier, legal_type, parties=parties) business.founding_date = datetime.fromisoformat(now) business.save() business_id = business.id diff --git a/queue_services/business-emailer/tests/unit/email_processors/test_change_of_registration_notification.py b/queue_services/business-emailer/tests/unit/email_processors/test_change_of_registration_notification.py deleted file mode 100644 index a192dcbe59..0000000000 --- a/queue_services/business-emailer/tests/unit/email_processors/test_change_of_registration_notification.py +++ /dev/null @@ -1,144 +0,0 @@ -# Copyright © 2022 Province of British Columbia -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -"""The Unit Tests for the Change of Registration email processor.""" -import base64 -from unittest.mock import patch - -import pytest -import requests_mock -from business_model.models import Business - -from business_emailer.email_processors import change_of_registration_notification -from tests.unit import prep_change_of_registration_filing - - -@pytest.mark.parametrize('status,legal_type,submitter_role', [ - ('PAID', Business.LegalTypes.SOLE_PROP.value, None), - ('COMPLETED', Business.LegalTypes.SOLE_PROP.value, None), - ('PAID', Business.LegalTypes.PARTNERSHIP.value, None), - ('COMPLETED', Business.LegalTypes.PARTNERSHIP.value, None), - - ('PAID', Business.LegalTypes.SOLE_PROP.value, 'staff'), - ('COMPLETED', Business.LegalTypes.SOLE_PROP.value, 'staff'), - ('PAID', Business.LegalTypes.PARTNERSHIP.value, 'staff'), - ('COMPLETED', Business.LegalTypes.PARTNERSHIP.value, 'staff'), -]) -def test_change_of_registration_notification(app, session, mocker, status, legal_type, submitter_role): - """Assert that email attributes are correct.""" - # setup filing + business for email - legal_name = 'test business' - parties = [{ - 'firstName': 'Jane', - 'lastName': 'Doe', - 'middleInitial': 'A', - 'partyType': 'person', - 'organizationName': '' - }] - filing = prep_change_of_registration_filing(session, 'FM1234567', '1', legal_type, - legal_name, submitter_role, parties) - token = 'token' - # test processor - mocker.patch( - 'business_emailer.email_processors.change_of_registration_notification.get_user_email_from_auth', - return_value='user@email.com') - with patch.object(change_of_registration_notification, '_get_pdfs', return_value=[]) as mock_get_pdfs: - email = change_of_registration_notification.process( - {'filingId': filing.id, 'type': 'changeOfRegistration', 'option': status}, token) - if status == 'PAID': - assert email['content']['subject'] == 'test business - Confirmation of Filing from the Business Registry' - else: - assert email['content']['subject'] == \ - 'test business - Change of Registration Documents from the Business Registry' - - if submitter_role: - assert f'{submitter_role}@email.com' in email['recipients'] - else: - assert 'user@email.com' in email['recipients'] - - if status == 'COMPLETED': - assert 'no_one@never.get' in email['recipients'] - if legal_type == Business.LegalTypes.PARTNERSHIP.value: - assert 'party@email.com' in email['recipients'] - - assert email['content']['body'] - assert email['content']['attachments'] == [] - assert mock_get_pdfs.call_args[0][0] == status - assert mock_get_pdfs.call_args[0][1] == token - if status == 'COMPLETED': - assert mock_get_pdfs.call_args[0][2]['identifier'] == 'FM1234567' - assert mock_get_pdfs.call_args[0][3] == filing - - -def _cor_filing(session, identifier='FM1234567'): - parties = [{ - 'firstName': 'Jane', 'lastName': 'Doe', 'middleInitial': 'A', - 'partyType': 'person', 'organizationName': '' - }] - return prep_change_of_registration_filing( - session, identifier, '1', Business.LegalTypes.SOLE_PROP.value, 'test business', None, parties) - - -def test_change_of_registration_attachments_paid(session, mocker, config): - """Assert PAID path attaches the filing PDF and the receipt.""" - identifier = 'FM1234567' - filing = _cor_filing(session, identifier) - token = 'token' - mocker.patch( - 'business_emailer.email_processors.change_of_registration_notification.get_user_email_from_auth', - return_value='user@email.com') - with requests_mock.Mocker() as m: - m.get( - f'{config.get("LEGAL_API_URL")}/businesses/{identifier}' - f'/filings/{filing.id}/documents/changeOfRegistration', - content=b'pdf_content_1', - status_code=200, - ) - m.post( - f'{config.get("PAY_API_URL")}/{filing.payment_token}/receipts', - content=b'pdf_content_2', - status_code=201, - ) - output = change_of_registration_notification.process( - {'filingId': filing.id, 'type': 'changeOfRegistration', 'option': 'PAID'}, token) - - attachments = output['content']['attachments'] - assert len(attachments) == 2 - assert attachments[0]['fileName'] == 'Change of Registration.pdf' - assert base64.b64decode(attachments[0]['fileBytes']).decode('utf-8') == 'pdf_content_1' - assert attachments[1]['fileName'] == 'Receipt.pdf' - assert base64.b64decode(attachments[1]['fileBytes']).decode('utf-8') == 'pdf_content_2' - - -def test_change_of_registration_attachments_completed(session, mocker, config): - """Assert COMPLETED path attaches only the Amended Registration Statement.""" - identifier = 'FM1234567' - filing = _cor_filing(session, identifier) - token = 'token' - mocker.patch( - 'business_emailer.email_processors.change_of_registration_notification.get_user_email_from_auth', - return_value='user@email.com') - with requests_mock.Mocker() as m: - m.get( - f'{config.get("LEGAL_API_URL")}/businesses/{identifier}' - f'/filings/{filing.id}/documents/amendedRegistrationStatement', - content=b'pdf_content_1', - status_code=200, - ) - output = change_of_registration_notification.process( - {'filingId': filing.id, 'type': 'changeOfRegistration', 'option': 'COMPLETED'}, token) - - attachments = output['content']['attachments'] - assert len(attachments) == 1 - assert attachments[0]['fileName'] == 'AmendedRegistrationStatement.pdf' - assert base64.b64decode(attachments[0]['fileBytes']).decode('utf-8') == 'pdf_content_1' diff --git a/queue_services/business-emailer/tests/unit/email_processors/test_email_processors_init.py b/queue_services/business-emailer/tests/unit/email_processors/test_email_processors_init.py index 3de7344d65..762947e387 100644 --- a/queue_services/business-emailer/tests/unit/email_processors/test_email_processors_init.py +++ b/queue_services/business-emailer/tests/unit/email_processors/test_email_processors_init.py @@ -17,11 +17,16 @@ from business_model.models import Business +from unittest.mock import patch + from business_emailer.email_processors import ( get_account_by_affiliated_identifier, get_entity_dashboard_url, get_filled_template, get_org_id_for_temp_identifier, + get_party_emails, + get_recipient_from_auth, + get_recipients, get_subject, substitute_template_parts, ) @@ -202,3 +207,216 @@ def test_get_subject_numbered_company(app, business_name, legal_type): expected_description = Business.BUSINESSES[Business.LegalTypes(legal_type)]['numberedDescription'] assert subject == f'{expected_description} - Incorporation Application Filed' + + +# --------------------------------------------------------------------------- +# get_party_emails +# --------------------------------------------------------------------------- + +def test_get_party_emails_returns_email_for_matching_role(app): + """Assert that get_party_emails returns the officer email for a party with a matching role.""" + parties = [ + { + 'officer': {'email': 'joe@example.com'}, + 'roles': [{'roleType': 'Completing Party'}, {'roleType': 'Proprietor'}] + } + ] + with app.app_context(): + result = get_party_emails(parties, ['Completing Party', 'Partner', 'Proprietor']) + assert result == ['joe@example.com'] + + +def test_get_party_emails_break_after_first_match_per_party(app): + """Assert that get_party_emails adds only one email per party even when multiple roles match.""" + parties = [ + { + 'officer': {'email': 'joe@example.com'}, + 'roles': [{'roleType': 'Completing Party'}, {'roleType': 'Partner'}] + } + ] + with app.app_context(): + result = get_party_emails(parties, ['Completing Party', 'Partner']) + assert result == ['joe@example.com'] + assert len(result) == 1 + + +def test_get_party_emails_skips_non_matching_role(app): + """Assert that get_party_emails returns an empty list when no party role matches.""" + parties = [ + { + 'officer': {'email': 'joe@example.com'}, + 'roles': [{'roleType': 'Director'}] + } + ] + with app.app_context(): + result = get_party_emails(parties, ['Completing Party', 'Partner', 'Proprietor']) + assert result == [] + + +def test_get_party_emails_includes_none_when_party_has_no_email(app): + """Assert that get_party_emails appends None when a matching party has no email field.""" + parties = [ + { + 'officer': {}, + 'roles': [{'roleType': 'Completing Party'}] + } + ] + with app.app_context(): + result = get_party_emails(parties, ['Completing Party']) + assert result == [None] + + +def test_get_party_emails_multiple_parties_each_contribute_one_email(app): + """Assert that get_party_emails collects one email per matching party.""" + parties = [ + { + 'officer': {'email': 'joe@example.com'}, + 'roles': [{'roleType': 'Partner'}] + }, + { + 'officer': {'email': 'jane@example.com'}, + 'roles': [{'roleType': 'Partner'}] + } + ] + with app.app_context(): + result = get_party_emails(parties, ['Partner']) + assert 'joe@example.com' in result + assert 'jane@example.com' in result + assert len(result) == 2 + + +# --------------------------------------------------------------------------- +# get_recipients +# --------------------------------------------------------------------------- + +def _make_filing_json(filing_type, header_name=None, contact_email='contact@example.com', + parties=None, business_identifier=None): + """Build a minimal filing_json dict for get_recipients tests.""" + header_name = header_name if header_name is not None else filing_type + filing_data = {} + if contact_email is not None: + filing_data['contactPoint'] = {'email': contact_email} + elif contact_email is None: + pass # omit contactPoint entirely + if parties is not None: + filing_data['parties'] = parties + result = { + 'filing': { + 'header': {'name': header_name}, + filing_type: filing_data, + } + } + if business_identifier: + result['filing']['business'] = {'identifier': business_identifier} + return result + + +def test_get_recipients_returns_contact_point_email(app): + """Assert that get_recipients returns the contactPoint email when no parties match.""" + filing_json = _make_filing_json( + 'incorporationApplication', + parties=[{'officer': {'email': 'other@example.com'}, 'roles': [{'roleType': 'Director'}]}] + ) + with app.app_context(): + result = get_recipients('PAID', filing_json) + assert result == 'contact@example.com' + + +def test_get_recipients_appends_party_emails_when_header_matches(app): + """Assert that party emails are appended when header.name matches filing_type.""" + filing_json = _make_filing_json( + 'incorporationApplication', + parties=[{'officer': {'email': 'comp@example.com'}, 'roles': [{'roleType': 'Completing Party'}]}] + ) + with app.app_context(): + result = get_recipients('PAID', filing_json) + assert 'contact@example.com' in result + assert 'comp@example.com' in result + + +def test_get_recipients_mras_option_excludes_party_emails(app): + """Assert that party emails are not appended when option is 'mras'.""" + filing_json = _make_filing_json( + 'incorporationApplication', + parties=[{'officer': {'email': 'comp@example.com'}, 'roles': [{'roleType': 'Completing Party'}]}] + ) + with app.app_context(): + result = get_recipients('mras', filing_json) + assert result == 'contact@example.com' + assert 'comp@example.com' not in result + + +def test_get_recipients_header_mismatch_excludes_party_emails(app): + """Assert that party emails are not appended when header.name differs from filing_type.""" + filing_json = _make_filing_json( + 'incorporationApplication', + header_name='someOtherFiling', + parties=[{'officer': {'email': 'comp@example.com'}, 'roles': [{'roleType': 'Completing Party'}]}] + ) + with app.app_context(): + result = get_recipients('PAID', filing_json) + assert result == 'contact@example.com' + assert 'comp@example.com' not in result + + +def test_get_recipients_missing_contact_point_coalesced_to_empty_string(app): + """Assert that a missing contactPoint email is coalesced to an empty string (not None).""" + filing_json = { + 'filing': { + 'header': {'name': 'incorporationApplication'}, + 'incorporationApplication': { + 'parties': [] + } + } + } + with app.app_context(): + result = get_recipients('PAID', filing_json) + # recipients starts as "" when contactPoint is absent; no parties, so stays "" + assert result == '' + + +def test_get_recipients_coop_identifier_skips_auth_call(app): + """Assert that CP (coop) identifiers return empty string without calling get_recipient_from_auth.""" + filing_json = { + 'filing': { + 'header': {'name': 'annualReport'}, + 'business': {'identifier': 'CP1234567'}, + } + } + with app.app_context(): + with patch('business_emailer.email_processors.get_recipient_from_auth') as mock_auth: + result = get_recipients('COMPLETED', filing_json, token='token', filing_type='annualReport') + assert result == '' + mock_auth.assert_not_called() + + +def test_get_recipients_non_coop_identifier_calls_get_recipient_from_auth(app): + """Assert that non-coop identifiers call get_recipient_from_auth for the business email.""" + filing_json = { + 'filing': { + 'header': {'name': 'annualReport'}, + 'business': {'identifier': 'BC1234567'}, + } + } + with app.app_context(): + with patch('business_emailer.email_processors.get_recipient_from_auth', + return_value='auth@example.com') as mock_auth: + result = get_recipients('COMPLETED', filing_json, token='token', filing_type='annualReport') + assert result == 'auth@example.com' + mock_auth.assert_called_once_with('BC1234567', 'token') + + +def test_get_recipients_uses_incorporationapplication_as_default_filing_type(app): + """Assert that filing_type defaults to 'incorporationApplication' when not provided.""" + filing_json = { + 'filing': { + 'header': {'name': 'incorporationApplication'}, + 'incorporationApplication': { + 'contactPoint': {'email': 'default@example.com'}, + 'parties': [] + } + } + } + with app.app_context(): + result = get_recipients('PAID', filing_json) + assert result == 'default@example.com' diff --git a/queue_services/business-emailer/tests/unit/email_processors/test_filing_notification.py b/queue_services/business-emailer/tests/unit/email_processors/test_filing_notification.py index 6d0ee0f256..6d2c22ef4f 100644 --- a/queue_services/business-emailer/tests/unit/email_processors/test_filing_notification.py +++ b/queue_services/business-emailer/tests/unit/email_processors/test_filing_notification.py @@ -18,14 +18,83 @@ import pytest import requests_mock -from business_model.models import Business +from business_model.models import Business, CorpType from registry_schemas.example_data import INCORPORATION_FILING_TEMPLATE from business_emailer.email_processors import filing_notification -from tests.unit import create_filing, prep_incorp_filing, prep_maintenance_filing +from tests.unit import (create_filing, prep_change_of_registration_filing, prep_incorp_filing, + prep_maintenance_filing, prep_registration_filing) from tests.unit.helpers import generate_temp_filing, make_future_effective, make_non_future_effective +# --------------------------------------------------------------------------- +# Shared fixtures and helpers +# --------------------------------------------------------------------------- + +@pytest.fixture +def mock_pdfs(mocker): + """Patch get_pdfs to return no attachments; yields the mock for call_args assertions.""" + return mocker.patch.object(filing_notification, 'get_pdfs', return_value=[]) + + +@pytest.fixture +def mock_recipients(mocker): + """Patch get_recipients to return a fixed recipient; yields the mock for call_args assertions.""" + return mocker.patch.object(filing_notification, 'get_recipients', return_value='test@test.com') + + +@pytest.fixture +def mock_user_email(mocker): + """Patch get_user_email_from_auth to return a fixed user email.""" + return mocker.patch.object(filing_notification, 'get_user_email_from_auth', return_value='user@email.com') + + +def process_filing(filing, filing_type, option, token='token'): + """Run the filing_notification processor for the given filing.""" + return filing_notification.process( + {'filingId': filing.id, 'type': filing_type, 'option': option}, token) + + +def assert_attachment(attachment, file_name, content=None, order=None): + """Assert an attachment's file name and (optionally) its decoded content and attach order.""" + assert attachment['fileName'] == file_name + if content is not None: + assert base64.b64decode(attachment['fileBytes']).decode('utf-8') == content + if order is not None: + assert attachment['attachOrder'] == order + + +def mock_filing_docs(m, config, identifier, filing, doc_contents, receipt=b'receipt_content'): + """Register the document GET mocks and the receipt POST mock for a filing. + + doc_contents maps document_type -> response bytes. Pass receipt=None to skip the receipt mock. + """ + for doc_type, content in doc_contents.items(): + m.get( + f'{config.get("LEGAL_API_URL")}/businesses/{identifier}' + f'/filings/{filing.id}/documents/{doc_type}', + content=content, + status_code=200, + ) + if receipt is not None: + m.post( + f'{config.get("PAY_API_URL")}/{filing.payment_token}/receipts', + content=receipt, + status_code=201, + ) + + +def firm_parties(): + """Return a fresh single-person FIRM party list.""" + return [{ + 'firstName': 'Jane', + 'lastName': 'Doe', + 'middleInitial': 'A', + 'partyType': 'person', + 'organizationName': '' + }] + + def _prep_numbered_incorp_filing(session, identifier, payment_id, legal_type): """Return a PAID IA filing for a numbered company (business has no legal name in DB).""" filing_template = copy.deepcopy(INCORPORATION_FILING_TEMPLATE) @@ -57,72 +126,64 @@ def _prep_numbered_incorp_filing(session, identifier, payment_id, legal_type): # test_incorp_notification (split from original parametrized test) # --------------------------------------------------------------------------- -def test_incorp_notification_completed(app, session): +def test_incorp_notification_completed(app, session, mock_pdfs): """Assert that IA COMPLETED returns an email with the Successful Incorporation subject.""" filing = prep_incorp_filing(session, 'BC1234567', '1', 'COMPLETED', 'BC') token = 'token' - with patch.object(filing_notification, 'get_pdfs', return_value=[]) as mock_get_pdfs: - email = filing_notification.process( - {'filingId': filing.id, 'type': 'incorporationApplication', 'option': 'COMPLETED'}, token) - - assert email is not None - assert email['content']['subject'] == 'test business - Successful Incorporation' - assert 'test@test.com' in email['recipients'] - assert email['content']['body'] - assert email['content']['attachments'] == [] - assert mock_get_pdfs.call_args[0][0] == token - assert mock_get_pdfs.call_args[0][1]['identifier'] == 'BC1234567' - assert mock_get_pdfs.call_args[0][1]['legalType'] == 'BC' - assert mock_get_pdfs.call_args[0][2] == filing - - -def test_incorp_notification_paid_non_future_returns_none(app, session): - """Assert that IA PAID non-future-effective returns None (no email sent).""" - filing = prep_incorp_filing(session, 'BC1234567', '1', 'PAID', 'BC') - make_non_future_effective(filing) - token = 'token' - result = filing_notification.process( - {'filingId': filing.id, 'type': 'incorporationApplication', 'option': 'PAID'}, token) + email = process_filing(filing, 'incorporationApplication', 'COMPLETED') - assert result is None + assert email is not None + assert email['content']['subject'] == 'test business - Successful Incorporation' + assert 'test@test.com' in email['recipients'] + assert email['content']['body'] + assert email['content']['attachments'] == [] + assert mock_pdfs.call_args[0][0] == token + assert mock_pdfs.call_args[0][1]['identifier'] == 'BC1234567' + assert mock_pdfs.call_args[0][1]['legalType'] == 'BC' + assert mock_pdfs.call_args[0][2] == filing -def test_incorp_notification_paid_future_effective(app, session): +def test_incorp_notification_paid_future_effective(app, session, mock_pdfs): """Assert that IA PAID future-effective returns an email with the Filed subject.""" filing = prep_incorp_filing(session, 'BC1234567', '1', 'PAID', 'BC') make_future_effective(filing) token = 'token' - with patch.object(filing_notification, 'get_pdfs', return_value=[]) as mock_get_pdfs: - email = filing_notification.process( - {'filingId': filing.id, 'type': 'incorporationApplication', 'option': 'PAID'}, token) + email = process_filing(filing, 'incorporationApplication', 'PAID') assert email is not None assert 'Incorporation Application Filed' in email['content']['subject'] assert email['content']['body'] assert email['content']['attachments'] == [] - assert mock_get_pdfs.call_args[0][0] == token - assert mock_get_pdfs.call_args[0][2] == filing + assert mock_pdfs.call_args[0][0] == token + assert mock_pdfs.call_args[0][2] == filing + + +@pytest.mark.parametrize('filing_type', ['incorporationApplication', 'registration']) +def test_paid_non_future_effective_returns_none(app, session, filing_type): + """Assert that a PAID non-future-effective filing returns None (no email is sent).""" + if filing_type == 'incorporationApplication': + filing = prep_incorp_filing(session, 'BC1234567', '1', 'PAID', 'BC') + make_non_future_effective(filing) + else: + filing = prep_registration_filing( + session, 'FM1234567', '1', 'PAID', Business.LegalTypes.SOLE_PROP.value, 'test business') + + result = process_filing(filing, filing_type, 'PAID') + + assert result is None # --------------------------------------------------------------------------- # test_numbered_incorp_notification # --------------------------------------------------------------------------- -@pytest.mark.parametrize('legal_type', [ - ('BEN'), - ('BC'), - ('ULC'), - ('CC'), -]) -def test_numbered_incorp_notification_future_effective(app, session, mocker, legal_type): +@pytest.mark.parametrize('legal_type', ['BEN', 'BC', 'ULC', 'CC']) +def test_numbered_incorp_notification_future_effective(app, session, legal_type, mock_pdfs): """Assert that future-effective PAID IA with no legal name uses the numbered description as business_name.""" # Create a filing where the business has no legal name (numbered company scenario) filing = _prep_numbered_incorp_filing(session, 'BC1234567', '1', legal_type) make_future_effective(filing) - token = 'token' - with patch.object(filing_notification, 'get_pdfs', return_value=[]): - email = filing_notification.process( - {'filingId': filing.id, 'type': 'incorporationApplication', 'option': 'PAID'}, token) + email = process_filing(filing, 'incorporationApplication', 'PAID') assert email is not None assert email['content']['body'] @@ -134,19 +195,11 @@ def test_numbered_incorp_notification_future_effective(app, session, mocker, leg assert "Business Name: Not Available" not in email['content']['body'] -@pytest.mark.parametrize('legal_type', [ - ('BEN'), - ('BC'), - ('ULC'), - ('CC'), -]) -def test_numbered_incorp_notification_completed(app, session, legal_type): +@pytest.mark.parametrize('legal_type', ['BEN', 'BC', 'ULC', 'CC']) +def test_numbered_incorp_notification_completed(app, session, legal_type, mock_pdfs): """Assert that for a COMPLETED IA the business legalName is used in the subject.""" filing = prep_incorp_filing(session, 'BC1234567', '1', 'COMPLETED', legal_type=legal_type) - token = 'token' - with patch.object(filing_notification, 'get_pdfs', return_value=[]): - email = filing_notification.process( - {'filingId': filing.id, 'type': 'incorporationApplication', 'option': 'COMPLETED'}, token) + email = process_filing(filing, 'incorporationApplication', 'COMPLETED') assert email is not None assert email['content']['body'] @@ -166,7 +219,8 @@ def test_numbered_incorp_notification_completed(app, session, legal_type): ('COMPLETED', 'alteration', None), ('COMPLETED', 'alteration', 'staff') ]) -def test_maintenance_notification(app, session, mocker, status, filing_type, submitter_role): +def test_maintenance_notification(app, session, status, filing_type, submitter_role, + mock_pdfs, mock_recipients, mock_user_email): """Assert that maintenance filings produce an email with the correct recipients and attachments.""" # setup filing + business for email filing = prep_maintenance_filing(session, 'BC1234567', '1', status, filing_type, submitter_role=submitter_role) @@ -174,33 +228,25 @@ def test_maintenance_notification(app, session, mocker, status, filing_type, sub make_future_effective(filing) token = 'token' # test processor - mocker.patch( - 'business_emailer.email_processors.filing_notification.get_user_email_from_auth', - return_value='user@email.com') - with patch.object(filing_notification, 'get_user_email_from_auth', return_value='user@email.com'): - with patch.object(filing_notification, 'get_pdfs', return_value=[]) as mock_get_pdfs: - with patch.object(filing_notification, 'get_recipients', return_value='test@test.com') \ - as mock_get_recipients: - email = filing_notification.process( - {'filingId': filing.id, 'type': filing_type, 'option': status}, token) - - if filing_type == 'alteration': - if submitter_role: - assert f'{submitter_role}@email.com' in email['recipients'] - else: - assert 'user@email.com' in email['recipients'] - - assert 'test@test.com' in email['recipients'] - assert email['content']['body'] - assert email['content']['attachments'] == [] - assert mock_get_pdfs.call_args[0][0] == token - assert mock_get_pdfs.call_args[0][1]['identifier'] == 'BC1234567' - assert mock_get_pdfs.call_args[0][1]['legalType'] == Business.LegalTypes.BCOMP.value - assert mock_get_pdfs.call_args[0][1]['legalName'] == 'test business' - assert mock_get_pdfs.call_args[0][2] == filing - assert mock_get_recipients.call_args[0][0] == status - assert mock_get_recipients.call_args[0][1] == filing.filing_json - assert mock_get_recipients.call_args[0][2] == token + email = process_filing(filing, filing_type, status) + + if filing_type == 'alteration': + if submitter_role: + assert f'{submitter_role}@email.com' in email['recipients'] + else: + assert 'user@email.com' in email['recipients'] + + assert 'test@test.com' in email['recipients'] + assert email['content']['body'] + assert email['content']['attachments'] == [] + assert mock_pdfs.call_args[0][0] == token + assert mock_pdfs.call_args[0][1]['identifier'] == 'BC1234567' + assert mock_pdfs.call_args[0][1]['legalType'] == Business.LegalTypes.BCOMP.value + assert mock_pdfs.call_args[0][1]['legalName'] == 'test business' + assert mock_pdfs.call_args[0][2] == filing + assert mock_recipients.call_args[0][0] == status + assert mock_recipients.call_args[0][1] == filing.filing_json + assert mock_recipients.call_args[0][2] == token def test_filing_attachments_ia_paid_future_effective(session, config): @@ -208,77 +254,30 @@ def test_filing_attachments_ia_paid_future_effective(session, config): identifier = 'BC1234567' filing = prep_incorp_filing(session, identifier, '1', 'PAID', 'BC') make_future_effective(filing) - token = 'token' with requests_mock.Mocker() as m: - m.get( - f'{config.get("LEGAL_API_URL")}/businesses/{identifier}' - f'/filings/{filing.id}/documents/incorporationApplication', - content=b'pdf_content_1', - status_code=200, - ) - m.post( - f'{config.get("PAY_API_URL")}/{filing.payment_token}/receipts', - content=b'pdf_content_2', - status_code=201, - ) - output = filing_notification.process( - {'filingId': filing.id, 'type': 'incorporationApplication', 'option': 'PAID'}, token) + mock_filing_docs(m, config, identifier, filing, + {'incorporationApplication': b'pdf_content_1'}, receipt=b'pdf_content_2') + output = process_filing(filing, 'incorporationApplication', 'PAID') assert output is not None attachments = output['content']['attachments'] # IA always adds the filing application PDF + receipt. assert len(attachments) == 2 - assert attachments[0]['fileName'] == 'Incorporation Application.pdf' - assert attachments[0]['attachOrder'] == '1' - assert base64.b64decode(attachments[0]['fileBytes']).decode('utf-8') == 'pdf_content_1' - assert attachments[1]['fileName'] == 'Receipt.pdf' - assert attachments[1]['attachOrder'] == '2' - assert base64.b64decode(attachments[1]['fileBytes']).decode('utf-8') == 'pdf_content_2' - - -def test_filing_attachments_ia_paid_non_future_returns_none(session, config): - """IA PAID non-future-effective: process returns None (no email is sent).""" - identifier = 'BC1234567' - filing = prep_incorp_filing(session, identifier, '1', 'PAID', 'BC') - make_non_future_effective(filing) - token = 'token' - result = filing_notification.process( - {'filingId': filing.id, 'type': 'incorporationApplication', 'option': 'PAID'}, token) - - assert result is None + assert_attachment(attachments[0], 'Incorporation Application.pdf', 'pdf_content_1', '1') + assert_attachment(attachments[1], 'Receipt.pdf', 'pdf_content_2', '2') def test_filing_attachments_ia_completed_bc(session, config): """IA COMPLETED BC: IncorporationApplication + Notice of Articles + Certificate of Incorporation + Receipt.""" identifier = 'BC1234567' filing = prep_incorp_filing(session, identifier, '1', 'COMPLETED', 'BC') - token = 'token' with requests_mock.Mocker() as m: - m.get( - f'{config.get("LEGAL_API_URL")}/businesses/{identifier}' - f'/filings/{filing.id}/documents/incorporationApplication', - content=b'pdf_content_0', - status_code=200, - ) - m.get( - f'{config.get("LEGAL_API_URL")}/businesses/{identifier}' - f'/filings/{filing.id}/documents/noticeOfArticles', - content=b'pdf_content_1', - status_code=200, - ) - m.get( - f'{config.get("LEGAL_API_URL")}/businesses/{identifier}' - f'/filings/{filing.id}/documents/certificateOfIncorporation', - content=b'pdf_content_2', - status_code=200, - ) - m.post( - f'{config.get("PAY_API_URL")}/{filing.payment_token}/receipts', - content=b'pdf_content_3', - status_code=201, - ) - output = filing_notification.process( - {'filingId': filing.id, 'type': 'incorporationApplication', 'option': 'COMPLETED'}, token) + mock_filing_docs(m, config, identifier, filing, { + 'incorporationApplication': b'pdf_content_0', + 'noticeOfArticles': b'pdf_content_1', + 'certificateOfIncorporation': b'pdf_content_2', + }, receipt=b'pdf_content_3') + output = process_filing(filing, 'incorporationApplication', 'COMPLETED') attachments = output['content']['attachments'] # IA COMPLETED BC: IncorporationApplication + NOA + Certificate + Receipt = 4 attachments @@ -295,39 +294,14 @@ def test_filing_attachments_ia_completed_coop(session, config): """IA COMPLETED COOP: IncorporationApplication + Certificate + Certified Rules + Memorandum + Receipt.""" identifier = 'CP1234567' filing = prep_incorp_filing(session, identifier, '1', 'COMPLETED', 'CP') - token = 'token' with requests_mock.Mocker() as m: - m.get( - f'{config.get("LEGAL_API_URL")}/businesses/{identifier}' - f'/filings/{filing.id}/documents/incorporationApplication', - content=b'pdf_content_0', - status_code=200, - ) - m.get( - f'{config.get("LEGAL_API_URL")}/businesses/{identifier}' - f'/filings/{filing.id}/documents/certificateOfIncorporation', - content=b'pdf_content_1', - status_code=200, - ) - m.get( - f'{config.get("LEGAL_API_URL")}/businesses/{identifier}' - f'/filings/{filing.id}/documents/certifiedRules', - content=b'pdf_content_2', - status_code=200, - ) - m.get( - f'{config.get("LEGAL_API_URL")}/businesses/{identifier}' - f'/filings/{filing.id}/documents/certifiedMemorandum', - content=b'pdf_content_3', - status_code=200, - ) - m.post( - f'{config.get("PAY_API_URL")}/{filing.payment_token}/receipts', - content=b'pdf_content_4', - status_code=201, - ) - output = filing_notification.process( - {'filingId': filing.id, 'type': 'incorporationApplication', 'option': 'COMPLETED'}, token) + mock_filing_docs(m, config, identifier, filing, { + 'incorporationApplication': b'pdf_content_0', + 'certificateOfIncorporation': b'pdf_content_1', + 'certifiedRules': b'pdf_content_2', + 'certifiedMemorandum': b'pdf_content_3', + }, receipt=b'pdf_content_4') + output = process_filing(filing, 'incorporationApplication', 'COMPLETED') attachments = output['content']['attachments'] # IA COMPLETED COOP: IncorporationApplication + Certificate + Rules + Memorandum + Receipt = 5 attachments @@ -341,127 +315,67 @@ def test_filing_attachments_ia_completed_coop(session, config): assert 'Receipt.pdf' in file_names -def test_filing_attachments_change_of_address_paid(session, mocker, config): +def test_filing_attachments_change_of_address_paid(session, config, mock_recipients): """changeOfAddress PAID: filing PDF + receipt.""" identifier = 'BC1234567' filing = prep_maintenance_filing(session, identifier, '1', 'PAID', 'changeOfAddress') make_future_effective(filing) - token = 'token' - - with patch.object(filing_notification, 'get_recipients', return_value='test@test.com'): - with requests_mock.Mocker() as m: - m.get( - f'{config.get("LEGAL_API_URL")}/businesses/{identifier}' - f'/filings/{filing.id}/documents/changeOfAddress', - content=b'pdf_content_1', - status_code=200, - ) - m.post( - f'{config.get("PAY_API_URL")}/{filing.payment_token}/receipts', - content=b'pdf_content_2', - status_code=201, - ) - output = filing_notification.process( - {'filingId': filing.id, 'type': 'changeOfAddress', 'option': 'PAID'}, token) + with requests_mock.Mocker() as m: + mock_filing_docs(m, config, identifier, filing, + {'changeOfAddress': b'pdf_content_1'}, receipt=b'pdf_content_2') + output = process_filing(filing, 'changeOfAddress', 'PAID') attachments = output['content']['attachments'] assert len(attachments) == 2 - assert attachments[0]['fileName'] == 'Address Change.pdf' - assert base64.b64decode(attachments[0]['fileBytes']).decode('utf-8') == 'pdf_content_1' - assert attachments[1]['fileName'] == 'Receipt.pdf' - assert base64.b64decode(attachments[1]['fileBytes']).decode('utf-8') == 'pdf_content_2' + assert_attachment(attachments[0], 'Address Change.pdf', 'pdf_content_1') + assert_attachment(attachments[1], 'Receipt.pdf', 'pdf_content_2') -def test_filing_attachments_annual_report_completed(session, mocker, config): +def test_filing_attachments_annual_report_completed(session, config, mock_recipients): """annualReport COMPLETED: filing PDF (prefixed with the AR year) + receipt.""" identifier = 'BC1234567' filing = prep_maintenance_filing(session, identifier, '1', 'COMPLETED', 'annualReport') - token = 'token' - with patch.object(filing_notification, 'get_recipients', return_value='test@test.com'): - with requests_mock.Mocker() as m: - m.get( - f'{config.get("LEGAL_API_URL")}/businesses/{identifier}' - f'/filings/{filing.id}/documents/annualReport', - content=b'pdf_content_1', - status_code=200, - ) - m.post( - f'{config.get("PAY_API_URL")}/{filing.payment_token}/receipts', - content=b'pdf_content_2', - status_code=201, - ) - output = filing_notification.process( - {'filingId': filing.id, 'type': 'annualReport', 'option': 'COMPLETED'}, token) + with requests_mock.Mocker() as m: + mock_filing_docs(m, config, identifier, filing, + {'annualReport': b'pdf_content_1'}, receipt=b'pdf_content_2') + output = process_filing(filing, 'annualReport', 'COMPLETED') attachments = output['content']['attachments'] # annualReport PAID: filing application PDF + receipt assert len(attachments) == 2 # _add_filing_document_pdf prefixes the AR filing PDF with the annualReportDate year (2018-04-08 -> 2018) - assert attachments[0]['fileName'] == '2018 Annual Report.pdf' - assert base64.b64decode(attachments[0]['fileBytes']).decode('utf-8') == 'pdf_content_1' - assert attachments[1]['fileName'] == 'Receipt.pdf' - assert base64.b64decode(attachments[1]['fileBytes']).decode('utf-8') == 'pdf_content_2' + assert_attachment(attachments[0], '2018 Annual Report.pdf', 'pdf_content_1') + assert_attachment(attachments[1], 'Receipt.pdf', 'pdf_content_2') -def test_filing_attachments_alteration_completed_name_change(session, mocker, config): +def test_filing_attachments_alteration_completed_name_change(session, config, mock_recipients, mock_user_email): """alteration COMPLETED (BC, with name change): NOA + Certificate of Name Change.""" identifier = 'BC1234567' filing = prep_maintenance_filing(session, identifier, '1', 'COMPLETED', 'alteration') # force the name-change branch via meta_data filing._meta_data = {'alteration': {'toLegalName': 'new name'}} filing.save() - token = 'token' - mocker.patch( - 'business_emailer.email_processors.filing_notification.get_user_email_from_auth', - return_value='user@email.com') - with patch.object(filing_notification, 'get_recipients', return_value='test@test.com'): - with requests_mock.Mocker() as m: - m.get( - f'{config.get("LEGAL_API_URL")}/businesses/{identifier}' - f'/filings/{filing.id}/documents/alteration', - content=b'pdf_content_1', - status_code=200, - ) - m.get( - f'{config.get("LEGAL_API_URL")}/businesses/{identifier}' - f'/filings/{filing.id}/documents/noticeOfArticles', - content=b'pdf_content_2', - status_code=200, - ) - m.get( - f'{config.get("LEGAL_API_URL")}/businesses/{identifier}' - f'/filings/{filing.id}/documents/certificateOfNameChange', - content=b'pdf_content_3', - status_code=200, - ) - m.post( - f'{config.get("PAY_API_URL")}/{filing.payment_token}/receipts', - content=b'pdf_content_4', - status_code=201, - ) - output = filing_notification.process( - {'filingId': filing.id, 'type': 'alteration', 'option': 'COMPLETED'}, token) + with requests_mock.Mocker() as m: + mock_filing_docs(m, config, identifier, filing, { + 'alteration': b'pdf_content_1', + 'noticeOfArticles': b'pdf_content_2', + 'certificateOfNameChange': b'pdf_content_3', + }, receipt=b'pdf_content_4') + output = process_filing(filing, 'alteration', 'COMPLETED') attachments = output['content']['attachments'] assert len(attachments) == 4 - assert attachments[0]['fileName'] == 'Alteration.pdf' - assert attachments[1]['fileName'] == 'Notice of Articles.pdf' - assert attachments[2]['fileName'] == 'Certificate of Name Change.pdf' - assert attachments[3]['fileName'] == 'Receipt.pdf' - assert base64.b64decode(attachments[0]['fileBytes']).decode('utf-8') == 'pdf_content_1' - assert base64.b64decode(attachments[1]['fileBytes']).decode('utf-8') == 'pdf_content_2' - assert base64.b64decode(attachments[2]['fileBytes']).decode('utf-8') == 'pdf_content_3' - assert base64.b64decode(attachments[3]['fileBytes']).decode('utf-8') == 'pdf_content_4' + assert_attachment(attachments[0], 'Alteration.pdf', 'pdf_content_1') + assert_attachment(attachments[1], 'Notice of Articles.pdf', 'pdf_content_2') + assert_attachment(attachments[2], 'Certificate of Name Change.pdf', 'pdf_content_3') + assert_attachment(attachments[3], 'Receipt.pdf', 'pdf_content_4') -def test_ia_future_effective_paid_body_contains_tombstone_fields(app, session): +def test_ia_future_effective_paid_body_contains_tombstone_fields(app, session, mock_pdfs): """Assert that IA future-effective PAID body contains business name and incorporation number.""" filing = prep_incorp_filing(session, 'BC1234567', '1', 'PAID', 'BC') make_future_effective(filing) - token = 'token' - with patch.object(filing_notification, 'get_pdfs', return_value=[]): - email = filing_notification.process( - {'filingId': filing.id, 'type': 'incorporationApplication', 'option': 'PAID'}, token) + email = process_filing(filing, 'incorporationApplication', 'PAID') assert email is not None body = email['content']['body'] @@ -474,13 +388,10 @@ def test_ia_future_effective_paid_body_contains_tombstone_fields(app, session): assert 'Incorporation Application Filed' in email['content']['subject'] -def test_ia_completed_body_and_subject(app, session): +def test_ia_completed_body_and_subject(app, session, mock_pdfs): """Assert that IA COMPLETED body uses non-future template and subject is Successful Incorporation.""" filing = prep_incorp_filing(session, 'BC1234567', '1', 'COMPLETED', 'BC') - token = 'token' - with patch.object(filing_notification, 'get_pdfs', return_value=[]): - email = filing_notification.process( - {'filingId': filing.id, 'type': 'incorporationApplication', 'option': 'COMPLETED'}, token) + email = process_filing(filing, 'incorporationApplication', 'COMPLETED') assert email is not None body = email['content']['body'] @@ -494,7 +405,7 @@ def test_ia_completed_body_and_subject(app, session): ("BC", None, "Not Available"), ("CP", None, None), ], ids=["BC with bn15", "BC with bn9", "BC with no bn", "CP doesn't show bn"]) -def test_business_number_rendering(app, session, legal_type, tax_id, expected): +def test_business_number_rendering(app, session, legal_type, tax_id, expected, mock_pdfs): """Assert business number renders as expected.""" identifier = f'{legal_type}1234567' @@ -503,79 +414,37 @@ def test_business_number_rendering(app, session, legal_type, tax_id, expected): business.tax_id = tax_id business.save() - token = 'token' - with patch.object(filing_notification, 'get_pdfs', return_value=[]): - email = filing_notification.process( - {'filingId': filing.id, 'type': 'incorporationApplication', 'option': 'COMPLETED'}, token) + email = process_filing(filing, 'incorporationApplication', 'COMPLETED') assert email is not None body = email['content']['body'] if expected: assert f'**Business Number:** {expected}' in body if expected == 'Not Available': - assert '## Business Number' in body + assert '## Business Number' in body else: assert '**Business Number:**' not in body assert '## Business Number' not in body -def test_future_attachments_list_in_ia_future_effective_paid_coop(app, session, mocker): - """Assert that CP future_attachments_list is used for COOP future-effective PAID IA.""" - filing = prep_incorp_filing(session, 'CP1234567', '1', 'PAID', 'CP') +@pytest.mark.parametrize(['identifier', 'legal_type'], [ + ('CP1234567', 'CP'), + ('BC1234567', 'BC'), +], ids=['coop', 'corp']) +def test_future_attachments_list_in_ia_future_effective_paid(app, session, config, identifier, legal_type): + """Assert that the future_attachments_list is used for COOP and non-COOP future-effective PAID IA.""" + filing = prep_incorp_filing(session, identifier, '1', 'PAID', legal_type) make_future_effective(filing) - token = 'token' with requests_mock.Mocker() as m: - m.get( - f'{app.config.get("LEGAL_API_URL")}/businesses/CP1234567' - f'/filings/{filing.id}/documents/incorporationApplication', - content=b'pdf_content_1', - status_code=200, - ) - m.post( - f'{app.config.get("PAY_API_URL")}/{filing.payment_token}/receipts', - content=b'pdf_content_2', - status_code=201, - ) - email = filing_notification.process( - {'filingId': filing.id, 'type': 'incorporationApplication', 'option': 'PAID'}, token) + mock_filing_docs(m, config, identifier, filing, + {'incorporationApplication': b'pdf_content_1'}, receipt=b'pdf_content_2') + email = process_filing(filing, 'incorporationApplication', 'PAID') assert email is not None attachments = email['content']['attachments'] assert len(attachments) == 2 - assert attachments[0]['fileName'] == 'Incorporation Application.pdf' - assert attachments[1]['fileName'] == 'Receipt.pdf' - assert base64.b64decode(attachments[0]['fileBytes']).decode('utf-8') == 'pdf_content_1' - assert base64.b64decode(attachments[1]['fileBytes']).decode('utf-8') == 'pdf_content_2' - - -def test_future_attachments_list_in_ia_future_effective_paid_corp(app, session, mocker): - """Assert that CORP future_attachments_list is used for non-COOP future-effective PAID IA.""" - filing = prep_incorp_filing(session, 'BC1234567', '1', 'PAID', 'BC') - make_future_effective(filing) - token = 'token' - with requests_mock.Mocker() as m: - m.get( - f'{app.config.get("LEGAL_API_URL")}/businesses/BC1234567' - f'/filings/{filing.id}/documents/incorporationApplication', - content=b'pdf_content_1', - status_code=200, - ) - m.post( - f'{app.config.get("PAY_API_URL")}/{filing.payment_token}/receipts', - content=b'pdf_content_2', - status_code=201, - ) - email = filing_notification.process( - {'filingId': filing.id, 'type': 'incorporationApplication', 'option': 'PAID'}, token) - - assert email is not None - assert email is not None - attachments = email['content']['attachments'] - assert len(attachments) == 2 - assert attachments[0]['fileName'] == 'Incorporation Application.pdf' - assert attachments[1]['fileName'] == 'Receipt.pdf' - assert base64.b64decode(attachments[0]['fileBytes']).decode('utf-8') == 'pdf_content_1' - assert base64.b64decode(attachments[1]['fileBytes']).decode('utf-8') == 'pdf_content_2' + assert_attachment(attachments[0], 'Incorporation Application.pdf', 'pdf_content_1') + assert_attachment(attachments[1], 'Receipt.pdf', 'pdf_content_2') @pytest.mark.parametrize(['filing_type', 'status', 'expected_header', 'expected_subject'], [ @@ -616,20 +485,14 @@ def test_future_attachments_list_in_ia_future_effective_paid_corp(app, session, 'test business - Successful Director Change', ), ]) -def test_maintenance_filing_fe_renders_body_and_subject(app, session, mocker, filing_type, status, expected_header, expected_subject): - """Assert alteration andaddress change future effective emails render the expected body and subject.""" +def test_maintenance_filing_fe_renders_body_and_subject(app, session, filing_type, status, expected_header, + expected_subject, mock_pdfs, mock_recipients, mock_user_email): + """Assert alteration and address change future effective emails render the expected body and subject.""" filing = prep_maintenance_filing(session, 'BC1234567', '1', status, filing_type) if status == 'PAID': make_future_effective(filing) filing.save() - token = 'token' - mocker.patch( - 'business_emailer.email_processors.filing_notification.get_user_email_from_auth', - return_value='user@email.com') - with patch.object(filing_notification, 'get_pdfs', return_value=[]): - with patch.object(filing_notification, 'get_recipients', return_value='test@test.com'): - email = filing_notification.process( - {'filingId': filing.id, 'type': filing_type, 'option': status}, token) + email = process_filing(filing, filing_type, status) assert email is not None body = email['content']['body'] @@ -637,3 +500,110 @@ def test_maintenance_filing_fe_renders_body_and_subject(app, session, mocker, fi assert not ".html]]" in body assert not ".md]]" in body assert email['content']['subject'] == expected_subject + + +# --------------------------------------------------------------------------- +# FIRM registration / changeOfRegistration via filing_notification +# --------------------------------------------------------------------------- + +@pytest.mark.parametrize(['status', 'filing_type', 'legal_type', 'submitter_role'], [ + ('COMPLETED', 'registration', Business.LegalTypes.SOLE_PROP.value, None), + ('COMPLETED', 'registration', Business.LegalTypes.PARTNERSHIP.value, None), + ('COMPLETED', 'changeOfRegistration', Business.LegalTypes.SOLE_PROP.value, None), + ('COMPLETED', 'changeOfRegistration', Business.LegalTypes.SOLE_PROP.value, 'staff'), + ('COMPLETED', 'changeOfRegistration', Business.LegalTypes.PARTNERSHIP.value, None), +]) +def test_firm_filing_via_filing_notification(app, session, status, filing_type, legal_type, submitter_role, + mock_pdfs, mock_recipients, mock_user_email): + """Assert that FIRM registration and changeOfRegistration produce correct emails via filing_notification.""" + legal_name = 'test business' + if filing_type == 'registration': + filing = prep_registration_filing(session, 'FM1234567', '1', status, legal_type, legal_name, firm_parties()) + else: + filing = prep_change_of_registration_filing( + session, 'FM1234567', '1', legal_type, legal_name, submitter_role, firm_parties()) + token = 'token' + email = process_filing(filing, filing_type, status) + + assert email is not None + body = email['content']['body'] + assert body + assert email['content']['attachments'] == [] + # FIRM legal types pass number_description="Registration" to the template, + # which renders as "Registration Number:" in business-tombstone.md. + assert 'Registration Number:' in body + assert '[[' not in body + assert ']]' not in body + assert '## Attachments' in body + if filing_type == 'registration': + assert '## About these documents' in body + assert '## Business Number' in body + assert mock_pdfs.call_args[0][1]['identifier'] == 'FM1234567' + assert mock_pdfs.call_args[0][2] == filing + assert mock_recipients.call_args[0][0] == status + assert mock_recipients.call_args[0][2] == token + assert 'test@test.com' in email['recipients'] + if filing_type == 'changeOfRegistration': + if submitter_role: + assert f'{submitter_role}@email.com' in email['recipients'] + else: + assert 'user@email.com' in email['recipients'] + + +def test_filing_attachments_registration_completed(session, config, mock_recipients): + """registration COMPLETED: Statement of Registration filing PDF + receipt.""" + identifier = 'FM1234567' + filing = prep_registration_filing( + session, identifier, '1', 'COMPLETED', Business.LegalTypes.SOLE_PROP.value, 'test business', firm_parties()) + with requests_mock.Mocker() as m: + mock_filing_docs(m, config, identifier, filing, + {'registration': b'pdf_content_1'}, receipt=b'pdf_content_2') + output = process_filing(filing, 'registration', 'COMPLETED') + + attachments = output['content']['attachments'] + # registration COMPLETED: Statement of Registration + Receipt + assert len(attachments) == 2 + # 'registration' is special-cased to the 'Statement of Registration' file name. + assert_attachment(attachments[0], 'Statement of Registration.pdf', 'pdf_content_1', '1') + assert_attachment(attachments[1], 'Receipt.pdf', 'pdf_content_2', '2') + + +def test_filing_attachments_change_of_registration_completed(session, config, mock_recipients, mock_user_email): + """changeOfRegistration COMPLETED: filing PDF + amended registration statement + receipt.""" + identifier = 'FM1234567' + filing = prep_change_of_registration_filing( + session, identifier, '1', Business.LegalTypes.SOLE_PROP.value, 'test business', None, firm_parties()) + with requests_mock.Mocker() as m: + mock_filing_docs(m, config, identifier, filing, { + 'changeOfRegistration': b'pdf_content_1', + 'amendedRegistrationStatement': b'pdf_content_2', + }, receipt=b'pdf_content_3') + output = process_filing(filing, 'changeOfRegistration', 'COMPLETED') + + attachments = output['content']['attachments'] + # changeOfRegistration COMPLETED: filing PDF + amendedRegistrationStatement (extraPdfTypes) + Receipt + assert len(attachments) == 3 + assert_attachment(attachments[0], 'Change of Registration.pdf', 'pdf_content_1', '1') + assert_attachment(attachments[1], 'Amended Registration Statement.pdf', 'pdf_content_2', '2') + assert_attachment(attachments[2], 'Receipt.pdf', 'pdf_content_3', '3') + + +@pytest.mark.parametrize(['filing_type', 'expected_subject_suffix'], [ + ('registration', 'Successful Registration'), + ('changeOfRegistration', 'Successful Change of Registration'), +]) +def test_firm_filing_subject(app, session, filing_type, expected_subject_suffix, + mock_pdfs, mock_recipients, mock_user_email): + """Assert that registration and changeOfRegistration subjects use the correct filing title.""" + legal_name = 'test business' + legal_type = Business.LegalTypes.SOLE_PROP.value + if filing_type == 'registration': + filing = prep_registration_filing(session, 'FM1234567', '1', 'COMPLETED', legal_type, legal_name, + firm_parties()) + else: + filing = prep_change_of_registration_filing( + session, 'FM1234567', '1', legal_type, legal_name, None, firm_parties()) + email = process_filing(filing, filing_type, 'COMPLETED') + + assert email is not None + assert email['content']['subject'] == f'JANE A DOE - {expected_subject_suffix}' diff --git a/queue_services/business-emailer/tests/unit/email_processors/test_registration_notification.py b/queue_services/business-emailer/tests/unit/email_processors/test_registration_notification.py deleted file mode 100644 index d3b70070da..0000000000 --- a/queue_services/business-emailer/tests/unit/email_processors/test_registration_notification.py +++ /dev/null @@ -1,112 +0,0 @@ -# Copyright © 2022 Province of British Columbia -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -"""The Unit Tests for the Registration email processor.""" -import base64 -from unittest.mock import patch - -import pytest -import requests_mock -from business_model.models import Business - -from business_emailer.email_processors import registration_notification -from tests.unit import prep_registration_filing - - -@pytest.mark.parametrize('status,legal_type', [ - ('PAID', Business.LegalTypes.SOLE_PROP.value), - ('COMPLETED', Business.LegalTypes.SOLE_PROP.value), - ('PAID', Business.LegalTypes.PARTNERSHIP.value), - ('COMPLETED', Business.LegalTypes.PARTNERSHIP.value), -]) -def test_registration_notification(app, session, mocker, status, legal_type): - """Assert that the legal name is changed.""" - # setup filing + business for email - legal_name = 'test business' - filing = prep_registration_filing(session, 'FM1234567', '1', status, legal_type, legal_name) - token = 'token' - # test processor - mocker.patch( - 'business_emailer.email_processors.registration_notification.get_entity_dashboard_url', - return_value='https://dummyurl.gov.bc.ca') - with patch.object(registration_notification, '_get_pdfs', return_value=[]) as mock_get_pdfs: - email = registration_notification.process( - {'filingId': filing.id, 'type': 'registration', 'option': status}, token) - if status == 'PAID': - assert email['content']['subject'] == legal_name + ' - Confirmation of Filing from the Business Registry' - else: - assert email['content']['subject'] == \ - legal_name + ' - Registration Documents from the Business Registry' - - assert 'joe@email.com' in email['recipients'] - if status == 'COMPLETED': - assert 'no_one@never.get' in email['recipients'] - if legal_type == Business.LegalTypes.PARTNERSHIP.value: - assert 'party@email.com' in email['recipients'] - - assert email['content']['body'] - assert email['content']['attachments'] == [] - assert mock_get_pdfs.call_args[0][0] == status - assert mock_get_pdfs.call_args[0][1] == token - if status == 'COMPLETED': - assert mock_get_pdfs.call_args[0][2]['identifier'] == 'FM1234567' - assert mock_get_pdfs.call_args[0][3] == filing - - -def test_registration_attachments_paid(session, mocker, config): - """Assert PAID path attaches only the receipt (no filing PDF).""" - identifier = 'FM1234567' - filing = prep_registration_filing( - session, identifier, '1', 'PAID', Business.LegalTypes.SOLE_PROP.value, 'test business') - token = 'token' - mocker.patch( - 'business_emailer.email_processors.registration_notification.get_entity_dashboard_url', - return_value='https://dummyurl.gov.bc.ca') - with requests_mock.Mocker() as m: - m.post( - f'{config.get("PAY_API_URL")}/{filing.payment_token}/receipts', - content=b'pdf_content_1', - status_code=201, - ) - output = registration_notification.process( - {'filingId': filing.id, 'type': 'registration', 'option': 'PAID'}, token) - - attachments = output['content']['attachments'] - assert len(attachments) == 1 - assert attachments[0]['fileName'] == 'Receipt.pdf' - assert base64.b64decode(attachments[0]['fileBytes']).decode('utf-8') == 'pdf_content_1' - - -def test_registration_attachments_completed(session, mocker, config): - """Assert COMPLETED path attaches only the Statement of Registration (no receipt).""" - identifier = 'FM1234567' - filing = prep_registration_filing( - session, identifier, '1', 'COMPLETED', Business.LegalTypes.SOLE_PROP.value, 'test business') - token = 'token' - mocker.patch( - 'business_emailer.email_processors.registration_notification.get_entity_dashboard_url', - return_value='https://dummyurl.gov.bc.ca') - with requests_mock.Mocker() as m: - m.get( - f'{config.get("LEGAL_API_URL")}/businesses/{identifier}' - f'/filings/{filing.id}/documents/registration', - content=b'pdf_content_1', - status_code=200, - ) - output = registration_notification.process( - {'filingId': filing.id, 'type': 'registration', 'option': 'COMPLETED'}, token) - - attachments = output['content']['attachments'] - assert len(attachments) == 1 - assert attachments[0]['fileName'] == 'Statement of Registration.pdf' - assert base64.b64decode(attachments[0]['fileBytes']).decode('utf-8') == 'pdf_content_1' diff --git a/queue_services/business-emailer/tests/unit/test_worker_dispatch.py b/queue_services/business-emailer/tests/unit/test_worker_dispatch.py index 2480a402e4..92128f2a29 100644 --- a/queue_services/business-emailer/tests/unit/test_worker_dispatch.py +++ b/queue_services/business-emailer/tests/unit/test_worker_dispatch.py @@ -30,7 +30,6 @@ ar_reminder_notification, bn_notification, cease_receiver_notification, - change_of_registration_notification, consent_amalgamation_out_notification, consent_continuation_out_notification, continuation_in_notification, @@ -44,7 +43,6 @@ name_request, notice_of_withdrawal_notification, nr_notification, - registration_notification, restoration_notification, special_resolution_notification, ) @@ -239,16 +237,6 @@ def test_dissolution_dispatches(app, session, mocker, mock_send_email): mock_send_email.assert_called_once_with(STUB_EMAIL, TOKEN) -def test_registration_dispatches(app, session, mocker, mock_send_email): - mock_process = mocker.patch.object(registration_notification, "process", return_value=STUB_EMAIL) - email = {"type": "registration", "option": "PAID"} - - worker.process_email(_ce({"email": email})) - - mock_process.assert_called_once_with(email, TOKEN) - mock_send_email.assert_called_once_with(STUB_EMAIL, TOKEN) - - def test_restoration_dispatches(app, session, mocker, mock_send_email): mock_process = mocker.patch.object(restoration_notification, "process", return_value=STUB_EMAIL) email = {"type": "restoration", "option": COMPLETED} @@ -259,16 +247,6 @@ def test_restoration_dispatches(app, session, mocker, mock_send_email): mock_send_email.assert_called_once_with(STUB_EMAIL, TOKEN) -def test_change_of_registration_dispatches(app, session, mocker, mock_send_email): - mock_process = mocker.patch.object(change_of_registration_notification, "process", return_value=STUB_EMAIL) - email = {"type": "changeOfRegistration", "option": COMPLETED} - - worker.process_email(_ce({"email": email})) - - mock_process.assert_called_once_with(email, TOKEN) - mock_send_email.assert_called_once_with(STUB_EMAIL, TOKEN) - - def test_correction_dispatches(app, session, mocker, mock_send_email): mock_process = mocker.patch.object(correction_notification, "process", return_value=STUB_EMAIL) email = {"type": "correction", "option": "PAID"} @@ -401,14 +379,22 @@ def test_cease_receiver_completed_dispatches(app, session, mocker, mock_send_ema # --------------------------------------------------------------------------- # -# FILING_TYPE_CONVERTER branch # +# filing_notification branch # # --------------------------------------------------------------------------- # -def test_filing_type_converter_dispatches(app, session, mocker, mock_send_email): - """An etype in FILING_TYPE_CONVERTER (e.g. 'alteration') that isn't handled - earlier routes to filing_notification.process.""" +@pytest.mark.parametrize('filing_type', [ + "alteration", + "annualReport", + "changeOfAddress", + "changeOfDirectors", + "changeOfRegistration", + "incorporationApplication", + "registration", +]) +def test_filing_notification_dispatches(app, session, mocker, mock_send_email, filing_type): + """Assert that the filing_notification branch works as expected.""" mock_process = mocker.patch.object(filing_notification, "process", return_value=STUB_EMAIL) - email = {"type": "alteration", "option": "PAID"} + email = {"type": filing_type, "option": "COMPLETED"} worker.process_email(_ce({"email": email})) From 203b3675badc320b1c94f2d19fa79bf44a599a5b Mon Sep 17 00:00:00 2001 From: Kial Jinnah Date: Mon, 29 Jun 2026 10:41:26 -0400 Subject: [PATCH 2/7] chore: cleanup sonarqube Signed-off-by: Kial Jinnah --- .../src/business_emailer/email_processors/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/queue_services/business-emailer/src/business_emailer/email_processors/__init__.py b/queue_services/business-emailer/src/business_emailer/email_processors/__init__.py index ff28b0ab1b..bae9b3138f 100644 --- a/queue_services/business-emailer/src/business_emailer/email_processors/__init__.py +++ b/queue_services/business-emailer/src/business_emailer/email_processors/__init__.py @@ -48,7 +48,7 @@ def get_filing_info(filing_id: str) -> tuple[Filing, dict, dict, str, str]: return filing, business_json, leg_tmz_filing_date, leg_tmz_effective_date -def get_party_emails(parties: list[dict], roles: list[str]) -> str: +def get_party_emails(parties: list[dict], roles: list[str]) -> list[str]: """Return the emails for the specified party types.""" recipients = [] for party in parties: From 63c47ca580e839188f842669ff47af98f06e73cb Mon Sep 17 00:00:00 2001 From: Kial Jinnah Date: Mon, 29 Jun 2026 13:58:54 -0400 Subject: [PATCH 3/7] Address PR comment Signed-off-by: Kial Jinnah --- .../src/business_emailer/email_processors/__init__.py | 4 ++-- .../tests/unit/email_processors/test_email_processors_init.py | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/queue_services/business-emailer/src/business_emailer/email_processors/__init__.py b/queue_services/business-emailer/src/business_emailer/email_processors/__init__.py index bae9b3138f..f5c9f94f91 100644 --- a/queue_services/business-emailer/src/business_emailer/email_processors/__init__.py +++ b/queue_services/business-emailer/src/business_emailer/email_processors/__init__.py @@ -53,8 +53,8 @@ def get_party_emails(parties: list[dict], roles: list[str]) -> list[str]: recipients = [] for party in parties: for role in party["roles"]: - if role["roleType"] in roles: - recipients.append(party["officer"].get("email")) + if role["roleType"] in roles and (email := party["officer"].get("email")): + recipients.append(email) break return recipients diff --git a/queue_services/business-emailer/tests/unit/email_processors/test_email_processors_init.py b/queue_services/business-emailer/tests/unit/email_processors/test_email_processors_init.py index 762947e387..3127080cd8 100644 --- a/queue_services/business-emailer/tests/unit/email_processors/test_email_processors_init.py +++ b/queue_services/business-emailer/tests/unit/email_processors/test_email_processors_init.py @@ -254,7 +254,7 @@ def test_get_party_emails_skips_non_matching_role(app): def test_get_party_emails_includes_none_when_party_has_no_email(app): - """Assert that get_party_emails appends None when a matching party has no email field.""" + """Assert that get_party_emails does not append when a matching party has no email field.""" parties = [ { 'officer': {}, @@ -263,7 +263,7 @@ def test_get_party_emails_includes_none_when_party_has_no_email(app): ] with app.app_context(): result = get_party_emails(parties, ['Completing Party']) - assert result == [None] + assert result == [] def test_get_party_emails_multiple_parties_each_contribute_one_email(app): From 7c668890fe7d3466aaaab131d879dd09fdee9286 Mon Sep 17 00:00:00 2001 From: Kial Jinnah Date: Tue, 30 Jun 2026 09:27:09 -0400 Subject: [PATCH 4/7] update get_recipients to address pr comments Signed-off-by: Kial Jinnah --- .../email_processors/__init__.py | 29 ++++--- .../email_processors/filing_notification.py | 8 +- .../business-emailer/tests/unit/__init__.py | 15 ++-- .../email_processors/test_bn_notification.py | 3 +- .../test_email_processors_init.py | 83 +++++-------------- .../test_filing_notification.py | 23 +++-- .../tests/unit/test_worker.py | 3 +- 7 files changed, 71 insertions(+), 93 deletions(-) diff --git a/queue_services/business-emailer/src/business_emailer/email_processors/__init__.py b/queue_services/business-emailer/src/business_emailer/email_processors/__init__.py index f5c9f94f91..461d4295d8 100644 --- a/queue_services/business-emailer/src/business_emailer/email_processors/__init__.py +++ b/queue_services/business-emailer/src/business_emailer/email_processors/__init__.py @@ -63,22 +63,27 @@ def get_party_emails(parties: list[dict], roles: list[str]) -> list[str]: def get_recipients(option: str, filing_json: dict, token: str | None = None, filing_type: str | None = None) -> str: """Get the recipients for the email output.""" recipients = "" - filing_type = filing_type if filing_type else "incorporationApplication" - if filing_json["filing"].get(filing_type): + identifier = filing_json["filing"]["business"]["identifier"] + is_coop = identifier[:2] == "CP" + + if filing_type and (filing_data := filing_json["filing"].get(filing_type)): # add filing contact point email - recipients = filing_json["filing"][filing_type].get("contactPoint", {}).get("email", "") or "" + recipients = filing_data.get("contactPoint", {}).get("email", "") or "" + # add relevant party emails - if (option != "mras" - and filing_json["filing"]["header"]["name"] == filing_type - and (parties := filing_json["filing"][filing_type].get("parties")) - and (party_emails := get_party_emails(parties, ["Completing Party", "Partner", "Proprietor"])) + # FUTURE: after amalg and continuation have completing party removed 'temp_logic' can be removed + temp_logic = filing_type in ["amalgamationApplication", "continuationIn"] and option in ["PAID", "bn"] + is_coop_incorp_paid = is_coop and filing_type == "incorporationApplication" and option == "PAID" + is_valid_filing = filing_type in ["changeOfRegistration", "registration", "correction", "dissolution"] + if ((temp_logic or is_coop_incorp_paid or is_valid_filing) + and (parties := filing_data.get("parties")) + and (party_emails := get_party_emails(parties, ["Completing Party", "Custodian", "Partner", "Proprietor"])) ): recipients = f"{recipients}, {', '.join(party_emails)}" - else: - identifier = filing_json["filing"]["business"]["identifier"] - if identifier[:2] != "CP": - # only add business email recipient for non-coop - recipients = get_recipient_from_auth(identifier, token) + + elif not is_coop: + # only add business email recipient for non-coop + recipients = get_recipient_from_auth(identifier, token) return recipients diff --git a/queue_services/business-emailer/src/business_emailer/email_processors/filing_notification.py b/queue_services/business-emailer/src/business_emailer/email_processors/filing_notification.py index 88691bdc95..30b73f0b38 100644 --- a/queue_services/business-emailer/src/business_emailer/email_processors/filing_notification.py +++ b/queue_services/business-emailer/src/business_emailer/email_processors/filing_notification.py @@ -21,6 +21,7 @@ get_filing_info, get_filled_template, get_pdfs, + get_recipient_from_auth, get_recipients, get_subject, get_user_email_from_auth, @@ -161,7 +162,12 @@ def process(email_info: dict, token: str) -> dict: ) # get recipients - recipients = get_recipients(status, filing.filing_json, token) + recipient_filing_type = None + if filing_type in ["incorporationApplication", "registration", "changeOfRegistration"]: + recipient_filing_type = filing_type + + recipients = get_recipients(status, filing.filing_json, token, recipient_filing_type) + if additional_recipients := _get_additional_recipients(filing, token): recipients = f"{recipients}, {additional_recipients}" diff --git a/queue_services/business-emailer/tests/unit/__init__.py b/queue_services/business-emailer/tests/unit/__init__.py index 4f21f314a6..4975e3c575 100644 --- a/queue_services/business-emailer/tests/unit/__init__.py +++ b/queue_services/business-emailer/tests/unit/__init__.py @@ -155,15 +155,17 @@ def prep_registration_filing(session, identifier, payment_id, option, legal_type gp_registration['filing']['registration'] = copy.deepcopy(REGISTRATION) gp_registration['filing']['registration']['startDate'] = now gp_registration['filing']['registration']['nameRequest']['legalName'] = legal_name - gp_registration['filing']['registration']['parties'][1]['officer']['email'] = 'party@email.com' + gp_registration['filing']['registration']['parties'][0]['officer']['email'] = 'party1@email.com' + gp_registration['filing']['registration']['parties'][1]['officer']['email'] = 'party2@email.com' + gp_registration['filing']['registration']['contactPoint']['email'] = 'contact@point.com' sp_registration = copy.deepcopy(FILING_HEADER) sp_registration['filing']['header']['name'] = 'registration' sp_registration['filing']['registration'] = copy.deepcopy(REGISTRATION) sp_registration['filing']['registration']['startDate'] = now - sp_registration['filing']['registration']['nameRequest']['legalType'] = 'SP' - sp_registration['filing']['registration']['nameRequest']['legalName'] = legal_name + sp_registration['filing']['registration']['parties'][0]['officer']['email'] = 'party1@email.com' sp_registration['filing']['registration']['businessType'] = 'SP' + sp_registration['filing']['registration']['contactPoint']['email'] = 'contact@point.com' sp_registration['filing']['registration']['parties'][0]['roles'] = [ { 'roleType': 'Completing Party', @@ -426,7 +428,8 @@ def prep_change_of_registration_filing(session, identifier, payment_id, legal_ty gp_change_of_registration = copy.deepcopy(FILING_HEADER) gp_change_of_registration['filing']['header']['name'] = 'changeOfRegistration' gp_change_of_registration['filing']['changeOfRegistration'] = copy.deepcopy(CHANGE_OF_REGISTRATION) - gp_change_of_registration['filing']['changeOfRegistration']['parties'][0]['officer']['email'] = 'party@email.com' + gp_change_of_registration['filing']['changeOfRegistration']['parties'][0]['officer']['email'] = 'party1@email.com' + gp_change_of_registration['filing']['changeOfRegistration']['contactPoint']['email'] = 'contact@point.com' sp_change_of_registration = copy.deepcopy(FILING_HEADER) sp_change_of_registration['filing']['header']['name'] = 'changeOfRegistration' @@ -443,7 +446,8 @@ def prep_change_of_registration_filing(session, identifier, payment_id, legal_ty } ] - sp_change_of_registration['filing']['changeOfRegistration']['parties'][0]['officer']['email'] = 'party@email.com' + sp_change_of_registration['filing']['changeOfRegistration']['parties'][0]['officer']['email'] = 'party1@email.com' + sp_change_of_registration['filing']['changeOfRegistration']['contactPoint']['email'] = 'contact@point.com' if legal_type == Business.LegalTypes.SOLE_PROP.value: filing_template = sp_change_of_registration @@ -760,6 +764,7 @@ def prep_continuation_in_filing(session, identifier, payment_id, option): if role['roleType'] == 'Completing Party': party['officer']['email'] = 'comp_party@email.com' filing_template['filing']['continuationIn']['contactPoint']['email'] = 'test@test.com' + filing_template['filing']['business'] = {'identifier': identifier} temp_identifier = generate_temp_filing() filing = create_filing(token=payment_id, filing_json=filing_template, diff --git a/queue_services/business-emailer/tests/unit/email_processors/test_bn_notification.py b/queue_services/business-emailer/tests/unit/email_processors/test_bn_notification.py index 7bdeb6dac7..c0f3b5310f 100644 --- a/queue_services/business-emailer/tests/unit/email_processors/test_bn_notification.py +++ b/queue_services/business-emailer/tests/unit/email_processors/test_bn_notification.py @@ -38,8 +38,7 @@ def test_incorporation_bn_notificaton(app, session): email = bn_notification.process( {'filingId': None, 'type': 'businessNumber', 'option': 'bn', 'identifier': 'BC1234567'}) # check email values - assert 'comp_party@email.com' in email['recipients'] - assert 'test@test.com' in email['recipients'] + assert 'test@test.com' == email['recipients'] assert email['content']['subject'] == f'{business.legal_name} - Business Number Information' assert email['content']['body'] assert email['content']['attachments'] == [] diff --git a/queue_services/business-emailer/tests/unit/email_processors/test_email_processors_init.py b/queue_services/business-emailer/tests/unit/email_processors/test_email_processors_init.py index 3127080cd8..713dd13c65 100644 --- a/queue_services/business-emailer/tests/unit/email_processors/test_email_processors_init.py +++ b/queue_services/business-emailer/tests/unit/email_processors/test_email_processors_init.py @@ -289,10 +289,8 @@ def test_get_party_emails_multiple_parties_each_contribute_one_email(app): # get_recipients # --------------------------------------------------------------------------- -def _make_filing_json(filing_type, header_name=None, contact_email='contact@example.com', - parties=None, business_identifier=None): +def _make_filing_json(filing_type, contact_email='contact@point.com', parties=None, identifier='BC1234567'): """Build a minimal filing_json dict for get_recipients tests.""" - header_name = header_name if header_name is not None else filing_type filing_data = {} if contact_email is not None: filing_data['contactPoint'] = {'email': contact_email} @@ -302,12 +300,11 @@ def _make_filing_json(filing_type, header_name=None, contact_email='contact@exam filing_data['parties'] = parties result = { 'filing': { - 'header': {'name': header_name}, + 'header': {'name': filing_type}, + 'business': {'identifier': identifier}, filing_type: filing_data, } } - if business_identifier: - result['filing']['business'] = {'identifier': business_identifier} return result @@ -318,59 +315,35 @@ def test_get_recipients_returns_contact_point_email(app): parties=[{'officer': {'email': 'other@example.com'}, 'roles': [{'roleType': 'Director'}]}] ) with app.app_context(): - result = get_recipients('PAID', filing_json) - assert result == 'contact@example.com' + result = get_recipients('PAID', filing_json, None, 'incorporationApplication') + assert result == 'contact@point.com' -def test_get_recipients_appends_party_emails_when_header_matches(app): - """Assert that party emails are appended when header.name matches filing_type.""" - filing_json = _make_filing_json( - 'incorporationApplication', - parties=[{'officer': {'email': 'comp@example.com'}, 'roles': [{'roleType': 'Completing Party'}]}] - ) - with app.app_context(): - result = get_recipients('PAID', filing_json) - assert 'contact@example.com' in result - assert 'comp@example.com' in result - - -def test_get_recipients_mras_option_excludes_party_emails(app): +@pytest.mark.parametrize('filing_type', [ + ('incorporationApplication'), + ('amalgamationApplication'), + ('continuationIn'), +]) +def test_get_recipients_mras_option_excludes_party_emails(app, filing_type): """Assert that party emails are not appended when option is 'mras'.""" filing_json = _make_filing_json( - 'incorporationApplication', + filing_type, parties=[{'officer': {'email': 'comp@example.com'}, 'roles': [{'roleType': 'Completing Party'}]}] ) with app.app_context(): - result = get_recipients('mras', filing_json) - assert result == 'contact@example.com' - assert 'comp@example.com' not in result - - -def test_get_recipients_header_mismatch_excludes_party_emails(app): - """Assert that party emails are not appended when header.name differs from filing_type.""" - filing_json = _make_filing_json( - 'incorporationApplication', - header_name='someOtherFiling', - parties=[{'officer': {'email': 'comp@example.com'}, 'roles': [{'roleType': 'Completing Party'}]}] - ) - with app.app_context(): - result = get_recipients('PAID', filing_json) - assert result == 'contact@example.com' + result = get_recipients('mras', filing_json, None, filing_type) + assert result == 'contact@point.com' assert 'comp@example.com' not in result def test_get_recipients_missing_contact_point_coalesced_to_empty_string(app): """Assert that a missing contactPoint email is coalesced to an empty string (not None).""" - filing_json = { - 'filing': { - 'header': {'name': 'incorporationApplication'}, - 'incorporationApplication': { - 'parties': [] - } - } - } + filing_json = _make_filing_json( + 'incorporationApplication', + None, + [{'officer': {'email': 'comp@example.com'}, 'roles': [{'roleType': 'Completing Party'}]}]) with app.app_context(): - result = get_recipients('PAID', filing_json) + result = get_recipients('PAID', filing_json, None, 'incorporationApplication') # recipients starts as "" when contactPoint is absent; no parties, so stays "" assert result == '' @@ -401,22 +374,6 @@ def test_get_recipients_non_coop_identifier_calls_get_recipient_from_auth(app): with app.app_context(): with patch('business_emailer.email_processors.get_recipient_from_auth', return_value='auth@example.com') as mock_auth: - result = get_recipients('COMPLETED', filing_json, token='token', filing_type='annualReport') + result = get_recipients('COMPLETED', filing_json, token='token') assert result == 'auth@example.com' mock_auth.assert_called_once_with('BC1234567', 'token') - - -def test_get_recipients_uses_incorporationapplication_as_default_filing_type(app): - """Assert that filing_type defaults to 'incorporationApplication' when not provided.""" - filing_json = { - 'filing': { - 'header': {'name': 'incorporationApplication'}, - 'incorporationApplication': { - 'contactPoint': {'email': 'default@example.com'}, - 'parties': [] - } - } - } - with app.app_context(): - result = get_recipients('PAID', filing_json) - assert result == 'default@example.com' diff --git a/queue_services/business-emailer/tests/unit/email_processors/test_filing_notification.py b/queue_services/business-emailer/tests/unit/email_processors/test_filing_notification.py index 6d2c22ef4f..3eb1f24aec 100644 --- a/queue_services/business-emailer/tests/unit/email_processors/test_filing_notification.py +++ b/queue_services/business-emailer/tests/unit/email_processors/test_filing_notification.py @@ -514,7 +514,7 @@ def test_maintenance_filing_fe_renders_body_and_subject(app, session, filing_typ ('COMPLETED', 'changeOfRegistration', Business.LegalTypes.PARTNERSHIP.value, None), ]) def test_firm_filing_via_filing_notification(app, session, status, filing_type, legal_type, submitter_role, - mock_pdfs, mock_recipients, mock_user_email): + mock_pdfs, mock_user_email): """Assert that FIRM registration and changeOfRegistration produce correct emails via filing_notification.""" legal_name = 'test business' if filing_type == 'registration': @@ -522,7 +522,7 @@ def test_firm_filing_via_filing_notification(app, session, status, filing_type, else: filing = prep_change_of_registration_filing( session, 'FM1234567', '1', legal_type, legal_name, submitter_role, firm_parties()) - token = 'token' + email = process_filing(filing, filing_type, status) assert email is not None @@ -535,18 +535,25 @@ def test_firm_filing_via_filing_notification(app, session, status, filing_type, assert '[[' not in body assert ']]' not in body assert '## Attachments' in body + assert mock_pdfs.call_args[0][1]['identifier'] == 'FM1234567' + assert mock_pdfs.call_args[0][2] == filing + assert 'contact@point.com' in email['recipients'] + assert 'party1@email.com' in email['recipients'] + if filing_type == 'registration': assert '## About these documents' in body assert '## Business Number' in body - assert mock_pdfs.call_args[0][1]['identifier'] == 'FM1234567' - assert mock_pdfs.call_args[0][2] == filing - assert mock_recipients.call_args[0][0] == status - assert mock_recipients.call_args[0][2] == token - assert 'test@test.com' in email['recipients'] - if filing_type == 'changeOfRegistration': + assert not mock_user_email.called + assert not f'{submitter_role}@email.com' in email['recipients'] + assert not 'user@email.com' in email['recipients'] + if legal_type == Business.LegalTypes.PARTNERSHIP.value: + assert 'party2@email.com' in email['recipients'] + + else: if submitter_role: assert f'{submitter_role}@email.com' in email['recipients'] else: + assert mock_user_email.called assert 'user@email.com' in email['recipients'] diff --git a/queue_services/business-emailer/tests/unit/test_worker.py b/queue_services/business-emailer/tests/unit/test_worker.py index 255a8ad9b4..a0ab1f581f 100644 --- a/queue_services/business-emailer/tests/unit/test_worker.py +++ b/queue_services/business-emailer/tests/unit/test_worker.py @@ -348,8 +348,7 @@ def test_process_bn_email(app, session): ) ) # check email values - assert 'comp_party@email.com' in mock_send_email.call_args[0][0]['recipients'] - assert 'test@test.com' in mock_send_email.call_args[0][0]['recipients'] + assert 'test@test.com' == mock_send_email.call_args[0][0]['recipients'] assert mock_send_email.call_args[0][0]['content']['subject'] == \ f'{business.legal_name} - Business Number Information' assert mock_send_email.call_args[0][0]['content']['body'] From d6ec06813f6696827450f81856814a92b44e753f Mon Sep 17 00:00:00 2001 From: Kial Jinnah Date: Tue, 30 Jun 2026 09:48:31 -0400 Subject: [PATCH 5/7] chore: lint and sonarqube Signed-off-by: Kial Jinnah --- .../email_processors/__init__.py | 2 +- .../email_processors/filing_notification.py | 1 - .../business-emailer/tests/unit/__init__.py | 21 +++++++++++-------- .../test_email_processors_init.py | 11 +++++----- .../test_filing_notification.py | 17 ++++++++------- 5 files changed, 27 insertions(+), 25 deletions(-) diff --git a/queue_services/business-emailer/src/business_emailer/email_processors/__init__.py b/queue_services/business-emailer/src/business_emailer/email_processors/__init__.py index 461d4295d8..381d419342 100644 --- a/queue_services/business-emailer/src/business_emailer/email_processors/__init__.py +++ b/queue_services/business-emailer/src/business_emailer/email_processors/__init__.py @@ -64,7 +64,7 @@ def get_recipients(option: str, filing_json: dict, token: str | None = None, fil """Get the recipients for the email output.""" recipients = "" identifier = filing_json["filing"]["business"]["identifier"] - is_coop = identifier[:2] == "CP" + is_coop = identifier.startswith("CP") if filing_type and (filing_data := filing_json["filing"].get(filing_type)): # add filing contact point email diff --git a/queue_services/business-emailer/src/business_emailer/email_processors/filing_notification.py b/queue_services/business-emailer/src/business_emailer/email_processors/filing_notification.py index 30b73f0b38..62d50fde81 100644 --- a/queue_services/business-emailer/src/business_emailer/email_processors/filing_notification.py +++ b/queue_services/business-emailer/src/business_emailer/email_processors/filing_notification.py @@ -21,7 +21,6 @@ get_filing_info, get_filled_template, get_pdfs, - get_recipient_from_auth, get_recipients, get_subject, get_user_email_from_auth, diff --git a/queue_services/business-emailer/tests/unit/__init__.py b/queue_services/business-emailer/tests/unit/__init__.py index 4975e3c575..e29973e577 100644 --- a/queue_services/business-emailer/tests/unit/__init__.py +++ b/queue_services/business-emailer/tests/unit/__init__.py @@ -63,7 +63,10 @@ 'alteration': ALTERATION } +CONTACT_POINT = 'contact@point.com' LEGAL_NAME = 'test business' +PARTY_EMAIL_1 = 'party1@email.com' +PARTY_EMAIL_2 = 'party2@email.com' def create_user(user_name: str): @@ -155,17 +158,17 @@ def prep_registration_filing(session, identifier, payment_id, option, legal_type gp_registration['filing']['registration'] = copy.deepcopy(REGISTRATION) gp_registration['filing']['registration']['startDate'] = now gp_registration['filing']['registration']['nameRequest']['legalName'] = legal_name - gp_registration['filing']['registration']['parties'][0]['officer']['email'] = 'party1@email.com' - gp_registration['filing']['registration']['parties'][1]['officer']['email'] = 'party2@email.com' - gp_registration['filing']['registration']['contactPoint']['email'] = 'contact@point.com' + gp_registration['filing']['registration']['parties'][0]['officer']['email'] = PARTY_EMAIL_1 + gp_registration['filing']['registration']['parties'][1]['officer']['email'] = PARTY_EMAIL_2 + gp_registration['filing']['registration']['contactPoint']['email'] = CONTACT_POINT sp_registration = copy.deepcopy(FILING_HEADER) sp_registration['filing']['header']['name'] = 'registration' sp_registration['filing']['registration'] = copy.deepcopy(REGISTRATION) sp_registration['filing']['registration']['startDate'] = now - sp_registration['filing']['registration']['parties'][0]['officer']['email'] = 'party1@email.com' + sp_registration['filing']['registration']['parties'][0]['officer']['email'] = PARTY_EMAIL_1 sp_registration['filing']['registration']['businessType'] = 'SP' - sp_registration['filing']['registration']['contactPoint']['email'] = 'contact@point.com' + sp_registration['filing']['registration']['contactPoint']['email'] = CONTACT_POINT sp_registration['filing']['registration']['parties'][0]['roles'] = [ { 'roleType': 'Completing Party', @@ -428,8 +431,8 @@ def prep_change_of_registration_filing(session, identifier, payment_id, legal_ty gp_change_of_registration = copy.deepcopy(FILING_HEADER) gp_change_of_registration['filing']['header']['name'] = 'changeOfRegistration' gp_change_of_registration['filing']['changeOfRegistration'] = copy.deepcopy(CHANGE_OF_REGISTRATION) - gp_change_of_registration['filing']['changeOfRegistration']['parties'][0]['officer']['email'] = 'party1@email.com' - gp_change_of_registration['filing']['changeOfRegistration']['contactPoint']['email'] = 'contact@point.com' + gp_change_of_registration['filing']['changeOfRegistration']['parties'][0]['officer']['email'] = PARTY_EMAIL_1 + gp_change_of_registration['filing']['changeOfRegistration']['contactPoint']['email'] = CONTACT_POINT sp_change_of_registration = copy.deepcopy(FILING_HEADER) sp_change_of_registration['filing']['header']['name'] = 'changeOfRegistration' @@ -446,8 +449,8 @@ def prep_change_of_registration_filing(session, identifier, payment_id, legal_ty } ] - sp_change_of_registration['filing']['changeOfRegistration']['parties'][0]['officer']['email'] = 'party1@email.com' - sp_change_of_registration['filing']['changeOfRegistration']['contactPoint']['email'] = 'contact@point.com' + sp_change_of_registration['filing']['changeOfRegistration']['parties'][0]['officer']['email'] = PARTY_EMAIL_1 + sp_change_of_registration['filing']['changeOfRegistration']['contactPoint']['email'] = CONTACT_POINT if legal_type == Business.LegalTypes.SOLE_PROP.value: filing_template = sp_change_of_registration diff --git a/queue_services/business-emailer/tests/unit/email_processors/test_email_processors_init.py b/queue_services/business-emailer/tests/unit/email_processors/test_email_processors_init.py index 713dd13c65..5ad35a0539 100644 --- a/queue_services/business-emailer/tests/unit/email_processors/test_email_processors_init.py +++ b/queue_services/business-emailer/tests/unit/email_processors/test_email_processors_init.py @@ -25,12 +25,13 @@ get_filled_template, get_org_id_for_temp_identifier, get_party_emails, - get_recipient_from_auth, get_recipients, get_subject, substitute_template_parts, ) +from tests.unit import CONTACT_POINT + @pytest.fixture def auth_url(app): @@ -289,13 +290,11 @@ def test_get_party_emails_multiple_parties_each_contribute_one_email(app): # get_recipients # --------------------------------------------------------------------------- -def _make_filing_json(filing_type, contact_email='contact@point.com', parties=None, identifier='BC1234567'): +def _make_filing_json(filing_type, contact_email=CONTACT_POINT, parties=None, identifier='BC1234567'): """Build a minimal filing_json dict for get_recipients tests.""" filing_data = {} if contact_email is not None: filing_data['contactPoint'] = {'email': contact_email} - elif contact_email is None: - pass # omit contactPoint entirely if parties is not None: filing_data['parties'] = parties result = { @@ -316,7 +315,7 @@ def test_get_recipients_returns_contact_point_email(app): ) with app.app_context(): result = get_recipients('PAID', filing_json, None, 'incorporationApplication') - assert result == 'contact@point.com' + assert result == CONTACT_POINT @pytest.mark.parametrize('filing_type', [ @@ -332,7 +331,7 @@ def test_get_recipients_mras_option_excludes_party_emails(app, filing_type): ) with app.app_context(): result = get_recipients('mras', filing_json, None, filing_type) - assert result == 'contact@point.com' + assert result == CONTACT_POINT assert 'comp@example.com' not in result diff --git a/queue_services/business-emailer/tests/unit/email_processors/test_filing_notification.py b/queue_services/business-emailer/tests/unit/email_processors/test_filing_notification.py index 3eb1f24aec..a556270062 100644 --- a/queue_services/business-emailer/tests/unit/email_processors/test_filing_notification.py +++ b/queue_services/business-emailer/tests/unit/email_processors/test_filing_notification.py @@ -18,12 +18,13 @@ import pytest import requests_mock -from business_model.models import Business, CorpType +from business_model.models import Business from registry_schemas.example_data import INCORPORATION_FILING_TEMPLATE from business_emailer.email_processors import filing_notification -from tests.unit import (create_filing, prep_change_of_registration_filing, prep_incorp_filing, - prep_maintenance_filing, prep_registration_filing) +from tests.unit import (CONTACT_POINT, PARTY_EMAIL_1, PARTY_EMAIL_2, + create_filing, prep_change_of_registration_filing, prep_incorp_filing, + prep_maintenance_filing, prep_registration_filing) from tests.unit.helpers import generate_temp_filing, make_future_effective, make_non_future_effective @@ -537,17 +538,17 @@ def test_firm_filing_via_filing_notification(app, session, status, filing_type, assert '## Attachments' in body assert mock_pdfs.call_args[0][1]['identifier'] == 'FM1234567' assert mock_pdfs.call_args[0][2] == filing - assert 'contact@point.com' in email['recipients'] - assert 'party1@email.com' in email['recipients'] + assert CONTACT_POINT in email['recipients'] + assert PARTY_EMAIL_1 in email['recipients'] if filing_type == 'registration': assert '## About these documents' in body assert '## Business Number' in body assert not mock_user_email.called - assert not f'{submitter_role}@email.com' in email['recipients'] - assert not 'user@email.com' in email['recipients'] + assert f'{submitter_role}@email.com' not in email['recipients'] + assert 'user@email.com' not in email['recipients'] if legal_type == Business.LegalTypes.PARTNERSHIP.value: - assert 'party2@email.com' in email['recipients'] + assert PARTY_EMAIL_2 in email['recipients'] else: if submitter_role: From baf8121aee75e2b367b42fd56be8372d8ba07799 Mon Sep 17 00:00:00 2001 From: Kial Jinnah Date: Tue, 30 Jun 2026 14:32:53 -0400 Subject: [PATCH 6/7] Adjust unit test bootstrap filings setup Signed-off-by: Kial Jinnah --- .../email_processors/filing_notification.py | 4 +- .../business-emailer/tests/unit/__init__.py | 48 +++++++++---------- .../test_correction_notification.py | 2 +- .../test_filing_notification.py | 21 ++++---- .../tests/unit/test_worker.py | 3 +- 5 files changed, 39 insertions(+), 39 deletions(-) diff --git a/queue_services/business-emailer/src/business_emailer/email_processors/filing_notification.py b/queue_services/business-emailer/src/business_emailer/email_processors/filing_notification.py index 62d50fde81..9ed7b29bcd 100644 --- a/queue_services/business-emailer/src/business_emailer/email_processors/filing_notification.py +++ b/queue_services/business-emailer/src/business_emailer/email_processors/filing_notification.py @@ -92,7 +92,8 @@ def process(email_info: dict, token: str) -> dict: new_business_filings = ["amalgamationApplication", "continuationIn", "incorporationApplication", "registration"] filing_data = filing.json.get("filing", {}).get(filing_type, {}) if filing_type in new_business_filings and not business: - # For new business filings, the business record is not created yet. So we get the business info from the nameRequest. + # For new business filings, the nameRequest contains relevant business details. + # We overwrite the business info from the nameRequest and then set the identifier back to the temp reg id. name_request = filing_data.get("nameRequest") business = name_request business["identifier"] = filing.temp_reg @@ -103,6 +104,7 @@ def process(email_info: dict, token: str) -> dict: if not legal_type or not filing_name or not business_identifier: # Should never happen - log and return. It will be skipped. current_app.logger.error("Missing legal_type, identifier and/or filing_name. Email: %s", email_info) + return skipped_coop_filing_types = ["changeOfDirectors", "changeOfAddress"] if legal_type == Business.LegalTypes.COOP.value and filing_type in skipped_coop_filing_types: diff --git a/queue_services/business-emailer/tests/unit/__init__.py b/queue_services/business-emailer/tests/unit/__init__.py index e29973e577..e51422574b 100644 --- a/queue_services/business-emailer/tests/unit/__init__.py +++ b/queue_services/business-emailer/tests/unit/__init__.py @@ -121,12 +121,10 @@ def create_filing(token=None, filing_json=None, business_id=None, def prep_incorp_filing(session, identifier, payment_id, option, legal_type=None): """Return a new incorp filing prepped for email notification.""" - business = create_business(identifier, legal_type=legal_type, legal_name=LEGAL_NAME) filing_template = copy.deepcopy(INCORPORATION_FILING_TEMPLATE) - filing_template['filing']['business'] = {'identifier': business.identifier} - if business.legal_type: - filing_template['filing']['business']['legalType'] = business.legal_type - filing_template['filing']['incorporationApplication']['nameRequest']['legalType'] = business.legal_type + if legal_type: + filing_template['filing']['business']['legalType'] = legal_type + filing_template['filing']['incorporationApplication']['nameRequest']['legalType'] = legal_type for party in filing_template['filing']['incorporationApplication']['parties']: for role in party['roles']: if role['roleType'] == 'Completing Party': @@ -134,14 +132,18 @@ def prep_incorp_filing(session, identifier, payment_id, option, legal_type=None) filing_template['filing']['incorporationApplication']['contactPoint']['email'] = 'test@test.com' temp_identifier = generate_temp_filing() + filing_template['filing']['business']['identifier'] = temp_identifier filing = create_filing(token=payment_id, filing_json=filing_template, - business_id=business.id, bootstrap_id=temp_identifier) + business_id=None, bootstrap_id=temp_identifier) filing.payment_completion_date = filing.filing_date - filing.save() - if option in ['COMPLETED', 'bn']: + + if option in ['COMPLETED', 'bn', 'mras']: + business = create_business(identifier, legal_type=legal_type, legal_name=LEGAL_NAME) + filing.business_id = business.id transaction_id = VersioningProxy.get_transaction_id(session()) filing.transaction_id = transaction_id - filing.save() + + filing.save() return filing @@ -188,29 +190,23 @@ def prep_registration_filing(session, identifier, payment_id, option, legal_type elif legal_type == Business.LegalTypes.PARTNERSHIP.value: filing_template = gp_registration - business_id = None - if option == 'PAID': - del filing_template['filing']['business'] - elif option == 'COMPLETED': - business = create_business(identifier, legal_type, parties=parties) - business.founding_date = datetime.fromisoformat(now) - business.save() - business_id = business.id - filing_template['filing']['business'] = { - 'identifier': business.identifier, - 'legalType': business.legal_type, - 'foundingDate': business.founding_date.isoformat() - } - temp_identifier = generate_temp_filing() + filing_template['filing']['business'] = { + 'identifier': temp_identifier, + 'legalType': legal_type + } filing = create_filing(token=payment_id, filing_json=filing_template, - business_id=business_id, bootstrap_id=temp_identifier) + business_id=None, bootstrap_id=temp_identifier) filing.payment_completion_date = filing.filing_date - filing.save() + if option in ['COMPLETED']: + business = create_business(identifier, legal_type, parties=parties) + business.founding_date = datetime.fromisoformat(now) + filing.business_id = business.id transaction_id = VersioningProxy.get_transaction_id(session()) filing.transaction_id = transaction_id - filing.save() + + filing.save() return filing diff --git a/queue_services/business-emailer/tests/unit/email_processors/test_correction_notification.py b/queue_services/business-emailer/tests/unit/email_processors/test_correction_notification.py index 8d788bcbf4..8383775824 100644 --- a/queue_services/business-emailer/tests/unit/email_processors/test_correction_notification.py +++ b/queue_services/business-emailer/tests/unit/email_processors/test_correction_notification.py @@ -92,7 +92,7 @@ def test_bc_correction_notification(app, session, status, legal_type): """Assert that email attributes are correct.""" # setup filing + business for email legal_name = 'test business' - original_filing = prep_incorp_filing(session, 'BC1234567', '1', status, legal_type=legal_type) + original_filing = prep_incorp_filing(session, 'BC1234567', '1', 'COMPLETED', legal_type=legal_type) token = 'token' business = Business.find_by_identifier('BC1234567') filing = prep_incorporation_correction_filing(session, business, original_filing.id, '1', status) diff --git a/queue_services/business-emailer/tests/unit/email_processors/test_filing_notification.py b/queue_services/business-emailer/tests/unit/email_processors/test_filing_notification.py index a556270062..cfd6f44e2f 100644 --- a/queue_services/business-emailer/tests/unit/email_processors/test_filing_notification.py +++ b/queue_services/business-emailer/tests/unit/email_processors/test_filing_notification.py @@ -252,11 +252,11 @@ def test_maintenance_notification(app, session, status, filing_type, submitter_r def test_filing_attachments_ia_paid_future_effective(session, config): """IA PAID future-effective: filing PDF + receipt are attached.""" - identifier = 'BC1234567' - filing = prep_incorp_filing(session, identifier, '1', 'PAID', 'BC') + filing = prep_incorp_filing(session, None, '1', 'PAID', 'BC') + temp_reg_id = filing.temp_reg make_future_effective(filing) with requests_mock.Mocker() as m: - mock_filing_docs(m, config, identifier, filing, + mock_filing_docs(m, config, temp_reg_id, filing, {'incorporationApplication': b'pdf_content_1'}, receipt=b'pdf_content_2') output = process_filing(filing, 'incorporationApplication', 'PAID') @@ -428,16 +428,17 @@ def test_business_number_rendering(app, session, legal_type, tax_id, expected, m assert '## Business Number' not in body -@pytest.mark.parametrize(['identifier', 'legal_type'], [ - ('CP1234567', 'CP'), - ('BC1234567', 'BC'), -], ids=['coop', 'corp']) -def test_future_attachments_list_in_ia_future_effective_paid(app, session, config, identifier, legal_type): +@pytest.mark.parametrize('legal_type', [ + ('CP'), + ('BC'), +]) +def test_future_attachments_list_in_ia_future_effective_paid(app, session, config, legal_type): """Assert that the future_attachments_list is used for COOP and non-COOP future-effective PAID IA.""" - filing = prep_incorp_filing(session, identifier, '1', 'PAID', legal_type) + filing = prep_incorp_filing(session, None, '1', 'PAID', legal_type) + temp_reg_id = filing.temp_reg make_future_effective(filing) with requests_mock.Mocker() as m: - mock_filing_docs(m, config, identifier, filing, + mock_filing_docs(m, config, temp_reg_id, filing, {'incorporationApplication': b'pdf_content_1'}, receipt=b'pdf_content_2') email = process_filing(filing, 'incorporationApplication', 'PAID') diff --git a/queue_services/business-emailer/tests/unit/test_worker.py b/queue_services/business-emailer/tests/unit/test_worker.py index a0ab1f581f..1997b59554 100644 --- a/queue_services/business-emailer/tests/unit/test_worker.py +++ b/queue_services/business-emailer/tests/unit/test_worker.py @@ -77,6 +77,7 @@ def test_process_incorp_email_paid_future_effective(app, session, mocker): """Assert that an INCORP PAID future-effective email is sent with the Filed subject.""" # setup filing + business for email filing = prep_incorp_filing(session, 'BC1234567', '1', 'PAID', 'BC') + temp_reg_id = filing.temp_reg make_future_effective(filing) token = '1' # test worker @@ -90,7 +91,7 @@ def test_process_incorp_email_paid_future_effective(app, session, mocker): ) assert mock_get_pdfs.call_args[0][0] == token - assert mock_get_pdfs.call_args[0][1]['identifier'] == 'BC1234567' + assert mock_get_pdfs.call_args[0][1]['identifier'] == temp_reg_id assert mock_get_pdfs.call_args[0][1]['legalType'] == 'BC' assert mock_get_pdfs.call_args[0][2] == filing From 427a968c1f8e8c5662456fb94de6585e6ce9bcbd Mon Sep 17 00:00:00 2001 From: Kial Jinnah Date: Tue, 30 Jun 2026 14:37:25 -0400 Subject: [PATCH 7/7] chore: sonarqube Signed-off-by: Kial Jinnah --- .../business_emailer/email_processors/filing_notification.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/queue_services/business-emailer/src/business_emailer/email_processors/filing_notification.py b/queue_services/business-emailer/src/business_emailer/email_processors/filing_notification.py index 9ed7b29bcd..0cb6cb538d 100644 --- a/queue_services/business-emailer/src/business_emailer/email_processors/filing_notification.py +++ b/queue_services/business-emailer/src/business_emailer/email_processors/filing_notification.py @@ -78,7 +78,7 @@ def _get_attachments_and_extra_pdf_types(status: str, filing_type: str, filing: return attachments, extra_pdf_types -def process(email_info: dict, token: str) -> dict: +def process(email_info: dict, token: str) -> dict | None: """Build the email the filing notification.""" current_app.logger.debug("filing_notification: %s", email_info) filing_type, status = email_info["type"], email_info["option"]