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

Faster unit tests by re-using GPG directory #5683

Merged
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
79 changes: 45 additions & 34 deletions securedrop/tests/conftest.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
# -*- coding: utf-8 -*-

import configparser
from typing import Any
from typing import Iterator
from pathlib import Path
from tempfile import TemporaryDirectory
from typing import Any, Generator

import pretty_bad_protocol as gnupg
import logging

import py
from flask import Flask
from hypothesis import settings
import os
Expand Down Expand Up @@ -84,42 +84,53 @@ def setUpTearDown():
_cleanup_test_securedrop_dataroot(original_config)


@pytest.fixture(scope='function')
def config(tmpdir: py.path.local) -> SDConfig:
'''Clone the module so we can modify it per test.'''
@pytest.fixture(scope="session")
def gpg_key_dir() -> Generator[Path, None, None]:
"""Set up the journalist test key in GPG and the parent folder.

This fixture takes about 2s to complete hence we use the "session" scope to only run it once.
"""
with TemporaryDirectory() as tmp_gpg_dir_name:
tmp_gpg_dir = Path(tmp_gpg_dir_name)

# GPG 2.1+ requires gpg-agent, see #4013
gpg_agent_config = tmp_gpg_dir / "gpg-agent.conf"
gpg_agent_config.write_text("allow-loopback-pinentry")

cnf = SDConfig()
# Import the test key in GPG
gpg = gnupg.GPG("gpg2", homedir=str(tmp_gpg_dir))
test_keys_dir = Path(__file__).parent / "files"
for ext in ["sec", "pub"]:
key_file = test_keys_dir / "test_journalist_key.{}".format(ext)
gpg.import_keys(key_file.read_text())

data = tmpdir.mkdir('data')
keys = data.mkdir('keys')
os.chmod(str(keys), 0o700)
store = data.mkdir('store')
tmp = data.mkdir('tmp')
sqlite = data.join('db.sqlite')
yield tmp_gpg_dir


@pytest.fixture(scope='function')
def config(gpg_key_dir: Path) -> Generator[SDConfig, None, None]:
config = SDConfig()
config.GPG_KEY_DIR = str(gpg_key_dir)

# GPG 2.1+ requires gpg-agent, see #4013
gpg_agent_config = str(keys.join('gpg-agent.conf'))
with open(gpg_agent_config, 'w+') as f:
f.write('allow-loopback-pinentry')
# Setup the filesystem for the application
with TemporaryDirectory() as data_dir_name:
data_dir = Path(data_dir_name)
config.SECUREDROP_DATA_ROOT = str(data_dir)

gpg = gnupg.GPG('gpg2', homedir=str(keys))
for ext in ['sec', 'pub']:
file_path = path.join(
path.dirname(__file__), 'files', 'test_journalist_key.{}'.format(ext)
)
with open(file_path) as f:
gpg.import_keys(f.read())
store_dir = data_dir / "store"
store_dir.mkdir()
config.STORE_DIR = str(store_dir)

cnf.SECUREDROP_DATA_ROOT = str(data)
cnf.GPG_KEY_DIR = str(keys)
cnf.STORE_DIR = str(store)
cnf.TEMP_DIR = str(tmp)
cnf.DATABASE_FILE = str(sqlite)
tmp_dir = data_dir / "tmp"
tmp_dir.mkdir()
config.TEMP_DIR = str(tmp_dir)

# create the db file
subprocess.check_call(['sqlite3', cnf.DATABASE_FILE, '.databases'])
# Create the db file
sqlite_db_path = data_dir / "db.sqlite"
config.DATABASE_FILE = str(sqlite_db_path)
subprocess.check_call(["sqlite3", config.DATABASE_FILE, ".databases"])

return cnf
yield config


@pytest.fixture(scope='function')
Expand All @@ -140,7 +151,7 @@ def alembic_config(config: SDConfig) -> str:


@pytest.fixture(scope='function')
def source_app(config: SDConfig) -> Iterator[Flask]:
def source_app(config: SDConfig) -> Generator[Flask, None, None]:
app = create_source_app(config)
app.config['SERVER_NAME'] = 'localhost.localdomain'
with app.app_context():
Expand All @@ -153,7 +164,7 @@ def source_app(config: SDConfig) -> Iterator[Flask]:


@pytest.fixture(scope='function')
def journalist_app(config: SDConfig) -> Iterator[Flask]:
def journalist_app(config: SDConfig) -> Generator[Flask, None, None]:
app = create_journalist_app(config)
app.config['SERVER_NAME'] = 'localhost.localdomain'
with app.app_context():
Expand Down