def test_delete_collections(self):
        """Test the "delete selected" checkboxes on the index page that can be
        used to delete multiple collections"""
        # first, add some sources
        num_sources = 2
        for i in range(num_sources):
            self.source_app.get('/generate')
            self.source_app.post('/create')
            self.source_app.post('/submit',
                                 data=dict(
                                     msg="This is a test " + str(i) + ".",
                                     fh=(StringIO(''), ''),
                                 ),
                                 follow_redirects=True)
            common.logout(self.source_app)

        rv = self.journalist_app.get('/')
        # get all the checkbox values
        soup = BeautifulSoup(rv.data)
        checkbox_values = [
            checkbox['value']
            for checkbox in soup.select('input[name="cols_selected"]')
        ]
        rv = self.journalist_app.post('/col/process',
                                      data=dict(action='delete',
                                                cols_selected=checkbox_values),
                                      follow_redirects=True)
        self.assertEqual(rv.status_code, 200)
        self.assertIn("%s collections deleted" % (num_sources, ), rv.data)

        # Make sure the collections are deleted from the filesystem
        self._wait_for(lambda: self.assertFalse(
            any([os.path.exists(store.path(sid)) for sid in checkbox_values])))
    def test_delete_collections(self):
        """Test the "delete selected" checkboxes on the index page that can be
        used to delete multiple collections"""
        # first, add some sources
        num_sources = 2
        for i in range(num_sources):
            self.source_app.get('/generate')
            self.source_app.post('/create')
            self.source_app.post('/submit', data=dict(
                msg="This is a test " + str(i) + ".",
                fh=(StringIO(''), ''),
            ), follow_redirects=True)
            common.logout(self.source_app)

        rv = self.journalist_app.get('/')
        # get all the checkbox values
        soup = BeautifulSoup(rv.data)
        checkbox_values = [checkbox['value'] for checkbox in
                           soup.select('input[name="cols_selected"]')]
        rv = self.journalist_app.post('/col/process', data=dict(
            action='delete',
            cols_selected=checkbox_values
        ), follow_redirects=True)
        self.assertEqual(rv.status_code, 200)
        self.assertIn("%s collections deleted" % (num_sources,), rv.data)

        # Make sure the collections are deleted from the filesystem
        self._wait_for(
            lambda: self.assertFalse(any([os.path.exists(store.path(sid)) for sid in checkbox_values]))
        )
    def test_login_and_logout(self):
        rv = self.client.get('/login')
        self.assertEqual(rv.status_code, 200)
        self.assertIn("Login to check for responses", rv.data)

        codename = self._new_codename()
        with self.client as c:
            rv = c.post('/login',
                        data=dict(codename=codename),
                        follow_redirects=True)
            self.assertEqual(rv.status_code, 200)
            self.assertIn("Submit documents and messages", rv.data)
            self.assertTrue(session['logged_in'])
            common.logout(c)

        with self.client as c:
            rv = self.client.post('/login',
                                  data=dict(codename='invalid'),
                                  follow_redirects=True)
            self.assertEqual(rv.status_code, 200)
            self.assertIn('Sorry, that is not a recognized codename.', rv.data)
            self.assertNotIn('logged_in', session)

        with self.client as c:
            rv = c.post('/login',
                        data=dict(codename=codename),
                        follow_redirects=True)
            self.assertEqual(rv.status_code, 200)
            self.assertTrue(session['logged_in'])
            rv = c.get('/logout', follow_redirects=True)
            self.assertTrue(not session)
            self.assertIn('Thank you for logging out!', rv.data)
Example #4
0
        def login_test(codename):
            rv = self.client.get("/login")
            self.assertEqual(rv.status_code, 200)
            self.assertIn("Login to check for responses", rv.data)

            with self.client as c:
                rv = c.post("/login", data=dict(codename=codename), follow_redirects=True)
                self.assertEqual(rv.status_code, 200)
                self.assertIn("Submit documents and messages", rv.data)
                self.assertTrue(session["logged_in"])
                common.logout(c)
Example #5
0
        def login_test(codename):
            rv = self.client.get('/login')
            self.assertEqual(rv.status_code, 200)
            self.assertIn("Login to check for responses", rv.data)

            with self.client as c:
                rv = c.post('/login', data=dict(codename=codename),
                            follow_redirects=True)
                self.assertEqual(rv.status_code, 200)
                self.assertIn("Submit documents and messages", rv.data)
                self.assertTrue(session['logged_in'])
                common.logout(c)
Example #6
0
def main():
    ENVIRONMENTS.extend(common.get('/environments').json())
    test_table()
    prepare_users()

    with ProcessPoolExecutor(
            max_workers=os.cpu_count(),
            mp_context=mp.get_context('fork'),
    ) as executor:
        try:
            common.login()
            run_tests(executor)
            executor.shutdown(wait=True)
        finally:
            common.logout()
Example #7
0
    def test_login_and_logout(self):
        rv = self.client.get("/login")
        self.assertEqual(rv.status_code, 200)
        self.assertIn("Login to check for responses", rv.data)

        codename = self._new_codename()
        with self.client as c:
            rv = c.post("/login", data=dict(codename=codename), follow_redirects=True)
            self.assertEqual(rv.status_code, 200)
            self.assertIn("Submit documents and messages", rv.data)
            self.assertTrue(session["logged_in"])
            common.logout(c)

        with self.client as c:
            rv = self.client.post("/login", data=dict(codename="invalid"), follow_redirects=True)
            self.assertEqual(rv.status_code, 200)
            self.assertIn("Sorry, that is not a recognized codename.", rv.data)
            self.assertNotIn("logged_in", session)
    def test_submit_message(self):
        """When a source creates an account, test that a new entry appears in the journalist interface"""
        test_msg = "This is a test message."

        with self.source_app as source_app:
            rv = source_app.get('/generate')
            rv = source_app.post('/create', follow_redirects=True)
            codename = session['codename']
            sid = g.sid
            # redirected to submission form
            rv = self.source_app.post('/submit',
                                      data=dict(
                                          msg=test_msg,
                                          fh=(StringIO(''), ''),
                                      ),
                                      follow_redirects=True)
            self.assertEqual(rv.status_code, 200)
            common.logout(source_app)

        # Request the Document Interface index
        rv = self.journalist_app.get('/')
        self.assertEqual(rv.status_code, 200)
        self.assertIn("Sources", rv.data)
        soup = BeautifulSoup(rv.data)

        # The source should have a "download unread" link that says "1 unread"
        col = soup.select('ul#cols > li')[0]
        unread_span = col.select('span.unread a')[0]
        self.assertIn("1 unread", unread_span.get_text())

        col_url = soup.select('ul#cols > li a')[0]['href']
        rv = self.journalist_app.get(col_url)
        self.assertEqual(rv.status_code, 200)
        soup = BeautifulSoup(rv.data)
        submission_url = soup.select('ul#submissions li a')[0]['href']
        self.assertIn("-msg", submission_url)
        span = soup.select('ul#submissions li span.info span')[0]
        self.assertRegexpMatches(span['title'], "\d+ bytes")

        rv = self.journalist_app.get(submission_url)
        self.assertEqual(rv.status_code, 200)
        decrypted_data = self.gpg.decrypt(rv.data)
        self.assertTrue(decrypted_data.ok)
        self.assertEqual(decrypted_data.data, test_msg)

        # delete submission
        rv = self.journalist_app.get(col_url)
        self.assertEqual(rv.status_code, 200)
        soup = BeautifulSoup(rv.data)
        doc_name = soup.select(
            'ul > li > input[name="doc_names_selected"]')[0]['value']
        rv = self.journalist_app.post('/bulk',
                                      data=dict(action='confirm_delete',
                                                sid=sid,
                                                doc_names_selected=doc_name))

        self.assertEqual(rv.status_code, 200)
        soup = BeautifulSoup(rv.data)
        self.assertIn("The following file has been selected for", rv.data)

        # confirm delete submission
        doc_name = soup.select
        doc_name = soup.select(
            'ul > li > input[name="doc_names_selected"]')[0]['value']
        rv = self.journalist_app.post('/bulk',
                                      data=dict(
                                          action='delete',
                                          sid=sid,
                                          doc_names_selected=doc_name,
                                      ),
                                      follow_redirects=True)
        self.assertEqual(rv.status_code, 200)
        soup = BeautifulSoup(rv.data)
        self.assertIn("Submission deleted.", rv.data)

        # confirm that submission deleted and absent in list of submissions
        rv = self.journalist_app.get(col_url)
        self.assertEqual(rv.status_code, 200)
        self.assertIn("No documents to display.", rv.data)

        # the file should be deleted from the filesystem
        # since file deletion is handled by a polling worker, this test needs
        # to wait for the worker to get the job and execute it
        self._wait_for(lambda: self.assertFalse(
            os.path.exists(store.path(sid, doc_name))))
    def helper_test_reply(self, test_reply, expected_success=True):
        test_msg = "This is a test message."

        with self.source_app as source_app:
            rv = source_app.get('/generate')
            rv = source_app.post('/create', follow_redirects=True)
            codename = session['codename']
            sid = g.sid
            # redirected to submission form
            rv = source_app.post('/submit',
                                 data=dict(
                                     msg=test_msg,
                                     fh=(StringIO(''), ''),
                                 ),
                                 follow_redirects=True)
            self.assertEqual(rv.status_code, 200)
            self.assertFalse(g.source.flagged)
            common.logout(source_app)

        rv = self.journalist_app.get('/')
        self.assertEqual(rv.status_code, 200)
        self.assertIn("Sources", rv.data)
        soup = BeautifulSoup(rv.data)
        col_url = soup.select('ul#cols > li a')[0]['href']

        rv = self.journalist_app.get(col_url)
        self.assertEqual(rv.status_code, 200)

        with self.source_app as source_app:
            rv = source_app.post('/login',
                                 data=dict(codename=codename),
                                 follow_redirects=True)
            self.assertEqual(rv.status_code, 200)
            self.assertFalse(g.source.flagged)
            common.logout(source_app)

        with self.journalist_app as journalist_app:
            rv = journalist_app.post('/flag', data=dict(sid=sid))
            self.assertEqual(rv.status_code, 200)

        with self.source_app as source_app:
            rv = source_app.post('/login',
                                 data=dict(codename=codename),
                                 follow_redirects=True)
            self.assertEqual(rv.status_code, 200)
            self.assertTrue(g.source.flagged)
            source_app.get('/lookup')
            self.assertTrue(g.source.flagged)
            common.logout(source_app)

        # Block until the reply keypair has been generated, so we can test
        # sending a reply
        _block_on_reply_keypair_gen(codename)

        # Create 2 replies to test deleting on journalist and source interface
        for i in range(2):
            rv = self.journalist_app.post('/reply',
                                          data=dict(sid=sid, msg=test_reply),
                                          follow_redirects=True)
            self.assertEqual(rv.status_code, 200)

        if not expected_success:
            pass
        else:
            self.assertIn("Thanks! Your reply has been stored.", rv.data)

        with self.journalist_app as journalist_app:
            rv = journalist_app.get(col_url)
            self.assertIn("reply-", rv.data)

        soup = BeautifulSoup(rv.data)

        # Download the reply and verify that it can be decrypted with the
        # journalist's key as well as the source's reply key
        sid = soup.select('input[name="sid"]')[0]['value']
        checkbox_values = [
            soup.select('input[name="doc_names_selected"]')[1]['value']
        ]
        rv = self.journalist_app.post('/bulk',
                                      data=dict(
                                          sid=sid,
                                          action='download',
                                          doc_names_selected=checkbox_values),
                                      follow_redirects=True)
        self.assertEqual(rv.status_code, 200)

        zf = zipfile.ZipFile(StringIO(rv.data), 'r')
        data = zf.read(zf.namelist()[0])
        self._can_decrypt_with_key(data, config.JOURNALIST_KEY)
        self._can_decrypt_with_key(data, crypto_util.getkey(sid), codename)

        # Test deleting reply on the journalist interface
        last_reply_number = len(
            soup.select('input[name="doc_names_selected"]')) - 1
        self.helper_filenames_delete(soup, last_reply_number)

        with self.source_app as source_app:
            rv = source_app.post('/login',
                                 data=dict(codename=codename),
                                 follow_redirects=True)
            self.assertEqual(rv.status_code, 200)
            rv = source_app.get('/lookup')
            self.assertEqual(rv.status_code, 200)

            if not expected_success:
                # there should be no reply
                self.assertNotIn("You have received a reply.", rv.data)
            else:
                self.assertIn(
                    "You have received a reply. For your security, please delete all replies when you're done with them.",
                    rv.data)
                self.assertIn(test_reply, rv.data)
                soup = BeautifulSoup(rv.data)
                msgid = soup.select(
                    'form.message > input[name="reply_filename"]')[0]['value']
                rv = source_app.post('/delete',
                                     data=dict(sid=sid, reply_filename=msgid),
                                     follow_redirects=True)
                self.assertEqual(rv.status_code, 200)
                self.assertIn("Reply deleted", rv.data)

                # Make sure the reply is deleted from the filesystem
                self._wait_for(lambda: self.assertFalse(
                    os.path.exists(store.path(sid, msgid))))

                common.logout(source_app)
    def test_submit_message(self):
        """When a source creates an account, test that a new entry appears in the journalist interface"""
        test_msg = "This is a test message."

        with self.source_app as source_app:
            rv = source_app.get('/generate')
            rv = source_app.post('/create', follow_redirects=True)
            codename = session['codename']
            sid = g.sid
            # redirected to submission form
            rv = self.source_app.post('/submit', data=dict(
                msg=test_msg,
                fh=(StringIO(''), ''),
            ), follow_redirects=True)
            self.assertEqual(rv.status_code, 200)
            common.logout(source_app)

        # Request the Document Interface index
        rv = self.journalist_app.get('/')
        self.assertEqual(rv.status_code, 200)
        self.assertIn("Sources", rv.data)
        soup = BeautifulSoup(rv.data)

        # The source should have a "download unread" link that says "1 unread"
        col = soup.select('ul#cols > li')[0]
        unread_span = col.select('span.unread a')[0]
        self.assertIn("1 unread", unread_span.get_text())

        col_url = soup.select('ul#cols > li a')[0]['href']
        rv = self.journalist_app.get(col_url)
        self.assertEqual(rv.status_code, 200)
        soup = BeautifulSoup(rv.data)
        submission_url = soup.select('ul#submissions li a')[0]['href']
        self.assertIn("-msg", submission_url)
        span = soup.select('ul#submissions li span.info span')[0]
        self.assertRegexpMatches(span['title'], "\d+ bytes")

        rv = self.journalist_app.get(submission_url)
        self.assertEqual(rv.status_code, 200)
        decrypted_data = self.gpg.decrypt(rv.data)
        self.assertTrue(decrypted_data.ok)
        self.assertEqual(decrypted_data.data, test_msg)

        # delete submission
        rv = self.journalist_app.get(col_url)
        self.assertEqual(rv.status_code, 200)
        soup = BeautifulSoup(rv.data)
        doc_name = soup.select(
            'ul > li > input[name="doc_names_selected"]')[0]['value']
        rv = self.journalist_app.post('/bulk', data=dict(
            action='confirm_delete',
            sid=sid,
            doc_names_selected=doc_name
        ))

        self.assertEqual(rv.status_code, 200)
        soup = BeautifulSoup(rv.data)
        self.assertIn("The following file has been selected for", rv.data)

        # confirm delete submission
        doc_name = soup.select
        doc_name = soup.select(
            'ul > li > input[name="doc_names_selected"]')[0]['value']
        rv = self.journalist_app.post('/bulk', data=dict(
            action='delete',
            sid=sid,
            doc_names_selected=doc_name,
        ), follow_redirects=True)
        self.assertEqual(rv.status_code, 200)
        soup = BeautifulSoup(rv.data)
        self.assertIn("Submission deleted.", rv.data)

        # confirm that submission deleted and absent in list of submissions
        rv = self.journalist_app.get(col_url)
        self.assertEqual(rv.status_code, 200)
        self.assertIn("No documents to display.", rv.data)

        # the file should be deleted from the filesystem
        # since file deletion is handled by a polling worker, this test needs
        # to wait for the worker to get the job and execute it
        self._wait_for(
            lambda: self.assertFalse(os.path.exists(store.path(sid, doc_name)))
        )
client.channel.software.create(sessionKey,
				'rhel-x86_64-server-6-epel',
				'EPEL 6 x86_64',
				'Extra Packages for Enterprise Linux 6 x86_64',
				'channel-x86_64',
				'rhel-x86_64-server-6',
				'sha256')
print '[OK]'

print 'Step 2: Create repo repo-rhel-x86_64-server-6-epel . . .',
client.channel.software.createRepo(sessionKey,
				'repo-rhel-x86_64-server-6-epel',
				'yum',
				'http://mirrors.servercentral.net/fedora/epel/6/x86_64/')
print '[OK]'

print 'Step 3: Associate EPEL repo to EPEL channel . . .',
client.channel.software.associateRepo(sessionKey,
					'rhel-x86_64-server-6-epel',
					'repo-rhel-x86_64-server-6-epel')
print '[OK]'

print 'Step 4: Initiating repo sync . . .',
client.channel.software.syncRepo(sessionKey, 'rhel-x86_64-server-6-epel')
print '[OK]'

print 'Step 5: Logout . . .',
common.logout(client, sessionKey)
print '[OK]'
print 'At this point, EPEL 6 will be syncing to your satellite. This may take some time. Grab a coffee.'
	def test_user_registration(self):
		self.register_user()
		self.login_user()
		self.change_profile()
		logout(self)
		self.register_duplicate_mail()
    def test_submit_file(self):
        """When a source creates an account, test that a new entry appears in the journalist interface"""
        test_file_contents = "This is a test file."
        test_filename = "test.txt"

        with self.source_app as source_app:
            rv = source_app.get('/generate')
            rv = source_app.post('/create', follow_redirects=True)
            codename = session['codename']
            sid = g.sid
            # redirected to submission form
            rv = self.source_app.post('/submit', data=dict(
                msg="",
                fh=(StringIO(test_file_contents), test_filename),
            ), follow_redirects=True)
            self.assertEqual(rv.status_code, 200)
            common.logout(source_app)

        rv = self.journalist_app.get('/')
        self.assertEqual(rv.status_code, 200)
        self.assertIn("Sources", rv.data)
        soup = BeautifulSoup(rv.data)
        col_url = soup.select('ul#cols > li a')[0]['href']

        rv = self.journalist_app.get(col_url)
        self.assertEqual(rv.status_code, 200)
        soup = BeautifulSoup(rv.data)
        submission_url = soup.select('ul#submissions li a')[0]['href']
        self.assertIn("-doc", submission_url)
        span = soup.select('ul#submissions li span.info span')[0]
        self.assertRegexpMatches(span['title'], "\d+ bytes")

        rv = self.journalist_app.get(submission_url)
        self.assertEqual(rv.status_code, 200)
        decrypted_data = self.gpg.decrypt(rv.data)
        self.assertTrue(decrypted_data.ok)

        sio = StringIO(decrypted_data.data)
        with gzip.GzipFile(mode='rb', fileobj=sio) as gzip_file:
            unzipped_decrypted_data = gzip_file.read()
        self.assertEqual(unzipped_decrypted_data, test_file_contents)

        # delete submission
        rv = self.journalist_app.get(col_url)
        self.assertEqual(rv.status_code, 200)
        soup = BeautifulSoup(rv.data)
        doc_name = soup.select(
            'ul > li > input[name="doc_names_selected"]')[0]['value']
        rv = self.journalist_app.post('/bulk', data=dict(
            action='confirm_delete',
            sid=sid,
            doc_names_selected=doc_name
        ))

        self.assertEqual(rv.status_code, 200)
        soup = BeautifulSoup(rv.data)
        self.assertIn("The following file has been selected for", rv.data)

        # confirm delete submission
        doc_name = soup.select
        doc_name = soup.select(
            'ul > li > input[name="doc_names_selected"]')[0]['value']
        rv = self.journalist_app.post('/bulk', data=dict(
            action='delete',
            sid=sid,
            doc_names_selected=doc_name,
        ), follow_redirects=True)
        self.assertEqual(rv.status_code, 200)
        soup = BeautifulSoup(rv.data)
        self.assertIn("Submission deleted.", rv.data)

        # confirm that submission deleted and absent in list of submissions
        rv = self.journalist_app.get(col_url)
        self.assertEqual(rv.status_code, 200)
        self.assertIn("No documents to display.", rv.data)

        # the file should be deleted from the filesystem
        # since file deletion is handled by a polling worker, this test needs
        # to wait for the worker to get the job and execute it
        self._wait_for(
            lambda: self.assertFalse(os.path.exists(store.path(sid, doc_name)))
        )
Example #14
0
 def test_logout(self):
     print("Testing logout")
     auth = common.make_user(GATEWAY_URL, common.generate_user())
     common.logout(GATEWAY_URL, auth)
(client, sessionKey) = common.login()
print '[OK]'

print 'Step 1: Create rhel-x86_64-server-6-epel as child of rhel-x86_64-server-6 . . .',
client.channel.software.create(sessionKey, 'rhel-x86_64-server-6-epel',
                               'EPEL 6 x86_64',
                               'Extra Packages for Enterprise Linux 6 x86_64',
                               'channel-x86_64', 'rhel-x86_64-server-6',
                               'sha256')
print '[OK]'

print 'Step 2: Create repo repo-rhel-x86_64-server-6-epel . . .',
client.channel.software.createRepo(
    sessionKey, 'repo-rhel-x86_64-server-6-epel', 'yum',
    'http://mirrors.servercentral.net/fedora/epel/6/x86_64/')
print '[OK]'

print 'Step 3: Associate EPEL repo to EPEL channel . . .',
client.channel.software.associateRepo(sessionKey, 'rhel-x86_64-server-6-epel',
                                      'repo-rhel-x86_64-server-6-epel')
print '[OK]'

print 'Step 4: Initiating repo sync . . .',
client.channel.software.syncRepo(sessionKey, 'rhel-x86_64-server-6-epel')
print '[OK]'

print 'Step 5: Logout . . .',
common.logout(client, sessionKey)
print '[OK]'
print 'At this point, EPEL 6 will be syncing to your satellite. This may take some time. Grab a coffee.'
    def helper_test_reply(self, test_reply, expected_success=True):
        test_msg = "This is a test message."

        with self.source_app as source_app:
            rv = source_app.get('/generate')
            rv = source_app.post('/create', follow_redirects=True)
            codename = session['codename']
            sid = g.sid
            # redirected to submission form
            rv = source_app.post('/submit', data=dict(
                msg=test_msg,
                fh=(StringIO(''), ''),
            ), follow_redirects=True)
            self.assertEqual(rv.status_code, 200)
            self.assertFalse(g.source.flagged)
            common.logout(source_app)

        rv = self.journalist_app.get('/')
        self.assertEqual(rv.status_code, 200)
        self.assertIn("Sources", rv.data)
        soup = BeautifulSoup(rv.data)
        col_url = soup.select('ul#cols > li a')[0]['href']

        rv = self.journalist_app.get(col_url)
        self.assertEqual(rv.status_code, 200)

        with self.source_app as source_app:
            rv = source_app.post('/login', data=dict(
                codename=codename), follow_redirects=True)
            self.assertEqual(rv.status_code, 200)
            self.assertFalse(g.source.flagged)
            common.logout(source_app)

        with self.journalist_app as journalist_app:
            rv = journalist_app.post('/flag', data=dict(
                sid=sid))
            self.assertEqual(rv.status_code, 200)

        with self.source_app as source_app:
            rv = source_app.post('/login', data=dict(
                codename=codename), follow_redirects=True)
            self.assertEqual(rv.status_code, 200)
            self.assertTrue(g.source.flagged)
            source_app.get('/lookup')
            self.assertTrue(g.source.flagged)
            common.logout(source_app)

        # Block until the reply keypair has been generated, so we can test
        # sending a reply
        _block_on_reply_keypair_gen(codename)

        # Create 2 replies to test deleting on journalist and source interface
        for i in range(2):
            rv = self.journalist_app.post('/reply', data=dict(
                sid=sid,
                msg=test_reply
            ), follow_redirects=True)
            self.assertEqual(rv.status_code, 200)

        if not expected_success:
            pass
        else:
            self.assertIn("Thanks! Your reply has been stored.", rv.data)

        with self.journalist_app as journalist_app:
            rv = journalist_app.get(col_url)
            self.assertIn("reply-", rv.data)

        soup = BeautifulSoup(rv.data)

        # Download the reply and verify that it can be decrypted with the
        # journalist's key as well as the source's reply key
        sid = soup.select('input[name="sid"]')[0]['value']
        checkbox_values = [soup.select('input[name="doc_names_selected"]')[1]['value']]
        rv = self.journalist_app.post('/bulk', data=dict(
            sid=sid,
            action='download',
            doc_names_selected=checkbox_values
        ), follow_redirects=True)
        self.assertEqual(rv.status_code, 200)

        zf = zipfile.ZipFile(StringIO(rv.data), 'r')
        data = zf.read(zf.namelist()[0])
        self._can_decrypt_with_key(data, config.JOURNALIST_KEY)
        self._can_decrypt_with_key(data, crypto_util.getkey(sid), codename)

        # Test deleting reply on the journalist interface
        last_reply_number = len(soup.select('input[name="doc_names_selected"]')) - 1
        self.helper_filenames_delete(soup, last_reply_number)

        with self.source_app as source_app:
            rv = source_app.post('/login', data=dict(codename=codename),
                                 follow_redirects=True)
            self.assertEqual(rv.status_code, 200)
            rv = source_app.get('/lookup')
            self.assertEqual(rv.status_code, 200)

            if not expected_success:
                # there should be no reply
                self.assertNotIn("You have received a reply.", rv.data)
            else:
                self.assertIn(
                    "You have received a reply. For your security, please delete all replies when you're done with them.", rv.data)
                self.assertIn(test_reply, rv.data)
                soup = BeautifulSoup(rv.data)
                msgid = soup.select('form.message > input[name="reply_filename"]')[0]['value']
                rv = source_app.post('/delete', data=dict(
                    sid=sid,
                    reply_filename=msgid
                ), follow_redirects=True)
                self.assertEqual(rv.status_code, 200)
                self.assertIn("Reply deleted", rv.data)

                # Make sure the reply is deleted from the filesystem
                self._wait_for(
                    lambda: self.assertFalse(os.path.exists(store.path(sid, msgid)))
                )

                common.logout(source_app)
    def test_submit_file(self):
        """When a source creates an account, test that a new entry appears in the journalist interface"""
        test_file_contents = "This is a test file."
        test_filename = "test.txt"

        with self.source_app as source_app:
            rv = source_app.get('/generate')
            rv = source_app.post('/create', follow_redirects=True)
            codename = session['codename']
            sid = g.sid
            # redirected to submission form
            rv = self.source_app.post('/submit',
                                      data=dict(
                                          msg="",
                                          fh=(StringIO(test_file_contents),
                                              test_filename),
                                      ),
                                      follow_redirects=True)
            self.assertEqual(rv.status_code, 200)
            common.logout(source_app)

        rv = self.journalist_app.get('/')
        self.assertEqual(rv.status_code, 200)
        self.assertIn("Sources", rv.data)
        soup = BeautifulSoup(rv.data)
        col_url = soup.select('ul#cols > li a')[0]['href']

        rv = self.journalist_app.get(col_url)
        self.assertEqual(rv.status_code, 200)
        soup = BeautifulSoup(rv.data)
        submission_url = soup.select('ul#submissions li a')[0]['href']
        self.assertIn("-doc", submission_url)
        span = soup.select('ul#submissions li span.info span')[0]
        self.assertRegexpMatches(span['title'], "\d+ bytes")

        rv = self.journalist_app.get(submission_url)
        self.assertEqual(rv.status_code, 200)
        decrypted_data = self.gpg.decrypt(rv.data)
        self.assertTrue(decrypted_data.ok)

        sio = StringIO(decrypted_data.data)
        with gzip.GzipFile(mode='rb', fileobj=sio) as gzip_file:
            unzipped_decrypted_data = gzip_file.read()
        self.assertEqual(unzipped_decrypted_data, test_file_contents)

        # delete submission
        rv = self.journalist_app.get(col_url)
        self.assertEqual(rv.status_code, 200)
        soup = BeautifulSoup(rv.data)
        doc_name = soup.select(
            'ul > li > input[name="doc_names_selected"]')[0]['value']
        rv = self.journalist_app.post('/bulk',
                                      data=dict(action='confirm_delete',
                                                sid=sid,
                                                doc_names_selected=doc_name))

        self.assertEqual(rv.status_code, 200)
        soup = BeautifulSoup(rv.data)
        self.assertIn("The following file has been selected for", rv.data)

        # confirm delete submission
        doc_name = soup.select
        doc_name = soup.select(
            'ul > li > input[name="doc_names_selected"]')[0]['value']
        rv = self.journalist_app.post('/bulk',
                                      data=dict(
                                          action='delete',
                                          sid=sid,
                                          doc_names_selected=doc_name,
                                      ),
                                      follow_redirects=True)
        self.assertEqual(rv.status_code, 200)
        soup = BeautifulSoup(rv.data)
        self.assertIn("Submission deleted.", rv.data)

        # confirm that submission deleted and absent in list of submissions
        rv = self.journalist_app.get(col_url)
        self.assertEqual(rv.status_code, 200)
        self.assertIn("No documents to display.", rv.data)

        # the file should be deleted from the filesystem
        # since file deletion is handled by a polling worker, this test needs
        # to wait for the worker to get the job and execute it
        self._wait_for(lambda: self.assertFalse(
            os.path.exists(store.path(sid, doc_name))))