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