1# Copyright 2016 Google Inc. All Rights Reserved. 2# 3# Licensed under the Apache License, Version 2.0 (the "License"); 4# you may not use this file except in compliance with the License. 5# You may obtain a copy of the License at 6# 7# http://www.apache.org/licenses/LICENSE-2.0 8# 9# Unless required by applicable law or agreed to in writing, software 10# distributed under the License is distributed on an "AS IS" BASIS, 11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12# See the License for the specific language governing permissions and 13# limitations under the License. 14 15from __future__ import absolute_import 16 17import datetime 18import unittest2 19from expects import be_none, expect, equal, raise_error 20 21from google.api.control import messages, metric_value, operation, timestamp 22from google.api.control import MetricKind 23 24_A_FLOAT_VALUE = 1.1 25_REALLY_EARLY = timestamp.to_rfc3339(datetime.datetime(1970, 1, 1, 0, 0, 0)) 26_EARLY = timestamp.to_rfc3339(datetime.datetime(1980, 1, 1, 10, 0, 0)) 27_LATER = timestamp.to_rfc3339(datetime.datetime(1980, 2, 2, 10, 0, 0)) 28_LATER_STILL = timestamp.to_rfc3339(datetime.datetime(1981, 2, 2, 10, 0, 0)) 29 30_TEST_LABELS = { 31 'key1': 'value1', 32 'key2': 'value2', 33} 34 35# in tests, the description field is not currently used, but should be filled 36_TESTS = [ 37 { 38 'description': 'update the start time to that of the earliest', 39 'kinds': None, 40 'initial': messages.Operation( 41 startTime=_EARLY, 42 endTime=_LATER 43 ), 44 'ops': [ 45 messages.Operation( 46 startTime=_REALLY_EARLY, 47 endTime=_LATER 48 ), 49 messages.Operation( 50 startTime=_LATER, 51 endTime=_LATER 52 ), 53 ], 54 'want': messages.Operation(startTime=_REALLY_EARLY, endTime=_LATER) 55 }, 56 { 57 'description': 'update the end time to that of the latest', 58 'kinds': None, 59 'initial': messages.Operation( 60 startTime=_EARLY, 61 endTime=_LATER 62 ), 63 'ops': [ 64 messages.Operation( 65 startTime=_EARLY, 66 endTime=_LATER 67 ), 68 messages.Operation( 69 startTime=_EARLY, 70 endTime=_LATER_STILL 71 ), 72 ], 73 'want': messages.Operation(startTime=_EARLY, endTime=_LATER_STILL) 74 }, 75 { 76 'description': 'combine the log entries', 77 'kinds': None, 78 'initial': messages.Operation( 79 startTime=_EARLY, 80 endTime=_LATER, 81 logEntries=[messages.LogEntry(textPayload='initial')] 82 ), 83 'ops': [ 84 messages.Operation( 85 startTime=_EARLY, 86 endTime=_LATER, 87 logEntries=[messages.LogEntry(textPayload='agg1')] 88 ), 89 messages.Operation( 90 startTime=_EARLY, 91 endTime=_LATER, 92 logEntries=[messages.LogEntry(textPayload='agg2')] 93 ), 94 ], 95 'want': messages.Operation( 96 startTime=_EARLY, 97 endTime=_LATER, 98 logEntries=[ 99 messages.LogEntry(textPayload='initial'), 100 messages.LogEntry(textPayload='agg1'), 101 messages.LogEntry(textPayload='agg2') 102 ] 103 ) 104 }, 105 { 106 'description': 'combines the metric value using the default kind', 107 'kinds': None, 108 'initial': messages.Operation( 109 startTime=_EARLY, 110 endTime=_LATER, 111 metricValueSets = [ 112 messages.MetricValueSet( 113 metricName='some_floats', 114 metricValues=[ 115 metric_value.create( 116 labels=_TEST_LABELS, 117 doubleValue=_A_FLOAT_VALUE, 118 endTime=_EARLY 119 ), 120 ] 121 ), 122 messages.MetricValueSet( 123 metricName='other_floats', 124 metricValues=[ 125 metric_value.create( 126 labels=_TEST_LABELS, 127 doubleValue=_A_FLOAT_VALUE, 128 endTime=_EARLY 129 ), 130 ] 131 ) 132 ] 133 ), 134 'ops': [ 135 messages.Operation( 136 startTime=_EARLY, 137 endTime=_LATER, 138 metricValueSets = [ 139 messages.MetricValueSet( 140 metricName='some_floats', 141 metricValues=[ 142 metric_value.create( 143 labels=_TEST_LABELS, 144 doubleValue=_A_FLOAT_VALUE, 145 endTime=_LATER 146 ), 147 ] 148 ), 149 ] 150 ), 151 messages.Operation( 152 startTime=_EARLY, 153 endTime=_LATER, 154 metricValueSets = [ 155 messages.MetricValueSet( 156 metricName='other_floats', 157 metricValues=[ 158 metric_value.create( 159 labels=_TEST_LABELS, 160 doubleValue=_A_FLOAT_VALUE, 161 endTime=_LATER_STILL 162 ), 163 ] 164 ) 165 ] 166 167 ), 168 ], 169 'want': messages.Operation( 170 startTime=_EARLY, 171 endTime=_LATER, 172 metricValueSets = [ 173 messages.MetricValueSet( 174 metricName='other_floats', 175 metricValues=[ 176 metric_value.create( 177 labels=_TEST_LABELS, 178 doubleValue=_A_FLOAT_VALUE * 2, 179 endTime=_LATER_STILL 180 ), 181 ] 182 ), 183 messages.MetricValueSet( 184 metricName='some_floats', 185 metricValues=[ 186 metric_value.create( 187 labels=_TEST_LABELS, 188 doubleValue=_A_FLOAT_VALUE * 2, 189 endTime=_LATER 190 ), 191 ] 192 ) 193 ] 194 ) 195 }, 196 { 197 'description': 'combines a metric value using a kind that is not DELTA', 198 'kinds': { 'some_floats': MetricKind.GAUGE }, 199 'initial': messages.Operation( 200 startTime=_EARLY, 201 endTime=_LATER, 202 metricValueSets = [ 203 messages.MetricValueSet( 204 metricName='some_floats', 205 metricValues=[ 206 metric_value.create( 207 labels=_TEST_LABELS, 208 doubleValue=_A_FLOAT_VALUE, 209 endTime=_EARLY 210 ), 211 ] 212 ), 213 messages.MetricValueSet( 214 metricName='other_floats', 215 metricValues=[ 216 metric_value.create( 217 labels=_TEST_LABELS, 218 doubleValue=_A_FLOAT_VALUE, 219 endTime=_EARLY 220 ), 221 ] 222 ) 223 ] 224 ), 225 'ops': [ 226 messages.Operation( 227 startTime=_EARLY, 228 endTime=_LATER, 229 metricValueSets = [ 230 messages.MetricValueSet( 231 metricName='some_floats', 232 metricValues=[ 233 metric_value.create( 234 labels=_TEST_LABELS, 235 doubleValue=_A_FLOAT_VALUE, 236 endTime=_LATER 237 ), 238 ] 239 ), 240 ] 241 ), 242 messages.Operation( 243 startTime=_EARLY, 244 endTime=_LATER, 245 metricValueSets = [ 246 messages.MetricValueSet( 247 metricName='other_floats', 248 metricValues=[ 249 metric_value.create( 250 labels=_TEST_LABELS, 251 doubleValue=_A_FLOAT_VALUE, 252 endTime=_LATER_STILL 253 ), 254 ] 255 ) 256 ] 257 258 ), 259 ], 260 'want': messages.Operation( 261 startTime=_EARLY, 262 endTime=_LATER, 263 metricValueSets = [ 264 messages.MetricValueSet( 265 metricName='other_floats', 266 metricValues=[ 267 metric_value.create( 268 labels=_TEST_LABELS, 269 doubleValue=_A_FLOAT_VALUE * 2, 270 endTime=_LATER_STILL 271 ), 272 ] 273 ), 274 messages.MetricValueSet( 275 metricName='some_floats', 276 metricValues=[ 277 metric_value.create( 278 labels=_TEST_LABELS, 279 doubleValue=_A_FLOAT_VALUE, 280 endTime=_LATER 281 ), 282 ] 283 ) 284 ] 285 ) 286 } 287] 288 289class TestOperationAggregation(unittest2.TestCase): 290 291 def test_should_aggregate_as_expected(self): 292 for t in _TESTS: 293 desc = t['description'] 294 initial = t['initial'] 295 want = t['want'] 296 agg = operation.Aggregator(initial, kinds=t['kinds']) 297 for o in t['ops']: 298 agg.add(o) 299 got = agg.as_operation() 300 try: 301 expect(got).to(equal(want)) 302 except AssertionError as e: 303 raise AssertionError('Failed to {0}\n{1}'.format(desc, e)) 304 305 306_INFO_TESTS = [ 307 (operation.Info( 308 referer='a_referer', 309 service_name='a_service_name'), 310 messages.Operation( 311 importance=messages.Operation.ImportanceValueValuesEnum.LOW, 312 startTime=_REALLY_EARLY, 313 endTime=_REALLY_EARLY)), 314 (operation.Info( 315 operation_id='an_op_id', 316 referer='a_referer', 317 service_name='a_service_name'), 318 messages.Operation( 319 importance=messages.Operation.ImportanceValueValuesEnum.LOW, 320 operationId='an_op_id', 321 startTime=_REALLY_EARLY, 322 endTime=_REALLY_EARLY)), 323 (operation.Info( 324 operation_id='an_op_id', 325 operation_name='an_op_name', 326 referer='a_referer', 327 service_name='a_service_name'), 328 messages.Operation( 329 importance=messages.Operation.ImportanceValueValuesEnum.LOW, 330 operationId='an_op_id', 331 operationName='an_op_name', 332 startTime=_REALLY_EARLY, 333 endTime=_REALLY_EARLY)), 334 (operation.Info( 335 api_key='an_api_key', 336 api_key_valid=True, 337 operation_id='an_op_id', 338 operation_name='an_op_name', 339 referer='a_referer', 340 service_name='a_service_name'), 341 messages.Operation( 342 importance=messages.Operation.ImportanceValueValuesEnum.LOW, 343 consumerId='api_key:an_api_key', 344 operationId='an_op_id', 345 operationName='an_op_name', 346 startTime=_REALLY_EARLY, 347 endTime=_REALLY_EARLY)), 348 (operation.Info( 349 api_key='an_api_key', 350 api_key_valid=False, 351 consumer_project_id='project_id', 352 operation_id='an_op_id', 353 operation_name='an_op_name', 354 referer='a_referer', 355 service_name='a_service_name'), 356 messages.Operation( 357 importance=messages.Operation.ImportanceValueValuesEnum.LOW, 358 consumerId='project:project_id', 359 operationId='an_op_id', 360 operationName='an_op_name', 361 startTime=_REALLY_EARLY, 362 endTime=_REALLY_EARLY)), 363] 364 365class TestInfo(unittest2.TestCase): 366 367 def test_should_construct_with_no_args(self): 368 expect(operation.Info()).not_to(be_none) 369 370 def test_should_convert_to_operation_ok(self): 371 timer = _DateTimeTimer() 372 for info, want in _INFO_TESTS: 373 expect(info.as_operation(timer=timer)).to(equal(want)) 374 375 376class _DateTimeTimer(object): 377 def __init__(self, auto=False): 378 self.auto = auto 379 self.time = datetime.datetime.utcfromtimestamp(0) 380 381 def __call__(self): 382 if self.auto: 383 self.tick() 384 return self.time 385 386 def tick(self): 387 self.time += datetime.timedelta(seconds=1) 388