예제 #1
0
    def __init__(self, parent, definition):
        """Constructor.

        Required Arguments:
            parent: Account object - parent is an Account object.
            definition: a dictionary of parameters. The 'gmail_thread_id' parameter
                is required to make method calls.
        """
        super(Thread, self).__init__(parent, 'threads/{gmail_thread_id}',
                                     definition)

        # This is going to be gross - prepare yourself

        if "messages" in definition:
            from contextio.lib.resources.message import Message
            self.messages = [
                Message(self.parent, message)
                for message in definition['messages']
            ]

        if 'sources' in definition:
            from contextio.lib.resources.source import Source
            self.sources = [
                Source(self.parent, source) for source in definition['sources']
            ]
예제 #2
0
    def test_get_thread_sets_thread_subject_if_message_has_subject(self, mock_request):
        mock_request.return_value = {

                "messages": [{"message_id": "foo", "gmail_thread_id": "foobar"}
            ]

        }
        params = {
            "include_body": 1,
            "include_headers": 1,
            "include_flags": 1,
            "body_type": "foobar",
            "limit": 1,
            "offset": 1

        }

        message = Message(Mock(spec=[]), {"message_id": "fake_message_id", "subject": "catpants"})
        message.get_thread(**params)

        self.assertEqual("catpants", message.thread.subject)
예제 #3
0
    def test_get_thread_sets_thread_subject_if_message_has_subject(
            self, mock_request):
        mock_request.return_value = {
            "messages": [{
                "message_id": "foo",
                "gmail_thread_id": "foobar"
            }]
        }
        params = {
            "include_body": 1,
            "include_headers": 1,
            "include_flags": 1,
            "body_type": "foobar",
            "limit": 1,
            "offset": 1
        }

        message = Message(Mock(spec=[]), {
            "message_id": "fake_message_id",
            "subject": "catpants"
        })
        message.get_thread(**params)

        self.assertEqual("catpants", message.thread.subject)
예제 #4
0
    def get_messages(self, **params):
        """Get current listings of email messages in a given folder.

        NOTE: this gets all messages including since last sync. It's fresher,
            but expect slower performance than using Account.get_messages()

        Documentation:
            http://context.io/docs/2.0/accounts/sources/folders/messages#get

        Optional Arguments:
            include_thread_size: integer - Set to 1 to include thread size in
                the result.
            include_body: integer - Set to 1 to include message bodies in the
                result. Since message bodies must be retrieved from the IMAP
                server, expect a performance hit when setting this parameter.
            body_type: string - Used when include_body is set to get only body
                parts of a given MIME-type (for example text/html)
            include_headers: mixed - Can be set to 0 (default), 1 or raw. If
                set to 1, complete message headers, parsed into an array, are
                included in the results. If set to raw, the headers are also
                included but as a raw unparsed string. Since full original
                headers bodies must be retrieved from the IMAP server, expect
                a performance hit when setting this parameter.
            include_flags: integer - Set to 1 to include IMAP flags for this
                message in the result. Since message flags must be retrieved
                from the IMAP server, expect a performance hit when setting
                this parameter.
            flag_seen: integer - Set to 1 to restrict list to messages having
                the \Seen flag set, set to 0 to have the messages with that
                flag unset (ie. list unread messages in the folder).
            limit: integer - The maximum number of results to return.
            offset: integer - Start the list at this offset (zero-based).

        Returns:
            a list of Message objects.
        """
        all_args = [
            "include_thread_size", "include_body", "body_type",
            "include_headers", "include_flags", "flag_seen", "limit", "offset"
        ]
        params = helpers.sanitize_params(params, all_args)

        return [
            Message(self, obj)
            for obj in self._request_uri('messages', params=params)
        ]
예제 #5
0
    def test_constructor_creates_message_object_with_all_attributes_in_keys_list_for_v2_0_api(
            self):
        mock_parent = Mock()
        mock_parent.api_version = "2.0"
        message = Message(mock_parent, {"message_id": "fake_message_id"})

        self.assertTrue(hasattr(message, 'date'))
        self.assertTrue(hasattr(message, 'date_indexed'))
        self.assertTrue(hasattr(message, 'addresses'))
        self.assertTrue(hasattr(message, 'person_info'))
        self.assertTrue(hasattr(message, 'email_message_id'))
        self.assertTrue(hasattr(message, 'message_id'))
        self.assertTrue(hasattr(message, 'gmail_message_id'))
        self.assertTrue(hasattr(message, 'gmail_thread_id'))
        self.assertTrue(hasattr(message, 'files'))
        self.assertTrue(hasattr(message, 'subject'))
        self.assertTrue(hasattr(message, 'folders'))
        self.assertTrue(hasattr(message, 'sources'))
예제 #6
0
    def get_messages(self, **params):
        """List messages where a contact is present.

        Documentation:
            http://context.io/docs/2.0/accounts/contacts/messages#get

        Optional Arguments:
            limit: integer - The maximum number of results to return.
            offset: integer - Start the list at this offset (zero-based).

        Returns:
            A list of Message objects
        """
        all_args = ['limit', 'offset']
        params = helpers.sanitize_params(params, all_args)

        return [
            Message(self.parent, obj)
            for obj in self._request_uri('messages', params=params)
        ]
예제 #7
0
    def test_constructor_creates_message_object_with_all_attributes_in_keys_list_for_lite_api(
            self):
        mock_parent = Mock()
        mock_parent.api_version = "lite"

        message = Message(mock_parent, {"message_id": "fake_message_id"})

        self.assertTrue(hasattr(message, "sent_at"))
        self.assertTrue(hasattr(message, "addresses"))
        self.assertTrue(hasattr(message, "subject"))
        self.assertTrue(hasattr(message, "email_message_id"))
        self.assertTrue(hasattr(message, "message_id"))
        self.assertTrue(hasattr(message, "list_headers"))
        self.assertTrue(hasattr(message, "in_reply_to"))
        self.assertTrue(hasattr(message, "references"))
        self.assertTrue(hasattr(message, "attachments"))
        self.assertTrue(hasattr(message, "bodies"))
        self.assertTrue(hasattr(message, "received_headers"))
        self.assertTrue(hasattr(message, "folders"))
        self.assertTrue(hasattr(message, "resource_url"))
        self.assertTrue(hasattr(message, "person_info"))
예제 #8
0
    def get_messages(self, **params):
        """List email messages for an account.

        GET method for the messages resource.

        Each of the email, to, from, cc and bcc parameters can be set to a
        comma-separated list of email addresses. These multiple addresses
        are treated as an OR combination.

        You can set more than one parameter when doing this call. Multiple
        parameters are treated as an AND combination.

        Optional Arguments:
            subject: string - Get messages whose subject matches this search
                string. To use regular expressions instead of simple string
                matching, make sure the string starts and ends with /.
            email: string - Email address of the contact for whom you want the
                latest messages exchanged with. By "exchanged with contact X"
                we mean any email received from contact X, sent to contact X
                or sent by anyone to both contact X and the source owner.
            to: string - Email address of a contact messages have been sent to.
            sender: string - Email address of a contact messages have been
                received from. Same as "from" in documentation. "from" is a
                python keyword and we can't use that...
            cc: string - Email address of a contact CC'ed on the messages.
            bcc: string - Email address of a contact BCC'ed on the messages.
            folder: string - Filter messages by the folder (or Gmail label).
                This parameter can be the complete folder name with the
                appropriate hierarchy delimiter for the mail server being
                queried (eg. Inbox/My folder) or the "symbolic name" of the
                folder (eg. \Starred). The symbolic name refers to attributes
                used to refer to special use folders in a language-independant
                way. See http://code.google.com/apis/gmail/imap/#xlist
                (Gmail specific) and RFC-6154.
            source: string - Filter messages by the account source label.
            file_name: string - Search for files based on their name. You can
                filter names using typical shell wildcards such as *, ? and []
                or regular expressions by enclosing the search expression in a
                leading / and trailing /. For example, *.pdf would give you
                all PDF files while /\.jpe?g$/ would return all files whose
                name ends with .jpg or .jpeg
            file_size_min: integer - Search for files based on their size (in bytes).
            file_size_max: integer - Search for files based on their size (in bytes).
            date_before: integer (unix time) - Only include messages before a
                given timestamp. The value this filter is applied to is the
                Date: header of the message which refers to the time the
                message is sent from the origin.
            date_after: integer (unix time) - Only include messages after a
                given timestamp. The value this filter is applied to is the
                Date: header of the message which refers to the time the
                message is sent from the origin.
            indexed_before: integer (unix time) - Only include messages
                indexed before a given timestamp. This is not the same as the
                date of the email, it is the time Context.IO indexed this
                message.
            indexed_after: integer (unix time) - Only include messages indexed
                after a given timestamp. This is not the same as the date of
                the email, it is the time Context.IO indexed this message.
            include_thread_size: integer - Set to 1 to include thread size in the result.
            include_body: integer - Set to 1 to include message bodies in the
                result. Since message bodies must be retrieved from the IMAP
                server, expect a performance hit when setting this parameter.
            include_headers: mixed - Can be set to 0 (default), 1 or raw. If
                set to 1, complete message headers, parsed into an array, are
                included in the results. If set to raw, the headers are also
                included but as a raw unparsed string. Since full original
                headers bodies must be retrieved from the IMAP server, expect
                a performance hit when setting this parameter.
            include_flags: integer - Set to 1 to include IMAP flags of
                messages in the result. Since message flags must be retrieved
                from the IMAP server, expect a performance hit when setting
                this parameter.
            body_type: string - Used when include_body is set to get only body
                parts of a given MIME-type (for example text/html)
            include_source: integer - Set to 1 to include message sources in the
                result. Since message sources must be retrieved from the IMAP server,
                expect a performance hit when setting this parameter.
            sort_order: string - The sort order of the returned results.
                Possible values are asc and desc
            limit: integer - The maximum number of results to return.
            offset: integer - Start the list at this offset (zero-based).

        Returns:
            A list of Message objects.
        """
        all_args = [
            "subject", "email", "to", "sender", "from_", "cc", "bcc", "folder",
            "date_before", "date_after", "indexed_before", "indexed_after",
            "include_thread_size", "include_body", "source", "file_name",
            "file_size_min", "file_size_max", "include_source",
            "include_headers", "include_flags", "body_type", "sort_order",
            "limit", "offset"
        ]

        params = helpers.sanitize_params(params, all_args)

        # workaround to send param "from" even though it's a reserved keyword
        # in python
        if 'sender' in params:
            params['from'] = params['sender']
            del params['sender']

        if 'from_' in params:
            params['from'] = params['from_']
            del params['from_']

        return [
            Message(self, obj)
            for obj in self._request_uri('messages', params=params)
        ]
예제 #9
0
 def setUp(self):
     self.message = Message(Mock(spec=[]),
                            {"message_id": "fake_message_id"})
예제 #10
0
class TestMessage(unittest.TestCase):
    def setUp(self):
        self.message = Message(Mock(spec=[]),
                               {"message_id": "fake_message_id"})

    def test_constructor_creates_message_object_with_all_attributes_in_keys_list_for_v2_0_api(
            self):
        mock_parent = Mock()
        mock_parent.api_version = "2.0"
        message = Message(mock_parent, {"message_id": "fake_message_id"})

        self.assertTrue(hasattr(message, 'date'))
        self.assertTrue(hasattr(message, 'date_indexed'))
        self.assertTrue(hasattr(message, 'addresses'))
        self.assertTrue(hasattr(message, 'person_info'))
        self.assertTrue(hasattr(message, 'email_message_id'))
        self.assertTrue(hasattr(message, 'message_id'))
        self.assertTrue(hasattr(message, 'gmail_message_id'))
        self.assertTrue(hasattr(message, 'gmail_thread_id'))
        self.assertTrue(hasattr(message, 'files'))
        self.assertTrue(hasattr(message, 'subject'))
        self.assertTrue(hasattr(message, 'folders'))
        self.assertTrue(hasattr(message, 'sources'))

    def test_constructor_creates_message_object_with_all_attributes_in_keys_list_for_lite_api(
            self):
        mock_parent = Mock()
        mock_parent.api_version = "lite"

        message = Message(mock_parent, {"message_id": "fake_message_id"})

        self.assertTrue(hasattr(message, "sent_at"))
        self.assertTrue(hasattr(message, "addresses"))
        self.assertTrue(hasattr(message, "subject"))
        self.assertTrue(hasattr(message, "email_message_id"))
        self.assertTrue(hasattr(message, "message_id"))
        self.assertTrue(hasattr(message, "list_headers"))
        self.assertTrue(hasattr(message, "in_reply_to"))
        self.assertTrue(hasattr(message, "references"))
        self.assertTrue(hasattr(message, "attachments"))
        self.assertTrue(hasattr(message, "bodies"))
        self.assertTrue(hasattr(message, "received_headers"))
        self.assertTrue(hasattr(message, "folders"))
        self.assertTrue(hasattr(message, "resource_url"))
        self.assertTrue(hasattr(message, "person_info"))

    @patch("contextio.lib.resources.base_resource.BaseResource.post")
    def test_post_sends_params_and_returns_True(self, mock_post):
        params = {
            "flag_answered": 0,
            "flag_draft": 0,
            "dst_folder": "foo",
            "dst_source": "bar",
            "move": 0,
            "flag_seen": 0,
            "flag_flagged": 0,
            "flag_deleted": 0
        }
        all_args = [
            'flag_answered', 'move', 'dst_folder', 'dst_source', 'flag_seen',
            'flag_flagged', 'flag_deleted', 'flag_draft'
        ]

        self.message.post(**params)

        mock_post.assert_called_with(all_args=all_args,
                                     params=params,
                                     required_args=['dst_folder'],
                                     return_bool=True)

    @patch("contextio.lib.resources.base_resource.BaseResource.post")
    def test_post_is_called_with_correct_args(self, mock_post):
        mock_post.return_value = {"success": True}
        req_args = [
            'dst_folder',
        ]
        all_args = [
            'flag_answered', 'move', 'dst_folder', 'dst_source', 'flag_seen',
            'flag_flagged', 'flag_deleted', 'flag_draft'
        ]

        self.message.post(return_bool=False, dst_folder="foobar")

        mock_post.assert_called_with(return_bool=False,
                                     params={"dst_folder": "foobar"},
                                     all_args=all_args,
                                     required_args=req_args)

    @patch("contextio.lib.resources.base_resource.BaseResource._request_uri")
    def test_delete_returns_boolean(self, mock_request):
        deleted_message = self.message.delete()

        self.assertEqual(True, deleted_message)

    @patch("contextio.lib.resources.base_resource.BaseResource._request_uri")
    def test_get_body_returns_list_of_dictionaries(self, mock_request):
        mock_request.return_value = [{"foo": "bar"}]

        response = self.message.get_body(type="foobar")

        mock_request.assert_called_with("body", params={"type": "foobar"})
        self.assertEqual(1, len(response))
        self.assertEqual([{"foo": "bar"}], response)

    @patch("contextio.lib.resources.base_resource.BaseResource._request_uri")
    def test_get_flags_returns_a_dictionary(self, mock_request):
        mock_request.return_value = {"foo": "bar"}

        response = self.message.get_flags()

        mock_request.assert_called_with("flags")
        self.assertEqual({"foo": "bar"}, self.message.flags)
        self.assertEqual({"foo": "bar"}, response)

    @patch("contextio.lib.resources.base_resource.BaseResource._request_uri")
    def test_post_flag_calls_request_uri_with_correct_args(self, mock_request):
        params = {
            "seen": 1,
            "answered": 1,
            "flagged": 1,
            "deleted": 0,
            "draft": 1
        }
        mock_request.return_value = {"success": True, "flags": ["foo", "bar"]}

        response = self.message.post_flag(**params)

        mock_request.assert_called_with("flags", method="POST", params=params)
        self.assertEqual(["foo", "bar"], self.message.flags)
        self.assertTrue(response)

    @patch("contextio.lib.resources.base_resource.BaseResource._request_uri")
    def test_get_folders_returns_a_list_of_dictionaries(self, mock_request):
        mock_request.return_value = [{"foo": "bar"}]

        response = self.message.get_folders()

        mock_request.assert_called_with("folders")
        self.assertEqual([{"foo": "bar"}], self.message.folders)
        self.assertEqual([{"foo": "bar"}], response)

    @patch("contextio.lib.resources.base_resource.BaseResource.post")
    def test_post_folder_calls_request_uri_with_correct_args(
            self, mock_request):
        params = {"add": "catpants", "remove": "dogpants"}
        mock_request.return_value = {"success": True}

        response = self.message.post_folder(**params)

        mock_request.assert_called_with("folders", params=params)

        self.assertTrue(response)

    @patch("contextio.lib.resources.base_resource.BaseResource._request_uri")
    def test_put_folders_calls_request_uri_with_correct_args(
            self, mock_request):
        body = "catpants"
        mock_request.return_value = {"success": True, "flags": ["foo", "bar"]}

        response = self.message.put_folders(body)

        mock_request.assert_called_with("folders", method="PUT", body=body)
        self.assertEqual("catpants", self.message.folders)
        self.assertTrue(response)

    @patch("contextio.lib.resources.base_resource.BaseResource._request_uri")
    def test_get_headers_returns_a_dictionary(self, mock_request):
        mock_request.return_value = {"foo": "bar"}

        response = self.message.get_headers(raw=1)

        mock_request.assert_called_with("headers", params={"raw": 1})
        self.assertEqual({"foo": "bar"}, self.message.headers)
        self.assertEqual({"foo": "bar"}, response)

    @patch("contextio.lib.resources.base_resource.BaseResource._request_uri")
    def test_get_source_returns_a_string(self, mock_request):
        mock_request.return_value = "catpants"

        response = self.message.get_source()

        mock_request.assert_called_with("source")
        self.assertEqual("catpants", self.message.source)
        self.assertEqual("catpants", response)

    @patch("contextio.lib.resources.base_resource.BaseResource._request_uri")
    def test_get_thread_returns_response_body_object_when_thread_id_exists(
            self, mock_request):
        mock_request.return_value = "catpants"

        response = self.message.get_source()

        mock_request.assert_called_with("source")
        self.assertEqual("catpants", self.message.source)
        self.assertEqual("catpants", response)

    @patch("contextio.lib.resources.base_resource.BaseResource._request_uri")
    def test_get_thread_returns_a_Thread_object_when_thread_id_exists(
            self, mock_request):
        mock_request.return_value = {
            "messages": [{
                "message_id": "foo",
                "gmail_thread_id": "foobar"
            }]
        }

        params = {
            "include_body": 1,
            "include_headers": 1,
            "include_flags": 1,
            "body_type": "foobar",
            "limit": 1,
            "offset": 1
        }

        response = self.message.get_thread(**params)

        mock_request.assert_called_with("thread", params=params)
        self.assertEqual(response, self.message.thread)
        self.assertIsInstance(response, Thread)

    @patch("contextio.lib.resources.base_resource.BaseResource._request_uri")
    def test_get_thread_returns_response_body_when_thread_id_does_not_exist(
            self, mock_request):
        mock_request.return_value = {"foo": "bar"}
        params = {
            "include_body": 1,
            "include_headers": 1,
            "include_flags": 1,
            "body_type": "foobar",
            "limit": 1,
            "offset": 1
        }
        response = self.message.get_thread(**params)

        mock_request.assert_called_with("thread", params=params)
        self.assertEqual({"foo": "bar"}, self.message.thread)
        self.assertEqual({"foo": "bar"}, response)

    @patch("contextio.lib.resources.base_resource.BaseResource._request_uri")
    def test_get_thread_sets_thread_subject_if_message_has_subject(
            self, mock_request):
        mock_request.return_value = {
            "messages": [{
                "message_id": "foo",
                "gmail_thread_id": "foobar"
            }]
        }
        params = {
            "include_body": 1,
            "include_headers": 1,
            "include_flags": 1,
            "body_type": "foobar",
            "limit": 1,
            "offset": 1
        }

        message = Message(Mock(spec=[]), {
            "message_id": "fake_message_id",
            "subject": "catpants"
        })
        message.get_thread(**params)

        self.assertEqual("catpants", message.thread.subject)
예제 #11
0
 def setUp(self):
     self.message = Message(Mock(spec=[]), {"message_id": "fake_message_id"})
예제 #12
0
class TestMessage(unittest.TestCase):
    def setUp(self):
        self.message = Message(Mock(spec=[]), {"message_id": "fake_message_id"})

    def test_constructor_creates_message_object_with_all_attributes_in_keys_list_for_v2_0_api(self):
        mock_parent = Mock()
        mock_parent.api_version = "2.0"
        message = Message(mock_parent, {"message_id": "fake_message_id"})

        self.assertTrue(hasattr(message, 'date'))
        self.assertTrue(hasattr(message, 'date_indexed'))
        self.assertTrue(hasattr(message, 'addresses'))
        self.assertTrue(hasattr(message, 'person_info'))
        self.assertTrue(hasattr(message, 'email_message_id'))
        self.assertTrue(hasattr(message, 'message_id'))
        self.assertTrue(hasattr(message, 'gmail_message_id'))
        self.assertTrue(hasattr(message, 'gmail_thread_id'))
        self.assertTrue(hasattr(message, 'files'))
        self.assertTrue(hasattr(message, 'subject'))
        self.assertTrue(hasattr(message, 'folders'))
        self.assertTrue(hasattr(message, 'sources'))

    def test_constructor_creates_message_object_with_all_attributes_in_keys_list_for_lite_api(self):
        mock_parent = Mock()
        mock_parent.api_version = "lite"

        message = Message(mock_parent, {"message_id": "fake_message_id"})

        self.assertTrue(hasattr(message, "sent_at"))
        self.assertTrue(hasattr(message, "addresses"))
        self.assertTrue(hasattr(message, "subject"))
        self.assertTrue(hasattr(message, "email_message_id"))
        self.assertTrue(hasattr(message, "message_id"))
        self.assertTrue(hasattr(message, "list_headers"))
        self.assertTrue(hasattr(message, "in_reply_to"))
        self.assertTrue(hasattr(message, "references"))
        self.assertTrue(hasattr(message, "attachments"))
        self.assertTrue(hasattr(message, "bodies"))
        self.assertTrue(hasattr(message, "received_headers"))
        self.assertTrue(hasattr(message, "folders"))
        self.assertTrue(hasattr(message, "resource_url"))
        self.assertTrue(hasattr(message, "person_info"))

    @patch("contextio.lib.resources.base_resource.BaseResource.post")
    def test_post_sends_params_and_returns_True(self, mock_post):
        params = {
            "flag_answered": 0,
            "flag_draft": 0,
            "dst_folder": "foo",
            "dst_source": "bar",
            "move": 0,
            "flag_seen": 0,
            "flag_flagged": 0,
            "flag_deleted": 0
        }
        all_args = ['flag_answered', 'move', 'dst_folder', 'dst_source', 'flag_seen',
            'flag_flagged', 'flag_deleted', 'flag_draft']

        self.message.post(**params)

        mock_post.assert_called_with(
            all_args=all_args, params=params, required_args=['dst_folder'], return_bool=True)

    @patch("contextio.lib.resources.base_resource.BaseResource.post")
    def test_post_is_called_with_correct_args(self, mock_post):
        mock_post.return_value = {"success": True}
        req_args = ['dst_folder', ]
        all_args = [
            'flag_answered', 'move', 'dst_folder', 'dst_source', 'flag_seen',
            'flag_flagged', 'flag_deleted', 'flag_draft'
        ]

        self.message.post(return_bool=False, dst_folder="foobar")

        mock_post.assert_called_with(return_bool=False, params={"dst_folder": "foobar"}, all_args=all_args, required_args=req_args)

    @patch("contextio.lib.resources.base_resource.BaseResource._request_uri")
    def test_delete_returns_boolean(self, mock_request):
        deleted_message = self.message.delete()

        self.assertEqual(True, deleted_message)

    @patch("contextio.lib.resources.base_resource.BaseResource._request_uri")
    def test_get_body_returns_list_of_dictionaries(self, mock_request):
        mock_request.return_value = [{"foo": "bar"}]

        response = self.message.get_body(type="foobar")

        mock_request.assert_called_with("body", params={"type": "foobar"})
        self.assertEqual(1, len(response))
        self.assertEqual([{"foo": "bar"}], response)

    @patch("contextio.lib.resources.base_resource.BaseResource._request_uri")
    def test_get_flags_returns_a_dictionary(self, mock_request):
        mock_request.return_value = {"foo": "bar"}

        response = self.message.get_flags()

        mock_request.assert_called_with("flags")
        self.assertEqual({"foo": "bar"}, self.message.flags)
        self.assertEqual({"foo": "bar"}, response)

    @patch("contextio.lib.resources.base_resource.BaseResource._request_uri")
    def test_post_flag_calls_request_uri_with_correct_args(self, mock_request):
        params = {"seen":1, "answered":1, "flagged":1, "deleted":0, "draft":1}
        mock_request.return_value = {"success": True, "flags": ["foo", "bar"]}

        response = self.message.post_flag(**params)

        mock_request.assert_called_with("flags", method="POST", params=params)
        self.assertEqual(["foo", "bar"], self.message.flags)
        self.assertTrue(response)

    @patch("contextio.lib.resources.base_resource.BaseResource._request_uri")
    def test_get_folders_returns_a_list_of_dictionaries(self, mock_request):
        mock_request.return_value = [{"foo": "bar"}]

        response = self.message.get_folders()

        mock_request.assert_called_with("folders")
        self.assertEqual([{"foo": "bar"}], self.message.folders)
        self.assertEqual([{"foo": "bar"}], response)

    @patch("contextio.lib.resources.base_resource.BaseResource.post")
    def test_post_folder_calls_request_uri_with_correct_args(self, mock_request):
        params = {"add": "catpants", "remove": "dogpants"}
        mock_request.return_value = {"success": True}

        response = self.message.post_folder(**params)

        mock_request.assert_called_with("folders", params=params)

        self.assertTrue(response)

    @patch("contextio.lib.resources.base_resource.BaseResource._request_uri")
    def test_put_folders_calls_request_uri_with_correct_args(self, mock_request):
        body = "catpants"
        mock_request.return_value = {"success": True, "flags": ["foo", "bar"]}

        response = self.message.put_folders(body)

        mock_request.assert_called_with("folders", method="PUT", body=body)
        self.assertEqual("catpants", self.message.folders)
        self.assertTrue(response)

    @patch("contextio.lib.resources.base_resource.BaseResource._request_uri")
    def test_get_headers_returns_a_dictionary(self, mock_request):
        mock_request.return_value = {"foo": "bar"}

        response = self.message.get_headers(raw=1)

        mock_request.assert_called_with("headers", params={"raw": 1})
        self.assertEqual({"foo": "bar"}, self.message.headers)
        self.assertEqual({"foo": "bar"}, response)

    @patch("contextio.lib.resources.base_resource.BaseResource._request_uri")
    def test_get_source_returns_a_string(self, mock_request):
        mock_request.return_value = "catpants"

        response = self.message.get_source()

        mock_request.assert_called_with("source")
        self.assertEqual("catpants", self.message.source)
        self.assertEqual("catpants", response)

    @patch("contextio.lib.resources.base_resource.BaseResource._request_uri")
    def test_get_thread_returns_response_body_object_when_thread_id_exists(self, mock_request):
        mock_request.return_value = "catpants"

        response = self.message.get_source()

        mock_request.assert_called_with("source")
        self.assertEqual("catpants", self.message.source)
        self.assertEqual("catpants", response)

    @patch("contextio.lib.resources.base_resource.BaseResource._request_uri")
    def test_get_thread_returns_a_Thread_object_when_thread_id_exists(self, mock_request):
        mock_request.return_value = {
            "messages": [{"message_id": "foo","gmail_thread_id": "foobar"}]
        }

        params = {
            "include_body": 1,
            "include_headers": 1,
            "include_flags": 1,
            "body_type": "foobar",
            "limit": 1,
            "offset": 1
        }

        response = self.message.get_thread(**params)

        mock_request.assert_called_with("thread", params=params)
        self.assertEqual(response, self.message.thread)
        self.assertIsInstance(response, Thread)

    @patch("contextio.lib.resources.base_resource.BaseResource._request_uri")
    def test_get_thread_returns_response_body_when_thread_id_does_not_exist(self, mock_request):
        mock_request.return_value = {"foo": "bar"}
        params = {
            "include_body": 1,
            "include_headers": 1,
            "include_flags": 1,
            "body_type": "foobar",
            "limit": 1,
            "offset": 1
        }
        response = self.message.get_thread(**params)

        mock_request.assert_called_with("thread", params=params)
        self.assertEqual({"foo": "bar"}, self.message.thread)
        self.assertEqual({"foo": "bar"}, response)

    @patch("contextio.lib.resources.base_resource.BaseResource._request_uri")
    def test_get_thread_sets_thread_subject_if_message_has_subject(self, mock_request):
        mock_request.return_value = {

                "messages": [{"message_id": "foo", "gmail_thread_id": "foobar"}
            ]

        }
        params = {
            "include_body": 1,
            "include_headers": 1,
            "include_flags": 1,
            "body_type": "foobar",
            "limit": 1,
            "offset": 1

        }

        message = Message(Mock(spec=[]), {"message_id": "fake_message_id", "subject": "catpants"})
        message.get_thread(**params)

        self.assertEqual("catpants", message.thread.subject)