def test_thread_received_recent_date(db, api_client, default_account): date1 = datetime.datetime(2015, 1, 1, 0, 0, 0) date2 = datetime.datetime(2012, 1, 1, 0, 0, 0) thread1 = add_fake_thread(db.session, default_account.namespace.id) date_dict = dict() add_fake_message(db.session, default_account.namespace.id, thread1, subject="Test Thread 1", received_date=date1, add_sent_category=True) add_fake_message(db.session, default_account.namespace.id, thread1, subject="Test Thread 1", received_date=date2) date_dict["Test Thread 1"] = date2 thread2 = add_fake_thread(db.session, default_account.namespace.id) add_fake_message(db.session, default_account.namespace.id, thread2, subject="Test Thread 2", received_date=date1, add_sent_category=True) date_dict["Test Thread 2"] = date1 resp = api_client.get_raw('/threads/') assert resp.status_code == 200 threads = json.loads(resp.data) for thread in threads: assert date_dict[thread['subject']] == \ datetime.datetime.fromtimestamp( thread['last_message_received_timestamp'])
def test_namespace_deletion(db, default_account): from inbox.models import Account, Thread, Message from inbox.models.util import delete_namespace models = [Thread, Message] namespace = default_account.namespace namespace_id = namespace.id account_id = default_account.id account = db.session.query(Account).get(account_id) assert account thread = add_fake_thread(db.session, namespace_id) message = add_fake_message(db.session, namespace_id, thread) for m in models: c = db.session.query(m).filter(m.namespace_id == namespace_id).count() print "count for", m, ":", c assert c != 0 fake_account = add_generic_imap_account(db.session) fake_account_id = fake_account.id assert fake_account_id != account.id and \ fake_account.namespace.id != namespace_id thread = add_fake_thread(db.session, fake_account.namespace.id) thread_id = thread.id message = add_fake_message(db.session, fake_account.namespace.id, thread) message_id = message.id assert len( db.session.query(Namespace).filter( Namespace.id == namespace_id).all()) > 0 # Delete namespace, verify data corresponding to this namespace /only/ # is deleted delete_namespace(account_id, namespace_id) db.session.commit() assert len( db.session.query(Namespace).filter( Namespace.id == namespace_id).all()) == 0 account = db.session.query(Account).get(account_id) assert not account for m in models: assert db.session.query(m).filter( m.namespace_id == namespace_id).count() == 0 fake_account = db.session.query(Account).get(fake_account_id) assert fake_account thread = db.session.query(Thread).get(thread_id) message = db.session.query(Message).get(message_id) assert thread and message
def test_generic_grouping(db, default_account): thread = add_fake_thread(db.session, default_account.namespace.id) message = add_fake_message(db.session, default_account.namespace.id, thread, subject="Golden Gate Park next Sat") folder = Folder(account=default_account, name='Inbox', canonical_name='inbox') ImapUid(message=message, account_id=default_account.id, msg_uid=2222, folder=folder) thread = add_fake_thread(db.session, default_account.namespace.id) account = add_generic_imap_account(db.session) message = add_fake_message(db.session, account.namespace.id, thread, subject="Golden Gate Park next Sat") thread = fetch_corresponding_thread(db.session, default_account.namespace.id, message) assert thread is None, ("fetch_similar_threads should " "heed namespace boundaries")
def test_thread_count(db, api_client, default_account): date1 = datetime.datetime(2015, 1, 1, 0, 0, 0) date2 = datetime.datetime(2012, 1, 1, 0, 0, 0) date3 = datetime.datetime(2010, 1, 1, 0, 0, 0) date4 = datetime.datetime(2009, 1, 1, 0, 0, 0) date5 = datetime.datetime(2008, 1, 1, 0, 0, 0) thread1 = add_fake_thread(db.session, default_account.namespace.id) thread2 = add_fake_thread(db.session, default_account.namespace.id) test_subject = "test_thread_view_count_with_category" for thread in [thread1, thread2]: add_fake_message(db.session, default_account.namespace.id, thread, subject=test_subject, received_date=date1) add_fake_message(db.session, default_account.namespace.id, thread, subject=test_subject, received_date=date2, add_sent_category=True) add_fake_message(db.session, default_account.namespace.id, thread, subject=test_subject, received_date=date3) add_fake_message(db.session, default_account.namespace.id, thread, subject=test_subject, received_date=date4, add_sent_category=True) add_fake_message(db.session, default_account.namespace.id, thread, subject=test_subject, received_date=date5) resp = api_client.get_raw('/threads/?view=count&in=sent') assert resp.status_code == 200 threads = json.loads(resp.data) assert threads['count'] == 2
def test_namespace_deletion(db, default_account): from inbox.models import Account, Thread, Message from inbox.models.util import delete_namespace models = [Thread, Message] namespace = default_account.namespace namespace_id = namespace.id account_id = default_account.id account = db.session.query(Account).get(account_id) assert account thread = add_fake_thread(db.session, namespace_id) message = add_fake_message(db.session, namespace_id, thread) for m in models: c = db.session.query(m).filter( m.namespace_id == namespace_id).count() print "count for", m, ":", c assert c != 0 fake_account = add_generic_imap_account(db.session) fake_account_id = fake_account.id assert fake_account_id != account.id and \ fake_account.namespace.id != namespace_id thread = add_fake_thread(db.session, fake_account.namespace.id) thread_id = thread.id message = add_fake_message(db.session, fake_account.namespace.id, thread) message_id = message.id assert len(db.session.query(Namespace).filter(Namespace.id == namespace_id).all()) > 0 # Delete namespace, verify data corresponding to this namespace /only/ # is deleted delete_namespace(account_id, namespace_id) db.session.commit() assert len(db.session.query(Namespace).filter(Namespace.id == namespace_id).all()) == 0 account = db.session.query(Account).get(account_id) assert not account for m in models: assert db.session.query(m).filter( m.namespace_id == namespace_id).count() == 0 fake_account = db.session.query(Account).get(fake_account_id) assert fake_account thread = db.session.query(Thread).get(thread_id) message = db.session.query(Message).get(message_id) assert thread and message
def test_distinct_results(api_client, db, default_namespace): """Test that limit and offset parameters work correctly when joining on multiple matching messages per thread.""" # Create a thread with multiple messages on it. first_thread = add_fake_thread(db.session, default_namespace.id) add_fake_message(db.session, default_namespace.id, first_thread, from_addr=[('', '*****@*****.**')], received_date=datetime.datetime.utcnow(), add_sent_category=True) add_fake_message(db.session, default_namespace.id, first_thread, from_addr=[('', '*****@*****.**')], received_date=datetime.datetime.utcnow(), add_sent_category=True) # Now create another thread with the same participants older_date = datetime.datetime.utcnow() - datetime.timedelta(hours=1) second_thread = add_fake_thread(db.session, default_namespace.id) add_fake_message(db.session, default_namespace.id, second_thread, from_addr=[('', '*****@*****.**')], received_date=older_date, add_sent_category=True) add_fake_message(db.session, default_namespace.id, second_thread, from_addr=[('', '*****@*****.**')], received_date=older_date, add_sent_category=True) second_thread.recentdate = older_date db.session.commit() filtered_results = api_client.get_data('/[email protected]' '&limit=1&offset=0') assert len(filtered_results) == 1 assert filtered_results[0]['id'] == first_thread.public_id filtered_results = api_client.get_data('/[email protected]' '&limit=1&offset=1') assert len(filtered_results) == 1 assert filtered_results[0]['id'] == second_thread.public_id filtered_results = api_client.get_data('/[email protected]' '&limit=2&offset=0') assert len(filtered_results) == 2 filtered_results = api_client.get_data('/[email protected]' '&limit=2&offset=1') assert len(filtered_results) == 1 # Ensure that it works when using the _in filter filtered_results = api_client.get_data('/threads?in=sent' '&limit=2&offset=0') assert len(filtered_results) == 2 filtered_results = api_client.get_data('/threads?in=sent' '&limit=1&offset=0') assert len(filtered_results) == 1
def test_file_transactions(db, default_namespace): from inbox.models.message import Message account = default_namespace.account thread = add_fake_thread(db.session, default_namespace.id) mime_msg = mime.create.multipart("mixed") mime_msg.append( mime.create.text("plain", "This is a message with attachments"), mime.create.attachment("image/png", "filler", "attached_image.png", "attachment"), mime.create.attachment("application/pdf", "filler", "attached_file.pdf", "attachment"), ) msg = Message.create_from_synced(account, 22, "[Gmail]/All Mail", datetime.utcnow(), mime_msg.to_string()) msg.thread = thread db.session.add(msg) db.session.commit() assert len(msg.parts) == 2 assert all(part.content_disposition == "attachment" for part in msg.parts) block_ids = [part.block.id for part in msg.parts] with db.session.no_autoflush: transaction = get_latest_transaction(db.session, "file", block_ids[0], default_namespace.id) assert transaction.command == "insert" transaction = get_latest_transaction(db.session, "file", block_ids[1], default_namespace.id) assert transaction.command == "insert"
def test_message_updates_create_thread_transaction(db, default_namespace): with db.session.no_autoflush: thr = add_fake_thread(db.session, default_namespace.id) msg = add_fake_message(db.session, default_namespace.id, thr) transaction = get_latest_transaction(db.session, "thread", thr.id, default_namespace.id) assert transaction.record_id == thr.id and transaction.object_type == "thread" assert transaction.command == "update" # An update to one of the message's propagated_attributes creates a # revision for the thread msg.is_read = True db.session.commit() new_transaction = get_latest_transaction(db.session, "thread", thr.id, default_namespace.id) assert new_transaction.id != transaction.id assert new_transaction.record_id == thr.id and new_transaction.object_type == "thread" assert new_transaction.command == "update" # An update to one of its other attributes does not msg.subject = "Ice cubes and dogs" db.session.commit() same_transaction = get_latest_transaction(db.session, "thread", thr.id, default_namespace.id) assert same_transaction.id == new_transaction.id
def test_basic_message_grouping(db, default_namespace): first_thread = add_fake_thread(db.session, default_namespace.id) first_thread.subject = 'Some kind of test' add_fake_message(db.session, default_namespace.id, thread=first_thread, subject='Some kind of test', from_addr=[('Karim Hamidou', '*****@*****.**')], to_addr=[('Eben Freeman', '*****@*****.**')], bcc_addr=[('Some person', '*****@*****.**')]) msg2 = add_fake_message(db.session, default_namespace.id, thread=None, subject='Re: Some kind of test', from_addr=[('Some random dude', '*****@*****.**')], to_addr=[('Karim Hamidou', '*****@*****.**')]) matched_thread = fetch_corresponding_thread(db.session, default_namespace.id, msg2) assert matched_thread is None, "the algo shouldn't thread different convos" msg3 = add_fake_message(db.session, default_namespace.id, thread=None) msg3.subject = 'Re: Some kind of test' msg3.from_addr = [('Eben Freeman', '*****@*****.**')] msg3.to_addr = [('Karim Hamidou', '*****@*****.**')] matched_thread = fetch_corresponding_thread(db.session, default_namespace.id, msg3) assert matched_thread is first_thread, "Should match on participants"
def test_basic_message_grouping(db, default_namespace): first_thread = add_fake_thread(db.session, default_namespace.id) first_thread.subject = 'Some kind of test' add_fake_message(db.session, default_namespace.id, thread=first_thread, subject='Some kind of test', from_addr=[('Karim Hamidou', '*****@*****.**')], to_addr=[('Eben Freeman', '*****@*****.**')], bcc_addr=[('Some person', '*****@*****.**')]) msg2 = add_fake_message(db.session, default_namespace.id, thread=None, subject='Re: Some kind of test', from_addr=[('Some random dude', '*****@*****.**') ], to_addr=[('Karim Hamidou', '*****@*****.**')]) matched_thread = fetch_corresponding_thread(db.session, default_namespace.id, msg2) assert matched_thread is None, "the algo shouldn't thread different convos" msg3 = add_fake_message(db.session, default_namespace.id, thread=None) msg3.subject = 'Re: Some kind of test' msg3.from_addr = [('Eben Freeman', '*****@*****.**')] msg3.to_addr = [('Karim Hamidou', '*****@*****.**')] matched_thread = fetch_corresponding_thread(db.session, default_namespace.id, msg3) assert matched_thread is first_thread, "Should match on participants"
def test_folder_delete(db, generic_account, folder_client, api_version): headers = dict() headers['Api-Version'] = api_version # Make a new message generic_thread = add_fake_thread(db.session, generic_account.namespace.id) gen_message = add_fake_message(db.session, generic_account.namespace.id, generic_thread) g_data = folder_client.get_raw('/folders/') # Add message to folder generic_folder = json.loads(g_data.data)[0] data = {"folder_id": generic_folder['id']} folder_client.put_data('/messages/{}'.format(gen_message.public_id), data) # Test that DELETE requests 403 on folders with items in them d_data = folder_client.delete('/folders/{}'.format(generic_folder['id'])) assert d_data.status_code == 400 # Make an empty folder resp = folder_client.post_data('/folders/', {"display_name": "Empty_Folder"}) empty_folder = json.loads(resp.data) # Test that DELETE requests delete empty folders d_data = folder_client.delete('/folders/{}'.format(empty_folder['id'])) assert d_data.status_code == 200 if api_version == API_VERSIONS[0]: # Did we update things optimistically? category_id = empty_folder['id'] category = db.session.query(Category).filter( Category.public_id == category_id).one() assert category.deleted_at != EPOCH assert category.is_deleted is True db.session.rollback()
def test_label_delete(db, gmail_account, label_client, api_version): headers = dict() headers['Api-Version'] = api_version # Make a new message gmail_thread = add_fake_thread(db.session, gmail_account.namespace.id) gmail_message = add_fake_message(db.session, gmail_account.namespace.id, gmail_thread) g_data = label_client.get_raw('/labels/', headers=headers) # Add label to message gmail_label = json.loads(g_data.data)[0] data = {"labels": [gmail_label['id']]} label_client.put_data('/messages/{}'.format(gmail_message.public_id), data, headers=headers) # DELETE requests should work on labels whether or not messages have them d_data = label_client.delete('/labels/{}'.format(gmail_label['id']), headers=headers) assert d_data.status_code == 200 if api_version == API_VERSIONS[0]: # Optimistic update. category_id = gmail_label['id'] category = db.session.query(Category).filter( Category.public_id == category_id).one() assert category.deleted_at != EPOCH assert category.is_deleted is True
def test_category_delete(db, gmail_account): """ Ensure that all associated MessageCategories are deleted when a Category is deleted """ api_client = new_api_client(db, gmail_account.namespace) po_data = api_client.post_data('/labels/', {"display_name": "Test_Label"}) assert po_data.status_code == 200 category_public_id = json.loads(po_data.data)['id'] category = db.session.query(Category).filter( Category.public_id == category_public_id).one() category_id = category.id for i in xrange(10): generic_thread = add_fake_thread(db.session, gmail_account.namespace.id) gen_message = add_fake_message(db.session, gmail_account.namespace.id, generic_thread) data = {"label_ids": [category_public_id]} resp = api_client.put_data( '/messages/{}'.format(gen_message.public_id), data) assert resp.status_code == 200 associated_mcs = db.session.query(MessageCategory). \ filter(MessageCategory.category_id == category_id).all() assert len(associated_mcs) == 10 db.session.delete(category) db.session.commit() assert db.session.query(MessageCategory). \ filter(MessageCategory.category_id == category_id).all() == []
def test_category_delete(db, gmail_account): """ Ensure that all associated MessageCategories are deleted when a Category is deleted """ api_client = new_api_client(db, gmail_account.namespace) po_data = api_client.post_data('/labels/', {"display_name": "Test_Label"}) assert po_data.status_code == 200 category_public_id = json.loads(po_data.data)['id'] category = db.session.query(Category).filter( Category.public_id == category_public_id).one() category_id = category.id for i in xrange(10): generic_thread = add_fake_thread(db.session, gmail_account.namespace.id) gen_message = add_fake_message(db.session, gmail_account.namespace.id, generic_thread) data = {"label_ids": [category_public_id]} resp = api_client.put_data('/messages/{}'. format(gen_message.public_id), data) assert resp.status_code == 200 associated_mcs = db.session.query(MessageCategory). \ filter(MessageCategory.category_id == category_id).all() assert len(associated_mcs) == 10 db.session.delete(category) db.session.commit() assert db.session.query(MessageCategory). \ filter(MessageCategory.category_id == category_id).all() == []
def test_message_delete(db, gmail_account): """ Ensure that all associated MessageCategories are deleted when a Message is deleted """ api_client = new_api_client(db, gmail_account.namespace) generic_thread = add_fake_thread(db.session, gmail_account.namespace.id) gen_message = add_fake_message(db.session, gmail_account.namespace.id, generic_thread) category_ids = [] for i in xrange(10): po_data = api_client.post_data('/labels/', {"display_name": str(i)}) assert po_data.status_code == 200 category_ids.append(json.loads(po_data.data)['id']) data = {"label_ids": category_ids} resp = api_client.put_data('/messages/{}'. format(gen_message.public_id), data) assert resp.status_code == 200 associated_mcs = db.session.query(MessageCategory). \ filter(MessageCategory.message_id == gen_message.id).all() assert len(associated_mcs) == 10 db.session.delete(gen_message) db.session.commit() assert db.session.query(MessageCategory). \ filter(MessageCategory.message_id == gen_message.id).all() == []
def test_file_transactions(db, default_namespace): from inbox.models.message import Message account = default_namespace.account thread = add_fake_thread(db.session, default_namespace.id) mime_msg = mime.create.multipart('mixed') mime_msg.append( mime.create.text('plain', 'This is a message with attachments'), mime.create.attachment('image/png', 'filler', 'attached_image.png', 'attachment'), mime.create.attachment('application/pdf', 'filler', 'attached_file.pdf', 'attachment')) msg = Message.create_from_synced(account, 22, '[Gmail]/All Mail', datetime.utcnow(), mime_msg.to_string()) msg.thread = thread db.session.add(msg) db.session.commit() assert len(msg.parts) == 2 assert all(part.content_disposition == 'attachment' for part in msg.parts) block_ids = [part.block.id for part in msg.parts] with db.session.no_autoflush: transaction = get_latest_transaction(db.session, 'file', block_ids[0], default_namespace.id) assert transaction.command == 'insert' transaction = get_latest_transaction(db.session, 'file', block_ids[1], default_namespace.id) assert transaction.command == 'insert'
def test_message_delete(db, gmail_account): """ Ensure that all associated MessageCategories are deleted when a Message is deleted """ api_client = new_api_client(db, gmail_account.namespace) generic_thread = add_fake_thread(db.session, gmail_account.namespace.id) gen_message = add_fake_message(db.session, gmail_account.namespace.id, generic_thread) category_ids = [] for i in xrange(10): po_data = api_client.post_data('/labels/', {"display_name": str(i)}) assert po_data.status_code == 200 category_ids.append(json.loads(po_data.data)['id']) data = {"label_ids": category_ids} resp = api_client.put_data('/messages/{}'.format(gen_message.public_id), data) assert resp.status_code == 200 associated_mcs = db.session.query(MessageCategory). \ filter(MessageCategory.message_id == gen_message.id).all() assert len(associated_mcs) == 10 db.session.delete(gen_message) db.session.commit() assert db.session.query(MessageCategory). \ filter(MessageCategory.message_id == gen_message.id).all() == []
def test_message_label_updates(db, api_client, default_account, api_version, custom_label): """Check that you can update a message (optimistically or not), and that the update is queued in the ActionLog.""" headers = dict() headers['Api-Version'] = api_version # Gmail threads, messages have a 'labels' field gmail_thread = add_fake_thread(db.session, default_account.namespace.id) gmail_message = add_fake_message(db.session, default_account.namespace.id, gmail_thread) resp_data = api_client.get_data( '/messages/{}'.format(gmail_message.public_id), headers=headers) assert resp_data['labels'] == [] category = custom_label.category update = dict(labels=[category.public_id]) resp = api_client.put_data( '/messages/{}'.format(gmail_message.public_id), update, headers=headers) resp_data = json.loads(resp.data) if api_version == API_VERSIONS[0]: assert len(resp_data['labels']) == 1 assert resp_data['labels'][0]['id'] == category.public_id else: assert resp_data['labels'] == []
def test_message_updates_create_thread_transaction(db, default_namespace): with db.session.no_autoflush: thr = add_fake_thread(db.session, default_namespace.id) msg = add_fake_message(db.session, default_namespace.id, thr) transaction = get_latest_transaction(db.session, 'thread', thr.id, default_namespace.id) assert (transaction.record_id == thr.id and transaction.object_type == 'thread') assert transaction.command == 'update' # An update to one of the message's propagated_attributes creates a # revision for the thread msg.is_read = True db.session.commit() new_transaction = get_latest_transaction(db.session, 'thread', thr.id, default_namespace.id) assert new_transaction.id != transaction.id assert (new_transaction.record_id == thr.id and new_transaction.object_type == 'thread') assert new_transaction.command == 'update' # An update to one of its other attributes does not msg.subject = 'Ice cubes and dogs' db.session.commit() same_transaction = get_latest_transaction(db.session, 'thread', thr.id, default_namespace.id) assert same_transaction.id == new_transaction.id
def test_message_label_updates(db, api_client, default_account, api_version, custom_label): """Check that you can update a message (optimistically or not), and that the update is queued in the ActionLog.""" headers = dict() headers['Api-Version'] = api_version # Gmail threads, messages have a 'labels' field gmail_thread = add_fake_thread(db.session, default_account.namespace.id) gmail_message = add_fake_message(db.session, default_account.namespace.id, gmail_thread) resp_data = api_client.get_data('/messages/{}'.format( gmail_message.public_id), headers=headers) assert resp_data['labels'] == [] category = custom_label.category update = dict(labels=[category.public_id]) resp = api_client.put_data('/messages/{}'.format(gmail_message.public_id), update, headers=headers) resp_data = json.loads(resp.data) if api_version == API_VERSIONS[0]: assert len(resp_data['labels']) == 1 assert resp_data['labels'][0]['id'] == category.public_id else: assert resp_data['labels'] == []
def test_thread_sent_recent_date(db, api_client, default_account): date1 = datetime.datetime(2015, 1, 1, 0, 0, 0) date2 = datetime.datetime(2012, 1, 1, 0, 0, 0) date3 = datetime.datetime(2010, 1, 1, 0, 0, 0) date4 = datetime.datetime(2009, 1, 1, 0, 0, 0) date5 = datetime.datetime(2008, 1, 1, 0, 0, 0) thread1 = add_fake_thread(db.session, default_account.namespace.id) test_subject = "test_thread_sent_recent_date" add_fake_message(db.session, default_account.namespace.id, thread1, subject=test_subject, received_date=date1) add_fake_message(db.session, default_account.namespace.id, thread1, subject=test_subject, received_date=date2, add_sent_category=True) add_fake_message(db.session, default_account.namespace.id, thread1, subject=test_subject, received_date=date3) add_fake_message(db.session, default_account.namespace.id, thread1, subject=test_subject, received_date=date4, add_sent_category=True) add_fake_message(db.session, default_account.namespace.id, thread1, subject=test_subject, received_date=date5) resp = api_client.get_raw('/threads/') assert resp.status_code == 200 threads = json.loads(resp.data) for thread in threads: # should only be one assert datetime.datetime.fromtimestamp( thread['last_message_sent_timestamp']) == date2
def test_accounttransactions(db, default_namespace): account = default_namespace.account transaction = get_latest_transaction(db.session, 'account', default_namespace.account.id, default_namespace.id) assert transaction.command == 'insert' transaction_id = transaction.id # Verify an AccountTransaction is created accounttransactions = db.session.query(AccountTransaction).filter( AccountTransaction.namespace_id == default_namespace.id).all() assert len(accounttransactions) == 1 accounttransaction = accounttransactions[0] assert accounttransaction.namespace_id == default_namespace.id assert accounttransaction.command == 'insert' assert accounttransaction.object_type == 'account' assert accounttransaction.record_id == default_namespace.account.id accounttransaction_id = accounttransaction.id with db.session.no_autoflush: # No Transaction or AccountTransaction records created account.last_synced_events = datetime.utcnow() db.session.commit() transaction = get_latest_transaction(db.session, 'account', default_namespace.account.id, default_namespace.id) assert transaction.id == transaction_id accounttransactions = db.session.query(AccountTransaction).filter( AccountTransaction.namespace_id == default_namespace.id).all() assert len(accounttransactions) == 1 assert accounttransactions[0].id == accounttransaction_id # Only Transaction record created thread = add_fake_thread(db.session, default_namespace.id) transaction = get_latest_transaction(db.session, 'thread', thread.id, default_namespace.id) assert transaction.id > transaction_id accounttransactions = db.session.query(AccountTransaction).filter( AccountTransaction.namespace_id == default_namespace.id).all() assert len(accounttransactions) == 1 assert accounttransactions[0].id == accounttransaction_id # Both Transaction or AccountTransaction records created account.sync_state = 'invalid' db.session.commit() transaction = get_latest_transaction(db.session, 'account', default_namespace.account.id, default_namespace.id) assert transaction.id > transaction_id assert transaction.command == 'update' accounttransactions = db.session.query(AccountTransaction).filter( AccountTransaction.namespace_id == default_namespace.id).all() assert len(accounttransactions) == 2 assert accounttransactions[1].id != accounttransaction_id assert accounttransactions[1].command == 'update'
def test_namespace_delete_cascade(db, default_account): from inbox.models import Account, Thread, Message models = [Thread, Message] namespace = default_account.namespace namespace_id = namespace.id account_id = default_account.id account = db.session.query(Account).get(account_id) assert account thread = add_fake_thread(db.session, namespace_id) add_fake_message(db.session, namespace_id, thread) for m in models: c = db.session.query(m).filter(m.namespace_id == namespace_id).count() print "count for", m, ":", c assert c != 0 fake_account = add_generic_imap_account(db.session) fake_account_id = fake_account.id assert fake_account_id != account.id and \ fake_account.namespace.id != namespace_id thread = add_fake_thread(db.session, fake_account.namespace.id) add_fake_message(db.session, fake_account.namespace.id, thread) assert len( db.session.query(Namespace).filter( Namespace.id == namespace_id).all()) > 0 # This test is separate from test_namespace_deletion because we want to # do a raw SQLAlchemy delete rather than using delete_namespace, which does # a bunch of extra work to ensure that objects associated with a Namespace # are actually deleted. db.session.query(Namespace).filter(Namespace.id == namespace_id).delete() db.session.commit() assert len( db.session.query(Namespace).filter( Namespace.id == namespace_id).all()) == 0
def create_from_synced(db, account, raw_message): thread = add_fake_thread(db.session, account.namespace.id) received_date = datetime.datetime.utcnow() m = Message.create_from_synced(account, 22, '[Gmail]/All Mail', received_date, raw_message) m.thread = thread db.session.add(m) db.session.commit() return m
def test_accounttransactions(db, default_namespace): account = default_namespace.account transaction = get_latest_transaction(db.session, "account", default_namespace.account.id, default_namespace.id) assert transaction.command == "insert" transaction_id = transaction.id # Verify an AccountTransaction is created accounttransactions = ( db.session.query(AccountTransaction).filter(AccountTransaction.namespace_id == default_namespace.id).all() ) assert len(accounttransactions) == 1 accounttransaction = accounttransactions[0] assert accounttransaction.namespace_id == default_namespace.id assert accounttransaction.command == "insert" assert accounttransaction.object_type == "account" assert accounttransaction.record_id == default_namespace.account.id accounttransaction_id = accounttransaction.id with db.session.no_autoflush: # No Transaction or AccountTransaction records created account.last_synced_events = datetime.utcnow() db.session.commit() transaction = get_latest_transaction(db.session, "account", default_namespace.account.id, default_namespace.id) assert transaction.id == transaction_id accounttransactions = ( db.session.query(AccountTransaction).filter(AccountTransaction.namespace_id == default_namespace.id).all() ) assert len(accounttransactions) == 1 assert accounttransactions[0].id == accounttransaction_id # Only Transaction record created thread = add_fake_thread(db.session, default_namespace.id) transaction = get_latest_transaction(db.session, "thread", thread.id, default_namespace.id) assert transaction.id > transaction_id accounttransactions = ( db.session.query(AccountTransaction).filter(AccountTransaction.namespace_id == default_namespace.id).all() ) assert len(accounttransactions) == 1 assert accounttransactions[0].id == accounttransaction_id # Both Transaction or AccountTransaction records created account.sync_state = "invalid" db.session.commit() transaction = get_latest_transaction(db.session, "account", default_namespace.account.id, default_namespace.id) assert transaction.id > transaction_id assert transaction.command == "update" accounttransactions = ( db.session.query(AccountTransaction).filter(AccountTransaction.namespace_id == default_namespace.id).all() ) assert len(accounttransactions) == 2 assert accounttransactions[1].id != accounttransaction_id assert accounttransactions[1].command == "update"
def test_message_insert_creates_transaction(db, default_namespace): with db.session.no_autoflush: thr = add_fake_thread(db.session, default_namespace.id) msg = add_fake_message(db.session, default_namespace.id, thr) transaction = get_latest_transaction(db.session, "message", msg.id, default_namespace.id) assert transaction.command == "insert" # Test that the thread gets revised too transaction = get_latest_transaction(db.session, "thread", thr.id, default_namespace.id) assert transaction.command == "update"
def test_namespace_delete_cascade(db, default_account): from inbox.models import Account, Thread, Message models = [Thread, Message] namespace = default_account.namespace namespace_id = namespace.id account_id = default_account.id account = db.session.query(Account).get(account_id) assert account thread = add_fake_thread(db.session, namespace_id) add_fake_message(db.session, namespace_id, thread) for m in models: c = db.session.query(m).filter( m.namespace_id == namespace_id).count() print "count for", m, ":", c assert c != 0 fake_account = add_generic_imap_account(db.session) fake_account_id = fake_account.id assert fake_account_id != account.id and \ fake_account.namespace.id != namespace_id thread = add_fake_thread(db.session, fake_account.namespace.id) add_fake_message(db.session, fake_account.namespace.id, thread) assert len(db.session.query(Namespace).filter(Namespace.id == namespace_id).all()) > 0 # This test is separate from test_namespace_deletion because we want to # do a raw SQLAlchemy delete rather than using delete_namespace, which does # a bunch of extra work to ensure that objects associated with a Namespace # are actually deleted. db.session.query(Namespace).filter(Namespace.id == namespace_id).delete() db.session.commit() assert len(db.session.query(Namespace).filter(Namespace.id == namespace_id).all()) == 0
def test_message_updates_create_transaction(db, default_namespace): with db.session.no_autoflush: thr = add_fake_thread(db.session, default_namespace.id) msg = add_fake_message(db.session, default_namespace.id, thr) msg.is_read = True db.session.commit() transaction = get_latest_transaction(db.session, "message", msg.id, default_namespace.id) assert transaction.record_id == msg.id assert transaction.object_type == "message" assert transaction.command == "update"
def test_message_insert_creates_transaction(db, default_namespace): with db.session.no_autoflush: thr = add_fake_thread(db.session, default_namespace.id) msg = add_fake_message(db.session, default_namespace.id, thr) transaction = get_latest_transaction(db.session, 'message', msg.id, default_namespace.id) assert transaction.command == 'insert' # Test that the thread gets revised too transaction = get_latest_transaction(db.session, 'thread', thr.id, default_namespace.id) assert transaction.command == 'update'
def test_object_type_distinguishes_messages_and_drafts(db, default_namespace): with db.session.no_autoflush: thr = add_fake_thread(db.session, default_namespace.id) msg = add_fake_message(db.session, default_namespace.id, thr) msg.is_draft = 1 db.session.commit() transaction = get_latest_transaction(db.session, "draft", msg.id, default_namespace.id) assert transaction.command == "update" db.session.delete(msg) db.session.commit() transaction = get_latest_transaction(db.session, "draft", msg.id, default_namespace.id) assert transaction.command == "delete"
def test_message_updates_create_transaction(db, default_namespace): with db.session.no_autoflush: thr = add_fake_thread(db.session, default_namespace.id) msg = add_fake_message(db.session, default_namespace.id, thr) msg.is_read = True db.session.commit() transaction = get_latest_transaction(db.session, 'message', msg.id, default_namespace.id) assert transaction.record_id == msg.id assert transaction.object_type == 'message' assert transaction.command == 'update'
def test_generic_drafts_flag_constrained_by_folder(db, generic_account, folder_role): msg_uid = 22 thread = add_fake_thread(db.session, generic_account.namespace.id) message = add_fake_message(db.session, generic_account.namespace.id, thread) folder = add_fake_folder(db.session, generic_account) add_fake_imapuid(db.session, generic_account.id, message, folder, msg_uid) new_flags = {msg_uid: Flags(('\\Draft',), None)} update_metadata(generic_account.id, folder.id, folder_role, new_flags, db.session) assert message.is_draft == (folder_role == 'drafts')
def test_generic_drafts_flag_constrained_by_folder(db, generic_account, folder_role): msg_uid = 22 thread = add_fake_thread(db.session, generic_account.namespace.id) message = add_fake_message(db.session, generic_account.namespace.id, thread) folder = add_fake_folder(db.session, generic_account) add_fake_imapuid(db.session, generic_account.id, message, folder, msg_uid) new_flags = {msg_uid: Flags(('\\Draft', ), None)} update_metadata(generic_account.id, folder.id, folder_role, new_flags, db.session) assert message.is_draft == (folder_role == 'drafts')
def test_object_type_distinguishes_messages_and_drafts(db, default_namespace): with db.session.no_autoflush: thr = add_fake_thread(db.session, default_namespace.id) msg = add_fake_message(db.session, default_namespace.id, thr) msg.is_draft = 1 db.session.commit() transaction = get_latest_transaction(db.session, 'draft', msg.id, default_namespace.id) assert transaction.command == 'update' db.session.delete(msg) db.session.commit() transaction = get_latest_transaction(db.session, 'draft', msg.id, default_namespace.id) assert transaction.command == 'delete'
def test_message_category_updates_create_transaction(db, default_namespace): with db.session.no_autoflush: thr = add_fake_thread(db.session, default_namespace.id) msg = add_fake_message(db.session, default_namespace.id, thr) cat = add_fake_category(db.session, default_namespace.id, "category") thread_trx_before_category_change = get_latest_transaction(db.session, "thread", thr.id, default_namespace.id) msg.categories = [cat] db.session.commit() latest_message_trx = get_latest_transaction(db.session, "message", msg.id, default_namespace.id) thread_trx_after_category_change = get_latest_transaction(db.session, "thread", thr.id, default_namespace.id) assert latest_message_trx.command == "update" assert thread_trx_before_category_change.id != thread_trx_after_category_change.id
def test_transaction_creation_for_self_referential_message_relationship(db, default_namespace): # Make sure that updating the self-refential relationship # `Message.reply_to_message` does not create a spurious update delta for # the parent message. thr = add_fake_thread(db.session, default_namespace.id) msg = add_fake_message(db.session, default_namespace.id, thr) reply = add_fake_message(db.session, default_namespace.id, thr) reply.reply_to_message = msg db.session.commit() assert reply.reply_to_message_id is not None assert msg.reply_to_message_id is None transaction = get_latest_transaction(db.session, "message", msg.id, default_namespace.id) assert transaction.record_id == msg.id assert transaction.object_type == "message" assert transaction.command == "insert"
def test_message_from_synced(db, new_message_from_synced, default_namespace): thread = add_fake_thread(db.session, default_namespace.id) m = new_message_from_synced assert m.namespace_id == default_namespace.id assert m.to_addr == [['Alice', '*****@*****.**']] assert m.cc_addr == [['Bob', '*****@*****.**']] assert m.subject == 'Hello' assert m.body == '<html>Hello World!</html>' assert m.data_sha256 m.thread = thread db.session.add(m) db.session.commit() assert (db.session.query(Block).filter( Block.namespace_id == default_namespace.id).count() == 0) assert len(m.parts) == 0
def folder_and_message_maps(db, default_account): folder_map, message_map = {}, {} for name in ('all', 'trash', 'spam'): # Create a folder display_name = name.capitalize() if name != 'all' else 'All Mail' folder = add_fake_folder(db.session, default_account, display_name, name) thread = add_fake_thread(db.session, default_account.namespace.id) # Create a message in the folder message = add_fake_message(db.session, default_account.namespace.id, thread) add_fake_imapuid(db.session, default_account.id, message, folder, 13) update_message_metadata(db.session, default_account, message, False) db.session.commit() folder_map[name] = folder message_map[name] = message return folder_map, message_map
def test_reject_incompatible_reply_thread_and_message( db, api_client, message, thread, default_namespace): alt_thread = add_fake_thread(db.session, default_namespace.id) add_fake_message(db.session, default_namespace.id, alt_thread) thread = api_client.get_data('/threads')[0] alt_message_id = api_client.get_data('/threads')[1]['message_ids'][0] alt_message = api_client.get_data('/messages/{}'.format(alt_message_id)) assert thread['id'] != alt_message['thread_id'] reply_draft = { 'subject': 'test reply', 'reply_to_message_id': alt_message['id'], 'thread_id': thread['id'] } r = api_client.post_data('/drafts', reply_draft) assert r.status_code == 400
def test_reject_incompatible_reply_thread_and_message(db, api_client, message, thread, default_namespace): alt_thread = add_fake_thread(db.session, default_namespace.id) add_fake_message(db.session, default_namespace.id, alt_thread) thread = api_client.get_data('/threads')[0] alt_message_id = api_client.get_data('/threads')[1]['message_ids'][0] alt_message = api_client.get_data('/messages/{}'.format(alt_message_id)) assert thread['id'] != alt_message['thread_id'] reply_draft = { 'subject': 'test reply', 'reply_to_message_id': alt_message['id'], 'thread_id': thread['id'] } r = api_client.post_data('/drafts', reply_draft) assert r.status_code == 400
def test_object_deletions_create_transaction(db, default_namespace): with db.session.no_autoflush: thr = add_fake_thread(db.session, default_namespace.id) msg = add_fake_message(db.session, default_namespace.id, thr) db.session.delete(msg) db.session.commit() transaction = get_latest_transaction(db.session, "message", msg.id, default_namespace.id) assert transaction.record_id == msg.id assert transaction.object_type == "message" assert transaction.command == "delete" db.session.delete(thr) db.session.commit() transaction = get_latest_transaction(db.session, "thread", thr.id, default_namespace.id) assert transaction.record_id == thr.id assert transaction.object_type == "thread" assert transaction.command == "delete"
def test_transaction_creation_for_self_referential_message_relationship( db, default_namespace): # Make sure that updating the self-refential relationship # `Message.reply_to_message` does not create a spurious update delta for # the parent message. thr = add_fake_thread(db.session, default_namespace.id) msg = add_fake_message(db.session, default_namespace.id, thr) reply = add_fake_message(db.session, default_namespace.id, thr) reply.reply_to_message = msg db.session.commit() assert reply.reply_to_message_id is not None assert msg.reply_to_message_id is None transaction = get_latest_transaction(db.session, 'message', msg.id, default_namespace.id) assert transaction.record_id == msg.id assert transaction.object_type == 'message' assert transaction.command == 'insert'
def test_ordering(api_client, db, default_namespace): for i in range(3): thr = add_fake_thread(db.session, default_namespace.id) received_date = (datetime.datetime.utcnow() + datetime.timedelta(seconds=22 * (i + 1))) add_fake_message(db.session, default_namespace.id, thr, received_date=received_date) ordered_results = api_client.get_data('/messages') ordered_dates = [result['date'] for result in ordered_results] assert ordered_dates == sorted(ordered_dates, reverse=True) ordered_results = api_client.get_data('/messages?limit=3') expected_public_ids = [ public_id for public_id, in db.session.query(Message.public_id). filter(Message.namespace_id == default_namespace.id). order_by(desc(Message.received_date)).limit(3)] assert expected_public_ids == [r['id'] for r in ordered_results]
def test_self_send(db, default_namespace): first_thread = add_fake_thread(db.session, default_namespace.id) first_thread.subject = 'Some kind of test' add_fake_message(db.session, default_namespace.id, thread=first_thread, subject='Some kind of test', from_addr=[('Karim Hamidou', '*****@*****.**')], to_addr=[('Karim Hamidou', '*****@*****.**')]) msg2 = add_fake_message(db.session, default_namespace.id, thread=None, subject='Re: Some kind of test', from_addr=[('Karim Hamidou', '*****@*****.**')], to_addr=[('Karim Hamidou', '*****@*****.**')]) matched_thread = fetch_corresponding_thread(db.session, default_namespace.id, msg2) assert matched_thread is first_thread, "Should match on self-send"
def test_thread_delete(db, gmail_account): """ Ensure that all associated Messages are deleted when a Thread is deleted.""" generic_thread = add_fake_thread(db.session, gmail_account.namespace.id) generic_message = add_fake_message(db.session, gmail_account.namespace.id, generic_thread) assert db.session.query(Thread). \ filter(Thread.id == generic_thread.id).all() == [generic_thread] assert db.session.query(Message). \ filter(Message.id == generic_message.id).all() == [generic_message] db.session.delete(generic_thread) db.session.commit() assert db.session.query(Thread). \ filter(Thread.id == generic_thread.id).all() == [] assert db.session.query(Message). \ filter(Message.id == generic_message.id).all() == []
def test_message_category_updates_create_transaction(db, default_namespace): with db.session.no_autoflush: thr = add_fake_thread(db.session, default_namespace.id) msg = add_fake_message(db.session, default_namespace.id, thr) cat = add_fake_category(db.session, default_namespace.id, 'category') thread_trx_before_category_change = get_latest_transaction( db.session, 'thread', thr.id, default_namespace.id) msg.categories = [cat] db.session.commit() latest_message_trx = get_latest_transaction(db.session, 'message', msg.id, default_namespace.id) thread_trx_after_category_change = get_latest_transaction( db.session, 'thread', thr.id, default_namespace.id) assert latest_message_trx.command == 'update' assert thread_trx_before_category_change.id != \ thread_trx_after_category_change.id
def test_object_deletions_create_transaction(db, default_namespace): with db.session.no_autoflush: thr = add_fake_thread(db.session, default_namespace.id) msg = add_fake_message(db.session, default_namespace.id, thr) db.session.delete(msg) db.session.commit() transaction = get_latest_transaction(db.session, 'message', msg.id, default_namespace.id) assert transaction.record_id == msg.id assert transaction.object_type == 'message' assert transaction.command == 'delete' db.session.delete(thr) db.session.commit() transaction = get_latest_transaction(db.session, 'thread', thr.id, default_namespace.id) assert transaction.record_id == thr.id assert transaction.object_type == 'thread' assert transaction.command == 'delete'
def add_completely_fake_account(db, email='*****@*****.**'): from inbox.models.backends.gmail import GmailAuthCredentials fake_account = add_fake_gmail_account(db.session, email_address=email) calendar = add_fake_calendar(db.session, fake_account.namespace.id) for i in random_range(1, 10): add_fake_event(db.session, fake_account.namespace.id, calendar=calendar, title='%s' % i) # Add fake Threads, Messages and ImapUids. folder = add_fake_folder(db.session, fake_account) for i in random_range(1, 4): th = add_fake_thread(db.session, fake_account.namespace.id) for j in random_range(1, 3): msg = add_fake_msg_with_calendar_part(db.session, fake_account, 'fake part', thread=th) db.session.add(msg) db.session.flush() for k in random_range(1, 2): add_fake_imapuid(db.session, fake_account.id, msg, folder, int('%s%s' % (msg.id, k))) # Add fake contacts for i in random_range(1, 5): add_fake_contact(db.session, fake_account.namespace.id, uid=str(i)) auth_creds = GmailAuthCredentials() auth_creds.gmailaccount = fake_account auth_creds.scopes = "email" auth_creds.g_id_token = "test" auth_creds.client_id = "test" auth_creds.client_secret = "test" auth_creds.refresh_token = "test" auth_creds.is_valid = True db.session.add(auth_creds) db.session.commit() return fake_account
def test_ordering(api_client, db, default_namespace): for i in range(3): thr = add_fake_thread(db.session, default_namespace.id) received_date = (datetime.datetime.utcnow() + datetime.timedelta(seconds=22 * (i + 1))) add_fake_message(db.session, default_namespace.id, thr, received_date=received_date) ordered_results = api_client.get_data('/messages') ordered_dates = [result['date'] for result in ordered_results] assert ordered_dates == sorted(ordered_dates, reverse=True) ordered_results = api_client.get_data('/messages?limit=3') expected_public_ids = [ public_id for public_id, in db.session.query(Message.public_id).filter( Message.namespace_id == default_namespace.id).order_by( desc(Message.received_date)).limit(3) ] assert expected_public_ids == [r['id'] for r in ordered_results]
def test_message_labels(db, gmail_account): # Because we're using the gmail_account namespace api_client = new_api_client(db, gmail_account.namespace) # Gmail threads, messages have a 'labels' field gmail_thread = add_fake_thread(db.session, gmail_account.namespace.id) gmail_message = add_fake_message(db.session, gmail_account.namespace.id, gmail_thread) resp_data = api_client.get_data( '/threads/{}'.format(gmail_thread.public_id)) assert resp_data['id'] == gmail_thread.public_id assert resp_data['object'] == 'thread' assert 'labels' in resp_data and 'folders' not in resp_data resp_data = api_client.get_data( '/messages/{}'.format(gmail_message.public_id)) assert resp_data['id'] == gmail_message.public_id assert resp_data['object'] == 'message' assert 'labels' in resp_data and 'folders' not in resp_data