summaryrefslogtreecommitdiff
path: root/tests
diff options
context:
space:
mode:
authorpennae <github@quasiparticle.net>2022-07-14 09:17:16 +0200
committerpennae <github@quasiparticle.net>2022-07-14 09:17:16 +0200
commit062cd0ff0176f1f9924e3ae89c0eaf01a8d6fd04 (patch)
treec9d0409c5ec2aa60edad4ee590b746cb645208e7 /tests
parent566d6ccee21fc52943cf1862222f2e9a8927403c (diff)
downloadminor-skulk-062cd0ff0176f1f9924e3ae89c0eaf01a8d6fd04.tar.gz
minor-skulk-062cd0ff0176f1f9924e3ae89c0eaf01a8d6fd04.tar.xz
minor-skulk-062cd0ff0176f1f9924e3ae89c0eaf01a8d6fd04.zip
allow integration tests to run in parallel
this doesn't do much for performance, but it does allow running the tests with a simple `cargo t`.
Diffstat (limited to 'tests')
-rw-r--r--tests/api.py13
-rw-r--r--tests/conftest.py15
-rw-r--r--tests/integration.rs70
-rw-r--r--tests/test_auth_account.py4
-rw-r--r--tests/test_profile.py16
5 files changed, 72 insertions, 46 deletions
diff --git a/tests/api.py b/tests/api.py
index 9d2f70d..32e1159 100644
--- a/tests/api.py
+++ b/tests/api.py
@@ -15,12 +15,13 @@ from cryptography.hazmat.primitives import serialization
from cryptography.hazmat.primitives.asymmetric import ec
from fxa.crypto import quick_stretch_password, derive_key, xor
-AUTH_URL = "http://localhost:8000/auth"
-PROFILE_URL = "http://localhost:8000/profile"
-OAUTH_URL = "http://localhost:8000/oauth"
-INVITE_URL = "http://localhost:8000/_invite"
-PUSH_PORT = 10264
-SMTP_PORT = 2525
+API_PORT = int(os.environ.get('API_PORT', 8000))
+PUSH_PORT = API_PORT + 1
+SMTP_PORT = int(os.environ.get('MAIL_PORT', 2525))
+AUTH_URL = f"http://localhost:{API_PORT}/auth"
+PROFILE_URL = f"http://localhost:{API_PORT}/profile"
+OAUTH_URL = f"http://localhost:{API_PORT}/oauth"
+INVITE_URL = f"http://localhost:{API_PORT}/_invite"
def auth_pw(email, pw):
return derive_key(quick_stretch_password(email, pw), "authPW").hex()
diff --git a/tests/conftest.py b/tests/conftest.py
index 15149cb..43b32f2 100644
--- a/tests/conftest.py
+++ b/tests/conftest.py
@@ -52,27 +52,30 @@ def _account(client, primary, email, mail_server):
if e.details['errno'] != 102:
raise
+email1 = f"test.account-{os.urandom(8).hex()}@test-auth"
+email2 = f"test.account2-{os.urandom(8).hex()}@test-auth"
+
@pytest.fixture(params=[True, False], ids=["primary", "secondary"])
def account(client, request, mail_server):
- for a in _account(client, request.param, "test.account@test-auth", mail_server):
+ for a in _account(client, request.param, email1, mail_server):
yield a
@pytest.fixture(params=[True, False], ids=["primary", "secondary"])
def account2(client, request, mail_server):
- for a in _account(client, request.param, "test.account2@test-auth", mail_server):
+ for a in _account(client, request.param, email2, mail_server):
yield a
@pytest.fixture
def unverified_account(client, mail_server):
- s = client.create_account("test.account@test-auth", "")
+ s = client.create_account(email1, "")
yield s
- s.destroy_account("test.account@test-auth", "")
+ s.destroy_account(s.email, "")
@pytest.fixture
def login(client, mail_server):
- return _login(client, "test.account@test-auth", mail_server)
+ return _login(client, email1, mail_server)
@pytest.fixture
def login2(client, mail_server):
- return _login(client, "test.account2@test-auth", mail_server)
+ return _login(client, email2, mail_server)
def _refresh_token(account, scope):
body = {
diff --git a/tests/integration.rs b/tests/integration.rs
index afded52..1866702 100644
--- a/tests/integration.rs
+++ b/tests/integration.rs
@@ -16,31 +16,53 @@ use rocket::{
extern crate rocket;
extern crate anyhow;
-async fn run_pytest(markers: &'static str, invite: bool) -> anyhow::Result<()> {
+async fn run_pytest(
+ markers: &'static str,
+ port_offset: u16,
+ invite_admin: Option<&'static str>,
+) -> anyhow::Result<()> {
dotenv::dotenv().ok();
// at this point this is only a test runner to be used by the nix build.
env::set_var("ROCKET_LOG_LEVEL", "off");
+ // allow multiple runs of the server, with different port offsets.
+ let port = 8000 + port_offset;
+ let mail_port = 2525 + port_offset;
let (tx, rx) = channel();
- let rocket = build().await?.attach(AdHoc::on_liftoff("notify startup", move |rocket| {
- Box::pin(async move {
- // add an invite code as-if generated during startup and move it to an
- // env var, emulating the user looking at the logs and copying the code.
- // invite_only needs this to function.
- if invite {
- let db = rocket.state::<Db>().unwrap();
- let tx = db.begin().await.unwrap();
- let mut code = [0; 32];
- OsRng.fill_bytes(&mut code);
- let code = base64::encode_config(code, URL_SAFE_NO_PAD);
- tx.add_invite_code(&code, Utc::now() + Duration::minutes(5)).await.unwrap();
- tx.commit().await.unwrap();
- env::set_var("INVITE_CODE", code);
- }
+ let rocket = rocket::build();
+ let figment = rocket
+ .figment()
+ .clone()
+ .merge((rocket::Config::PORT, port))
+ .merge(("location", format!("http://localhost:{}", port)))
+ .merge(("mail_port", mail_port));
+ let figment = if let Some(admin) = invite_admin {
+ figment.merge(("invite_only", true)).merge(("invite_admin_address", admin))
+ } else {
+ figment.merge(("invite_only", false))
+ };
+ let rocket = build(rocket.configure(figment)).await?.attach(AdHoc::on_liftoff(
+ "notify startup",
+ move |rocket| {
+ Box::pin(async move {
+ // add an invite code as-if generated during startup and move it to an
+ // env var, emulating the user looking at the logs and copying the code.
+ // invite_only needs this to function.
+ if invite_admin.is_some() {
+ let db = rocket.state::<Db>().unwrap();
+ let tx = db.begin().await.unwrap();
+ let mut code = [0; 32];
+ OsRng.fill_bytes(&mut code);
+ let code = base64::encode_config(code, URL_SAFE_NO_PAD);
+ tx.add_invite_code(&code, Utc::now() + Duration::minutes(5)).await.unwrap();
+ tx.commit().await.unwrap();
+ env::set_var("INVITE_CODE", code);
+ }
- tx.send(()).unwrap();
- })
- }));
+ tx.send(()).unwrap();
+ })
+ },
+ ));
spawn(async move { rocket.launch().await });
let test = spawn(async move {
@@ -49,6 +71,9 @@ async fn run_pytest(markers: &'static str, invite: bool) -> anyhow::Result<()> {
.arg("-vvv")
.arg("-m")
.arg(markers)
+ .env("API_PORT", port.to_string())
+ .env("MAIL_PORT", mail_port.to_string())
+ .kill_on_drop(true)
.spawn()
.expect("failed to spawn");
child.wait().await
@@ -61,13 +86,10 @@ async fn run_pytest(markers: &'static str, invite: bool) -> anyhow::Result<()> {
#[async_test]
async fn open() -> anyhow::Result<()> {
- env::set_var("ROCKET_INVITE_ONLY", "false");
- run_pytest("not invite", false).await
+ run_pytest("not invite", 100, None).await
}
#[async_test]
async fn invite_only() -> anyhow::Result<()> {
- env::set_var("ROCKET_INVITE_ONLY", "true");
- env::set_var("ROCKET_INVITE_ADMIN_ADDRESS", "test.account@test-auth");
- run_pytest("invite", true).await
+ run_pytest("invite", 200, Some("test.account.invite@test-auth")).await
}
diff --git a/tests/test_auth_account.py b/tests/test_auth_account.py
index 68a407b..0e7e22d 100644
--- a/tests/test_auth_account.py
+++ b/tests/test_auth_account.py
@@ -298,7 +298,7 @@ def test_create_badinvite(client):
@pytest.mark.invite
def test_create_invite(client, mail_server):
# all in one test because restarting the server from python would be a pain
- c = client.create_account("test.account@test-auth", "", invite=os.environ['INVITE_CODE'])
+ c = client.create_account("test.account.invite@test-auth", "", invite=os.environ['INVITE_CODE'])
c2 = None
try:
invite = Invite(c.props['sessionToken'])
@@ -319,7 +319,7 @@ def test_create_invite(client, mail_server):
assert 'url' in code
code = code['url'].split('/')[-1]
# code allows registration
- c2 = client.create_account("test.account2@test-auth", "", invite=code)
+ c2 = client.create_account("test.account.invite2@test-auth", "", invite=code)
(to, body) = mail_server.wait()
data = json.loads(base64.urlsafe_b64decode(body.split("#/verify/", maxsplit=1)[1]).decode('utf8'))
c2.post_a('/recovery_email/verify_code', { 'uid': data['uid'], 'code': data['code'] })
diff --git a/tests/test_profile.py b/tests/test_profile.py
index 5e7308a..1e21ecc 100644
--- a/tests/test_profile.py
+++ b/tests/test_profile.py
@@ -51,7 +51,7 @@ def test_profile(account, profile):
resp = profile.get_a("/profile")
assert resp == {
'amrValues': None,
- 'avatar': 'http://localhost:8000/avatars/00000000000000000000000000000000',
+ 'avatar': f'http://localhost:{API_PORT}/avatars/00000000000000000000000000000000',
'avatarDefault': True,
'displayName': None,
'email': account.email,
@@ -65,7 +65,7 @@ def test_display_name(account, profile):
resp = profile.get_a("/profile")
assert resp == {
'amrValues': None,
- 'avatar': 'http://localhost:8000/avatars/00000000000000000000000000000000',
+ 'avatar': f'http://localhost:{API_PORT}/avatars/00000000000000000000000000000000',
'avatarDefault': True,
'displayName': None,
'email': account.email,
@@ -78,7 +78,7 @@ def test_display_name(account, profile):
resp = profile.get_a("/profile")
assert resp == {
'amrValues': None,
- 'avatar': 'http://localhost:8000/avatars/00000000000000000000000000000000',
+ 'avatar': f'http://localhost:{API_PORT}/avatars/00000000000000000000000000000000',
'avatarDefault': True,
'displayName': 'foo',
'email': account.email,
@@ -91,7 +91,7 @@ def test_display_name(account, profile):
def test_avatar(account, profile):
resp = profile.get_a("/avatar")
assert resp == {
- 'avatar': 'http://localhost:8000/avatars/00000000000000000000000000000000',
+ 'avatar': f'http://localhost:{API_PORT}/avatars/00000000000000000000000000000000',
'avatarDefault': True,
'id': '00000000000000000000000000000000'
}
@@ -101,11 +101,11 @@ def test_avatar_upload(account, profile):
profile.post_a("/avatar/upload", "foo", headers={'content-type': 'image/png'})
resp = profile.get_a("/avatar")
new_id = resp['id']
- assert resp['avatar'] != 'http://localhost:8000/avatars/00000000000000000000000000000000'
+ assert resp['avatar'] != f'http://localhost:{API_PORT}/avatars/00000000000000000000000000000000'
assert not resp['avatarDefault']
assert resp['id'] != '00000000000000000000000000000000'
resp = profile.get_a("/profile")
- assert resp['avatar'] != 'http://localhost:8000/avatars/00000000000000000000000000000000'
+ assert resp['avatar'] != f'http://localhost:{API_PORT}/avatars/00000000000000000000000000000000'
assert not resp['avatarDefault']
def test_avatar_delete(account, profile):
@@ -116,14 +116,14 @@ def test_avatar_delete(account, profile):
profile.delete_a(f"/avatar/{new_id}")
resp = profile.get_a("/avatar")
assert resp == {
- 'avatar': 'http://localhost:8000/avatars/00000000000000000000000000000000',
+ 'avatar': f'http://localhost:{API_PORT}/avatars/00000000000000000000000000000000',
'avatarDefault': True,
'id': '00000000000000000000000000000000'
}
resp = profile.get_a("/profile")
assert resp == {
'amrValues': None,
- 'avatar': 'http://localhost:8000/avatars/00000000000000000000000000000000',
+ 'avatar': f'http://localhost:{API_PORT}/avatars/00000000000000000000000000000000',
'avatarDefault': True,
'displayName': None,
'email': account.email,