Skip to content

Commit 714b43d

Browse files
committed
Fix tests by creating simulated mailbox server
Since the re-write of the mailboxer server to rust, the tests of this repository cannot run. Created a simulation server for tests instead
1 parent 2ccdaa0 commit 714b43d

3 files changed

Lines changed: 98 additions & 40 deletions

File tree

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ authors = [{ name = "Rotem Yaari", email = "vmalloc@gmail.com" }]
2828
"Homepage" = "https://github.com/getslash/mailboxer-python"
2929

3030
[project.optional-dependencies]
31-
testing = ["Flask-Loopback", "pylint", "pytest"]
31+
testing = ["dataclasses_json", "Flask-Loopback", "pylint", "pytest"]
3232

3333
[tool.hatch.build.targets.wheel]
3434
packages = ["mailboxer"]

tests/conftest.py

Lines changed: 11 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,3 @@
1-
import os
2-
import shutil
3-
import subprocess
4-
import sys
5-
import tempfile
61
import uuid
72

83
from urlobject import URLObject as URL
@@ -11,44 +6,21 @@
116
import pytest
127
from mailboxer import Mailboxer
138

14-
sys.path.insert(0, os.path.join(
15-
os.path.abspath(os.path.dirname(__file__)),
16-
"..", ".env", "mailboxer"))
17-
from flask_app.app import create_app
18-
from flask_app import models
9+
from .flask_app import create_app, app_initializations
1910

20-
def pytest_addoption(parser):
21-
parser.addoption("--setup-db", action="store_true", default=False)
2211

2312
@pytest.fixture(scope="session")
24-
def db_engine(request):
25-
if request.config.getoption("--setup-db"):
26-
tmpdir = tempfile.mkdtemp()
27-
subprocess.check_call("pg_ctl init -D {0} -w".format(tmpdir), shell=True)
28-
subprocess.check_call("pg_ctl start -D {0} -w".format(tmpdir), shell=True)
29-
30-
@request.addfinalizer
31-
def finalize():
32-
subprocess.check_call("pg_ctl stop -D {0} -w -m immediate".format(tmpdir), shell=True)
33-
shutil.rmtree(tmpdir)
34-
35-
subprocess.check_call("createdb mailboxer", shell=True)
36-
37-
with create_app().app_context():
38-
models.db.session.close()
39-
models.db.drop_all()
40-
models.db.create_all()
41-
42-
43-
@pytest.fixture(scope="session")
44-
def mailboxer_url(request, db_engine):
45-
loopback = FlaskLoopback(create_app())
13+
def mailboxer_url(request):
14+
app = create_app()
15+
loopback = FlaskLoopback(app)
4616
hostname = str(uuid.uuid1())
47-
loopback.activate_address((hostname, 80))
48-
@request.addfinalizer
49-
def close():
50-
loopback.deactivate_address((hostname, 80))
51-
return URL("http://{0}".format(hostname))
17+
port = 80
18+
loopback.activate_address((hostname, port))
19+
with app.app_context():
20+
app_initializations()
21+
yield URL(f"http://{hostname}:{port}")
22+
loopback.deactivate_address((hostname, port))
23+
5224

5325
@pytest.fixture
5426
def mailboxer(mailboxer_url):

tests/flask_app.py

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
import datetime
2+
import dataclasses
3+
from dataclasses_json import DataClassJsonMixin
4+
from itertools import count
5+
from typing import Any, Dict
6+
from flask import Flask, g, request, jsonify
7+
8+
9+
@dataclasses.dataclass
10+
class Mailbox(DataClassJsonMixin):
11+
id: int
12+
address: str
13+
last_activity: str
14+
15+
16+
@dataclasses.dataclass
17+
class Email(DataClassJsonMixin):
18+
id: int
19+
mailbox_id: int
20+
fromaddr: str
21+
message: str
22+
timestamp: str
23+
sent_via_ssl: bool
24+
read: bool
25+
26+
27+
def app_initializations() -> None:
28+
g.mailboxes = {}
29+
g.emails = {}
30+
g.mailboxes_count = count(1)
31+
32+
33+
def create_app() -> Flask:
34+
app = Flask("MailboxerSimulation")
35+
36+
def _get_success_response() -> Dict[str, str]:
37+
return {"result": "ok"}
38+
39+
def _get_paginated_response(objects: list[Any]) -> Dict[str, Any]:
40+
page = request.args.get("page", default=1, type=int)
41+
page_size = request.args.get("page_size", default=1000, type=int)
42+
result_objects = objects[(page - 1) * page_size : page_size * page]
43+
return {
44+
"metadata": {"total_num_objects": len(objects)},
45+
"result": result_objects,
46+
}
47+
48+
def now() -> str:
49+
return datetime.datetime.now(tz=datetime.timezone.utc).isoformat()
50+
51+
@app.route("/v2/mailboxes", methods=["GET"])
52+
def query_mailboxes():
53+
sorted_mailboxes = sorted(
54+
g.mailboxes.values(), key=lambda mailbox: mailbox.address
55+
)
56+
return _get_paginated_response(sorted_mailboxes)
57+
58+
@app.route("/v2/mailboxes", methods=["POST"])
59+
def create_mailbox():
60+
data = request.get_json(silent=True)
61+
if not isinstance(data, dict):
62+
return jsonify({"error": "Not JSON body"}), 400
63+
address = data.get("address")
64+
if not isinstance(address, str):
65+
return jsonify({"error": "Invalid address {address!r}"}), 400
66+
67+
g.mailboxes[address] = Mailbox(
68+
id=next(g.mailboxes_count), address=address, last_activity=now()
69+
)
70+
return _get_success_response()
71+
72+
@app.route("/v2/mailboxes/<address>", methods=["DELETE"])
73+
def delete_mailbox(address: str):
74+
g.mailboxes.pop(address, None)
75+
return _get_success_response()
76+
77+
@app.route("/v2/mailboxes/<address>/emails", methods=["GET"])
78+
def query_mailbox_emails(address: str):
79+
emails = g.emails.get(address, [])
80+
return _get_paginated_response(emails)
81+
82+
@app.route("/v2/vacuum", methods=["POST"])
83+
def vacuum():
84+
return _get_success_response()
85+
86+
return app

0 commit comments

Comments
 (0)