xref: /aosp_15_r20/external/autotest/site_utils/pubsub_utils_unittest.py (revision 9c5db1993ded3edbeafc8092d69fe5de2ee02df7)
1*9c5db199SXin Li#!/usr/bin/env python3
2*9c5db199SXin Li# Copyright 2016 The Chromium OS Authors. All rights reserved.
3*9c5db199SXin Li# Use of this source code is governed by a BSD-style license that can be
4*9c5db199SXin Li# found in the LICENSE file.
5*9c5db199SXin Li
6*9c5db199SXin Li"""Unit test for pubsub_utils.py"""
7*9c5db199SXin Li
8*9c5db199SXin Lifrom __future__ import print_function
9*9c5db199SXin Liimport os
10*9c5db199SXin Liimport unittest
11*9c5db199SXin Lifrom unittest.mock import patch
12*9c5db199SXin Lifrom unittest.mock import MagicMock
13*9c5db199SXin Li
14*9c5db199SXin Liimport common
15*9c5db199SXin Li
16*9c5db199SXin Li# TODO(crbug.com/1050892): The unittests rely on apiclient in chromite.
17*9c5db199SXin Liimport autotest_lib.utils.frozen_chromite  # pylint: disable=unused-import
18*9c5db199SXin Li
19*9c5db199SXin Lifrom apiclient import discovery
20*9c5db199SXin Lifrom oauth2client.client import ApplicationDefaultCredentialsError
21*9c5db199SXin Lifrom oauth2client.client import GoogleCredentials
22*9c5db199SXin Lifrom googleapiclient.errors import UnknownApiNameOrVersion
23*9c5db199SXin Li
24*9c5db199SXin Lifrom autotest_lib.site_utils import pubsub_utils
25*9c5db199SXin Li
26*9c5db199SXin Li_TEST_CLOUD_SERVICE_ACCOUNT_FILE = '/tmp/test-credential'
27*9c5db199SXin Li
28*9c5db199SXin Li
29*9c5db199SXin Liclass MockedPubSub(object):
30*9c5db199SXin Li    """A mocked PubSub handle."""
31*9c5db199SXin Li    def __init__(self, test, topic, msg, retry, ret_val=None,
32*9c5db199SXin Li                 raise_except=False):
33*9c5db199SXin Li        self.test = test
34*9c5db199SXin Li        self.topic = topic
35*9c5db199SXin Li        self.msg = msg
36*9c5db199SXin Li        self.retry = retry
37*9c5db199SXin Li        self.ret_val = ret_val
38*9c5db199SXin Li        self.raise_except = raise_except
39*9c5db199SXin Li
40*9c5db199SXin Li    def projects(self):
41*9c5db199SXin Li        """Mocked PubSub projects."""
42*9c5db199SXin Li        return self
43*9c5db199SXin Li
44*9c5db199SXin Li    def topics(self):
45*9c5db199SXin Li        """Mocked PubSub topics."""
46*9c5db199SXin Li        return self
47*9c5db199SXin Li
48*9c5db199SXin Li    def publish(self, topic, body):
49*9c5db199SXin Li        """Mocked PubSub publish method.
50*9c5db199SXin Li
51*9c5db199SXin Li        @param topic: PubSub topic string.
52*9c5db199SXin Li        @param body: PubSub notification body.
53*9c5db199SXin Li        """
54*9c5db199SXin Li        self.test.assertEquals(self.topic, topic)
55*9c5db199SXin Li        self.test.assertEquals(self.msg, body['messages'][0])
56*9c5db199SXin Li        return self
57*9c5db199SXin Li
58*9c5db199SXin Li    def execute(self, num_retries):
59*9c5db199SXin Li        """Mocked PubSub execute method.
60*9c5db199SXin Li
61*9c5db199SXin Li        @param num_retries: Number of retries.
62*9c5db199SXin Li        """
63*9c5db199SXin Li        self.test.assertEquals(self.retry, num_retries)
64*9c5db199SXin Li        if self.raise_except:
65*9c5db199SXin Li            raise Exception()
66*9c5db199SXin Li        return self.ret_val
67*9c5db199SXin Li
68*9c5db199SXin Li
69*9c5db199SXin Lidef _create_sample_message():
70*9c5db199SXin Li    """Creates a sample pubsub message."""
71*9c5db199SXin Li    msg_payload = {'data': 'sample data'}
72*9c5db199SXin Li    msg_attributes = {}
73*9c5db199SXin Li    msg_attributes['var'] = 'value'
74*9c5db199SXin Li    msg_payload['attributes'] = msg_attributes
75*9c5db199SXin Li
76*9c5db199SXin Li    return msg_payload
77*9c5db199SXin Li
78*9c5db199SXin Li
79*9c5db199SXin Liclass PubSubTests(unittest.TestCase):
80*9c5db199SXin Li    """Tests for pubsub related functios."""
81*9c5db199SXin Li
82*9c5db199SXin Li    def setUp(self):
83*9c5db199SXin Li        patcher = patch.object(os.path, 'isfile')
84*9c5db199SXin Li        self.isfile_mock = patcher.start()
85*9c5db199SXin Li        self.addCleanup(patcher.stop)
86*9c5db199SXin Li        creds_patcher = patch.object(GoogleCredentials, 'from_stream')
87*9c5db199SXin Li        self.creds_mock = creds_patcher.start()
88*9c5db199SXin Li        self.addCleanup(creds_patcher.stop)
89*9c5db199SXin Li
90*9c5db199SXin Li    def test_pubsub_with_no_service_account(self):
91*9c5db199SXin Li        """Test getting the pubsub service"""
92*9c5db199SXin Li        with self.assertRaises(pubsub_utils.PubSubException):
93*9c5db199SXin Li            pubsub_utils.PubSubClient()
94*9c5db199SXin Li
95*9c5db199SXin Li    def test_pubsub_with_non_existing_service_account(self):
96*9c5db199SXin Li        """Test getting the pubsub service"""
97*9c5db199SXin Li        self.isfile_mock.return_value = False
98*9c5db199SXin Li        with self.assertRaises(pubsub_utils.PubSubException):
99*9c5db199SXin Li            pubsub_utils.PubSubClient(_TEST_CLOUD_SERVICE_ACCOUNT_FILE)
100*9c5db199SXin Li        self.isfile_mock.assert_called_with(_TEST_CLOUD_SERVICE_ACCOUNT_FILE)
101*9c5db199SXin Li
102*9c5db199SXin Li    def test_pubsub_with_corrupted_service_account(self):
103*9c5db199SXin Li        """Test pubsub with corrupted service account."""
104*9c5db199SXin Li
105*9c5db199SXin Li        self.isfile_mock.return_value = True
106*9c5db199SXin Li        self.creds_mock.side_effect = ApplicationDefaultCredentialsError
107*9c5db199SXin Li
108*9c5db199SXin Li        with self.assertRaises(pubsub_utils.PubSubException):
109*9c5db199SXin Li            pubsub_utils.PubSubClient(_TEST_CLOUD_SERVICE_ACCOUNT_FILE)
110*9c5db199SXin Li
111*9c5db199SXin Li        self.creds_mock.assert_called_with(_TEST_CLOUD_SERVICE_ACCOUNT_FILE)
112*9c5db199SXin Li        self.isfile_mock.assert_called_with(_TEST_CLOUD_SERVICE_ACCOUNT_FILE)
113*9c5db199SXin Li
114*9c5db199SXin Li    def test_pubsub_with_invalid_service_account(self):
115*9c5db199SXin Li        """Test pubsubwith invalid service account."""
116*9c5db199SXin Li        self.isfile_mock.return_value = True
117*9c5db199SXin Li        credentials = MagicMock(GoogleCredentials)
118*9c5db199SXin Li        self.creds_mock.return_value = credentials
119*9c5db199SXin Li
120*9c5db199SXin Li        credentials.create_scoped_required.return_value = True
121*9c5db199SXin Li        credentials.create_scoped.return_value = credentials
122*9c5db199SXin Li
123*9c5db199SXin Li        with patch.object(discovery, 'build') as discovery_mock:
124*9c5db199SXin Li            discovery_mock.side_effect = UnknownApiNameOrVersion
125*9c5db199SXin Li
126*9c5db199SXin Li            with self.assertRaises(pubsub_utils.PubSubException):
127*9c5db199SXin Li                msg = _create_sample_message()
128*9c5db199SXin Li                pubsub_client = pubsub_utils.PubSubClient(
129*9c5db199SXin Li                        _TEST_CLOUD_SERVICE_ACCOUNT_FILE)
130*9c5db199SXin Li                pubsub_client.publish_notifications('test_topic', [msg])
131*9c5db199SXin Li
132*9c5db199SXin Li            credentials.create_scoped.assert_called_with(
133*9c5db199SXin Li                    pubsub_utils.PUBSUB_SCOPES)
134*9c5db199SXin Li            discovery_mock.assert_called_with(pubsub_utils.PUBSUB_SERVICE_NAME,
135*9c5db199SXin Li                                              pubsub_utils.PUBSUB_VERSION,
136*9c5db199SXin Li                                              credentials=credentials)
137*9c5db199SXin Li        self.creds_mock.assert_called_with(_TEST_CLOUD_SERVICE_ACCOUNT_FILE)
138*9c5db199SXin Li        self.isfile_mock.assert_called_with(_TEST_CLOUD_SERVICE_ACCOUNT_FILE)
139*9c5db199SXin Li
140*9c5db199SXin Li    def test_publish_notifications(self):
141*9c5db199SXin Li        """Test getting the pubsub service"""
142*9c5db199SXin Li        self.isfile_mock.return_value = True
143*9c5db199SXin Li        credentials = MagicMock(GoogleCredentials)
144*9c5db199SXin Li        self.creds_mock.return_value = credentials
145*9c5db199SXin Li
146*9c5db199SXin Li        credentials.create_scoped_required.return_value = True
147*9c5db199SXin Li        credentials.create_scoped.return_value = credentials
148*9c5db199SXin Li
149*9c5db199SXin Li        with patch.object(discovery, 'build') as discovery_mock:
150*9c5db199SXin Li            msg = _create_sample_message()
151*9c5db199SXin Li            discovery_mock.return_value = MockedPubSub(
152*9c5db199SXin Li                    self,
153*9c5db199SXin Li                    'test_topic',
154*9c5db199SXin Li                    msg,
155*9c5db199SXin Li                    pubsub_utils.DEFAULT_PUBSUB_NUM_RETRIES,
156*9c5db199SXin Li                    # use tuple ('123') instead of list just for easy to
157*9c5db199SXin Li                    # write the test.
158*9c5db199SXin Li                    ret_val={'messageIds': ('123')})
159*9c5db199SXin Li
160*9c5db199SXin Li            pubsub_client = pubsub_utils.PubSubClient(
161*9c5db199SXin Li                    _TEST_CLOUD_SERVICE_ACCOUNT_FILE)
162*9c5db199SXin Li            msg_ids = pubsub_client.publish_notifications('test_topic', [msg])
163*9c5db199SXin Li            self.assertEquals(('123'), msg_ids)
164*9c5db199SXin Li
165*9c5db199SXin Li            credentials.create_scoped.assert_called_with(
166*9c5db199SXin Li                    pubsub_utils.PUBSUB_SCOPES)
167*9c5db199SXin Li            discovery_mock.assert_called_with(pubsub_utils.PUBSUB_SERVICE_NAME,
168*9c5db199SXin Li                                              pubsub_utils.PUBSUB_VERSION,
169*9c5db199SXin Li                                              credentials=credentials)
170*9c5db199SXin Li        self.creds_mock.assert_called_with(_TEST_CLOUD_SERVICE_ACCOUNT_FILE)
171*9c5db199SXin Li        self.isfile_mock.assert_called_with(_TEST_CLOUD_SERVICE_ACCOUNT_FILE)
172*9c5db199SXin Li
173*9c5db199SXin Li
174*9c5db199SXin Liif __name__ == '__main__':
175*9c5db199SXin Li    unittest.main()
176