def test_update_draft(api_client): with freeze_time(datetime.now()) as freezer: original_draft = {'subject': 'original draft', 'body': 'parent draft'} r = api_client.post_data('/drafts', original_draft) draft_public_id = json.loads(r.data)['id'] version = json.loads(r.data)['version'] assert version == 0 freezer.tick() updated_draft = { 'subject': 'updated draft', 'body': 'updated draft', 'version': version } r = api_client.put_data('/drafts/{}'.format(draft_public_id), updated_draft) updated_public_id = json.loads(r.data)['id'] updated_version = json.loads(r.data)['version'] assert updated_public_id == draft_public_id assert updated_version > 0 drafts = api_client.get_data('/drafts') assert len(drafts) == 1 assert drafts[0]['id'] == updated_public_id # Check that the thread is updated too. thread = api_client.get_data('/threads/{}'.format( drafts[0]['thread_id'])) assert thread['subject'] == 'updated draft' assert thread['first_message_timestamp'] == drafts[0]['date'] assert thread['last_message_timestamp'] == drafts[0]['date']
def test_account_expanded(db, api_client, generic_account, gmail_account): # Generic accounts expose a `server_settings` attribute # Custom IMAP api_client = new_api_client(db, generic_account.namespace) resp_data = api_client.get_data('/account/?view=expanded') assert resp_data['provider'] == 'custom' assert 'server_settings' in resp_data assert set(resp_data['server_settings']) == set({ 'imap_host': 'imap.custom.com', 'smtp_host': 'smtp.custom.com', 'imap_port': 993, 'smtp_port': 587, 'ssl_required': True }) # Yahoo yahoo_account = add_fake_yahoo_account(db.session) api_client = new_api_client(db, yahoo_account.namespace) resp_data = api_client.get_data('/account/?view=expanded') assert resp_data['provider'] == 'yahoo' assert 'server_settings' in resp_data assert set(resp_data['server_settings']) == set({ 'imap_host': 'imap.mail.yahoo.com', 'smtp_host': 'smtp.mail.yahoo.com', 'imap_port': 993, 'smtp_port': 587, 'ssl_required': True }) # Gmail accounts don't expose a `server_settings` attribute api_client = new_api_client(db, gmail_account.namespace) resp_data = api_client.get_data('/account/?view=expanded') assert resp_data['provider'] == 'gmail' assert 'server_settings' not in resp_data
def test_api_get(db, api_client, calendar): e_data = {'title': 'subj', 'when': {'time': 1}, 'calendar_id': calendar.public_id, 'location': 'NylasHQ'} e_data2 = {'title': 'subj2', 'when': {'time': 1}, 'calendar_id': calendar.public_id, 'location': 'NylasHQ'} api_client.post_data('/events', e_data) api_client.post_data('/events', e_data2) event_list = api_client.get_data('/events') event_ids = [event['id'] for event in event_list] c1found = False c2found = False for c_id in event_ids: event = api_client.get_data('/events/' + c_id) if event['title'] == 'subj': c1found = True if event['title'] == 'subj2': c2found = True assert c1found assert c2found
def test_account(db, api_client, generic_account, gmail_account): # Because we're using the generic_account namespace api_client = new_api_client(db, generic_account.namespace) resp_data = api_client.get_data('/account') assert resp_data['id'] == generic_account.namespace.public_id assert resp_data['object'] == 'account' assert resp_data['account_id'] == generic_account.namespace.public_id assert resp_data['email_address'] == generic_account.email_address assert resp_data['name'] == generic_account.name assert resp_data['organization_unit'] == 'folder' assert 'sync_state' in resp_data assert 'server_settings' not in resp_data # Because we're using the gmail account namespace api_client = new_api_client(db, gmail_account.namespace) resp_data = api_client.get_data('/account') assert resp_data['id'] == gmail_account.namespace.public_id assert resp_data['provider'] == 'gmail' assert resp_data['organization_unit'] == 'label' assert 'sync_state' in resp_data assert 'server_settings' not in resp_data
def test_delete_from_readonly_calendar(db, default_namespace, api_client): add_fake_event(db.session, default_namespace.id, calendar=db.session.query(Calendar).filter( Calendar.namespace_id == default_namespace.id, Calendar.read_only == True).first(), # noqa read_only=True) calendar_list = api_client.get_data('/calendars') read_only_calendar = None for c in calendar_list: if c['read_only']: read_only_calendar = c break events = api_client.get_data('/events?calendar_id={}'.format( read_only_calendar['id'])) for event in events: if event['read_only']: read_only_event = event break assert read_only_calendar assert read_only_event e_id = read_only_event['id'] resp = api_client.delete('/events/{}'.format(e_id)) assert resp.status_code == 400
def test_account_expanded(db, api_client, generic_account, gmail_account): # Generic accounts expose a `server_settings` attribute # Custom IMAP api_client = new_api_client(db, generic_account.namespace) resp_data = api_client.get_data('/account/?view=expanded') assert resp_data['provider'] == 'custom' assert 'server_settings' in resp_data assert set(resp_data['server_settings']) == set({ 'imap_host': 'imap.custom.com', 'smtp_host': 'smtp.custom.com', 'imap_port': 993, 'smtp_port': 587, 'ssl_required': True}) # Yahoo yahoo_account = add_fake_yahoo_account(db.session) api_client = new_api_client(db, yahoo_account.namespace) resp_data = api_client.get_data('/account/?view=expanded') assert resp_data['provider'] == 'yahoo' assert 'server_settings' in resp_data assert set(resp_data['server_settings']) == set({ 'imap_host': 'imap.mail.yahoo.com', 'smtp_host': 'smtp.mail.yahoo.com', 'imap_port': 993, 'smtp_port': 587, 'ssl_required': True}) # Gmail accounts don't expose a `server_settings` attribute api_client = new_api_client(db, gmail_account.namespace) resp_data = api_client.get_data('/account/?view=expanded') assert resp_data['provider'] == 'gmail' assert 'server_settings' not in resp_data
def test_delete_from_readonly_calendar(db, default_namespace, api_client): add_fake_event( db.session, default_namespace.id, calendar=db.session.query(Calendar).filter( Calendar.namespace_id == default_namespace.id, Calendar.read_only == True).first(), # noqa read_only=True) calendar_list = api_client.get_data('/calendars') read_only_calendar = None for c in calendar_list: if c['read_only']: read_only_calendar = c break events = api_client.get_data('/events?calendar_id={}'.format( read_only_calendar['id'])) for event in events: if event['read_only']: read_only_event = event break assert read_only_calendar assert read_only_event e_id = read_only_event['id'] resp = api_client.delete('/events/{}'.format(e_id)) assert resp.status_code == 400
def test_api_get(contacts_provider, contact_sync, db, api_client, default_namespace): contacts_provider.supply_contact('Contact One', '*****@*****.**') contacts_provider.supply_contact('Contact Two', '*****@*****.**') contact_sync.provider = contacts_provider contact_sync.sync() contact_list = api_client.get_data('/contacts') contact_ids = [contact['id'] for contact in contact_list] c1found = False c2found = False for c_id in contact_ids: contact = api_client.get_data('/contacts/' + c_id) if contact['name'] == 'Contact One': c1found = True if contact['name'] == 'Contact Two': c2found = True assert c1found assert c2found
def test_events_are_condensed(api_client, message): """ Test that multiple revisions of the same object are rolled up in the delta response. """ ts = int(time.time() + 22) cursor = get_cursor(api_client, ts) # Modify a message, then modify it again message_id = api_client.get_data('/messages/')[0]['id'] message_path = '/messages/{}'.format(message_id) api_client.put_data(message_path, {'unread': True}) api_client.put_data(message_path, {'unread': False}) api_client.put_data(message_path, {'unread': True}) # Check that successive modifies are condensed. sync_data = api_client.get_data('/delta?cursor={}'.format(cursor)) deltas = sync_data['deltas'] # A message modify propagates to its thread message_deltas = [d for d in deltas if d['object'] == 'message'] assert len(message_deltas) == 1 delta = message_deltas[0] assert delta['object'] == 'message' and delta['event'] == 'modify' assert delta['attributes']['unread'] is True
def test_namespace_limiting(db, api_client, default_namespaces): dt = datetime.datetime.utcnow() subject = dt.isoformat() namespaces = db.session.query(Namespace).all() assert len(namespaces) > 1 for ns in namespaces: thread = Thread(namespace=ns, subjectdate=dt, recentdate=dt, subject=subject) add_fake_message(db.session, ns.id, thread, received_date=dt, subject=subject) db.session.add(Block(namespace=ns, filename=subject)) db.session.commit() for ns in namespaces: r = api_client.get_data('/threads?subject={}'.format(subject)) assert len(r) == 1 r = api_client.get_data('/messages?subject={}'.format(subject)) assert len(r) == 1 r = api_client.get_data('/files?filename={}'.format(subject)) assert len(r) == 1
def test_api_expand_recurring_message(db, api_client, message, recurring_event): # This is a regression test for https://phab.nylas.com/T3556 # ("InflatedEvent should not be committed" exception in API"). event = recurring_event event.message = message db.session.commit() events = api_client.get_data('/events?expand_recurring=false') assert len(events) == 1 # Make sure the recurrence info is on the recurring event for e in events: if e['title'] == 'recurring-weekly': assert e.get('recurrence') is not None assert e.get('message_id') is not None r = api_client.get_raw('/events?expand_recurring=true') assert r.status_code == 200 all_events = api_client.get_data('/events?expand_recurring=true') assert len(all_events) != 0 for event in all_events: assert event['master_event_id'] is not None assert 'message_id' not in event
def test_create_draft_with_attachments(api_client, attachments, example_draft): attachment_ids = [] upload_path = '/files' for filename, path in attachments: data = {'file': (open(path, 'rb'), filename)} r = api_client.post_raw(upload_path, data=data) assert r.status_code == 200 attachment_id = json.loads(r.data)[0]['id'] attachment_ids.append(attachment_id) first_attachment = attachment_ids.pop() example_draft['file_ids'] = [first_attachment] r = api_client.post_data('/drafts', example_draft) assert r.status_code == 200 returned_draft = json.loads(r.data) draft_public_id = returned_draft['id'] assert returned_draft['version'] == 0 example_draft['version'] = returned_draft['version'] assert len(returned_draft['files']) == 1 attachment_ids.append(first_attachment) example_draft['file_ids'] = attachment_ids r = api_client.put_data('/drafts/{}'.format(draft_public_id), example_draft) assert r.status_code == 200 returned_draft = json.loads(r.data) assert len(returned_draft['files']) == 3 assert returned_draft['version'] == 1 example_draft['version'] = returned_draft['version'] # Make sure we can't delete the files now for file_id in attachment_ids: r = api_client.delete('/files/{}'.format(file_id)) assert r.status_code == 400 # Now remove the attachment example_draft['file_ids'] = [first_attachment] r = api_client.put_data('/drafts/{}'.format(draft_public_id), example_draft) draft_data = api_client.get_data('/drafts/{}'.format(draft_public_id)) assert len(draft_data['files']) == 1 assert draft_data['version'] == 2 example_draft['version'] = draft_data['version'] example_draft['file_ids'] = [] r = api_client.put_data('/drafts/{}'.format(draft_public_id), example_draft) draft_data = api_client.get_data('/drafts/{}'.format(draft_public_id)) assert r.status_code == 200 assert len(draft_data['files']) == 0 assert draft_data['version'] == 3 # now that they're not attached, we should be able to delete them for file_id in attachment_ids: r = api_client.delete('/files/{}'.format(file_id)) assert r.status_code == 200
def test_get_with_id(api_client, uploaded_file_ids, filename): # See comment in uploaded_file_ids() if filename == 'piece-jointe.jpg': filename = u'pièce-jointe.jpg' elif filename == 'andra-moi-ennepe.txt': filename = u'ἄνδρα μοι ἔννεπε' elif filename == 'long-non-ascii-filename.txt': filename = 100 * u'μ' in_file = api_client.get_data(u'/files?filename={}'.format(filename))[0] data = api_client.get_data('/files/{}'.format(in_file['id'])) assert data['filename'] == filename
def test_gmail_message_search(api_client, default_account, patch_token_manager, patch_gmail_search_response, sorted_gmail_messages, is_streaming): search_client = get_search_client(default_account) assert isinstance(search_client, GmailSearchClient) if is_streaming: messages = api_client.get_data('/messages/search/streaming?q=blah%20blah%20blah') else: messages = api_client.get_data('/messages/search?q=blah%20blah%20blah') assert_search_result(sorted_gmail_messages, messages)
def test_gmail_search_unicode(db, api_client, test_gmail_thread, patch_token_manager, patch_gmail_search_response, default_account, sorted_gmail_messages, sorted_gmail_threads, is_streaming): search_client = get_search_client(default_account) assert isinstance(search_client, GmailSearchClient) if is_streaming: threads = api_client.get_data('/threads/search/streaming?q=存档') else: threads = api_client.get_data('/threads/search?q=存档') assert_search_result(sorted_gmail_threads, threads)
def test_get_invalid(api_client, uploaded_file_ids): data = api_client.get_data('/files/0000000000000000000000000') assert data['message'].startswith("Couldn't find file") data = api_client.get_data('/files/!') assert data['message'].startswith("Invalid id") data = api_client.get_data('/files/0000000000000000000000000/download') assert data['message'].startswith("Couldn't find file") data = api_client.get_data('/files/!/download') assert data['message'].startswith("Invalid id") r = api_client.delete('/files/0000000000000000000000000') assert r.status_code == 404 r = api_client.delete('/files/!') assert r.status_code == 400
def test_api_create(db, api_client, calendar, default_account): e_data = { 'title': 'Friday Office Party', 'calendar_id': calendar.public_id, 'when': { 'time': 1407542195 }, 'location': 'Nylas HQ', } e_resp = api_client.post_data('/events', e_data) e_resp_data = json.loads(e_resp.data) assert e_resp_data['object'] == 'event' assert e_resp_data['account_id'] == default_account.namespace.public_id assert e_resp_data['title'] == e_data['title'] assert e_resp_data['location'] == e_data['location'] assert e_resp_data['when']['time'] == e_data['when']['time'] assert 'id' in e_resp_data e_id = e_resp_data['id'] e_get_resp = api_client.get_data('/events/' + e_id) assert e_get_resp['object'] == 'event' assert e_get_resp['account_id'] == default_account.namespace.public_id assert e_get_resp['id'] == e_id assert e_get_resp['title'] == e_data['title'] assert e_get_resp['when']['time'] == e_data['when']['time']
def test_adding_a_custom_label_preserves_other_labels(db, api_client, default_account, folder_and_message_maps, label): folder_map, message_map = folder_and_message_maps message = message_map[label] resp_data = api_client.get_data('/messages/{}'.format(message.public_id)) labels = resp_data['labels'] assert len(labels) == 1 assert labels[0]['name'] == label existing_label = labels[0]['id'] custom_label = add_fake_label(db.session, default_account, '<3', None) db.session.commit() # Adding only a custom label does not move a message to a different folder # i.e. does not change its 'all'/ 'trash'/ 'spam' labels. response = api_client.put_data( '/messages/{}'.format(message.public_id), {'label_ids': [custom_label.category.public_id, existing_label]}) labels = json.loads(response.data)['labels'] assert len(labels) == 2 assert set([l['name'] for l in labels]) == set([label, None]) assert '<3' in [l['display_name'] for l in labels]
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_adding_a_mutually_exclusive_label_replaces_the_other( db, api_client, default_account, folder_and_message_maps, label): # Verify a Gmail message can only have ONE of the 'all', 'trash', 'spam' # labels at a time. We specifically test that adding 'all'/ 'trash'/ 'spam' # to a message in one of the other two folders *replaces* # the existing label with the label being added. folder_map, message_map = folder_and_message_maps label_to_add = folder_map[label] for key in message_map: if key == label: continue message = message_map[key] resp_data = api_client.get_data('/messages/{}'.format( message.public_id)) labels = resp_data['labels'] assert len(labels) == 1 assert labels[0]['name'] == key existing_label = labels[0]['id'] # Adding 'all'/ 'trash'/ 'spam' removes the existing one, # irrespective of whether it's provided in the request or not. response = api_client.put_data( '/messages/{}'.format(message.public_id), {'label_ids': [label_to_add.category.public_id, existing_label]}) labels = json.loads(response.data)['labels'] assert len(labels) == 1 assert labels[0]['name'] == label
def test_conflicting_updates(api_client): original_draft = { 'subject': 'parent draft', 'body': 'parent draft' } r = api_client.post_data('/drafts', original_draft) original_public_id = json.loads(r.data)['id'] version = json.loads(r.data)['version'] updated_draft = { 'subject': 'updated draft', 'body': 'updated draft', 'version': version } r = api_client.put_data('/drafts/{}'.format(original_public_id), updated_draft) assert r.status_code == 200 updated_public_id = json.loads(r.data)['id'] updated_version = json.loads(r.data)['version'] assert updated_version != version conflicting_draft = { 'subject': 'conflicting draft', 'body': 'conflicting draft', 'version': version } r = api_client.put_data('/drafts/{}'.format(original_public_id), conflicting_draft) assert r.status_code == 409 drafts = api_client.get_data('/drafts') assert len(drafts) == 1 assert drafts[0]['id'] == updated_public_id
def test_adding_inbox_adds_all_and_removes_trash_spam( db, api_client, default_account, folder_and_message_maps, label): # Verify a Gmail message in 'trash', 'spam' cannot have 'inbox'. # This time we test that adding 'inbox' to a message in the 'trash'/ 'spam' # moves it to 'all' in addition to adding 'inbox'. folder_map, message_map = folder_and_message_maps message = message_map[label] resp_data = api_client.get_data('/messages/{}'.format(message.public_id)) labels = resp_data['labels'] assert len(labels) == 1 assert labels[0]['name'] == label existing_label = labels[0]['id'] inbox_label = add_fake_label(db.session, default_account, 'Inbox', 'inbox') db.session.commit() # Adding 'inbox' adds 'all', replacing 'trash'/ 'spam' if needed. response = api_client.put_data( '/messages/{}'.format(message.public_id), {'label_ids': [inbox_label.category.public_id, existing_label]}) db.session.commit() labels = json.loads(response.data)['labels'] assert len(labels) == 2 assert set([l['name'] for l in labels]) == set(['all', 'inbox'])
def test_adding_a_mutually_exclusive_label_does_not_affect_custom_labels( db, api_client, default_account, folder_and_message_maps, label): folder_map, message_map = folder_and_message_maps label_to_add = folder_map[label] for key in message_map: if key == label: continue message = message_map[key] add_custom_label(db, default_account, message) resp_data = api_client.get_data('/messages/{}'.format(message.public_id)) labels = resp_data['labels'] assert len(labels) == 2 assert key in [l['name'] for l in labels] assert '<3' in [l['display_name'] for l in labels] # Adding only 'all'/ 'trash'/ 'spam' does not change custom labels. response = api_client.put_data( '/messages/{}'.format(message.public_id), {'label_ids': [label_to_add.category.public_id] + [l['id'] for l in labels]}) labels = json.loads(response.data)['labels'] assert len(labels) == 2 assert label in [l['name'] for l in labels] assert '<3' in [l['display_name'] for l in labels]
def test_adding_a_mutually_exclusive_label_replaces_the_other( db, api_client, default_account, folder_and_message_maps, label): # Verify a Gmail message can only have ONE of the 'all', 'trash', 'spam' # labels at a time. We specifically test that adding 'all'/ 'trash'/ 'spam' # to a message in one of the other two folders *replaces* # the existing label with the label being added. folder_map, message_map = folder_and_message_maps label_to_add = folder_map[label] for key in message_map: if key == label: continue message = message_map[key] resp_data = api_client.get_data('/messages/{}'.format(message.public_id)) labels = resp_data['labels'] assert len(labels) == 1 assert labels[0]['name'] == key existing_label = labels[0]['id'] # Adding 'all'/ 'trash'/ 'spam' removes the existing one, # irrespective of whether it's provided in the request or not. response = api_client.put_data( '/messages/{}'.format(message.public_id), {'label_ids': [label_to_add.category.public_id, existing_label]}) labels = json.loads(response.data)['labels'] assert len(labels) == 1 assert labels[0]['name'] == label
def test_api_override_serialization(db, api_client, default_namespace, recurring_event): event = recurring_event override = Event(original_start_time=event.start, master_event_uid=event.uid, namespace_id=default_namespace.id, calendar_id=event.calendar_id) override.update(event) override.uid = event.uid + "_" + event.start.strftime("%Y%m%dT%H%M%SZ") override.master = event override.master_event_uid = event.uid override.cancelled = True db.session.add(override) db.session.commit() filter = 'starts_after={}&ends_before={}'.format( urlsafe(event.start.replace(hours=-1)), urlsafe(event.start.replace(weeks=+1))) events = api_client.get_data('/events?' + filter) # We should have the base event and the override back, but no extras; # this allows clients to do their own expansion, should they ever desire # to experience the joy that is RFC 2445 section 4.8.5.4. assert len(events) == 2 assert events[0].get('object') == 'event' assert events[0].get('recurrence') is not None assert events[1].get('object') == 'event' assert events[1].get('status') == 'cancelled'
def test_conflicting_updates(api_client): original_draft = {'subject': 'parent draft', 'body': 'parent draft'} r = api_client.post_data('/drafts', original_draft) original_public_id = json.loads(r.data)['id'] version = json.loads(r.data)['version'] updated_draft = { 'subject': 'updated draft', 'body': 'updated draft', 'version': version } r = api_client.put_data('/drafts/{}'.format(original_public_id), updated_draft) assert r.status_code == 200 updated_public_id = json.loads(r.data)['id'] updated_version = json.loads(r.data)['version'] assert updated_version != version conflicting_draft = { 'subject': 'conflicting draft', 'body': 'conflicting draft', 'version': version } r = api_client.put_data('/drafts/{}'.format(original_public_id), conflicting_draft) assert r.status_code == 409 drafts = api_client.get_data('/drafts') assert len(drafts) == 1 assert drafts[0]['id'] == updated_public_id
def test_api_pessimistic_delete(db, api_client, calendar, default_account): e_data = { 'title': '', 'calendar_id': calendar.public_id, 'when': { 'time': 1407542195 }, } e_resp = api_client.post_data('/events', e_data, headers={"Api-Version": API_VERSIONS[1]}) e_resp_data = json.loads(e_resp.data) assert e_resp_data['object'] == 'event' assert e_resp_data['title'] == e_data['title'] assert e_resp_data['when']['time'] == e_data['when']['time'] assert 'id' in e_resp_data e_id = e_resp_data['id'] e_delete_resp = api_client.delete('/events/' + e_id, headers={"Api-Version": API_VERSIONS[1]}) assert e_delete_resp.status_code == 200 e_resp = api_client.get_data('/events/' + e_id) assert e_resp['status'] == 'confirmed'
def test_api_list(db, api_client, calendar): e_data = {'title': 'subj', 'description': 'body1', 'calendar_id': calendar.public_id, 'when': {'time': 1}, 'location': 'NylasHQ'} e_data2 = {'title': 'subj2', 'description': 'body2', 'calendar_id': calendar.public_id, 'when': {'time': 1}, 'location': 'NylasHQ'} api_client.post_data('/events', e_data) api_client.post_data('/events', e_data2) event_list = api_client.get_data('/events') event_titles = [event['title'] for event in event_list] assert 'subj' in event_titles assert 'subj2' in event_titles event_descriptions = [event['description'] for event in event_list] assert 'body1' in event_descriptions assert 'body2' in event_descriptions event_ids = [event['id'] for event in event_list] for e_id in event_ids: ev = db.session.query(Event).filter_by(public_id=e_id).one() db.session.delete(ev) db.session.commit()
def test_adding_a_mutually_exclusive_label_does_not_affect_custom_labels( db, api_client, default_account, folder_and_message_maps, label): folder_map, message_map = folder_and_message_maps label_to_add = folder_map[label] for key in message_map: if key == label: continue message = message_map[key] add_custom_label(db, default_account, message) resp_data = api_client.get_data('/messages/{}'.format( message.public_id)) labels = resp_data['labels'] assert len(labels) == 2 assert key in [l['name'] for l in labels] assert '<3' in [l['display_name'] for l in labels] # Adding only 'all'/ 'trash'/ 'spam' does not change custom labels. response = api_client.put_data( '/messages/{}'.format(message.public_id), { 'label_ids': [label_to_add.category.public_id] + [l['id'] for l in labels] }) labels = json.loads(response.data)['labels'] assert len(labels) == 2 assert label in [l['name'] for l in labels] assert '<3' in [l['display_name'] for l in labels]
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_query_target(db, api_client, thread, default_namespace): cat = Category(namespace_id=default_namespace.id, name='inbox', display_name='Inbox', type_='label') for _ in range(3): message = add_fake_message(db.session, default_namespace.id, thread, to_addr=[('Bob', '*****@*****.**')], from_addr=[('Alice', '*****@*****.**')], subject='some subject') message.categories.add(cat) db.session.commit() results = api_client.get_data('/messages?in=inbox') assert len(results) == 3 count = api_client.get_data('/messages?in=inbox&view=count') assert count['count'] == 3
def test_send_draft(db, api_client, example_draft, default_account): r = api_client.post_data('/drafts', example_draft) assert r.status_code == 200 public_id = json.loads(r.data)['id'] version = json.loads(r.data)['version'] r = api_client.post_data('/send', {'draft_id': public_id, 'version': version}) assert r.status_code == 200 draft = api_client.get_data('/drafts/{}'.format(public_id)) assert draft is not None assert draft['object'] != 'draft' with crispin_client(default_account.id, default_account.provider) as c: criteria = ['NOT DELETED', 'SUBJECT "{0}"'.format( example_draft['subject'])] c.conn.select_folder(default_account.drafts_folder.name, readonly=False) draft_uids = c.conn.search(criteria) assert not draft_uids, 'Message still in Drafts folder' c.conn.select_folder(default_account.sent_folder.name, readonly=False) sent_uids = c.conn.search(criteria) assert sent_uids, 'Message missing from Sent folder' c.conn.delete_messages(sent_uids) c.conn.expunge()
def test_api_create(db, api_client, calendar): e_data = { 'title': 'Friday Office Party', 'when': {'time': 1407542195}, 'calendar_id': calendar.public_id, 'participants': [{ 'name': 'alyssa p. hacker', 'email': '*****@*****.**' }] } e_resp = api_client.post_data('/events', e_data) e_resp_data = json.loads(e_resp.data) assert len(e_resp_data['participants']) == 1 participant = e_resp_data['participants'][0] assert participant['name'] == e_data['participants'][0]['name'] assert participant['email'] == e_data['participants'][0]['email'] assert participant['status'] == 'noreply' e_resp_data = api_client.get_data('/events/' + e_resp_data['id']) assert len(e_resp_data['participants']) == 1 participant = e_resp_data['participants'][0] assert participant['name'] == e_data['participants'][0]['name'] assert participant['email'] == e_data['participants'][0]['email'] assert participant['status'] == 'noreply'
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_adding_inbox_adds_all_and_removes_trash_spam(db, api_client, default_account, folder_and_message_maps, label): # Verify a Gmail message in 'trash', 'spam' cannot have 'inbox'. # This time we test that adding 'inbox' to a message in the 'trash'/ 'spam' # moves it to 'all' in addition to adding 'inbox'. folder_map, message_map = folder_and_message_maps message = message_map[label] resp_data = api_client.get_data('/messages/{}'.format(message.public_id)) labels = resp_data['labels'] assert len(labels) == 1 assert labels[0]['name'] == label existing_label = labels[0]['id'] inbox_label = add_fake_label(db.session, default_account, 'Inbox', 'inbox') db.session.commit() # Adding 'inbox' adds 'all', replacing 'trash'/ 'spam' if needed. response = api_client.put_data( '/messages/{}'.format(message.public_id), {'label_ids': [inbox_label.category.public_id, existing_label]}) db.session.commit() labels = json.loads(response.data)['labels'] assert len(labels) == 2 assert set([l['name'] for l in labels]) == set(['all', 'inbox'])
def test_delete_remote_draft(db, api_client, message): message.is_draft = True db.session.commit() drafts = api_client.get_data('/drafts') assert len(drafts) == 1 public_id = drafts[0]['id'] version = drafts[0]['version'] assert public_id == message.public_id and version == message.version api_client.delete('/drafts/{}'.format(public_id), {'version': version}) # Check that drafts were deleted drafts = api_client.get_data('/drafts') assert not drafts
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_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_resource_views(resource_name, db, api_client, generic_account, message, thread, event, label, contact, folder): """Exercises various tests for views, mostly related to filtering. Note: this only tests views, it assumes the resources are working as expected.""" # Folders don't work with GMail accounts, need generic IMAP if resource_name == 'folders': api_client = new_api_client(db, generic_account.namespace) elements = api_client.get_data('/{}'.format(resource_name)) count = api_client.get_data('/{}?view=count'.format(resource_name)) assert count["count"] == len(elements) ids = api_client.get_data('/{}?view=ids'.format(resource_name)) for i, elem in enumerate(elements): assert isinstance(ids[i], basestring), \ "&views=ids should return string" assert elem["id"] == ids[i], "view=ids should preserve order"
def test_multisend_init_new_draft(patch_smtp, api_client, example_draft): r = api_client.post_data('/send-multiple', example_draft) assert r.status_code == 200 draft_public_id = json.loads(r.data)['id'] # Test that the sent draft can't be sent normally now r = api_client.post_data('/send', { 'draft_id': draft_public_id, 'version': 0 }) assert r.status_code == 400 # It's not a draft anymore drafts = api_client.get_data('/drafts') assert not drafts # We can retrieve it as a message, but it's not "sent" yet message = api_client.get_data('/messages/{}'.format(draft_public_id)) assert message['object'] == 'message'
def test_multisend_init_new_draft(patch_smtp, api_client, example_draft): r = api_client.post_data('/send-multiple', example_draft) assert r.status_code == 200 draft_public_id = json.loads(r.data)['id'] # Test that the sent draft can't be sent normally now r = api_client.post_data('/send', {'draft_id': draft_public_id, 'version': 0}) assert r.status_code == 400 # It's not a draft anymore drafts = api_client.get_data('/drafts') assert not drafts # We can retrieve it as a message, but it's not "sent" yet message = api_client.get_data('/messages/{}'.format(draft_public_id)) assert message['object'] == 'message'
def test_delete(api_client, uploaded_file_ids, draft): non_attachment_id = uploaded_file_ids.pop() attachment_id = uploaded_file_ids.pop() draft['file_ids'] = [attachment_id] r = api_client.post_data('/drafts', draft) assert r.status_code == 200 # Test that we can delete a non-attachment r = api_client.delete('/files/{}'.format(non_attachment_id)) assert r.status_code == 200 data = api_client.get_data('/files/{}'.format(non_attachment_id)) assert data['message'].startswith("Couldn't find file") # Make sure that we cannot delete attachments r = api_client.delete('/files/{}'.format(attachment_id)) assert r.status_code == 400 data = api_client.get_data('/files/{}'.format(attachment_id)) assert data['id'] == attachment_id
def test_update_to_nonexistent_draft(api_client): updated_draft = { 'subject': 'updated draft', 'body': 'updated draft', 'version': 22 } r = api_client.put_data('/drafts/{}'.format('notarealid'), updated_draft) assert r.status_code == 404 drafts = api_client.get_data('/drafts') assert len(drafts) == 0
def test_create_draft_replying_to_message(api_client, message): message = api_client.get_data('/messages')[0] reply_draft = { 'subject': 'test reply', 'body': 'test reply', 'reply_to_message_id': message['id'] } r = api_client.post_data('/drafts', reply_draft) data = json.loads(r.data) assert data['reply_to_message_id'] == message['id'] assert data['thread_id'] == message['thread_id']
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]