diff --git a/ciscosparkapi/api/messages.py b/ciscosparkapi/api/messages.py index c5d3f2a..6c33ee0 100644 --- a/ciscosparkapi/api/messages.py +++ b/ciscosparkapi/api/messages.py @@ -161,8 +161,7 @@ def list(self, roomId, mentionedPeople=None, before=None, """ # Process args assert isinstance(roomId, string_types) - assert mentionedPeople is None or \ - isinstance(mentionedPeople, string_types) + assert mentionedPeople is None or isinstance(mentionedPeople, list) assert before is None or isinstance(before, string_types) assert beforeMessage is None or isinstance(beforeMessage, string_types) assert max is None or isinstance(max, int) diff --git a/ciscosparkapi/api/people.py b/ciscosparkapi/api/people.py index 47a056d..2986c2f 100644 --- a/ciscosparkapi/api/people.py +++ b/ciscosparkapi/api/people.py @@ -220,7 +220,7 @@ def create(self, emails, **person_attributes): """ # Process args - assert isinstance(emails, string_types) and len(emails) == 1 + assert isinstance(emails, list) and len(emails) == 1 post_data = {} post_data['emails'] = emails post_data.update(person_attributes) @@ -302,6 +302,24 @@ def get(self, personId): # Return a Person object created from the response JSON data return Person(json_obj) + def delete(self, personId): + """Remove a person from the system. + + Only an admin can remove a person. + + Args: + personId(string_types): The personID of the person. + + Raises: + AssertionError: If the parameter types are incorrect. + SparkApiError: If the Cisco Spark cloud returns an error. + + """ + # Process args + assert isinstance(personId, string_types) + # API request + self._session.delete('people/' + personId) + def me(self): """Get the person details of the account accessing the API 'me'. diff --git a/tests/api/test_licenses.py b/tests/api/test_licenses.py new file mode 100644 index 0000000..9b04a57 --- /dev/null +++ b/tests/api/test_licenses.py @@ -0,0 +1,63 @@ +# -*- coding: utf-8 -*- + +"""pytest Licenses API wrapper tests and fixtures.""" + + +import pytest + +import ciscosparkapi + + +# Helper Functions + +def get_list_of_licenses(api, orgId=None, max=None): + return api.licenses.list(orgId=orgId, max=max) + + +def get_license_by_id(api, licenseId): + return api.licenses.get(licenseId) + + +def is_valid_license(obj): + return isinstance(obj, ciscosparkapi.License) and obj.id is not None + + +def are_valid_licenses(iterable): + return all([is_valid_license(obj) for obj in iterable]) + + +# pytest Fixtures + +@pytest.fixture(scope="session") +def licenses_list(api): + return list(get_list_of_licenses(api)) + + +@pytest.fixture(scope="session") +def licenses_dict(licenses_list): + return {lic.name: lic for lic in licenses_list} + + +# Tests + +class TestLicensesAPI(object): + """Test LicensesAPI methods.""" + + def test_list_licenses(self, licenses_list): + assert are_valid_licenses(licenses_list) + + def test_list_licenses_with_paging(self, api): + paging_generator = get_list_of_licenses(api, max=1) + licenses = list(paging_generator) + assert licenses > 1 + assert are_valid_licenses(licenses) + + def test_get_licenses_for_organization(self, api, me): + licenses = list(get_list_of_licenses(api, orgId=me.orgId)) + assert are_valid_licenses(licenses) + + def test_get_license_by_id(self, api, licenses_list): + assert len(licenses_list) >= 1 + license_id = licenses_list[0].id + license = get_license_by_id(api, licenseId=license_id) + assert is_valid_license(license) diff --git a/tests/api/test_memberships.py b/tests/api/test_memberships.py index f85133c..805fbb0 100644 --- a/tests/api/test_memberships.py +++ b/tests/api/test_memberships.py @@ -3,6 +3,8 @@ """pytest Memberships functions, fixtures and tests.""" +import itertools + import pytest import ciscosparkapi @@ -10,38 +12,228 @@ # Helper Functions -def add_people_to_room(api, room, emails): - for email in emails: - api.memberships.create(room.id, personEmail=email) +def add_person_to_room_by_email(api, room, person, isModerator=False): + return api.memberships.create(room.id, + personEmail=person.emails[0], + isModerator=isModerator) + + +def add_person_to_room_by_id(api, room, person, isModerator=False): + return api.memberships.create(room.id, + personId=person.id, + isModerator=isModerator) + + +def add_people_to_room_by_email(api, room, people): + return [add_person_to_room_by_email(api, room, person) + for person in people] + + +def add_people_to_room_by_id(api, room, people): + return [add_person_to_room_by_id(api, room, person) + for person in people] + + +def get_room_membership_list(api, room, **kwargs): + return list(api.memberships.list(roomId=room.id, **kwargs)) + + +def get_my_membership(api, room, me): + memberships = get_room_membership_list(api, room, personId=me.id) + assert len(memberships) == 1 + membership = memberships[0] + return membership + + +def get_membership_by_id(api, id): + return api.memberships.get(id) + + +def make_moderator(api, membership): + return api.memberships.update(membership.id, isModerator=True) + + +def delete_membership(api, membership): + api.memberships.delete(membership.id) def empty_room(api, me, room): - """Remove all memberships from a room (except the caller's membership).""" + """Remove all memberships from a room (except me).""" memberships = api.memberships.list(room.id) for membership in memberships: if membership.personId != me.id: - api.memberships.delete(membership.id) + delete_membership(api, membership) + + +def is_valid_membership(membership): + return isinstance(membership, ciscosparkapi.Membership) \ + and membership.id is not None + + +def are_valid_memberships(iterable): + are_valid = (is_valid_membership(item) for item in iterable) + return all(are_valid) + + +def membership_exists(api, membership): + try: + get_membership_by_id(api, membership.id) + except ciscosparkapi.SparkApiError: + return False + else: + return True # pytest Fixtures @pytest.fixture(scope="session") -def group_room_with_members(api, me, group_room, email_addresses): - add_people_to_room(api, group_room, email_addresses) - yield group_room - empty_room(api, me, group_room) +def authenticated_user_memberships(api, group_room, team_room, direct_rooms): + return list(api.memberships.list()) @pytest.fixture(scope="session") -def team_room_with_members(api, me, team_room, email_addresses): - add_people_to_room(api, team_room, email_addresses) - yield team_room - empty_room(api, me, team_room) +def me_group_room_moderator(api, group_room, me): + membership_id = get_my_membership(api, group_room, me) + return make_moderator(api, membership_id) -@pytest.fixture -def temp_group_room_with_members(api, me, temp_group_room, email_addresses): - add_people_to_room(api, temp_group_room, email_addresses) - yield temp_group_room - empty_room(api, me, temp_group_room) +@pytest.fixture(scope="session") +def group_room_member_added_by_email(api, me_group_room_moderator, + group_room, test_people): + person = test_people["member_added_by_email"] + membership = add_person_to_room_by_email(api, group_room, person) + + yield membership + + delete_membership(api, membership) + + +@pytest.fixture(scope="session") +def group_room_member_added_by_id(api, me_group_room_moderator, + group_room, test_people): + person = test_people["member_added_by_id"] + membership = add_person_to_room_by_id(api, group_room, person) + + yield membership + + delete_membership(api, membership) + + +@pytest.fixture(scope="session") +def group_room_moderator_added_by_email(api, me_group_room_moderator, + group_room, test_people): + person = test_people["moderator_added_by_email"] + membership = add_person_to_room_by_email(api, group_room, person, + isModerator=True) + + yield membership + delete_membership(api, membership) + + +@pytest.fixture(scope="session") +def group_room_moderator_added_by_id(api, me_group_room_moderator, + group_room, test_people): + person = test_people["moderator_added_by_id"] + membership = add_person_to_room_by_id(api, group_room, person, + isModerator=True) + + yield membership + + delete_membership(api, membership) + + +@pytest.fixture(scope="session") +def additional_group_room_memberships(group_room_member_added_by_email, + group_room_member_added_by_id, + group_room_moderator_added_by_email, + group_room_moderator_added_by_id): + return [group_room_member_added_by_email, + group_room_member_added_by_id, + group_room_moderator_added_by_email, + group_room_moderator_added_by_id] + + +@pytest.fixture(scope="session") +def group_room_with_members(group_room, additional_group_room_memberships): + return group_room + + +# Tests + +class TestMembershipsAPI(object): + """Test MembershipsAPI methods.""" + + def test_get_membership_details(self, api, me_group_room_moderator): + membership_id = me_group_room_moderator.id + membership = get_membership_by_id(api, membership_id) + assert is_valid_membership(membership) + + def test_list_user_memberships(self, authenticated_user_memberships): + assert len(authenticated_user_memberships) >= 3 + assert are_valid_memberships(authenticated_user_memberships) + + def test_list_user_memberships_with_paging(self, api, add_rooms, + authenticated_user_memberships): + page_size = 1 + pages = 3 + num_memberships = pages * page_size + if len(authenticated_user_memberships) < num_memberships: + add_rooms(num_memberships - len(authenticated_user_memberships)) + memberships = api.memberships.list(max=page_size) + memberships_list = list(itertools.islice(memberships, num_memberships)) + assert len(memberships_list) == num_memberships + assert are_valid_memberships(memberships_list) + + def test_create_membership_by_email(self, + group_room_member_added_by_email): + assert is_valid_membership(group_room_member_added_by_email) + + def test_create_membership_by_person_id(self, + group_room_member_added_by_id): + assert is_valid_membership(group_room_member_added_by_id) + + def test_create_moderator_by_email(self, + group_room_moderator_added_by_email): + assert is_valid_membership(group_room_moderator_added_by_email) + + def test_create_moderator_by_person_id(self, + group_room_moderator_added_by_id): + assert is_valid_membership(group_room_moderator_added_by_id) + + def test_update_membership_make_moderator(self, + me_group_room_moderator): + assert is_valid_membership(me_group_room_moderator) + assert me_group_room_moderator.isModerator + + def test_delete_membership(self, api, group_room, test_people): + person = test_people["not_a_member"] + membership = add_person_to_room_by_id(api, group_room, person) + assert is_valid_membership(membership) + delete_membership(api, membership) + assert not membership_exists(api, membership) + + def test_list_room_memberships(self, api, group_room_with_members): + memberships = get_room_membership_list(api, group_room_with_members) + assert len(memberships) > 1 + assert are_valid_memberships(memberships) + + def test_filter_room_memberships_by_personEmail(self, api, test_people, + group_room_with_members): + email = test_people["member_added_by_email"].emails[0] + memberships = get_room_membership_list(api, group_room_with_members, + personEmail=email) + assert len(memberships) == 1 + membership = memberships[0] + assert is_valid_membership(membership) + assert membership.roomId == group_room_with_members.id + + def test_filter_room_memberships_by_personId(self, api, test_people, + group_room_with_members): + id = test_people["member_added_by_id"].id + memberships = get_room_membership_list(api, group_room_with_members, + personId=id) + assert len(memberships) == 1 + membership = memberships[0] + assert is_valid_membership(membership) + assert membership.roomId == group_room_with_members.id diff --git a/tests/api/test_messages.py b/tests/api/test_messages.py index 8bea049..eee881e 100644 --- a/tests/api/test_messages.py +++ b/tests/api/test_messages.py @@ -2,31 +2,211 @@ """pytest Messages functions, fixtures and tests.""" +import itertools import pytest import ciscosparkapi -from tests.utils import create_string +from tests.conftest import TEST_FILE_URL +from tests.utils import create_string, download_file # Helper Functions +def create_message(api, **message_attributes): + return api.messages.create(**message_attributes) +def get_message_by_id(api, id): + return api.messages.get(id) + + +def delete_message(api, message): + api.messages.delete(message.id) + + +def delete_messages(api, iterable): + for message in iterable: + delete_message(api, message) + + +def list_messages(api, roomId, **query_parameters): + return list(api.messages.list(roomId, **query_parameters)) + + +def is_valid_message(obj): + return isinstance(obj, ciscosparkapi.Message) and obj.id is not None + + +def are_valid_messages(iterable): + return all([is_valid_message(obj) for obj in iterable]) + + +def message_exists(api, message): + try: + api.messages.get(message.id) + except ciscosparkapi.SparkApiError: + return False + else: + return True + # pytest Fixtures @pytest.fixture(scope="session") -def direct_messages(api, email_addresses): - msg_text = create_string("Message") +def direct_message_by_email(api, test_people): + person = test_people["member_added_by_email"] + message = create_message(api, toPersonEmail=person.emails[0], + text=create_string("Message")) + + yield message + + delete_message(api, message) + + +@pytest.fixture(scope="session") +def direct_message_by_id(api, test_people): + person = test_people["member_added_by_id"] + message = create_message(api, toPersonId=person.id, + text=create_string("Message")) + + yield message + + delete_message(api, message) + + +@pytest.fixture(scope="session") +def send_group_room_message(api): messages = [] - for email in email_addresses: - messages.append(api.messages.create(toPersonEmail=email, - text=msg_text)) - yield messages - delete_messages(api, messages) + def inner_function(roomId, **message_attributes): + message = create_message(api, roomId=roomId, **message_attributes) + messages.append(message) + return message + + yield inner_function -def delete_messages(api, messages): for message in messages: - api.messages.delete(message.id) + try: + delete_message(api, message) + except ciscosparkapi.SparkApiError as e: + pass + + +@pytest.fixture(scope="session") +def group_room_text_message(group_room, send_group_room_message): + text = create_string("Message") + return send_group_room_message(group_room.id, text=text) + + +@pytest.fixture(scope="session") +def group_room_markdown_message(group_room, send_group_room_message, me, + group_room_member_added_by_email, + group_room_text_message): + # Uses / depends on group_room_text_message to ensure this message is + # created after group_room_text_message, so that we can be sure that a + # message exists 'before' this one - used to test 'before' list filters. + mention_email = me.emails[0] + markdown = create_string(", This is **markdown** with a " + "mention.".format(mention_email)) + return send_group_room_message(group_room.id, markdown=markdown) + + +@pytest.fixture(scope="session") +def group_room_file_by_URL_message(group_room, send_group_room_message): + text = "File posted via URL" + return send_group_room_message(group_room.id, text=text, + files=[TEST_FILE_URL]) + + +@pytest.fixture(scope="session") +def group_room_file_by_local_upload_message(api, group_room, local_file, + send_group_room_message): + text = "File posted via URL" + return send_group_room_message(group_room.id, text=text, + files=[local_file]) + + +@pytest.fixture(scope="session") +def group_room_messages(api, group_room, + group_room_text_message, + group_room_markdown_message, + group_room_file_by_URL_message, + group_room_file_by_local_upload_message): + return list(list_messages(api, group_room.id)) + + +@pytest.fixture(scope="session") +def direct_messages(api, direct_message_by_email, direct_message_by_id): + return [direct_message_by_email, direct_message_by_id] + + +# Tests + +class TestMessagesAPI(object): + """Test MessagesAPI methods.""" + + def test_create_direct_messages_by_email(self, direct_message_by_email): + assert is_valid_message(direct_message_by_email) + + def test_create_direct_messages_by_id(self, direct_message_by_id): + assert is_valid_message(direct_message_by_id) + + def test_create_text_message(self, group_room_text_message): + assert is_valid_message(group_room_text_message) + + def test_create_markdown_message(self, group_room_markdown_message): + assert is_valid_message(group_room_markdown_message) + + def test_post_file_by_url(self, group_room_file_by_URL_message): + assert is_valid_message(group_room_file_by_URL_message) + + def test_post_file_by_local_upload(self, group_room_file_by_local_upload_message): + assert is_valid_message(group_room_file_by_local_upload_message) + + def test_get_message_by_id(self, api, group_room_text_message): + message = get_message_by_id(api, group_room_text_message.id) + assert is_valid_message(message) + + def test_delete_message(self, api, group_room, send_group_room_message): + text = create_string("Message") + message = create_message(api, roomId=group_room.id, text=text) + assert is_valid_message(message) + delete_message(api, message) + assert not message_exists(api, message) + + def test_list_all_messages_in_room(self, group_room_messages): + assert len(group_room_messages) >= 1 + assert are_valid_messages(group_room_messages) + + def test_list_messages_before(self, api, group_room, + group_room_markdown_message): + message_list = list_messages(api, group_room.id, + before=group_room_markdown_message.created) + assert len(message_list) >= 1 + assert are_valid_messages(message_list) + + def test_list_messages_before_message(self, api, group_room, + group_room_markdown_message): + message_list = list_messages(api, group_room.id, + beforeMessage=group_room_markdown_message.id) + assert len(message_list) >= 1 + assert are_valid_messages(message_list) + + def test_list_messages_with_paging(self, api, group_room, + group_room_messages): + page_size = 1 + pages = 3 + num_messages = pages * page_size + assert len(group_room_messages) >= num_messages + messages = list_messages(api, group_room.id, max=page_size) + messages_list = list(itertools.islice(messages, num_messages)) + assert len(messages_list) == num_messages + assert are_valid_messages(messages_list) + + # TODO: Investigate API list messages with 'me' mentions not working + # def test_list_messages_with_me_mention(self, api, group_room, me): + # messages = list_messages(api, group_room.id, mentionedPeople=["me"]) + # messages_list = list(messages) + # assert len(messages_list) >= 1 + # assert are_valid_messages(messages_list) diff --git a/tests/api/test_organizations.py b/tests/api/test_organizations.py new file mode 100644 index 0000000..f130c45 --- /dev/null +++ b/tests/api/test_organizations.py @@ -0,0 +1,47 @@ +# -*- coding: utf-8 -*- + +"""pytest Organizations API wrapper tests and fixtures.""" + + +import pytest + +import ciscosparkapi + + +# Helper Functions + +def get_list_of_organizations(api, max=None): + return api.organizations.list(max=max) + + +def get_organization_by_id(api, orgId): + return api.organizations.get(orgId) + + +def is_valid_organization(obj): + return isinstance(obj, ciscosparkapi.Organization) and obj.id is not None + +def are_valid_organizations(iterable): + return all([is_valid_organization(obj) for obj in iterable]) + + +# pytest Fixtures + +@pytest.fixture(scope="session") +def organizations_list(api): + return list(get_list_of_organizations(api)) + + +# Tests + +class TestOrganizationsAPI(object): + """Test OrganizationsAPI methods.""" + + def test_list_organizations(self, organizations_list): + assert are_valid_organizations(organizations_list) + + def test_get_organization_by_id(self, api, organizations_list): + assert len(organizations_list) >= 1 + organization_id = organizations_list[0].id + organization = get_organization_by_id(api, orgId=organization_id) + assert is_valid_organization(organization) diff --git a/tests/api/test_people.py b/tests/api/test_people.py index a399fa4..af768ea 100644 --- a/tests/api/test_people.py +++ b/tests/api/test_people.py @@ -3,15 +3,115 @@ """pytest People functions, fixtures and tests.""" +import itertools + import pytest import ciscosparkapi -from tests.utils import create_string +from tests.conftest import TEST_FILE_URL # Helper Functions +def is_valid_person(obj): + return isinstance(obj, ciscosparkapi.Person) and obj.id is not None + + +def are_valid_people(iterable): + return all([is_valid_person(obj) for obj in iterable]) + + +def get_person_by_id(api, id): + return api.people.get(id) + + +def list_people(api, **search_attribute): + return list(api.people.list(**search_attribute)) + + +def get_person_by_email(api, email): + list_of_people = list_people(api, email=email) + if list_of_people: + # If found, there should only be one Spark account associated with an + # single e-mail address. + assert len(list_of_people) == 1 + return list_of_people[0] + else: + return None + + +def create_person(api, emails, **person_attributes): + return api.people.create(emails, **person_attributes) + + +def update_person(api, person, **person_attributes): + return api.people.update(person.id, **person_attributes) + + +def delete_person(api, person): + # Temporarily disabling test account deletion to workon account + # capabilities issues. + # TODO: Enable test account clean-up. + # api.people.delete(person.id) + pass + + +def get_new_test_person(api, get_new_email_address, licenses_dict): + person_email = get_new_email_address() + person = get_person_by_email(api, person_email) + if person: + return person + else: + emails = [person_email] + display_name = "ciscosparkapi" + first_name = "ciscosparkapi" + last_name = "ciscosparkapi" + licenses = [licenses_dict["Messaging"].id] + person = create_person(api, emails, + displayName=display_name, + firstName=first_name, + lastName=last_name, + licenses=licenses) + assert is_valid_person(person) + return person + + +# Helper Classes + +class TestPeople(object): + """Creates, tracks and manages test accounts 'people' used by the tests.""" + + def __init__(self, api, get_new_email_address, licenses_dict): + super(TestPeople, self).__init__() + self._api = api + self._get_new_email_address = get_new_email_address + self._licenses_dict = licenses_dict + self.test_people = {} + def __getitem__(self, item): + if self.test_people.get(item): + return self.test_people[item] + else: + new_test_person = get_new_test_person(self._api, + self._get_new_email_address, + self._licenses_dict) + self.test_people[item] = new_test_person + return new_test_person + + @property + def list(self): + return self.test_people.values() + + def len(self): + return len(self.list) + + def __iter__(self): + return iter(self.list) + + def __del__(self): + for person in self.test_people.values(): + delete_person(self._api, person) + pass # pytest Fixtures @@ -19,3 +119,84 @@ @pytest.fixture(scope="session") def me(api): return api.people.me() + + +@pytest.fixture(scope="session") +def test_people(api, get_new_email_address, licenses_dict): + test_people = TestPeople(api, get_new_email_address, licenses_dict) + yield test_people + del test_people + + +@pytest.fixture() +def temp_person(api, get_new_email_address, licenses_dict): + person = get_new_test_person(api, get_new_email_address, licenses_dict) + yield person + delete_person(api, person) + + +@pytest.fixture() +def people_in_group_room(api, group_room_memberships): + return [get_person_by_id(api, membership.personId) + for membership in group_room_memberships] + + +# Tests + +class TestPeopleAPI(object): + """Test PeopleAPI methods.""" + + def test_create_person(self, test_people): + person = test_people["not_a_member"] + assert is_valid_person(person) + + # TODO: Investigate update person API not working + # def test_update_person(self, api, temp_person, roles_dict, licenses_dict, + # get_new_email_address): + # # Note: Not testing updating orgId + # updated_attributes = { + # "emails": [get_new_email_address()], + # "displayName": temp_person.displayName + " Updated", + # "firstName": temp_person.firstName + " Updated", + # "lastName": temp_person.lastName + " Updated", + # "avatar": TEST_FILE_URL, + # "roles": [roles_dict["Read-only administrator"].id], + # "licenses": [licenses_dict["Messaging"].id, + # licenses_dict["Meeting 25 party"].id], + # } + # updated_person = update_person(api, temp_person, **updated_attributes) + # assert is_valid_person(updated_person) + # for attribute, value in updated_attributes: + # assert getattr(updated_person, attribute, default=None) == value + + def test_get_my_details(self, me): + assert is_valid_person(me) + + def test_get_person_details(self, api, test_people): + person_id = test_people["not_a_member"].id + person = get_person_by_id(api, person_id) + assert is_valid_person(person) + + def test_list_people_by_email(self, api, test_people): + email = test_people["not_a_member"].emails[0] + list_of_people = list_people(api, email=email) + assert len(list_of_people) >= 1 + assert are_valid_people(list_of_people) + + def test_list_people_by_display_name(self, api, test_people): + display_name = test_people["not_a_member"].displayName + list_of_people = list_people(api, displayName=display_name) + assert len(list_of_people) >= 1 + assert are_valid_people(list_of_people) + + def test_list_people_with_paging(self, api, test_people, + additional_group_room_memberships): + page_size = 1 + pages = 3 + num_people = pages * page_size + assert test_people.len() >= num_people + display_name = test_people["not_a_member"].displayName + people = api.people.list(displayName=display_name, max=page_size) + people_list = list(itertools.islice(people, num_people)) + assert len(people_list) == num_people + assert are_valid_people(people_list) diff --git a/tests/api/test_roles.py b/tests/api/test_roles.py new file mode 100644 index 0000000..784220b --- /dev/null +++ b/tests/api/test_roles.py @@ -0,0 +1,59 @@ +# -*- coding: utf-8 -*- + +"""pytest Roles API wrapper tests and fixtures.""" + + +import pytest + +import ciscosparkapi + + +# Helper Functions + +def get_list_of_roles(api, max=None): + return api.roles.list(max=max) + + +def get_role_by_id(api, roleId): + return api.roles.get(roleId) + + +def is_valid_role(obj): + return isinstance(obj, ciscosparkapi.Role) and obj.id is not None + + +def are_valid_roles(iterable): + return all([is_valid_role(obj) for obj in iterable]) + + +# pytest Fixtures + +@pytest.fixture(scope="session") +def roles_list(api): + return list(get_list_of_roles(api)) + + +@pytest.fixture(scope="session") +def roles_dict(roles_list): + return {role.name: role for role in roles_list} + + +# Tests + +class TestRolesAPI(object): + """Test RolesAPI methods.""" + + def test_list_roles(self, roles_list): + assert are_valid_roles(roles_list) + + def test_list_roles_with_paging(self, api): + paging_generator = get_list_of_roles(api, max=1) + roles = list(paging_generator) + assert roles > 1 + assert are_valid_roles(roles) + + def test_get_role_by_id(self, api, roles_list): + assert len(roles_list) >= 1 + role_id = roles_list[0].id + role = get_role_by_id(api, roleId=role_id) + assert is_valid_role(role) diff --git a/tests/api/test_rooms.py b/tests/api/test_rooms.py index 09338fe..f2b7b9f 100644 --- a/tests/api/test_rooms.py +++ b/tests/api/test_rooms.py @@ -24,13 +24,12 @@ def delete_room(api, room): api.rooms.delete(room.id) -def is_valid_room(room): - return isinstance(room, ciscosparkapi.Room) and room.id is not None +def is_valid_room(obj): + return isinstance(obj, ciscosparkapi.Room) and obj.id is not None -def are_valid_rooms(rooms_iterable): - rooms_are_valid = (is_valid_room(room) for room in rooms_iterable) - return all(rooms_are_valid) +def are_valid_rooms(iterable): + return all([is_valid_room(obj) for obj in iterable]) def room_exists(api, room): @@ -45,8 +44,10 @@ def room_exists(api, room): # pytest Fixtures @pytest.fixture(scope="session") -def rooms_list(api): - return list(api.rooms.list()) +def group_room(api): + room = create_room(api, create_string("Room")) + yield room + delete_room(api, room) @pytest.fixture(scope="session") @@ -54,13 +55,6 @@ def direct_rooms(api, direct_messages): return [api.rooms.get(message.roomId) for message in direct_messages] -@pytest.fixture(scope="session") -def group_room(api): - room = create_room(api, create_string("Room")) - yield room - delete_room(api, room) - - @pytest.fixture(scope="session") def team_room(api, team): team_room = create_team_room(api, team, create_string("Team Room")) @@ -68,6 +62,11 @@ def team_room(api, team): delete_room(api, team_room) +@pytest.fixture(scope="session") +def rooms_list(api, group_room, direct_rooms, team_room): + return list(api.rooms.list()) + + @pytest.fixture def temp_group_room(api): room = create_room(api, create_string("Room")) @@ -79,16 +78,19 @@ def temp_group_room(api): @pytest.fixture def add_rooms(api): rooms = [] + def inner(num_rooms): for i in range(num_rooms): rooms.append(create_room(api, create_string("Additional Room"))) return rooms + yield inner + for room in rooms: delete_room(api, room) -# Room Tests +# Tests class TestRoomsAPI(object): """Test RoomsAPI methods.""" @@ -113,37 +115,32 @@ def test_delete_room(self, api, temp_group_room): api.rooms.delete(temp_group_room.id) assert not room_exists(api, temp_group_room) - @pytest.mark.usefixtures("group_room") - def test_list_group_rooms(self, api): + def test_list_group_rooms(self, api, group_room): group_rooms_list = list(api.rooms.list(type='group')) assert len(group_rooms_list) > 0 assert are_valid_rooms(group_rooms_list) - @pytest.mark.usefixtures("team_room") - def test_list_team_rooms(self, api, team): + def test_list_team_rooms(self, api, team, team_room): team_rooms_list = list(api.rooms.list(teamId=team.id)) assert len(team_rooms_list) > 0 assert are_valid_rooms(team_rooms_list) - @pytest.mark.usefixtures("direct_rooms") - def test_list_direct_rooms(self, api): + def test_list_direct_rooms(self, api, direct_rooms): direct_rooms_list = list(api.rooms.list(type='direct')) assert len(direct_rooms_list) > 0 assert are_valid_rooms(direct_rooms_list) - @pytest.mark.usefixtures("group_room", "team_room", "direct_rooms") def test_list_all_rooms(self, rooms_list): assert len(rooms_list) > 0 assert are_valid_rooms(rooms_list) - @pytest.mark.usefixtures("group_room", "team_room", "direct_rooms") - def test_list_all_rooms_with_paging(self, api, rooms_list, add_rooms): - page_size = 2 + def test_list_rooms_with_paging(self, api, rooms_list, add_rooms): + page_size = 1 pages = 3 num_rooms = pages * page_size if len(rooms_list) < num_rooms: add_rooms(num_rooms - len(rooms_list)) rooms = api.rooms.list(max=page_size) rooms_list = list(itertools.islice(rooms, num_rooms)) - assert len(rooms_list) == 6 + assert len(rooms_list) == num_rooms assert are_valid_rooms(rooms_list) diff --git a/tests/api/test_teammemberships.py b/tests/api/test_teammemberships.py index 69b4a4b..334d84a 100644 --- a/tests/api/test_teammemberships.py +++ b/tests/api/test_teammemberships.py @@ -3,31 +3,209 @@ """pytest Team Memberships functions, fixtures and tests.""" +import itertools + import pytest import ciscosparkapi -from tests.utils import create_string # Helper Functions -def add_people_to_team(api, team, emails): - for email in emails: - api.team_memberships.create(team.id, personEmail=email) +def add_person_to_team_by_email(api, team, person, isModerator=False): + return api.team_memberships.create(team.id, + personEmail=person.emails[0], + isModerator=isModerator) + + +def add_person_to_team_by_id(api, team, person, isModerator=False): + return api.team_memberships.create(team.id, + personId=person.id, + isModerator=isModerator) + + +def get_team_membership_list(api, team, **kwargs): + return list(api.team_memberships.list(teamId=team.id, **kwargs)) + + +def get_team_membership_by_id(api, id): + return api.team_memberships.get(id) + + +def make_moderator(api, team_membership): + return api.team_memberships.update(team_membership.id, isModerator=True) + + +def delete_membership(api, team_membership): + api.team_memberships.delete(team_membership.id) def empty_team(api, me, team): - """Remove all memberships from a team (except the caller's membership).""" - memberships = api.team_memberships.list(team.id) - for membership in memberships: + """Remove all team_memberships from a team (except me).""" + team_memberships = api.team_memberships.list(team.id) + for membership in team_memberships: if membership.personId != me.id: - api.team_memberships.delete(membership.id) + delete_membership(api, membership) + + +def is_valid_team_membership(membership): + return isinstance(membership, ciscosparkapi.TeamMembership) \ + and membership.id is not None + + +def are_valid_team_memberships(iterable): + are_valid = (is_valid_team_membership(item) for item in iterable) + return all(are_valid) + + +def membership_exists(api, membership): + try: + get_team_membership_by_id(api, membership.id) + except ciscosparkapi.SparkApiError: + return False + else: + return True # pytest Fixtures @pytest.fixture(scope="session") -def team_with_members(api, me, team, email_addresses): - add_people_to_team(api, team, email_addresses) - yield team - empty_team(api, me, team) +def my_team_membership(api, me, team): + team_memberships = get_team_membership_list(api, team) + for membership in team_memberships: + if membership.personId == me.id: + return membership + +# Cisco Spark API Documentation says that you should be able to retrieve a list +# of all of your team memberships; however, calling the API endpoint without +# specifying a teamId returns an error (and the docs say that a teamId is +# required). #DocumentationBug +# TODO: Report documentation / API bug on retrieving a user's team memberships +# @pytest.fixture(scope="session") +# def authenticated_user_team_memberships(api, team): +# return list(api.team_memberships.list()) + + +@pytest.fixture(scope="session") +def make_me_team_moderator(api, my_team_membership): + return make_moderator(api, my_team_membership) + + +@pytest.fixture(scope="session") +def team_member_added_by_email(api, make_me_team_moderator, team, test_people): + person = test_people["member_added_by_email"] + membership = add_person_to_team_by_email(api, team, person) + + yield membership + + delete_membership(api, membership) + + +@pytest.fixture(scope="session") +def team_member_added_by_id(api, make_me_team_moderator, team, test_people): + person = test_people["member_added_by_id"] + membership = add_person_to_team_by_id(api, team, person) + + yield membership + + delete_membership(api, membership) + + +@pytest.fixture(scope="session") +def team_moderator_added_by_email(api, make_me_team_moderator, team, + test_people): + person = test_people["moderator_added_by_email"] + membership = add_person_to_team_by_email(api, team, person, + isModerator=True) + + yield membership + + delete_membership(api, membership) + + +@pytest.fixture(scope="session") +def team_moderator_added_by_id(api, make_me_team_moderator, team, test_people): + person = test_people["moderator_added_by_id"] + membership = add_person_to_team_by_id(api, team, person, + isModerator=True) + + yield membership + + delete_membership(api, membership) + + +@pytest.fixture(scope="session") +def additional_team_memberships(team_member_added_by_email, + team_member_added_by_id, + team_moderator_added_by_email, + team_moderator_added_by_id): + return [team_member_added_by_email, + team_member_added_by_id, + team_moderator_added_by_email, + team_moderator_added_by_id] + + +@pytest.fixture(scope="session") +def team_with_members(team, additional_team_memberships): + return team + + +# Tests + +class TestTeamMembershipsAPI(object): + """Test MembershipsAPI methods.""" + + def test_get_team_membership_details(self, api, my_team_membership): + membership = get_team_membership_by_id(api, my_team_membership.id) + assert is_valid_team_membership(membership) + + # def test_list_user_team_memberships(self, + # authenticated_user_team_memberships): + # assert len(authenticated_user_team_memberships) >= 1 + # assert are_valid_team_memberships(authenticated_user_team_memberships) + # + # def test_list_user_team_memberships_with_paging(self, api, add_teams, + # authenticated_user_team_memberships): + # page_size = 1 + # pages = 3 + # num_memberships = pages * page_size + # if len(authenticated_user_team_memberships) < num_memberships: + # add_teams(num_memberships + # - len(authenticated_user_team_memberships)) + # team_memberships = api.team_memberships.list(max=page_size) + # memberships_list = list(itertools.islice(team_memberships, + # num_memberships)) + # assert len(memberships_list) == num_memberships + # assert are_valid_team_memberships(memberships_list) + + def test_create_team_membership_by_email(self, team_member_added_by_email): + assert is_valid_team_membership(team_member_added_by_email) + + def test_create_team_membership_by_person_id(self, + team_member_added_by_id): + assert is_valid_team_membership(team_member_added_by_id) + + def test_create_team_moderator_by_email(self, + team_moderator_added_by_email): + assert is_valid_team_membership(team_moderator_added_by_email) + + def test_create_team_moderator_by_person_id(self, + team_moderator_added_by_id): + assert is_valid_team_membership(team_moderator_added_by_id) + + def test_update_membership_make_moderator(self, + make_me_team_moderator): + assert is_valid_team_membership(make_me_team_moderator) + assert make_me_team_moderator.isModerator + + def test_delete_membership(self, api, team, test_people): + person = test_people["not_a_member"] + membership = add_person_to_team_by_id(api, team, person) + assert is_valid_team_membership(membership) + delete_membership(api, membership) + assert not membership_exists(api, membership) + + def test_list_team_memberships(self, api, team_with_members): + team_memberships = get_team_membership_list(api, team_with_members) + assert len(team_memberships) > 1 + assert are_valid_team_memberships(team_memberships) diff --git a/tests/api/test_teams.py b/tests/api/test_teams.py index 3cb78d3..e4122f0 100644 --- a/tests/api/test_teams.py +++ b/tests/api/test_teams.py @@ -3,6 +3,8 @@ """pytest Team functions, fixtures and tests.""" +import itertools + import pytest import ciscosparkapi @@ -15,10 +17,33 @@ def create_team(api, name): return api.teams.create(name) +def get_team_details_by_id(api, team_id): + team = api.teams.get(team_id) + return team + + def delete_team(api, team): api.teams.delete(team.id) +def is_valid_team(obj): + return isinstance(obj, ciscosparkapi.Team) and obj.id is not None + + +def are_valid_teams(iterable): + return all([is_valid_team(obj) for obj in iterable]) + + +def team_exists(api, team): + try: + get_team_details_by_id(api, team.id) + except ciscosparkapi.SparkApiError: + return False + else: + return True + + + # pytest Fixtures @pytest.fixture(scope="session") @@ -26,3 +51,72 @@ def team(api): team = create_team(api, create_string("Team")) yield team delete_team(api, team) + + +@pytest.fixture(scope="session") +def teams_list(api, team): + return list(api.teams.list()) + + +@pytest.fixture +def temp_team(api): + team = create_team(api, create_string("Team")) + + yield team + + if team_exists(api, team): + delete_team(api, team) + + +@pytest.fixture +def add_teams(api): + teams = [] + + def inner(num_teams): + for i in range(num_teams): + teams.append(create_team(api, create_string("Additional Team"))) + return teams + + yield inner + + for team in teams: + delete_team(api, team) + + +# Tests + +class TestTeamsAPI(object): + """Test TeamsAPI methods.""" + + def test_create_team(self, team): + assert is_valid_team(team) + + def test_get_team_details(self, api, team): + team = get_team_details_by_id(api, team.id) + assert is_valid_team(team) + + # TODO: Investigate update team name API not working + # def test_update_team_name(self, api, team): + # new_name = create_string("Updated Team") + # team = api.teams.update(team.id, name=new_name) + # assert is_valid_team(team) + # assert team.title == new_name + + def test_delete_team(self, api, temp_team): + api.teams.delete(temp_team.id) + assert not team_exists(api, temp_team) + + def test_list_teams(self, teams_list): + assert len(teams_list) > 0 + assert are_valid_teams(teams_list) + + def test_list_teams_with_paging(self, api, teams_list, add_teams): + page_size = 1 + pages = 3 + num_teams = pages * page_size + if len(teams_list) < num_teams: + add_teams(num_teams - len(teams_list)) + teams = api.teams.list(max=page_size) + teams_list = list(itertools.islice(teams, num_teams)) + assert len(teams_list) == num_teams + assert are_valid_teams(teams_list) diff --git a/tests/conftest.py b/tests/conftest.py index b110bce..956495b 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -1,14 +1,14 @@ # -*- coding: utf-8 -*- - """pytest configuration and top-level fixtures.""" -import pytest +import os +import string +import tempfile -EMAIL_ADDRESSES = [ - 'test98@cmlccie.com', - 'test99@cmlccie.com', -] +import pytest + +from tests.utils import download_file pytest_plugins = [ @@ -21,11 +21,53 @@ 'tests.api.test_teammemberships', 'tests.api.test_teams', 'tests.api.test_webhooks', + 'tests.api.test_organizations', + 'tests.api.test_licenses', + 'tests.api.test_roles', ] +TEST_DOMAIN = "cmlccie.com" +TEST_FILE_URL = "https://developer.ciscospark.com/images/logo_spark_lg@256.png" + + +email_template = string.Template("test${number}@" + TEST_DOMAIN) + + +# Helper Functions +def new_email_generator(): + i = 50 + while True: + email_address = email_template.substitute(number=i) + i += 1 + yield email_address + + # pytest Fixtures +@pytest.fixture("session") +def temp_directory(): + directory_abs_path = tempfile.mkdtemp() + + yield directory_abs_path + + os.rmdir(directory_abs_path) + + +@pytest.fixture("session") +def local_file(temp_directory): + file = download_file(TEST_FILE_URL, temp_directory) + + yield file + + os.remove(file) + + @pytest.fixture(scope="session") -def email_addresses(): - return EMAIL_ADDRESSES +def get_new_email_address(): + generator = new_email_generator() + + def inner_function(): + return generator.next() + + return inner_function diff --git a/tests/utils.py b/tests/utils.py index 5f472b4..2094355 100644 --- a/tests/utils.py +++ b/tests/utils.py @@ -4,8 +4,11 @@ import datetime +import os import string +import requests + STRING_PREFIX = "ciscosparkapi py.test" STRING_TEMPLATE = string.Template("$prefix $item [$datetime]") @@ -14,6 +17,23 @@ # Helper Functions def create_string(item): + """Create strings for tests; prefixed-timestamped strings.""" return STRING_TEMPLATE.substitute(prefix=STRING_PREFIX, item=item, datetime=str(datetime.datetime.now())) + + +def download_file(url, local_directory, local_filename=None): + """Download a file from a remote URL to a local directory.""" + # http://stackoverflow.com/questions/16694907/ + # how-to-download-large-file-in-python-with-requests-py + local_filename = local_filename if local_filename \ + else url.split('/')[-1] + local_path = os.path.normpath(os.path.join(local_directory, + local_filename)) + response = requests.get(url, stream=True) + with open(local_path, 'wb') as f: + for chunk in response.iter_content(chunk_size=1024): + if chunk: + f.write(chunk) + return local_path