opentf-toolkit-nightly 0.55.0.dev938__py3-none-any.whl → 0.55.0.dev950__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -585,7 +585,10 @@ def is_uuid(uuid: str) -> bool:
585
585
 
586
586
 
587
587
  def make_status_response(
588
- reason: str, message: str, details: Optional[Dict[str, Any]] = None
588
+ reason: str,
589
+ message: str,
590
+ details: Optional[Dict[str, Any]] = None,
591
+ silent: bool = False,
589
592
  ) -> Response:
590
593
  """Return a new status response object.
591
594
 
@@ -597,6 +600,7 @@ def make_status_response(
597
600
  # Optional parameters:
598
601
 
599
602
  - details: a dictionary or None (None by default)
603
+ - silent: a boolean (False by default)
600
604
 
601
605
  # Returned value
602
606
 
@@ -613,10 +617,11 @@ def make_status_response(
613
617
  - code: an integer (derived from `reason`)
614
618
  """
615
619
  code = REASON_STATUS[reason]
616
- if code // 100 == 4:
617
- logging.warning(message)
618
- elif code // 100 == 5:
619
- logging.error(message)
620
+ if not silent:
621
+ if code // 100 == 4:
622
+ logging.warning(message)
623
+ elif code // 100 == 5:
624
+ logging.error(message)
620
625
  return make_response(
621
626
  {
622
627
  'kind': 'Status',
@@ -14,7 +14,8 @@
14
14
 
15
15
  """Test case metadata retrieval helpers"""
16
16
 
17
- from typing import Any, Dict, Generator, List, Optional, Set
17
+ from typing import Any, Dict, List, Optional
18
+
18
19
  from collections import defaultdict
19
20
 
20
21
  from opentf.commons.expressions import evaluate_bool
@@ -68,17 +69,14 @@ def _as_list(what) -> List[str]:
68
69
  ## Datasource: Testcases
69
70
 
70
71
 
71
- def in_scope(expr: str, contexts: Dict[str, Any], scopes_errors: Set[str]) -> bool:
72
- """Safely evaluate quality gate scope."""
72
+ def in_scope(expr: str, contexts: Dict[str, Any]) -> bool:
73
+ """Safely evaluate datasource scope."""
73
74
  try:
74
75
  return evaluate_bool(expr, contexts)
75
76
  except ValueError as err:
76
- msg = f'Invalid conditional {expr}: {err}.'
77
- scopes_errors.add(msg)
77
+ raise ValueError(f'Invalid conditional {expr}: {err}.')
78
78
  except KeyError as err:
79
- msg = f'Nonexisting context entry in expression {expr}: {err}.'
80
- scopes_errors.add(msg)
81
- return False
79
+ raise ValueError(f'Nonexisting context entry in expression {expr}: {err}.')
82
80
 
83
81
 
84
82
  def get_testresults(events: List[Dict[str, Any]]) -> List[Dict[str, Any]]:
@@ -300,10 +298,9 @@ def _get_timestamp(
300
298
 
301
299
  def _get_testresult_timestamps(
302
300
  events: List[Dict[str, Any]],
303
- testresults: List[Dict[str, Any]],
304
- testcase_metadata: Dict[str, Any],
301
+ testcases: Dict[str, Any],
305
302
  ):
306
- """Set timestamp for each testcase in testcase_metadata.
303
+ """Set timestamp for each testcase in testcases.
307
304
 
308
305
  The timestamp is the one of the originating ProviderResult.
309
306
  """
@@ -314,19 +311,42 @@ def _get_testresult_timestamps(
314
311
  }
315
312
 
316
313
  origins_results = defaultdict(list)
317
- for item in testresults:
318
- for result in item['spec']['testResults']:
319
- origins_results[result['attachment_origin']].append(result['id'])
314
+ for uuid, data in testcases.items():
315
+ origins_results[data['execution']].append(uuid)
320
316
 
321
317
  for event in filter(lambda event: event['kind'] == 'ExecutionResult', events):
322
318
  for attachment in event['metadata'].get('attachments', {}).values():
323
319
  if attachment['uuid'] in origins_results:
324
320
  timestamp = _get_timestamp(event, providerid_creationtimestamps)
325
321
  for result_id in origins_results[attachment['uuid']]:
326
- testcase_metadata[result_id]['timestamp'] = timestamp
322
+ testcases[result_id]['timestamp'] = timestamp
327
323
 
328
324
 
329
- def get_testcases(events: List[Dict[str, Any]]) -> Dict[str, Dict[str, Any]]:
325
+ def _make_testcase_from_testresult(
326
+ item: Dict[str, Any], execution_id: str, labels: Dict[str, Any], scope: str
327
+ ) -> Dict[str, Any]:
328
+ testcase = {
329
+ 'name': item['name'],
330
+ 'status': item['status'],
331
+ 'duration': item.get('duration', 0),
332
+ 'execution': execution_id,
333
+ 'test': labels.copy(),
334
+ }
335
+ testcase['test']['status'] = item['status']
336
+ if item['status'] in FAILURE_STATUSES:
337
+ for key in DETAILS_KEYS:
338
+ if item.get(key):
339
+ testcase[key] = item[key]
340
+ if item.get('errorsList'):
341
+ testcase['errorsList'] = item['errorsList']
342
+ if not in_scope(scope, testcase):
343
+ return {}
344
+ return testcase
345
+
346
+
347
+ def get_testcases(
348
+ events: List[Dict[str, Any]], scope: str = 'true'
349
+ ) -> Dict[str, Dict[str, Any]]:
330
350
  """Extract metadata for each test result.
331
351
 
332
352
  Test results are Notification events with a `.spec.testResults`
@@ -344,12 +364,13 @@ def get_testcases(events: List[Dict[str, Any]]) -> Dict[str, Dict[str, Any]]:
344
364
  - name: a string, the test case name
345
365
  - status: a string, the test case status
346
366
  - duration: a string, the test case execution time in ms
367
+ - execution: a string, the test case execution (=related attachment) uuid
347
368
  - timestamp: a string, provider creation timestamp
348
369
  - test: a dictionary, the test case metadata
349
370
  - failureDetails|errorDetails|warningDetails: a dictionary with test
350
371
  case failure details
351
- - errorsList: a Robot Framework specific list with execution general
352
- errors
372
+ - errorsList: a provider generated attachment specific list with execution general
373
+ errors (currently for Robot Framework only)
353
374
 
354
375
  `testcases` is a dictionary of entries like:
355
376
 
@@ -358,7 +379,8 @@ def get_testcases(events: List[Dict[str, Any]]) -> Dict[str, Dict[str, Any]]:
358
379
  "name": "<<<[Test suite#]Test case name>>>",
359
380
  "status": "<<<SUCCESS|FAILURE|ERROR|SKIPPED>>>",
360
381
  "duration": "<<<test execution time in ms>>>",
361
- "timestamp": "<<<provider creation timestamp>>>"
382
+ "timestamp": "<<<provider creation timestamp>>>",
383
+ "execution": "<<<execution ID>>>",
362
384
  "test": {
363
385
  "job": "<<<job name>>>",
364
386
  "uses": "<<<provider function>>>",
@@ -384,39 +406,32 @@ def get_testcases(events: List[Dict[str, Any]]) -> Dict[str, Dict[str, Any]]:
384
406
  # Raised exceptions
385
407
 
386
408
  A _ValueError_ exception is raised if there were no test results in
387
- `events`.
409
+ `events` or some scope errors occured retrieving test results.
388
410
  """
389
- testcases = {}
390
- results = False
391
411
  if _uses_inception(events):
392
412
  testresults = _get_inception_testresults(events)
393
413
  else:
394
414
  testresults = get_testresults(events)
395
415
 
416
+ if not testresults:
417
+ raise ValueError('No test results in events.')
418
+
419
+ testcases = {}
396
420
  for testresult in testresults:
397
- results = True
398
- labels = _get_testresult_labels(
399
- testresult['metadata']['attachment_origin'][0], events
400
- )
421
+ execution_id = testresult['metadata']['attachment_origin'][0]
422
+ labels = _get_testresult_labels(execution_id, events)
401
423
  if not labels:
402
424
  continue
403
- for testcase in testresult['spec']['testResults']:
404
- testcases[testcase['id']] = {
405
- 'name': testcase['name'],
406
- 'status': testcase['status'].lower(),
407
- 'duration': testcase.get('duration', 0),
408
- 'test': labels.copy(),
409
- }
410
- testcases[testcase['id']]['test']['status'] = testcase['status']
411
- data = {}
412
- if testcase['status'] in FAILURE_STATUSES:
413
- data = {key: testcase[key] for key in DETAILS_KEYS if testcase.get(key)}
414
- if testcase.get('errorsList'):
415
- data['errorsList'] = testcase['errorsList']
416
- testcases[testcase['id']].update(data)
417
- if not results:
418
- raise ValueError('No test results in events.')
419
- _get_testresult_timestamps(events, testresults, testcases)
425
+ for item in testresult['spec']['testResults']:
426
+ if testcase := _make_testcase_from_testresult(
427
+ item, execution_id, labels, scope
428
+ ):
429
+ testcases[item['id']] = testcase
430
+ if not testcases:
431
+ raise ValueError(f'No test cases matching scope `{scope}`.')
432
+
433
+ _get_testresult_timestamps(events, testcases)
434
+
420
435
  return testcases
421
436
 
422
437
 
@@ -424,9 +439,7 @@ def get_testcases(events: List[Dict[str, Any]]) -> Dict[str, Dict[str, Any]]:
424
439
  ## Datasource: Tags
425
440
 
426
441
 
427
- def get_tags(
428
- events: List[Dict[str, Any]], testcase_metadata: Optional[Dict[str, Any]] = None
429
- ) -> Dict[str, Any]:
442
+ def get_tags(events: List[Dict[str, Any]], scope: str = 'true') -> Dict[str, Any]:
430
443
  """Extract metadata for each execution environment tag.
431
444
 
432
445
  # Required parameters:
@@ -452,7 +465,7 @@ def get_tags(
452
465
  ```
453
466
  """
454
467
  try:
455
- testcase_metadata = testcase_metadata or get_testcases(events)
468
+ testcase_metadata = get_testcases(events, scope)
456
469
  except ValueError as err:
457
470
  raise ValueError(str(err) + ' Cannot extract metadata for tags.')
458
471
  tags = {}
@@ -471,7 +484,7 @@ def get_tags(
471
484
  ## Datasource: Jobs
472
485
 
473
486
 
474
- def _evaluate_test_results(jobs_testcases: Dict[str, Any]) -> Dict[str, Any]:
487
+ def _evaluate_test_results(jobs_testcases: Dict[str, Any]) -> Dict[str, Dict[str, int]]:
475
488
  """Summarize job testcases.
476
489
 
477
490
  # Returned value
@@ -494,9 +507,7 @@ def _evaluate_test_results(jobs_testcases: Dict[str, Any]) -> Dict[str, Any]:
494
507
  return summaries
495
508
 
496
509
 
497
- def get_jobs(
498
- events: List[Dict[str, Any]], testcase_metadata: Optional[Dict[str, Any]] = None
499
- ) -> Dict[str, Any]:
510
+ def get_jobs(events: List[Dict[str, Any]], scope: str = 'true') -> Dict[str, Any]:
500
511
  """Extract metadata for each job.
501
512
 
502
513
  # Required parameters:
@@ -534,7 +545,7 @@ def get_jobs(
534
545
  ```
535
546
  """
536
547
  try:
537
- testcase_metadata = testcase_metadata or get_testcases(events)
548
+ testcase_metadata = get_testcases(events, scope)
538
549
  except ValueError as err:
539
550
  raise ValueError(str(err) + ' Cannot extract metadata for jobs.')
540
551
  jobs_testcases = _get_sorted_testcases(testcase_metadata, ['test', 'job'])
@@ -555,7 +566,7 @@ def get_jobs(
555
566
  job['metadata']['name'],
556
567
  {
557
568
  'runs-on': job['runs-on'],
558
- 'counts': {k.lower(): v for k, v in results[job_name].items()},
569
+ 'counts': results[job_name],
559
570
  'variables': {
560
571
  name: value for name, value in job.get('variables', {}).items()
561
572
  },
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: opentf-toolkit-nightly
3
- Version: 0.55.0.dev938
3
+ Version: 0.55.0.dev950
4
4
  Summary: OpenTestFactory Orchestrator Toolkit
5
5
  Home-page: https://gitlab.com/henixdevelopment/open-source/opentestfactory/python-toolkit
6
6
  Author: Martin Lafaix
@@ -1,7 +1,7 @@
1
- opentf/commons/__init__.py,sha256=ITzg1zfZgA5-4wvmJfjLN94_Z06HeMl0szd6dalrrKY,21839
1
+ opentf/commons/__init__.py,sha256=KRY8ShQw_0ZZ0oEOiKi4-xnWofE_QsFjEe1T8wUxJ-w,21952
2
2
  opentf/commons/auth.py,sha256=bM2Z3kxm2Wku1lKXaRAIg37LHvXWAXIZIqjplDfN2P8,15899
3
3
  opentf/commons/config.py,sha256=GmvInVnUsXIwlNfgTQeQ_pPs97GeGTGn2S2QZEFwss8,7828
4
- opentf/commons/datasources.py,sha256=OkPp0allFp-G9qBkU4Fxc6aGiytkMO3Xr3KLl4E5zWM,18234
4
+ opentf/commons/datasources.py,sha256=EIssc6CyC8s5FYVZtdEo7IpCQxX92mFntYWmsoO62go,18412
5
5
  opentf/commons/expressions.py,sha256=A68F27Our8oVVphUrRvB5haSlqj2YCrH2OxHPNLBio4,19251
6
6
  opentf/commons/pubsub.py,sha256=7khxAHVZiwJRcwIBJ6MPR-f3xY9144-2eNLROwq5F-4,5894
7
7
  opentf/commons/schemas.py,sha256=lokZCU-wmsIkzVA-TVENtC7Io_GmYxrP-FQaOOowg4s,4044
@@ -48,8 +48,8 @@ opentf/scripts/startup.py,sha256=Da2zo93pBWbdRmj-wgekgLcF94rpNc3ZkbvR8R0w8XY,212
48
48
  opentf/toolkit/__init__.py,sha256=g3DiTZlSvvzZWKgM8qU47muLqjQrpWZ6M6PWZ-sBsvQ,19610
49
49
  opentf/toolkit/channels.py,sha256=Cng3b4LUsxvCHUbp_skys9CFcKZMfcKhA_ODg_EAlIE,17156
50
50
  opentf/toolkit/core.py,sha256=L1fT4YzwZjqE7PUXhJL6jSVQge3ohBQv5UBb9DAC6oo,9320
51
- opentf_toolkit_nightly-0.55.0.dev938.dist-info/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
52
- opentf_toolkit_nightly-0.55.0.dev938.dist-info/METADATA,sha256=1FipCBdX1nal-7wQEMhR1QrdYvJUuPMAGcEPjLAzDco,1945
53
- opentf_toolkit_nightly-0.55.0.dev938.dist-info/WHEEL,sha256=GJ7t_kWBFywbagK5eo9IoUwLW6oyOeTKmQ-9iHFVNxQ,92
54
- opentf_toolkit_nightly-0.55.0.dev938.dist-info/top_level.txt,sha256=_gPuE6GTT6UNXy1DjtmQSfCcZb_qYA2vWmjg7a30AGk,7
55
- opentf_toolkit_nightly-0.55.0.dev938.dist-info/RECORD,,
51
+ opentf_toolkit_nightly-0.55.0.dev950.dist-info/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
52
+ opentf_toolkit_nightly-0.55.0.dev950.dist-info/METADATA,sha256=gT2hqExdA5awFpPmq43fIso434TuFdAQOrSlqz1EsG4,1945
53
+ opentf_toolkit_nightly-0.55.0.dev950.dist-info/WHEEL,sha256=GJ7t_kWBFywbagK5eo9IoUwLW6oyOeTKmQ-9iHFVNxQ,92
54
+ opentf_toolkit_nightly-0.55.0.dev950.dist-info/top_level.txt,sha256=_gPuE6GTT6UNXy1DjtmQSfCcZb_qYA2vWmjg7a30AGk,7
55
+ opentf_toolkit_nightly-0.55.0.dev950.dist-info/RECORD,,