From e8a911d4e332981379c8c1ff6f72997e2d68e26f Mon Sep 17 00:00:00 2001 From: Rhys Short Date: Mon, 8 Jul 2024 12:20:46 +0100 Subject: [PATCH] Re-run store_derived_evidence fetchers Ensure store_derived_evidence fetchers are re-run if dependant evidence is not yet available, this is achieved by get_evidence_dependency, rather than get_evidence_by_path. --- compliance/evidence.py | 14 +++++-- test/t_compliance/t_evidence/test_evidence.py | 40 +++++++++++++++++++ 2 files changed, 50 insertions(+), 4 deletions(-) create mode 100644 test/t_compliance/t_evidence/test_evidence.py diff --git a/compliance/evidence.py b/compliance/evidence.py index 3980729a..6b84780c 100644 --- a/compliance/evidence.py +++ b/compliance/evidence.py @@ -863,8 +863,11 @@ def store_derived_evidence(evidences, target): For example, ``src/evidence.json``. """ - def decorator(f): - @wraps(f) # required for preserving the function context + def decorator(fn): + # generate the information for re-running + fetcher = f"{fn.__module__}.{fn.__qualname__}" + + @wraps(fn) # required for preserving the function context def wrapper(self, *args, **kwargs): target_path = target if not target_path.startswith("derived/"): @@ -872,8 +875,11 @@ def wrapper(self, *args, **kwargs): target_evidence = get_evidence_by_path(target_path) if self.locker.validate(target_evidence): return - depends = [get_evidence_by_path(e, self.locker) for e in evidences] - content = f(self, *depends) + depends = [ + get_evidence_dependency(e, self.locker, fetcher=fetcher) + for e in evidences + ] + content = fn(self, *depends) target_evidence.set_content(content) self.locker.add_evidence(target_evidence) diff --git a/test/t_compliance/t_evidence/test_evidence.py b/test/t_compliance/t_evidence/test_evidence.py new file mode 100644 index 00000000..591e95e0 --- /dev/null +++ b/test/t_compliance/t_evidence/test_evidence.py @@ -0,0 +1,40 @@ +import unittest +from unittest.mock import MagicMock, create_autospec + +from compliance.evidence import store_derived_evidence +from compliance.locker import Locker +from compliance.utils.exceptions import DependencyUnavailableError, StaleEvidenceError + + +class TestEvidence(unittest.TestCase): + + def test_store_derived_evidence_adds_to_rerun(self): + """ + Ensure that when running a fetcher that stores derived evidence + that it is re-run if one of the dependant evidence is not available. + """ + self.locker = create_autospec(Locker) + self.locker.dependency_rerun = [] + self.locker.validate.side_effect = [False, StaleEvidenceError] + self.locker.repo_url = "https://my.locker.url" + self.locker.get_evidence_metadata = MagicMock() + self.locker.get_evidence_metadata.return_value = None + self.locker.get_evidence.side_effect = StaleEvidenceError + + with self.assertRaises(DependencyUnavailableError): + self.fetch_some_derived_evidence() + + self.assertEquals(1, len(self.locker.dependency_rerun)) + f = self.fetch_some_derived_evidence + self.assertDictEqual( + self.locker.dependency_rerun[0], + { + "module": f.__module__, + "class": self.__class__.__name__, + "method": f.__name__, + }, + ) + + @store_derived_evidence(["raw/cos/cos_bucket_metadata.json"], target="cos/bar.json") + def fetch_some_derived_evidence(self, cos_metadata): + return "{}"