Skip to content

Commit

Permalink
✨ Testsuite updates and some tests (#3)
Browse files Browse the repository at this point in the history
* 📦 Updates dev reqs

* ⚙️ Updates test settings

* 🚜 Refactor test settings

* 💚 Adds a simple model test

This is what we were working towards and why we made the other changes.

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* 💚 Adds router tests

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* ⚙️ 🔨 mypy fixes?

* 📝 commits blank file to make mypy happy

* 🚜 Adds/updates lint+mypy tasks

* ⚙️ Fixes coverage ignore migrations

* ⚙️ Sets coverage to 33% (we will increase this)

* ⚙️ ⬇️ Drop coverage

* ⚙️ Configures pytest-coverage support

* 💚 Adds test_runrelay cmd

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* add intro to README

* adjust formatting

* adjust wording

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* add docker publish job to release workflow

* add if checks to release workflow

* add correct DATABASES settings for PgBouncer (#7)

* add default email settings to service (#9)

* fix router name in README

* move version to pyproject.toml

* add changelog

* add py.typed file, aspirationally

* fix backend name in README

* change test workflow trigger (#14)

* move version back to package (#13)

* remove Django 4.0 (#12)

* add support for user settings from environ (#15)

* add support for user settings from environ

* make mypy happy

* add more logging to relay (#19)

* 🤖 Bump docker/build-push-action from 4 to 5 (#16)

Bumps [docker/build-push-action](https://github.com/docker/build-push-action) from 4 to 5.
- [Release notes](https://github.com/docker/build-push-action/releases)
- [Commits](docker/build-push-action@v4...v5)

---
updated-dependencies:
- dependency-name: docker/build-push-action
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* 🤖 Bump docker/login-action from 2 to 3 (#17)

Bumps [docker/login-action](https://github.com/docker/login-action) from 2 to 3.
- [Release notes](https://github.com/docker/login-action/releases)
- [Commits](docker/login-action@v2...v3)

---
updated-dependencies:
- dependency-name: docker/login-action
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* 🤖 Bump docker/setup-buildx-action from 2 to 3 (#18)

Bumps [docker/setup-buildx-action](https://github.com/docker/setup-buildx-action) from 2 to 3.
- [Release notes](https://github.com/docker/setup-buildx-action/releases)
- [Commits](docker/setup-buildx-action@v2...v3)

---
updated-dependencies:
- dependency-name: docker/setup-buildx-action
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* 🤖 [pre-commit.ci] pre-commit autoupdate (#20)

updates:
- [github.com/adamchainz/django-upgrade: 1.14.1 → 1.15.0](adamchainz/django-upgrade@1.14.1...1.15.0)
- [github.com/astral-sh/ruff-pre-commit: v0.0.290 → v0.0.291](astral-sh/ruff-pre-commit@v0.0.290...v0.0.291)

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>

* 🚜 Run pytest-cov

* ⬆️ Bumps coverage to check to >50%

* ⚙️ Run normal pytest

* 🚜 Adds python -m to all the things

* 🚜 Updates nox entrypoint too

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
Co-authored-by: Josh <josh@joshthomas.dev>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
  • Loading branch information
4 people authored Sep 25, 2023
1 parent b9fd73d commit 60f5cef
Show file tree
Hide file tree
Showing 10 changed files with 160 additions and 57 deletions.
6 changes: 3 additions & 3 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ jobs:
- name: Run tests
run: |
nox --session "tests-${{ matrix.python-version }}(django='${{ matrix.django-version }}')"
python -m nox --session "tests-${{ matrix.python-version }}(django='${{ matrix.django-version }}')"
tests:
runs-on: ubuntu-latest
Expand Down Expand Up @@ -88,7 +88,7 @@ jobs:
# https://hynek.me/articles/ditch-codecov-python/
- name: Run tests
run: |
coverage run -m pytest
python -m pytest
python -m coverage html --skip-covered --skip-empty
python -m coverage report | sed 's/^/ /' >> $GITHUB_STEP_SUMMARY
python -m coverage report --fail-under=100
python -m coverage report --fail-under=50
9 changes: 5 additions & 4 deletions Justfile
Original file line number Diff line number Diff line change
Expand Up @@ -48,9 +48,7 @@ test:
python -m nox --reuse-existing-virtualenvs

coverage:
rm -rf htmlcov
python -m coverage run -m pytest
python -m coverage html --skip-covered --skip-empty
python -m nox --reuse-existing-virtualenvs --session "coverage"

types:
python -m mypy .
Expand Down Expand Up @@ -235,4 +233,7 @@ envsync:
##################

lint:
pre-commit run --all-files
python -m nox --reuse-existing-virtualenvs --session "lint"

mypy:
python -m nox --reuse-existing-virtualenvs --session "mypy"
22 changes: 21 additions & 1 deletion noxfile.py
Original file line number Diff line number Diff line change
Expand Up @@ -49,4 +49,24 @@ def tests(session, django):
else:
session.install(f"django=={django}")

session.run("pytest", "-n", "auto", "--dist", "loadfile")
session.run("python", "-m", "pytest", "-n", "auto", "--dist", "loadfile")


@nox.session
def coverage(session):
session.install(".[dev]")
session.run("python", "-m", "pytest")
session.run("python", "-m", "coverage", "html", "--skip-covered", "--skip-empty")
session.run("python", "-m", "coverage", "report", "--fail-under=50")


@nox.session
def lint(session):
session.install(".[lint]")
session.run("pre-commit", "run", "--all-files")


@nox.session
def mypy(session):
session.install(".[dev]")
session.run("mypy")
34 changes: 17 additions & 17 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -41,20 +41,21 @@ requires-python = ">=3.8"

[project.optional-dependencies]
dev = [
"black",
"coverage[toml]",
"django-stubs",
"django-stubs-ext",
"hatch",
"model_bakery",
"mypy",
"nox",
"pytest",
"pytest-cov",
"pytest-django",
"pytest-randomly",
"pytest-reverse",
"pytest-xdist",
"ruff",
]
lint = ["pre-commit"]
psycopg = ["psycopg[binary]"]
psycopg2 = ["psycopg2-binary"]

Expand Down Expand Up @@ -112,21 +113,25 @@ version_pattern = "YYYY.INC1"

[tool.coverage.run]
omit = [
"src/email_relay/*/migrations/*",
"tests/*",
"manage.py",
"service.py"
"service.py",
"src/email_relay/migrations/*",
"tests/*",
]
source = ["src/email_relay"]
source = ["email_relay"]

[tool.coverage.paths]
source = ["src"]


[tool.django-stubs]
django_settings_module = "tests.settings"
strict_settings = false

[tool.mypy]
mypy_path = "src/"
namespace_packages = false
check_untyped_defs = true
files = [
"src.email_relay",
]
no_implicit_optional = true
plugins = [
"mypy_django_plugin.main",
Expand All @@ -137,24 +142,19 @@ warn_unused_ignores = true

[[tool.mypy.overrides]]
ignore_errors = true
module = [
"src.email_relay.*.migrations.*",
]

[[tool.mypy.overrides]]
ignore_missing_imports = true
module = []
module = "tests.*"

[tool.mypy_django_plugin]
ignore_missing_model_attributes = true

[tool.pytest.ini_options]
DJANGO_SETTINGS_MODULE = "tests.settings"
django_find_project = false
pythonpath = ". src"
addopts = "--create-db -n auto --dist loadfile"
addopts = "--create-db --cov=email_relay -n auto --dist loadfile"
norecursedirs = ".* bin build dist *.egg htmlcov logs node_modules templates venv"
python_files = "tests.py test_*.py *_tests.py"
testpaths = ["tests"]

[tool.ruff]
ignore = ["E501", "E741"] # temporary
Expand Down
2 changes: 1 addition & 1 deletion src/email_relay/backend.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
class RelayDatabaseEmailBackend(BaseEmailBackend):
def send_messages(self, email_messages: Sequence[EmailMessage]) -> int:
messages = Message.objects.bulk_create(
[Message(email=email) for email in email_messages], # type: ignore[misc]
[Message(email=email) for email in email_messages],
app_settings.MESSAGES_BATCH_SIZE,
)
return len(messages)
45 changes: 45 additions & 0 deletions tests/conftest.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
from __future__ import annotations

import logging

from django.conf import settings

from email_relay.conf import EMAIL_RELAY_DATABASE_ALIAS

pytest_plugins = [] # type: ignore


# Settings fixtures to bootstrap our tests
def pytest_configure(config):
logging.disable(logging.CRITICAL)

settings.configure(
ALLOWED_HOSTS=["*"],
CACHES={
"default": {
"BACKEND": "django.core.cache.backends.dummy.DummyCache",
}
},
DATABASES={
"default": {
"ENGINE": "django.db.backends.sqlite3",
"NAME": ":memory:",
},
EMAIL_RELAY_DATABASE_ALIAS: {
"ENGINE": "django.db.backends.sqlite3",
"NAME": ":memory:",
},
},
DATABASE_ROUTERS=[
"email_relay.db.EmailDatabaseRouter",
],
EMAIL_BACKEND="django.core.mail.backends.locmem.EmailBackend",
INSTALLED_APPS=[
"django.contrib.contenttypes",
"email_relay",
],
LOGGING_CONFIG=None,
PASSWORD_HASHERS=["django.contrib.auth.hashers.MD5PasswordHasher"],
SECRET_KEY="NOTASECRET",
USE_TZ=True,
)
31 changes: 0 additions & 31 deletions tests/settings.py
Original file line number Diff line number Diff line change
@@ -1,31 +0,0 @@
from __future__ import annotations

from email_relay.conf import EMAIL_RELAY_DATABASE_ALIAS

ALLOWED_HOSTS = ["*"]

DATABASES = {
"default": {
"ENGINE": "django.db.backends.sqlite3",
"NAME": ":memory:",
},
EMAIL_RELAY_DATABASE_ALIAS: {
"ENGINE": "django.db.backends.sqlite3",
"NAME": ":memory:",
},
}

DATABASE_ROUTERS = [
"email_relay.db.EmailRelayDatabaseRouter",
]


EMAIL_BACKEND = "email_relay.backend.DatabaseEmailBackend"

INSTALLED_APPS = [
"email_relay",
]

SECRET_KEY = "NOTASECRET"

USE_TZ = True
12 changes: 12 additions & 0 deletions tests/test_models.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
from __future__ import annotations

import pytest
from model_bakery import baker

from email_relay.models import Message


@pytest.mark.django_db(databases=["default", "email_relay_db"])
def test_message():
baker.make("email_relay.Message")
assert Message.objects.all().count() == 1
41 changes: 41 additions & 0 deletions tests/test_router.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
from __future__ import annotations

import pytest

from email_relay.conf import app_settings
from email_relay.db import EmailDatabaseRouter


# Mock model with app_label "email_relay"
class MockModel:
class _meta:
app_label = "email_relay"


# Mock model with app_label "some_other_app"
class MockModelOther:
class _meta:
app_label = "some_other_app"


@pytest.fixture
def router():
return EmailDatabaseRouter()


def test_db_for_read(router):
assert router.db_for_read(MockModel) == app_settings.DATABASE_ALIAS
assert router.db_for_read(MockModelOther) == "default"


def test_db_for_write(router):
assert router.db_for_write(MockModel) == app_settings.DATABASE_ALIAS
assert router.db_for_write(MockModelOther) == "default"


def test_allow_relation(router):
assert router.allow_relation(None, None)


def test_allow_migrate(router):
assert router.allow_migrate("some_db", "some_app_label")
15 changes: 15 additions & 0 deletions tests/test_runrelay.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
from __future__ import annotations

import pytest
from django.core.management import call_command


def test_runrelay_help():
# We'll capture the output of the command
with pytest.raises(SystemExit) as exec_info:
# call_command will execute our command as if we ran it from the command line
# the 'stdout' argument captures the command output
call_command("runrelay", "--help")

# Asserting that the command exits with a successful exit code (0 for help command)
assert exec_info.value.code == 0

0 comments on commit 60f5cef

Please sign in to comment.