Example #1
0
def test_update_sources(homedir, mocker):
    """
    Check that:

    * Existing sources are updated in the local database.
    * New sources have an entry in the local database.
    * Local sources not returned by the remote server are deleted from the
      local database.
    """
    mock_session = mocker.MagicMock()
    # Some source objects from the API, one of which will exist in the local
    # database, the other will NOT exist in the local source database (this
    # will be added to the database)
    source_update = make_remote_source()
    source_create = make_remote_source()
    remote_sources = [source_update, source_create]
    # Some local source objects. One already exists in the API results (this
    # will be updated), one does NOT exist in the API results (this will be
    # deleted from the local database).
    local_source1 = mocker.MagicMock()
    local_source1.uuid = source_update.uuid
    local_source2 = mocker.MagicMock()
    local_source2.uuid = str(uuid.uuid4())
    local_sources = [local_source1, local_source2]
    update_sources(remote_sources, local_sources, mock_session, homedir)
    # Check the expected local source object has been updated with values from
    # the API.
    assert local_source1.journalist_designation == \
        source_update.journalist_designation
    assert local_source1.is_flagged == source_update.is_flagged
    assert local_source1.public_key == source_update.key['public']
    assert local_source1.interaction_count == source_update.interaction_count
    assert local_source1.is_starred == source_update.is_starred
    assert local_source1.last_updated == parse(source_update.last_updated)
    # Check the expected local source object has been created with values from
    # the API.
    assert mock_session.add.call_count == 1
    new_source = mock_session.add.call_args_list[0][0][0]
    assert new_source.uuid == source_create.uuid
    assert new_source.journalist_designation == \
        source_create.journalist_designation
    assert new_source.is_flagged == source_create.is_flagged
    assert new_source.public_key == source_create.key['public']
    assert new_source.interaction_count == source_create.interaction_count
    assert new_source.is_starred == source_create.is_starred
    assert new_source.last_updated == parse(source_create.last_updated)
    # Ensure the record for the local source that is missing from the results
    # of the API is deleted.
    mock_session.delete.assert_called_once_with(local_source2)
    # Session is committed to database.
    assert mock_session.commit.call_count == 1
Example #2
0
def test_update_sources_deletes_files_associated_with_the_source(
        homedir, mocker):
    """
    Check that:

    * Sources are deleted on disk after sync.
    """
    mock_session = mocker.MagicMock()

    # Test scenario: one source locally, no sources on server.
    remote_sources = []

    # A local source object. To ensure that all submissions/replies from
    # various stages of processing are cleaned up, we'll add several filenames
    # associated with each message, document, and reply for each stage of processing.
    # This simulates if a step failed.
    msg_server_filename = '1-pericardial-surfacing-msg.gpg'
    msg_local_filename_decrypted = '1-pericardial-surfacing-msg'

    file_server_filename = '1-pericardial-surfacing-doc.gz.gpg'
    file_local_filename_decompressed = '1-pericardial-surfacing-doc'
    file_local_filename_decrypted = '1-pericardial-surfacing-doc.gz'

    reply_server_filename = '1-pericardial-surfacing-reply.gpg'
    reply_local_filename_decrypted = '1-pericardial-surfacing-reply'

    # Here we're not mocking out the models use so that we can use the collection attribute.
    local_source = factory.Source()
    file_submission = db.Submission(source=local_source,
                                    uuid="test",
                                    size=123,
                                    filename=file_server_filename,
                                    download_url='http://test/test')
    msg_submission = db.Submission(source=local_source,
                                   uuid="test",
                                   size=123,
                                   filename=msg_server_filename,
                                   download_url='http://test/test')
    user = db.User('hehe')
    reply = db.Reply(source=local_source,
                     journalist=user,
                     filename=reply_server_filename,
                     size=1234,
                     uuid='test')
    local_source.submissions = [file_submission, msg_submission]
    local_source.replies = [reply]

    # Make the test files on disk in tmpdir so we can check they get deleted.
    test_filename_absolute_paths = []
    for test_filename in [
            msg_server_filename, msg_local_filename_decrypted,
            file_server_filename, file_local_filename_decompressed,
            file_local_filename_decrypted, reply_server_filename,
            reply_local_filename_decrypted
    ]:
        abs_server_filename = add_test_file_to_temp_dir(homedir, test_filename)
        test_filename_absolute_paths.append(abs_server_filename)

    local_sources = [local_source]
    update_sources(remote_sources, local_sources, mock_session, homedir)

    # Ensure the files associated with the reply are deleted on disk.
    for test_filename in test_filename_absolute_paths:
        assert not os.path.exists(test_filename)

    # Ensure the record for the local source is gone, along with its
    # related files.
    mock_session.delete.assert_called_with(local_source)

    # Session is committed to database.
    assert mock_session.commit.call_count == 1
Example #3
0
def test_update_sources(homedir, mocker, session_maker, session):
    """
    Check that:

    * Existing sources are updated in the local database.
    * New sources have an entry in the local database.
    * Local sources not returned by the remote server are deleted from the
      local database.
    """
    # This remote source exists locally and will be updated.
    source_update = factory.RemoteSource(
        journalist_designation="source update")

    # This remote source does not exist locally and will be created.
    source_create = factory.RemoteSource(
        journalist_designation="source create")

    remote_sources = [source_update, source_create]

    # This local source already exists in the API results and will be updated.
    local_source1 = factory.Source(
        journalist_designation=source_update.journalist_designation,
        uuid=source_update.uuid,
        public_key=None,
        fingerprint=None,
    )

    # This local source does not exist in the API results and will be
    # deleted from the local database.
    local_source2 = factory.Source(journalist_designation="beep_boop")

    session.add(local_source1)
    session.add(local_source2)
    session.commit()

    local_sources = [local_source1, local_source2]

    file_delete_fcn = mocker.patch(
        "securedrop_client.storage.delete_source_collection")

    update_sources(remote_sources, local_sources, session, homedir)

    # Check the expected local source object has been updated with values from
    # the API.
    updated_source = session.query(
        db.Source).filter_by(uuid=source_update.uuid).one()
    assert updated_source.journalist_designation == source_update.journalist_designation
    assert updated_source.is_flagged == source_update.is_flagged
    assert updated_source.public_key == source_update.key["public"]
    assert updated_source.fingerprint == source_update.key["fingerprint"]
    assert updated_source.interaction_count == source_update.interaction_count
    assert updated_source.is_starred == source_update.is_starred
    assert updated_source.last_updated == parse(source_update.last_updated)

    # Check the expected local source object has been created with values from
    # the API.
    new_source = session.query(
        db.Source).filter_by(uuid=source_create.uuid).one()
    assert new_source.uuid == source_create.uuid
    assert new_source.journalist_designation == source_create.journalist_designation
    assert new_source.is_flagged == source_create.is_flagged
    assert new_source.public_key == source_create.key["public"]
    assert new_source.fingerprint == source_create.key["fingerprint"]
    assert new_source.interaction_count == source_create.interaction_count
    assert new_source.is_starred == source_create.is_starred
    assert new_source.last_updated == parse(source_create.last_updated)

    # Check that the local source not present in the API results was deleted.
    with pytest.raises(NoResultFound):
        session.query(db.Source).filter_by(uuid=local_source2.uuid).one()

    # Ensure that we called the method to delete the source collection.
    # This will delete any content in that source's data directory.
    assert file_delete_fcn.call_count == 1