Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

local smoke tests #1923

Merged
merged 8 commits into from
May 6, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .devcontainer/scripts/notify-dev-entrypoint.sh
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ echo -e "complete -F __start_kubectl k" >> ~/.zshrc

# Smoke test
# requires adding files .env_staging and .env_prod to the root of the project
echo -e "alias smoke-local='cd /workspace && cp .env_smoke_local tests_smoke/.env && poetry run make smoke-test-local'" >> ~/.zshrc
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is some nice automation here regarding running the smoke tests in the different environments. Should we add some sort of docs so devs are aware they can do this? Maybe a bit about how/where to store the environment-specific env files?

Maybe its obvious, but it might also be nice to let people know they will need to create a service, some templates, and an API key in order to run them locally.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes! good idea - I've added a README.

echo -e "alias smoke-staging='cd /workspace && cp .env_smoke_staging tests_smoke/.env && poetry run make smoke-test'" >> ~/.zshrc
echo -e "alias smoke-prod='cd /workspace && cp .env_smoke_prod tests_smoke/.env && poetry run make smoke-test'" >> ~/.zshrc

Expand Down
4 changes: 4 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,10 @@ format:
smoke-test:
cd tests_smoke && poetry run python smoke_test.py

.PHONY: smoke-test-local
smoke-test-local:
cd tests_smoke && poetry run python smoke_test.py --local --nofiles

.PHONY: run
run: ## Run the web app
poetry run flask run -p 6011 --host=0.0.0.0
Expand Down
14 changes: 14 additions & 0 deletions tests_smoke/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
# Smoke Tests

This repository contains a set of smoke tests for our application. Smoke testing, also known as "Build Verification Testing", is a type of software testing that comprises of a non-exhaustive set of tests that aim at ensuring that the most important functions work. The phrase 'smoke testing' comes from the hardware testing, where you plug in a new piece of hardware and turn it on for the first time. If it starts smoking, you know you have a problem.

## Getting Started

These smoke tests are designed to run in the api devcontainer.

in the root of the repo create `.env` files for the environments you with to smoke test, for example `.env_smoke_local`, `.env_smoke_staging`, and `.env_smoke_prod`. For required values see the [.env.example](.env.example) file).

## Running the tests

in the devcontainer run the aliases `smoke-local`, `smoke-staging`, or `smoke-prod` to run the tests.

8 changes: 3 additions & 5 deletions tests_smoke/smoke/common.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,6 @@
import json
import os
import time

# from notifications_utils.s3 import s3upload as utils_s3upload
import urllib
import uuid
from enum import Enum
Expand Down Expand Up @@ -41,6 +39,7 @@ class Config:
EMAIL_TEMPLATE_ID = os.environ.get("SMOKE_EMAIL_TEMPLATE_ID")
SMS_TEMPLATE_ID = os.environ.get("SMOKE_SMS_TEMPLATE_ID")
API_KEY = os.environ.get("SMOKE_API_KEY", "")
JOB_SIZE = int(os.environ.get("SMOKE_JOB_SIZE", 2))


boto_session = Session(
Expand All @@ -67,8 +66,8 @@ def rows_to_csv(rows: List[List[str]]):
return output.getvalue()


def job_line(data: str, number_of_lines: int) -> Iterator[List[str]]:
return map(lambda n: [data, f"var{n}"], range(0, number_of_lines))
def job_line(data: str, number_of_lines: int, prefix: str = "") -> Iterator[List[str]]:
return map(lambda n: [data, f"{prefix} {n}"], range(0, number_of_lines))


def pretty_print(data: Any):
Expand Down Expand Up @@ -120,7 +119,6 @@ def job_succeeded(service_id: str, job_id: str) -> bool:
return success


# from notifications_utils.s3 import s3upload as utils_s3upload
def utils_s3upload(filedata, region, bucket_name, file_location, content_type="binary/octet-stream", tags=None):
_s3 = boto_session.resource("s3")

Expand Down
19 changes: 11 additions & 8 deletions tests_smoke/smoke/test_admin_csv.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,13 @@
)


def test_admin_csv(notification_type: Notification_type):
def test_admin_csv(notification_type: Notification_type, local: bool = False):
print(f"test_admin_csv ({notification_type.value})... ", end="", flush=True)

if notification_type == Notification_type.EMAIL:
data = rows_to_csv([["email address", "var"], *job_line(Config.EMAIL_TO, 2)])
data = rows_to_csv([["email address", "var"], *job_line(Config.EMAIL_TO, Config.JOB_SIZE, prefix="smoke test admin csv")])
else:
data = rows_to_csv([["phone number", "var"], *job_line(Config.SMS_TO, 2)])
data = rows_to_csv([["phone number", "var"], *job_line(Config.SMS_TO, Config.JOB_SIZE, prefix="smoke test admin csv")])

upload_id = s3upload(Config.SERVICE_ID, data)
metadata_kwargs = {
Expand All @@ -42,8 +42,11 @@ def test_admin_csv(notification_type: Notification_type):
print("FAILED: post to send_notification failed")
exit(1)

success = job_succeeded(Config.SERVICE_ID, upload_id)
if not success:
print("FAILED: job didn't finish successfully")
exit(1)
print("Success")
if local:
print(f"Check manually for {Config.JOB_SIZE} {notification_type.value}s")
else:
success = job_succeeded(Config.SERVICE_ID, upload_id)
if not success:
print("FAILED: job didn't finish successfully")
exit(1)
print("Success")
19 changes: 11 additions & 8 deletions tests_smoke/smoke/test_admin_one_off.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
from .common import Config, Notification_type, pretty_print, single_succeeded


def test_admin_one_off(notification_type: Notification_type):
def test_admin_one_off(notification_type: Notification_type, local: bool = False):
print(f"test_admin_one_off ({notification_type.value})... ", end="", flush=True)

token = create_jwt_token(Config.ADMIN_CLIENT_SECRET, client_id=Config.ADMIN_CLIENT_USER_NAME)
Expand All @@ -17,7 +17,7 @@ def test_admin_one_off(notification_type: Notification_type):
"to": to,
"template_id": template_id,
"created_by": Config.USER_ID,
"personalisation": {"var": "var"},
"personalisation": {"var": "smoke test admin one off"},
},
headers={"Authorization": f"Bearer {token}"},
)
Expand All @@ -28,9 +28,12 @@ def test_admin_one_off(notification_type: Notification_type):
print("FAILED: post to send_notification failed")
exit(1)

uri = f"{Config.API_HOST_NAME}/service/{Config.SERVICE_ID}/notifications/{body['id']}"
success = single_succeeded(uri, use_jwt=True)
if not success:
print("FAILED: job didn't finish successfully")
exit(1)
print("Success")
if local:
print(f"Check manually for 1 {notification_type.value}")
else:
uri = f"{Config.API_HOST_NAME}/service/{Config.SERVICE_ID}/notifications/{body['id']}"
success = single_succeeded(uri, use_jwt=True)
if not success:
print("FAILED: job didn't finish successfully")
exit(1)
print("Success")
17 changes: 10 additions & 7 deletions tests_smoke/smoke/test_api_bulk.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
)


def test_api_bulk(notification_type: Notification_type):
def test_api_bulk(notification_type: Notification_type, local: bool = False):
print(f"test_api_bulk ({notification_type.value})... ", end="", flush=True)
template_id = Config.EMAIL_TEMPLATE_ID if notification_type == Notification_type.EMAIL else Config.SMS_TEMPLATE_ID
to = Config.EMAIL_TO if notification_type == Notification_type.EMAIL else Config.SMS_TO
Expand All @@ -23,7 +23,7 @@ def test_api_bulk(notification_type: Notification_type):
json={
"name": f"My bulk name {datetime.utcnow().isoformat()}",
"template_id": template_id,
"csv": rows_to_csv([[header, "var"], *job_line(to, 2)]),
"csv": rows_to_csv([[header, "var"], *job_line(to, Config.JOB_SIZE, prefix="smoke test api bulk")]),
},
headers={"Authorization": f"ApiKey-v1 {Config.API_KEY}"},
)
Expand All @@ -32,8 +32,11 @@ def test_api_bulk(notification_type: Notification_type):
print("FAILED: post failed")
exit(1)

success = job_succeeded(Config.SERVICE_ID, response.json()["data"]["id"])
if not success:
print("FAILED: job didn't finish successfully")
exit(1)
print("Success")
if local:
print(f"Check manually for {Config.JOB_SIZE} {notification_type.value}s")
else:
success = job_succeeded(Config.SERVICE_ID, response.json()["data"]["id"])
if not success:
print("FAILED: job didn't finish successfully")
exit(1)
print("Success")
20 changes: 11 additions & 9 deletions tests_smoke/smoke/test_api_one_off.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
)


def test_api_one_off(notification_type: Notification_type, attachment_type: Attachment_type = Attachment_type.NONE):
def test_api_one_off(notification_type: Notification_type, attachment_type: Attachment_type = Attachment_type.NONE, local: bool = False):
if attachment_type is Attachment_type.NONE:
print(f"test_api_oneoff ({notification_type.value})... ", end="", flush=True)
else:
Expand Down Expand Up @@ -51,7 +51,7 @@ def test_api_one_off(notification_type: Notification_type, attachment_type: Atta
}
else:
data["personalisation"] = {
"var": "var",
"var": "smoke test api one off",
}

response = requests.post(
Expand All @@ -64,10 +64,12 @@ def test_api_one_off(notification_type: Notification_type, attachment_type: Atta
print(f"FAILED: post to v2/notifications/{notification_type.value} failed")
exit(1)

uri = response.json()["uri"]

success = single_succeeded(uri, use_jwt=False)
if not success:
print("FAILED: job didn't finish successfully")
exit(1)
print("Success")
if local:
print(f"Check manually for 1 {notification_type.value}")
else:
uri = response.json()["uri"]
success = single_succeeded(uri, use_jwt=False)
if not success:
print("FAILED: job didn't finish successfully")
exit(1)
print("Success")
21 changes: 15 additions & 6 deletions tests_smoke/smoke_test.py
Original file line number Diff line number Diff line change
@@ -1,19 +1,28 @@
import argparse

from smoke.common import Attachment_type, Config, Notification_type # type: ignore
from smoke.test_admin_csv import test_admin_csv # type: ignore
from smoke.test_admin_one_off import test_admin_one_off # type: ignore
from smoke.test_api_bulk import test_api_bulk # type: ignore
from smoke.test_api_one_off import test_api_one_off # type: ignore

if __name__ == "__main__":
parser = argparse.ArgumentParser()
parser.add_argument("-l", "--local", default=False, action='store_true', help="run locally, do not check for delivery success (default false)")
parser.add_argument("--nofiles", default=False, action='store_true', help="do not send files (default false)")
args = parser.parse_args()

print("API Smoke test\n")
for key in ["API_HOST_NAME", "SERVICE_ID", "EMAIL_TEMPLATE_ID", "SMS_TEMPLATE_ID", "EMAIL_TO", "SMS_TO"]:
print(f"{key:>17}: {Config.__dict__[key]}")
print("")

for notification_type in [Notification_type.EMAIL, Notification_type.SMS]:
test_admin_one_off(notification_type)
test_admin_csv(notification_type)
test_api_one_off(notification_type)
test_api_bulk(notification_type)
test_api_one_off(Notification_type.EMAIL, Attachment_type.ATTACHED)
test_api_one_off(Notification_type.EMAIL, Attachment_type.LINK)
test_admin_one_off(notification_type, local=args.local)
test_admin_csv(notification_type, local=args.local)
test_api_one_off(notification_type, local=args.local)
test_api_bulk(notification_type, local=args.local)

if not args.nofiles:
test_api_one_off(Notification_type.EMAIL, attachment_type=Attachment_type.ATTACHED, local=args.local)
test_api_one_off(Notification_type.EMAIL, attachment_type=Attachment_type.LINK, local=args.local)
Loading