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

Add alembic tests #241

Merged
merged 9 commits into from
Feb 28, 2019
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
4 changes: 3 additions & 1 deletion .circleci/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,14 @@ jobs:
steps:
- checkout

- run: sudo apt-get install -y sqlite3

- run:
name: Install requirements and run tests
command: |
pipenv install --keep-outdated --dev
export PYTHONPATH=$PYTHONPATH:. # so alembic can get to Base metadata
pipenv run make check
pipenv run make check --keep-going

- run:
name: Check Python dependencies for known vulnerabilities
Expand Down
25 changes: 15 additions & 10 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -85,21 +85,26 @@ After you run the server container, the journalist interface API will be running
To ensure that file decryption works, please import [this test private key](https://raw.githubusercontent.com/freedomofpress/securedrop/0a901362b84a5378fba80e9cd0ffe4542bdcd598/securedrop/tests/files/test_journalist_key.sec) into your GnuPG keyring. Submissions in the SecureDrop server dev environment can be decrypted with this test key.


## Run linter
## Run the tests and checks

```
make lint
```

## Run tests
To run everything, run:

```
make test
```bash
make check
```

## Generate and run database migrations

```
alembic revision --autogenerate -m "describe your revision here"
```bash
rm -f svs.sqlite
sqlite3 svs.sqlite .databases > /dev/null
alembic upgrade head
alembic revision --autogenerate -m "describe your revision here"
make test-alembic
```

### Merging Migrations

This project aims to have at most one migration per release. There may be cases where this is not feasible,
but developers should merge their migration into the latest migration that has been generated since the last
release. The above mentioned autogenerate command will not do this for you.
heartsucker marked this conversation as resolved.
Show resolved Hide resolved
24 changes: 0 additions & 24 deletions alembic/versions/0ca2cc448074_added_fingerprint.py

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,48 +1,46 @@
"""Initial migration
"""init

Revision ID: 5344fc3a3d3f
Revision ID: 2f363b3d680e
Revises:
Create Date: 2018-07-18 18:11:06.781732
Create Date: 2019-02-08 12:07:47.062397

"""
from alembic import op
import sqlalchemy as sa


# revision identifiers, used by Alembic.
revision = '5344fc3a3d3f'
revision = '2f363b3d680e'
down_revision = None
branch_labels = None
depends_on = None


def upgrade():
op.create_table(
'users',
'sources',
sa.Column('id', sa.Integer(), nullable=False),
sa.Column('uuid', sa.String(length=36), nullable=False),
sa.Column('username', sa.String(length=255), nullable=False),
sa.PrimaryKeyConstraint('id'),
sa.UniqueConstraint('username')
sa.Column('journalist_designation', sa.String(length=255), nullable=False),
sa.Column('document_count', sa.Integer(), server_default='0', nullable=False),
sa.Column('is_flagged', sa.Boolean(name='is_flagged'), server_default='false',
nullable=True),
sa.Column('public_key', sa.Text(), nullable=True),
sa.Column('fingerprint', sa.String(length=64), nullable=True),
sa.Column('interaction_count', sa.Integer(), server_default='0', nullable=False),
sa.Column('is_starred', sa.Boolean(name='is_starred'), server_default='false',
nullable=True),
sa.Column('last_updated', sa.DateTime(), nullable=True),
sa.PrimaryKeyConstraint('id', name=op.f('pk_sources')),
sa.UniqueConstraint('uuid', name=op.f('uq_sources_uuid'))
)
op.create_table(
'sources',
'users',
sa.Column('id', sa.Integer(), nullable=False),
sa.Column('uuid', sa.String(length=36), nullable=False),
sa.Column('journalist_designation', sa.String(length=255),
nullable=False),
sa.Column('is_flagged', sa.Boolean(name='ck_sources_is_flagged'),
nullable=True,
server_default="false"),
sa.Column('public_key', sa.Text(), nullable=True),
sa.Column('interaction_count', sa.Integer(), nullable=False,
server_default="0"),
sa.Column('is_starred', sa.Boolean(name='ck_sources_is_starred'),
nullable=True,
server_default="false"),
sa.Column('last_updated', sa.DateTime(), nullable=True),
sa.PrimaryKeyConstraint('id'),
sa.UniqueConstraint('uuid')
sa.Column('username', sa.String(length=255), nullable=False),
sa.PrimaryKeyConstraint('id', name=op.f('pk_users')),
sa.UniqueConstraint('uuid', name=op.f('uq_users_uuid'))
)
op.create_table(
'replies',
Expand All @@ -51,36 +49,34 @@ def upgrade():
sa.Column('source_id', sa.Integer(), nullable=True),
sa.Column('journalist_id', sa.Integer(), nullable=True),
sa.Column('filename', sa.String(length=255), nullable=False),
sa.Column('size', sa.Integer(), nullable=False),
sa.Column('is_downloaded', sa.Boolean(
name='ck_replies_is_downloaded'), nullable=True),
sa.ForeignKeyConstraint(['journalist_id'], ['users.id'], ),
sa.ForeignKeyConstraint(['source_id'], ['sources.id'], ),
sa.PrimaryKeyConstraint('id'),
sa.UniqueConstraint('uuid')
sa.Column('size', sa.Integer(), nullable=True),
sa.Column('is_downloaded', sa.Boolean(name='is_downloaded'), nullable=True),
sa.ForeignKeyConstraint(['journalist_id'], ['users.id'],
name=op.f('fk_replies_journalist_id_users')),
sa.ForeignKeyConstraint(['source_id'], ['sources.id'],
name=op.f('fk_replies_source_id_sources')),
sa.PrimaryKeyConstraint('id', name=op.f('pk_replies')),
sa.UniqueConstraint('uuid', name=op.f('uq_replies_uuid'))
)
op.create_table(
'submissions',
sa.Column('id', sa.Integer(), nullable=False),
sa.Column('uuid', sa.String(length=36), nullable=False),
sa.Column('filename', sa.String(length=255), nullable=False),
sa.Column('size', sa.Integer(), nullable=False),
sa.Column('is_downloaded',
sa.Boolean(name='ck_submissions_is_downloaded'),
nullable=True),
sa.Column('is_read',
sa.Boolean(name='ck_submissions_is_read'),
nullable=True),
sa.Column('source_id', sa.Integer(), nullable=True),
sa.Column('download_url', sa.String(length=255), nullable=False),
sa.ForeignKeyConstraint(['source_id'], ['sources.id'], ),
sa.PrimaryKeyConstraint('id'),
sa.UniqueConstraint('uuid')
sa.Column('is_downloaded', sa.Boolean(name='is_downloaded'), nullable=True),
sa.Column('is_read', sa.Boolean(name='is_read'), nullable=True),
sa.Column('source_id', sa.Integer(), nullable=True),
sa.ForeignKeyConstraint(['source_id'], ['sources.id'],
name=op.f('fk_submissions_source_id_sources')),
sa.PrimaryKeyConstraint('id', name=op.f('pk_submissions')),
sa.UniqueConstraint('uuid', name=op.f('uq_submissions_uuid'))
)


def downgrade():
op.drop_table('users')
op.drop_table('submissions')
op.drop_table('replies')
op.drop_table('users')
op.drop_table('sources')
25 changes: 0 additions & 25 deletions alembic/versions/cd0fbb73bf8e_capture_document_count.py

This file was deleted.

4 changes: 2 additions & 2 deletions securedrop_client/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@
current_locale, encoding = locale.getdefaultlocale()
# Get the language code.
language_code = current_locale[:2]
except (TypeError, ValueError):
language_code = 'en'
except (TypeError, ValueError): # pragma: no cover
language_code = 'en' # pragma: no cover
# DEBUG/TRANSLATE: override the language code here (e.g. to Chinese).
# language_code = 'zh'
gettext.translation('securedrop_client', localedir=localedir,
Expand Down
15 changes: 7 additions & 8 deletions securedrop_client/db.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
convention = {
"ix": 'ix_%(column_0_label)s',
"uq": "uq_%(table_name)s_%(column_0_name)s",
"ck": "ck_%(table_name)s_%(constraint_name)s",
"ck": "ck_%(table_name)s_%(column_0_name)s",
"fk": "fk_%(table_name)s_%(column_0_name)s_%(referred_table_name)s",
"pk": "pk_%(table_name)s"
}
Expand All @@ -28,17 +28,16 @@ class Source(Base):

__tablename__ = 'sources'

# TODO - add number_of_docs
id = Column(Integer, primary_key=True)
uuid = Column(String(36), unique=True, nullable=False)
journalist_designation = Column(String(255), nullable=False)
document_count = Column(Integer, server_default="0", nullable=False)
is_flagged = Column(Boolean(name='ck_sources_is_flagged'),
is_flagged = Column(Boolean(name='is_flagged'),
server_default="false")
public_key = Column(Text, nullable=True)
fingerprint = Column(String(64))
interaction_count = Column(Integer, server_default="0", nullable=False)
is_starred = Column(Boolean(name='ck_sources_is_starred'),
is_starred = Column(Boolean(name='is_starred'),
server_default="false")
last_updated = Column(DateTime)

Expand Down Expand Up @@ -78,11 +77,11 @@ class Submission(Base):
download_url = Column(String(255), nullable=False)

# This is whether the submission has been downloaded in the local database.
is_downloaded = Column(Boolean(name='ck_submissions_is_downloaded'),
is_downloaded = Column(Boolean(name='is_downloaded'),
default=False)

# This reflects read status stored on the server.
is_read = Column(Boolean(name='ck_submissions_is_read'),
is_read = Column(Boolean(name='is_read'),
default=False)

source_id = Column(Integer, ForeignKey('sources.id'))
Expand Down Expand Up @@ -123,7 +122,7 @@ class Reply(Base):
size = Column(Integer)

# This is whether the reply has been downloaded in the local database.
is_downloaded = Column(Boolean(name='ck_replies_is_downloaded'),
is_downloaded = Column(Boolean(name='is_downloaded'),
default=False)

def __repr__(self):
Expand All @@ -136,7 +135,7 @@ class User(Base):

id = Column(Integer, primary_key=True)
uuid = Column(String(36), unique=True, nullable=False)
username = Column(String(255), nullable=False, unique=True)
username = Column(String(255), nullable=False)

def __init__(self, username):
self.username = username
Expand Down
29 changes: 27 additions & 2 deletions tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import tempfile
import pytest

from configparser import ConfigParser
from datetime import datetime
from securedrop_client.config import Config
from securedrop_client.db import Base, make_engine, Source
Expand Down Expand Up @@ -48,13 +49,37 @@ def config(homedir) -> str:
return full_path


@pytest.fixture(scope='function')
def alembic_config(homedir):
return _alembic_config(homedir)


def _alembic_config(homedir):
base_dir = os.path.join(os.path.dirname(__file__), '..')
migrations_dir = os.path.join(base_dir, 'alembic')
ini = ConfigParser()
ini.read(os.path.join(base_dir, 'alembic.ini'))

ini.set('alembic', 'script_location', os.path.join(migrations_dir))
ini.set('alembic', 'sqlalchemy.url',
'sqlite:///{}'.format(os.path.join(homedir, 'svs.sqlite')))

alembic_path = os.path.join(homedir, 'alembic.ini')

with open(alembic_path, 'w') as f:
ini.write(f)

return alembic_path


@pytest.fixture(scope='function')
def session(homedir):
engine = make_engine(homedir)
Base.metadata.create_all(bind=engine)
Base.metadata.create_all(bind=engine, checkfirst=False)
Session = sessionmaker(bind=engine)
session = Session()
return session
yield session
session.close()


@pytest.fixture(scope='function')
Expand Down
1 change: 0 additions & 1 deletion tests/gui/test_main.py
Original file line number Diff line number Diff line change
Expand Up @@ -264,7 +264,6 @@ def test_conversation_pending_message(mocker):


def test_set_status(mocker):

"""
Ensure the status bar's text is updated.
"""
Expand Down
Loading