From d189f2c16870deb683e62cd06a6072b008eab05d Mon Sep 17 00:00:00 2001 From: Paul Horton Date: Thu, 17 Feb 2022 11:41:37 +0000 Subject: [PATCH] BREAKING CHANGE: added new model `BomRef` unlocking logic later to ensure uniquness and dependency references (#174) Signed-off-by: Paul Horton --- cyclonedx/model/bom_ref.py | 53 +++++++++ cyclonedx/model/component.py | 12 +- cyclonedx/model/service.py | 12 +- cyclonedx/output/serializer/json.py | 10 +- cyclonedx/output/xml.py | 9 +- tests/data.py | 4 + .../json/1.2/bom_services_complex.json | 4 +- .../json/1.2/bom_services_nested.json | 8 +- .../json/1.2/bom_services_simple.json | 6 +- .../json/1.3/bom_services_complex.json | 4 +- .../json/1.3/bom_services_nested.json | 8 +- .../json/1.3/bom_services_simple.json | 6 +- .../json/1.4/bom_services_complex.json | 4 +- .../json/1.4/bom_services_nested.json | 6 +- .../json/1.4/bom_services_simple.json | 6 +- .../fixtures/xml/1.2/bom_services_complex.xml | 2 +- .../fixtures/xml/1.2/bom_services_simple.xml | 2 +- .../fixtures/xml/1.3/bom_services_complex.xml | 2 +- .../fixtures/xml/1.3/bom_services_nested.xml | 2 +- .../fixtures/xml/1.3/bom_services_simple.xml | 2 +- .../fixtures/xml/1.4/bom_services_complex.xml | 2 +- .../fixtures/xml/1.4/bom_services_nested.xml | 2 +- .../fixtures/xml/1.4/bom_services_simple.xml | 2 +- tests/test_model_component.py | 6 +- tests/test_model_service.py | 8 +- tests/test_output_json.py | 83 +++++-------- tests/test_output_xml.py | 112 +++++++----------- 27 files changed, 192 insertions(+), 185 deletions(-) create mode 100644 cyclonedx/model/bom_ref.py diff --git a/cyclonedx/model/bom_ref.py b/cyclonedx/model/bom_ref.py new file mode 100644 index 00000000..69bd9f31 --- /dev/null +++ b/cyclonedx/model/bom_ref.py @@ -0,0 +1,53 @@ +# encoding: utf-8 + +# This file is part of CycloneDX Python Lib +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# SPDX-License-Identifier: Apache-2.0 +# Copyright (c) OWASP Foundation. All Rights Reserved. +from typing import Optional +from uuid import uuid4 + + +class BomRef: + """ + An identifier that can be used to reference objects elsewhere in the BOM. + + This copies a similar pattern used in the CycloneDX Python Library. + + .. note:: + See https://github.com/CycloneDX/cyclonedx-php-library/blob/master/docs/dev/decisions/BomDependencyDataModel.md + """ + + def __init__(self, value: Optional[str] = None) -> None: + self.value = value or str(uuid4()) + + @property + def value(self) -> str: + return self._value + + @value.setter + def value(self, value: str) -> None: + self._value = value + + def __eq__(self, other: object) -> bool: + if isinstance(other, BomRef): + return hash(other) == hash(self) + return False + + def __hash__(self) -> int: + return hash(self.value) + + def __repr__(self) -> str: + return self.value diff --git a/cyclonedx/model/component.py b/cyclonedx/model/component.py index c8151746..8d07db45 100644 --- a/cyclonedx/model/component.py +++ b/cyclonedx/model/component.py @@ -20,13 +20,13 @@ from enum import Enum from os.path import exists from typing import Iterable, Optional, Set -from uuid import uuid4 # See https://github.com/package-url/packageurl-python/issues/65 from packageurl import PackageURL # type: ignore from . import AttachedText, Copyright, ExternalReference, HashAlgorithm, HashType, IdentifiableAction, LicenseChoice, \ OrganizationalEntity, Property, sha1sum, XsUri +from .bom_ref import BomRef from .issue import IssueType from .release_note import ReleaseNotes from .vulnerability import Vulnerability @@ -692,7 +692,7 @@ def __init__(self, *, name: str, component_type: ComponentType = ComponentType.L ) -> None: self.type = component_type self.mime_type = mime_type - self.bom_ref = bom_ref or str(uuid4()) + self._bom_ref = BomRef(value=bom_ref) self.supplier = supplier self.author = author self.publisher = publisher @@ -766,7 +766,7 @@ def mime_type(self, mime_type: Optional[str]) -> None: self._mime_type = mime_type @property - def bom_ref(self) -> str: + def bom_ref(self) -> BomRef: """ An optional identifier which can be used to reference the component elsewhere in the BOM. Every bom-ref MUST be unique within the BOM. @@ -774,14 +774,10 @@ def bom_ref(self) -> str: If a value was not provided in the constructor, a UUIDv4 will have been assigned. Returns: - `str` as a unique identifiers for this Component + `BomRef` """ return self._bom_ref - @bom_ref.setter - def bom_ref(self, bom_ref: str) -> None: - self._bom_ref = bom_ref - @property def supplier(self) -> Optional[OrganizationalEntity]: """ diff --git a/cyclonedx/model/service.py b/cyclonedx/model/service.py index e695ea9c..5f387973 100644 --- a/cyclonedx/model/service.py +++ b/cyclonedx/model/service.py @@ -15,9 +15,9 @@ # SPDX-License-Identifier: Apache-2.0 # Copyright (c) OWASP Foundation. All Rights Reserved. from typing import Iterable, Optional, Set -from uuid import uuid4 from . import ExternalReference, DataClassification, LicenseChoice, OrganizationalEntity, Property, XsUri +from .bom_ref import BomRef from .release_note import ReleaseNotes """ @@ -46,7 +46,7 @@ def __init__(self, *, name: str, bom_ref: Optional[str] = None, provider: Option services: Optional[Iterable['Service']] = None, release_notes: Optional[ReleaseNotes] = None, ) -> None: - self.bom_ref = bom_ref or str(uuid4()) + self._bom_ref = BomRef(value=bom_ref) self.provider = provider self.group = group self.name = name @@ -63,7 +63,7 @@ def __init__(self, *, name: str, bom_ref: Optional[str] = None, provider: Option self.properties = set(properties or []) @property - def bom_ref(self) -> Optional[str]: + def bom_ref(self) -> BomRef: """ An optional identifier which can be used to reference the service elsewhere in the BOM. Uniqueness is enforced within all elements and children of the root-level bom element. @@ -71,14 +71,10 @@ def bom_ref(self) -> Optional[str]: If a value was not provided in the constructor, a UUIDv4 will have been assigned. Returns: - `str` unique identifier for this Service + `BomRef` unique identifier for this Service """ return self._bom_ref - @bom_ref.setter - def bom_ref(self, bom_ref: Optional[str]) -> None: - self._bom_ref = bom_ref - @property def provider(self) -> Optional[OrganizationalEntity]: """ diff --git a/cyclonedx/output/serializer/json.py b/cyclonedx/output/serializer/json.py index 858e536b..3598c8ec 100644 --- a/cyclonedx/output/serializer/json.py +++ b/cyclonedx/output/serializer/json.py @@ -16,7 +16,6 @@ # # SPDX-License-Identifier: Apache-2.0 # Copyright (c) OWASP Foundation. All Rights Reserved. - from datetime import datetime from decimal import Decimal from enum import Enum @@ -28,8 +27,9 @@ # See https://github.com/package-url/packageurl-python/issues/65 from packageurl import PackageURL # type: ignore -from cyclonedx.model import XsUri -from cyclonedx.model.component import Component +from ...model import XsUri +from ...model.bom_ref import BomRef +from ...model.component import Component HYPHENATED_ATTRIBUTES = [ 'bom_ref', 'mime_type', 'x_trust_boundary' @@ -40,6 +40,10 @@ class CycloneDxJSONEncoder(JSONEncoder): def default(self, o: Any) -> Any: + # BomRef + if isinstance(o, BomRef): + return str(o) + # datetime if isinstance(o, datetime): return o.isoformat() diff --git a/cyclonedx/output/xml.py b/cyclonedx/output/xml.py index acc5a5fb..94ad2043 100644 --- a/cyclonedx/output/xml.py +++ b/cyclonedx/output/xml.py @@ -27,6 +27,7 @@ from ..model import AttachedText, ExternalReference, HashType, IdentifiableAction, LicenseChoice, \ OrganizationalEntity, OrganizationalContact, Property, Tool from ..model.bom import Bom +from ..model.bom_ref import BomRef from ..model.component import Component, Patch from ..model.release_note import ReleaseNotes from ..model.service import Service @@ -174,7 +175,7 @@ def _add_metadata_element(self) -> None: def _add_component_element(self, component: Component) -> ElementTree.Element: element_attributes = {'type': component.type.value} if self.component_supports_bom_ref_attribute() and component.bom_ref: - element_attributes['bom-ref'] = component.bom_ref + element_attributes['bom-ref'] = str(component.bom_ref) if self.component_supports_mime_type_attribute() and component.mime_type: element_attributes['mime-type'] = component.mime_type @@ -450,7 +451,7 @@ def _add_properties_element(properties: Set[Property], parent_element: ElementTr def _add_service_element(self, service: Service) -> ElementTree.Element: element_attributes = {} if service.bom_ref: - element_attributes['bom-ref'] = service.bom_ref + element_attributes['bom-ref'] = str(service.bom_ref) service_element = ElementTree.Element('service', element_attributes) @@ -654,10 +655,10 @@ def _get_vulnerability_as_xml_element_post_1_4(self, vulnerability: Vulnerabilit return vulnerability_element @staticmethod - def _get_vulnerability_as_xml_element_pre_1_3(bom_ref: str, + def _get_vulnerability_as_xml_element_pre_1_3(bom_ref: BomRef, vulnerability: Vulnerability) -> ElementTree.Element: vulnerability_element = ElementTree.Element('v:vulnerability', { - 'ref': bom_ref + 'ref': str(bom_ref) }) # id diff --git a/tests/data.py b/tests/data.py index cd0571fc..2e5fc805 100644 --- a/tests/data.py +++ b/tests/data.py @@ -45,6 +45,10 @@ MOCK_UUID_5 = 'bb5911d6-1a1d-41c9-b6e0-46e848d16655' MOCK_UUID_6 = 'df70b5f1-8f53-47a4-be48-669ae78795e6' +TEST_UUIDS = [ + MOCK_UUID_1, MOCK_UUID_2, MOCK_UUID_3, MOCK_UUID_4, MOCK_UUID_5, MOCK_UUID_6 +] + def get_bom_with_component_setuptools_basic() -> Bom: return Bom(components=[get_component_setuptools_simple()]) diff --git a/tests/fixtures/json/1.2/bom_services_complex.json b/tests/fixtures/json/1.2/bom_services_complex.json index cbf93031..8cc31cd2 100644 --- a/tests/fixtures/json/1.2/bom_services_complex.json +++ b/tests/fixtures/json/1.2/bom_services_complex.json @@ -15,7 +15,7 @@ ], "component": { "type": "library", - "bom-ref": "cd3e9c95-9d41-49e7-9924-8cf0465ae789", + "bom-ref": "17e3b199-dc0b-42ef-bfdd-1fa81a1e3eda", "name": "cyclonedx-python-lib", "version": "1.0.0" } @@ -76,7 +76,7 @@ ] }, { - "bom-ref": "17e3b199-dc0b-42ef-bfdd-1fa81a1e3eda", + "bom-ref": "be2c6502-7e9a-47db-9a66-e34f729810a3", "name": "my-second-service" } ] diff --git a/tests/fixtures/json/1.2/bom_services_nested.json b/tests/fixtures/json/1.2/bom_services_nested.json index b1a8a0d2..46de1b3b 100644 --- a/tests/fixtures/json/1.2/bom_services_nested.json +++ b/tests/fixtures/json/1.2/bom_services_nested.json @@ -3,7 +3,7 @@ "bomFormat": "CycloneDX", "metadata": { "component": { - "bom-ref": "bb5911d6-1a1d-41c9-b6e0-46e848d16655", + "bom-ref": "cd3e9c95-9d41-49e7-9924-8cf0465ae789", "name": "cyclonedx-python-lib", "type": "library", "version": "1.0.0" @@ -72,7 +72,7 @@ }, "services": [ { - "bom-ref": "df70b5f1-8f53-47a4-be48-669ae78795e6", + "bom-ref": "be2c6502-7e9a-47db-9a66-e34f729810a3", "name": "first-nested-service" }, { @@ -105,11 +105,11 @@ "x-trust-boundary": true }, { - "bom-ref": "df70b5f1-8f53-47a4-be48-669ae78795e6", + "bom-ref": "0b049d09-64c0-4490-a0f5-c84d9aacf857", "name": "my-second-service", "services": [ { - "bom-ref": "df70b5f1-8f53-47a4-be48-669ae78795e6", + "bom-ref": "17e3b199-dc0b-42ef-bfdd-1fa81a1e3eda", "group": "what-group", "name": "yet-another-nested-service", "provider": { diff --git a/tests/fixtures/json/1.2/bom_services_simple.json b/tests/fixtures/json/1.2/bom_services_simple.json index 696cafe4..db950b0e 100644 --- a/tests/fixtures/json/1.2/bom_services_simple.json +++ b/tests/fixtures/json/1.2/bom_services_simple.json @@ -15,18 +15,18 @@ ], "component": { "type": "library", - "bom-ref": "cd3e9c95-9d41-49e7-9924-8cf0465ae789", + "bom-ref": "0b049d09-64c0-4490-a0f5-c84d9aacf857", "name": "cyclonedx-python-lib", "version": "1.0.0" } }, "services": [ { - "bom-ref": "bb5911d6-1a1d-41c9-b6e0-46e848d16655", + "bom-ref": "be2c6502-7e9a-47db-9a66-e34f729810a3", "name": "my-first-service" }, { - "bom-ref": "bb5911d6-1a1d-41c9-b6e0-46e848d16655", + "bom-ref": "17e3b199-dc0b-42ef-bfdd-1fa81a1e3eda", "name": "my-second-service" } ] diff --git a/tests/fixtures/json/1.3/bom_services_complex.json b/tests/fixtures/json/1.3/bom_services_complex.json index 24d0ca06..64460386 100644 --- a/tests/fixtures/json/1.3/bom_services_complex.json +++ b/tests/fixtures/json/1.3/bom_services_complex.json @@ -15,7 +15,7 @@ ], "component": { "type": "library", - "bom-ref": "bb5911d6-1a1d-41c9-b6e0-46e848d16655", + "bom-ref": "17e3b199-dc0b-42ef-bfdd-1fa81a1e3eda", "name": "cyclonedx-python-lib", "version": "1.0.0" } @@ -86,7 +86,7 @@ ] }, { - "bom-ref": "0b049d09-64c0-4490-a0f5-c84d9aacf857", + "bom-ref": "be2c6502-7e9a-47db-9a66-e34f729810a3", "name": "my-second-service" } ] diff --git a/tests/fixtures/json/1.3/bom_services_nested.json b/tests/fixtures/json/1.3/bom_services_nested.json index 23c0bd1d..216e52e0 100644 --- a/tests/fixtures/json/1.3/bom_services_nested.json +++ b/tests/fixtures/json/1.3/bom_services_nested.json @@ -3,7 +3,7 @@ "bomFormat": "CycloneDX", "metadata": { "component": { - "bom-ref": "0b049d09-64c0-4490-a0f5-c84d9aacf857", + "bom-ref": "cd3e9c95-9d41-49e7-9924-8cf0465ae789", "name": "cyclonedx-python-lib", "type": "library", "version": "1.0.0" @@ -82,7 +82,7 @@ }, "services": [ { - "bom-ref": "cd3e9c95-9d41-49e7-9924-8cf0465ae789", + "bom-ref": "be2c6502-7e9a-47db-9a66-e34f729810a3", "name": "first-nested-service" }, { @@ -115,11 +115,11 @@ "x-trust-boundary": true }, { - "bom-ref": "cd3e9c95-9d41-49e7-9924-8cf0465ae789", + "bom-ref": "0b049d09-64c0-4490-a0f5-c84d9aacf857", "name": "my-second-service", "services": [ { - "bom-ref": "cd3e9c95-9d41-49e7-9924-8cf0465ae789", + "bom-ref": "17e3b199-dc0b-42ef-bfdd-1fa81a1e3eda", "group": "what-group", "name": "yet-another-nested-service", "provider": { diff --git a/tests/fixtures/json/1.3/bom_services_simple.json b/tests/fixtures/json/1.3/bom_services_simple.json index a16b1ed9..31bb109b 100644 --- a/tests/fixtures/json/1.3/bom_services_simple.json +++ b/tests/fixtures/json/1.3/bom_services_simple.json @@ -15,18 +15,18 @@ ], "component": { "type": "library", - "bom-ref": "17e3b199-dc0b-42ef-bfdd-1fa81a1e3eda", + "bom-ref": "0b049d09-64c0-4490-a0f5-c84d9aacf857", "name": "cyclonedx-python-lib", "version": "1.0.0" } }, "services": [ { - "bom-ref": "cd3e9c95-9d41-49e7-9924-8cf0465ae789", + "bom-ref": "be2c6502-7e9a-47db-9a66-e34f729810a3", "name": "my-first-service" }, { - "bom-ref": "cd3e9c95-9d41-49e7-9924-8cf0465ae789", + "bom-ref": "17e3b199-dc0b-42ef-bfdd-1fa81a1e3eda", "name": "my-second-service" } ] diff --git a/tests/fixtures/json/1.4/bom_services_complex.json b/tests/fixtures/json/1.4/bom_services_complex.json index 78784f48..656a8bbf 100644 --- a/tests/fixtures/json/1.4/bom_services_complex.json +++ b/tests/fixtures/json/1.4/bom_services_complex.json @@ -49,7 +49,7 @@ ], "component": { "type": "library", - "bom-ref": "df70b5f1-8f53-47a4-be48-669ae78795e6", + "bom-ref": "17e3b199-dc0b-42ef-bfdd-1fa81a1e3eda", "name": "cyclonedx-python-lib", "version": "1.0.0" } @@ -179,7 +179,7 @@ ] }, { - "bom-ref": "cd3e9c95-9d41-49e7-9924-8cf0465ae789", + "bom-ref": "be2c6502-7e9a-47db-9a66-e34f729810a3", "name": "my-second-service" } ] diff --git a/tests/fixtures/json/1.4/bom_services_nested.json b/tests/fixtures/json/1.4/bom_services_nested.json index 93705699..e0e01221 100644 --- a/tests/fixtures/json/1.4/bom_services_nested.json +++ b/tests/fixtures/json/1.4/bom_services_nested.json @@ -3,7 +3,7 @@ "bomFormat": "CycloneDX", "metadata": { "component": { - "bom-ref": "be2c6502-7e9a-47db-9a66-e34f729810a3", + "bom-ref": "cd3e9c95-9d41-49e7-9924-8cf0465ae789", "name": "cyclonedx-python-lib", "type": "library", "version": "1.0.0" @@ -175,7 +175,7 @@ }, "services": [ { - "bom-ref": "17e3b199-dc0b-42ef-bfdd-1fa81a1e3eda", + "bom-ref": "be2c6502-7e9a-47db-9a66-e34f729810a3", "name": "first-nested-service" }, { @@ -208,7 +208,7 @@ "x-trust-boundary": true }, { - "bom-ref": "17e3b199-dc0b-42ef-bfdd-1fa81a1e3eda", + "bom-ref": "0b049d09-64c0-4490-a0f5-c84d9aacf857", "name": "my-second-service", "services": [ { diff --git a/tests/fixtures/json/1.4/bom_services_simple.json b/tests/fixtures/json/1.4/bom_services_simple.json index ba87360d..c23f4c63 100644 --- a/tests/fixtures/json/1.4/bom_services_simple.json +++ b/tests/fixtures/json/1.4/bom_services_simple.json @@ -49,18 +49,18 @@ ], "component": { "type": "library", - "bom-ref": "be2c6502-7e9a-47db-9a66-e34f729810a3", + "bom-ref": "0b049d09-64c0-4490-a0f5-c84d9aacf857", "name": "cyclonedx-python-lib", "version": "1.0.0" } }, "services": [ { - "bom-ref": "0b049d09-64c0-4490-a0f5-c84d9aacf857", + "bom-ref": "be2c6502-7e9a-47db-9a66-e34f729810a3", "name": "my-first-service" }, { - "bom-ref": "0b049d09-64c0-4490-a0f5-c84d9aacf857", + "bom-ref": "17e3b199-dc0b-42ef-bfdd-1fa81a1e3eda", "name": "my-second-service" } ] diff --git a/tests/fixtures/xml/1.2/bom_services_complex.xml b/tests/fixtures/xml/1.2/bom_services_complex.xml index f330ee7a..1dd5f92d 100644 --- a/tests/fixtures/xml/1.2/bom_services_complex.xml +++ b/tests/fixtures/xml/1.2/bom_services_complex.xml @@ -9,7 +9,7 @@ VERSION - + cyclonedx-python-lib 1.0.0 diff --git a/tests/fixtures/xml/1.2/bom_services_simple.xml b/tests/fixtures/xml/1.2/bom_services_simple.xml index e02ea1b7..a6cf63c2 100644 --- a/tests/fixtures/xml/1.2/bom_services_simple.xml +++ b/tests/fixtures/xml/1.2/bom_services_simple.xml @@ -9,7 +9,7 @@ VERSION - + cyclonedx-python-lib 1.0.0 diff --git a/tests/fixtures/xml/1.3/bom_services_complex.xml b/tests/fixtures/xml/1.3/bom_services_complex.xml index 56e2cce9..8d2f93c0 100644 --- a/tests/fixtures/xml/1.3/bom_services_complex.xml +++ b/tests/fixtures/xml/1.3/bom_services_complex.xml @@ -9,7 +9,7 @@ VERSION - + cyclonedx-python-lib 1.0.0 diff --git a/tests/fixtures/xml/1.3/bom_services_nested.xml b/tests/fixtures/xml/1.3/bom_services_nested.xml index df05c422..4ebb2152 100644 --- a/tests/fixtures/xml/1.3/bom_services_nested.xml +++ b/tests/fixtures/xml/1.3/bom_services_nested.xml @@ -9,7 +9,7 @@ VERSION - + cyclonedx-python-lib 1.0.0 diff --git a/tests/fixtures/xml/1.3/bom_services_simple.xml b/tests/fixtures/xml/1.3/bom_services_simple.xml index c20a7c55..d3feae46 100644 --- a/tests/fixtures/xml/1.3/bom_services_simple.xml +++ b/tests/fixtures/xml/1.3/bom_services_simple.xml @@ -9,7 +9,7 @@ VERSION - + cyclonedx-python-lib 1.0.0 diff --git a/tests/fixtures/xml/1.4/bom_services_complex.xml b/tests/fixtures/xml/1.4/bom_services_complex.xml index e8eb750c..d7187c56 100644 --- a/tests/fixtures/xml/1.4/bom_services_complex.xml +++ b/tests/fixtures/xml/1.4/bom_services_complex.xml @@ -35,7 +35,7 @@ - + cyclonedx-python-lib 1.0.0 diff --git a/tests/fixtures/xml/1.4/bom_services_nested.xml b/tests/fixtures/xml/1.4/bom_services_nested.xml index 0312b4d8..2a3348ef 100644 --- a/tests/fixtures/xml/1.4/bom_services_nested.xml +++ b/tests/fixtures/xml/1.4/bom_services_nested.xml @@ -35,7 +35,7 @@ - + cyclonedx-python-lib 1.0.0 diff --git a/tests/fixtures/xml/1.4/bom_services_simple.xml b/tests/fixtures/xml/1.4/bom_services_simple.xml index 1dea0823..8eb59474 100644 --- a/tests/fixtures/xml/1.4/bom_services_simple.xml +++ b/tests/fixtures/xml/1.4/bom_services_simple.xml @@ -35,7 +35,7 @@ - + cyclonedx-python-lib 1.0.0 diff --git a/tests/test_model_component.py b/tests/test_model_component.py index b1c05927..4ae89060 100644 --- a/tests/test_model_component.py +++ b/tests/test_model_component.py @@ -54,7 +54,7 @@ def test_not_same(self) -> None: class TestModelComponent(TestCase): - @patch('cyclonedx.model.component.uuid4', return_value='6f266d1c-760f-4552-ae3b-41a9b74232fa') + @patch('cyclonedx.model.bom_ref.uuid4', return_value='6f266d1c-760f-4552-ae3b-41a9b74232fa') def test_empty_basic_component(self, mock_uuid: Mock) -> None: c = Component( name='test-component', version='1.2.3' @@ -63,7 +63,7 @@ def test_empty_basic_component(self, mock_uuid: Mock) -> None: self.assertEqual(c.name, 'test-component') self.assertEqual(c.type, ComponentType.LIBRARY) self.assertIsNone(c.mime_type) - self.assertEqual(c.bom_ref, '6f266d1c-760f-4552-ae3b-41a9b74232fa') + self.assertEqual(str(c.bom_ref), '6f266d1c-760f-4552-ae3b-41a9b74232fa') self.assertIsNone(c.supplier) self.assertIsNone(c.author) self.assertIsNone(c.publisher) @@ -81,7 +81,7 @@ def test_empty_basic_component(self, mock_uuid: Mock) -> None: self.assertEqual(len(c.get_vulnerabilities()), 0) - @patch('cyclonedx.model.component.uuid4', return_value='6f266d1c-760f-4552-ae3b-41a9b74232fa') + @patch('cyclonedx.model.bom_ref.uuid4', return_value='6f266d1c-760f-4552-ae3b-41a9b74232fa') def test_multiple_basic_components(self, mock_uuid: Mock) -> None: c1 = Component( name='test-component', version='1.2.3' diff --git a/tests/test_model_service.py b/tests/test_model_service.py index 12f589cf..555bcad3 100644 --- a/tests/test_model_service.py +++ b/tests/test_model_service.py @@ -24,12 +24,12 @@ class TestModelService(TestCase): - @patch('cyclonedx.model.service.uuid4', return_value='77d15ab9-5602-4cca-8ed2-59ae579aafd3') + @patch('cyclonedx.model.bom_ref.uuid4', return_value='77d15ab9-5602-4cca-8ed2-59ae579aafd3') def test_minimal_service(self, mock_uuid: Mock) -> None: s = Service(name='my-test-service') mock_uuid.assert_called() self.assertEqual(s.name, 'my-test-service') - self.assertEqual(s.bom_ref, '77d15ab9-5602-4cca-8ed2-59ae579aafd3') + self.assertEqual(str(s.bom_ref), '77d15ab9-5602-4cca-8ed2-59ae579aafd3') self.assertIsNone(s.provider) self.assertIsNone(s.group) self.assertIsNone(s.version) @@ -44,7 +44,7 @@ def test_minimal_service(self, mock_uuid: Mock) -> None: self.assertFalse(s.release_notes) self.assertFalse(s.properties) - @patch('cyclonedx.model.service.uuid4', return_value='859ff614-35a7-4d37-803b-d89130cb2577') + @patch('cyclonedx.model.bom_ref.uuid4', return_value='859ff614-35a7-4d37-803b-d89130cb2577') def test_service_with_services(self, mock_uuid: Mock) -> None: parent_service = Service(name='parent-service') parent_service.services = [ @@ -53,7 +53,7 @@ def test_service_with_services(self, mock_uuid: Mock) -> None: ] mock_uuid.assert_called() self.assertEqual(parent_service.name, 'parent-service') - self.assertEqual(parent_service.bom_ref, '859ff614-35a7-4d37-803b-d89130cb2577') + self.assertEqual(str(parent_service.bom_ref), '859ff614-35a7-4d37-803b-d89130cb2577') self.assertIsNone(parent_service.provider) self.assertIsNone(parent_service.group) self.assertIsNone(parent_service.version) diff --git a/tests/test_output_json.py b/tests/test_output_json.py index 109b121d..2f8d6ab0 100644 --- a/tests/test_output_json.py +++ b/tests/test_output_json.py @@ -26,9 +26,8 @@ get_bom_with_services_simple, get_bom_with_component_toml_1, \ get_bom_with_component_setuptools_no_component_version, \ get_bom_with_component_setuptools_with_release_notes, get_bom_with_component_setuptools_with_vulnerability, \ - MOCK_UUID_1, get_bom_just_complete_metadata, MOCK_UUID_2, MOCK_UUID_3, MOCK_UUID_4, MOCK_UUID_5, \ - get_bom_with_services_complex, MOCK_UUID_6, get_bom_with_nested_services, \ - get_bom_with_component_setuptools_complete, get_bom_with_external_references + MOCK_UUID_1, get_bom_just_complete_metadata, MOCK_UUID_2, MOCK_UUID_3, TEST_UUIDS, get_bom_with_services_complex, \ + get_bom_with_nested_services, get_bom_with_component_setuptools_complete, get_bom_with_external_references from tests.base import BaseJsonTestCase @@ -178,7 +177,7 @@ def test_bom_v1_3_component_with_vulnerability(self) -> None: fixture='bom_setuptools.json' ) - @patch('cyclonedx.model.component.uuid4', return_value=MOCK_UUID_1) + @patch('cyclonedx.model.bom_ref.uuid4', return_value=MOCK_UUID_1) def test_bom_v1_4_with_metadata_component(self, mock_uuid: Mock) -> None: self._validate_json_bom( bom=get_bom_just_complete_metadata(), schema_version=SchemaVersion.V1_4, @@ -186,7 +185,7 @@ def test_bom_v1_4_with_metadata_component(self, mock_uuid: Mock) -> None: ) mock_uuid.assert_called() - @patch('cyclonedx.model.component.uuid4', return_value=MOCK_UUID_2) + @patch('cyclonedx.model.bom_ref.uuid4', return_value=MOCK_UUID_2) def test_bom_v1_3_with_metadata_component(self, mock_uuid: Mock) -> None: self._validate_json_bom( bom=get_bom_just_complete_metadata(), schema_version=SchemaVersion.V1_3, @@ -194,7 +193,7 @@ def test_bom_v1_3_with_metadata_component(self, mock_uuid: Mock) -> None: ) mock_uuid.assert_called() - @patch('cyclonedx.model.component.uuid4', return_value=MOCK_UUID_3) + @patch('cyclonedx.model.bom_ref.uuid4', return_value=MOCK_UUID_3) def test_bom_v1_2_with_metadata_component(self, mock_uuid: Mock) -> None: self._validate_json_bom( bom=get_bom_just_complete_metadata(), schema_version=SchemaVersion.V1_2, @@ -202,95 +201,77 @@ def test_bom_v1_2_with_metadata_component(self, mock_uuid: Mock) -> None: ) mock_uuid.assert_called() - @patch('cyclonedx.model.component.uuid4', return_value=MOCK_UUID_1) - @patch('cyclonedx.model.service.uuid4', return_value=MOCK_UUID_3) - def test_bom_v1_4_services_simple(self, mock_uuid_c: Mock, mock_uuid_s: Mock) -> None: + @patch('cyclonedx.model.bom_ref.uuid4', side_effect=TEST_UUIDS) + def test_bom_v1_4_services_simple(self, mock_uuid: Mock) -> None: self._validate_json_bom( bom=get_bom_with_services_simple(), schema_version=SchemaVersion.V1_4, fixture='bom_services_simple.json' ) - mock_uuid_c.assert_called() - mock_uuid_s.assert_called() + mock_uuid.assert_called() - @patch('cyclonedx.model.component.uuid4', return_value=MOCK_UUID_2) - @patch('cyclonedx.model.service.uuid4', return_value=MOCK_UUID_4) - def test_bom_v1_3_services_simple(self, mock_uuid_c: Mock, mock_uuid_s: Mock) -> None: + @patch('cyclonedx.model.bom_ref.uuid4', side_effect=TEST_UUIDS) + def test_bom_v1_3_services_simple(self, mock_uuid: Mock) -> None: self._validate_json_bom( bom=get_bom_with_services_simple(), schema_version=SchemaVersion.V1_3, fixture='bom_services_simple.json' ) - mock_uuid_c.assert_called() - mock_uuid_s.assert_called() + mock_uuid.assert_called() - @patch('cyclonedx.model.component.uuid4', return_value=MOCK_UUID_4) - @patch('cyclonedx.model.service.uuid4', return_value=MOCK_UUID_5) - def test_bom_v1_2_services_simple(self, mock_uuid_c: Mock, mock_uuid_s: Mock) -> None: + @patch('cyclonedx.model.bom_ref.uuid4', side_effect=TEST_UUIDS) + def test_bom_v1_2_services_simple(self, mock_uuid: Mock) -> None: self._validate_json_bom( bom=get_bom_with_services_simple(), schema_version=SchemaVersion.V1_2, fixture='bom_services_simple.json' ) - mock_uuid_c.assert_called() - mock_uuid_s.assert_called() + mock_uuid.assert_called() - @patch('cyclonedx.model.component.uuid4', return_value=MOCK_UUID_6) - @patch('cyclonedx.model.service.uuid4', return_value=MOCK_UUID_4) - def test_bom_v1_4_services_complex(self, mock_uuid_c: Mock, mock_uuid_s: Mock) -> None: + @patch('cyclonedx.model.bom_ref.uuid4', side_effect=TEST_UUIDS) + def test_bom_v1_4_services_complex(self, mock_uuid: Mock) -> None: self._validate_json_bom( bom=get_bom_with_services_complex(), schema_version=SchemaVersion.V1_4, fixture='bom_services_complex.json' ) - mock_uuid_c.assert_called() - mock_uuid_s.assert_called() + mock_uuid.assert_called() - @patch('cyclonedx.model.component.uuid4', return_value=MOCK_UUID_5) - @patch('cyclonedx.model.service.uuid4', return_value=MOCK_UUID_3) - def test_bom_v1_3_services_complex(self, mock_uuid_c: Mock, mock_uuid_s: Mock) -> None: + @patch('cyclonedx.model.bom_ref.uuid4', side_effect=TEST_UUIDS) + def test_bom_v1_3_services_complex(self, mock_uuid: Mock) -> None: self._validate_json_bom( bom=get_bom_with_services_complex(), schema_version=SchemaVersion.V1_3, fixture='bom_services_complex.json' ) - mock_uuid_c.assert_called() - mock_uuid_s.assert_called() + mock_uuid.assert_called() - @patch('cyclonedx.model.component.uuid4', return_value=MOCK_UUID_4) - @patch('cyclonedx.model.service.uuid4', return_value=MOCK_UUID_2) - def test_bom_v1_2_services_complex(self, mock_uuid_c: Mock, mock_uuid_s: Mock) -> None: + @patch('cyclonedx.model.bom_ref.uuid4', side_effect=TEST_UUIDS) + def test_bom_v1_2_services_complex(self, mock_uuid: Mock) -> None: self._validate_json_bom( bom=get_bom_with_services_complex(), schema_version=SchemaVersion.V1_2, fixture='bom_services_complex.json' ) - mock_uuid_c.assert_called() - mock_uuid_s.assert_called() + mock_uuid.assert_called() - @patch('cyclonedx.model.component.uuid4', return_value=MOCK_UUID_1) - @patch('cyclonedx.model.service.uuid4', return_value=MOCK_UUID_2) - def test_bom_v1_4_services_nested(self, mock_uuid_c: Mock, mock_uuid_s: Mock) -> None: + @patch('cyclonedx.model.bom_ref.uuid4', side_effect=TEST_UUIDS) + def test_bom_v1_4_services_nested(self, mock_uuid: Mock) -> None: self._validate_json_bom( bom=get_bom_with_nested_services(), schema_version=SchemaVersion.V1_4, fixture='bom_services_nested.json' ) - mock_uuid_c.assert_called() - mock_uuid_s.assert_called() + mock_uuid.assert_called() - @patch('cyclonedx.model.component.uuid4', return_value=MOCK_UUID_3) - @patch('cyclonedx.model.service.uuid4', return_value=MOCK_UUID_4) - def test_bom_v1_3_services_nested(self, mock_uuid_c: Mock, mock_uuid_s: Mock) -> None: + @patch('cyclonedx.model.bom_ref.uuid4', side_effect=TEST_UUIDS) + def test_bom_v1_3_services_nested(self, mock_uuid: Mock) -> None: self._validate_json_bom( bom=get_bom_with_nested_services(), schema_version=SchemaVersion.V1_3, fixture='bom_services_nested.json' ) - mock_uuid_c.assert_called() - mock_uuid_s.assert_called() + mock_uuid.assert_called() - @patch('cyclonedx.model.component.uuid4', return_value=MOCK_UUID_5) - @patch('cyclonedx.model.service.uuid4', return_value=MOCK_UUID_6) - def test_bom_v1_2_services_nested(self, mock_uuid_c: Mock, mock_uuid_s: Mock) -> None: + @patch('cyclonedx.model.bom_ref.uuid4', side_effect=TEST_UUIDS) + def test_bom_v1_2_services_nested(self, mock_uuid: Mock) -> None: self._validate_json_bom( bom=get_bom_with_nested_services(), schema_version=SchemaVersion.V1_2, fixture='bom_services_nested.json' ) - mock_uuid_c.assert_called() - mock_uuid_s.assert_called() + mock_uuid.assert_called() # Helper methods def _validate_json_bom(self, bom: Bom, schema_version: SchemaVersion, fixture: str) -> None: diff --git a/tests/test_output_xml.py b/tests/test_output_xml.py index 780e0de3..a2bed4bd 100644 --- a/tests/test_output_xml.py +++ b/tests/test_output_xml.py @@ -24,15 +24,11 @@ from data import get_bom_with_component_setuptools_basic, get_bom_with_component_setuptools_with_cpe, \ get_bom_with_component_toml_1, get_bom_with_component_setuptools_no_component_version, \ get_bom_with_component_setuptools_with_release_notes, get_bom_with_component_setuptools_with_vulnerability, \ - MOCK_UUID_1, MOCK_UUID_2, MOCK_UUID_3, MOCK_UUID_4, MOCK_UUID_5, MOCK_UUID_6, get_bom_just_complete_metadata, \ + MOCK_UUID_1, MOCK_UUID_4, MOCK_UUID_5, MOCK_UUID_6, TEST_UUIDS, get_bom_just_complete_metadata, \ get_bom_with_nested_services, get_bom_with_services_simple, get_bom_with_services_complex, \ get_bom_with_component_setuptools_complete, get_bom_with_external_references from tests.base import BaseXmlTestCase -TEST_UUIDS = [ - MOCK_UUID_1, MOCK_UUID_2, MOCK_UUID_3, MOCK_UUID_4, MOCK_UUID_5, MOCK_UUID_6 -] - class TestOutputXml(BaseXmlTestCase): @@ -258,7 +254,7 @@ def test_bom_v1_0_component_with_vulnerability(self) -> None: fixture='bom_setuptools.xml' ) - @patch('cyclonedx.model.component.uuid4', return_value=MOCK_UUID_6) + @patch('cyclonedx.model.bom_ref.uuid4', return_value=MOCK_UUID_6) def test_bom_v1_4_with_metadata_component(self, mock_uuid: Mock) -> None: self._validate_xml_bom( bom=get_bom_just_complete_metadata(), schema_version=SchemaVersion.V1_4, @@ -266,7 +262,7 @@ def test_bom_v1_4_with_metadata_component(self, mock_uuid: Mock) -> None: ) mock_uuid.assert_called() - @patch('cyclonedx.model.component.uuid4', return_value=MOCK_UUID_5) + @patch('cyclonedx.model.bom_ref.uuid4', return_value=MOCK_UUID_5) def test_bom_v1_3_with_metadata_component(self, mock_uuid: Mock) -> None: self._validate_xml_bom( bom=get_bom_just_complete_metadata(), schema_version=SchemaVersion.V1_3, @@ -274,7 +270,7 @@ def test_bom_v1_3_with_metadata_component(self, mock_uuid: Mock) -> None: ) mock_uuid.assert_called() - @patch('cyclonedx.model.component.uuid4', return_value=MOCK_UUID_4) + @patch('cyclonedx.model.bom_ref.uuid4', return_value=MOCK_UUID_4) def test_bom_v1_2_with_metadata_component(self, mock_uuid: Mock) -> None: self._validate_xml_bom( bom=get_bom_just_complete_metadata(), schema_version=SchemaVersion.V1_2, @@ -282,7 +278,7 @@ def test_bom_v1_2_with_metadata_component(self, mock_uuid: Mock) -> None: ) mock_uuid.assert_called() - @patch('cyclonedx.model.component.uuid4', return_value=MOCK_UUID_1) + @patch('cyclonedx.model.bom_ref.uuid4', return_value=MOCK_UUID_1) def test_bom_v1_1_with_metadata_component(self, mock_uuid: Mock) -> None: self._validate_xml_bom( bom=get_bom_just_complete_metadata(), schema_version=SchemaVersion.V1_1, @@ -290,7 +286,7 @@ def test_bom_v1_1_with_metadata_component(self, mock_uuid: Mock) -> None: ) mock_uuid.assert_called() - @patch('cyclonedx.model.component.uuid4', return_value=MOCK_UUID_1) + @patch('cyclonedx.model.bom_ref.uuid4', return_value=MOCK_UUID_1) def test_bom_v1_0_with_metadata_component(self, mock_uuid: Mock) -> None: self._validate_xml_bom( bom=get_bom_just_complete_metadata(), schema_version=SchemaVersion.V1_0, @@ -298,125 +294,101 @@ def test_bom_v1_0_with_metadata_component(self, mock_uuid: Mock) -> None: ) mock_uuid.assert_called() - @patch('cyclonedx.model.component.uuid4', return_value=MOCK_UUID_6) - @patch('cyclonedx.model.service.uuid4', side_effect=TEST_UUIDS) - def test_bom_v1_4_services_simple(self, mock_uuid_c: Mock, mock_uuid_s: Mock) -> None: + @patch('cyclonedx.model.bom_ref.uuid4', side_effect=TEST_UUIDS) + def test_bom_v1_4_services_simple(self, mock_uuid: Mock) -> None: self._validate_xml_bom( bom=get_bom_with_services_simple(), schema_version=SchemaVersion.V1_4, fixture='bom_services_simple.xml' ) - mock_uuid_c.assert_called() - mock_uuid_s.assert_called() + mock_uuid.assert_called() - @patch('cyclonedx.model.component.uuid4', return_value=MOCK_UUID_5) - @patch('cyclonedx.model.service.uuid4', side_effect=TEST_UUIDS) - def test_bom_v1_3_services_simple(self, mock_uuid_c: Mock, mock_uuid_s: Mock) -> None: + @patch('cyclonedx.model.bom_ref.uuid4', side_effect=TEST_UUIDS) + def test_bom_v1_3_services_simple(self, mock_uuid: Mock) -> None: self._validate_xml_bom( bom=get_bom_with_services_simple(), schema_version=SchemaVersion.V1_3, fixture='bom_services_simple.xml' ) - mock_uuid_c.assert_called() - mock_uuid_s.assert_called() + mock_uuid.assert_called() - @patch('cyclonedx.model.component.uuid4', return_value=MOCK_UUID_4) - @patch('cyclonedx.model.service.uuid4', side_effect=TEST_UUIDS) - def test_bom_v1_2_services_simple(self, mock_uuid_c: Mock, mock_uuid_s: Mock) -> None: + @patch('cyclonedx.model.bom_ref.uuid4', side_effect=TEST_UUIDS) + def test_bom_v1_2_services_simple(self, mock_uuid: Mock) -> None: self._validate_xml_bom( bom=get_bom_with_services_simple(), schema_version=SchemaVersion.V1_2, fixture='bom_services_simple.xml' ) - mock_uuid_c.assert_called() - mock_uuid_s.assert_called() + mock_uuid.assert_called() - @patch('cyclonedx.model.component.uuid4', return_value=MOCK_UUID_1) - @patch('cyclonedx.model.service.uuid4', side_effect=TEST_UUIDS) - def test_bom_v1_1_services_simple(self, mock_uuid_c: Mock, mock_uuid_s: Mock) -> None: + @patch('cyclonedx.model.bom_ref.uuid4', side_effect=TEST_UUIDS) + def test_bom_v1_1_services_simple(self, mock_uuid: Mock) -> None: self._validate_xml_bom( bom=get_bom_with_services_simple(), schema_version=SchemaVersion.V1_1, fixture='bom_empty.xml' ) - mock_uuid_c.assert_called() - mock_uuid_s.assert_called() + mock_uuid.assert_called() - @patch('cyclonedx.model.component.uuid4', return_value=MOCK_UUID_1) - @patch('cyclonedx.model.service.uuid4', side_effect=TEST_UUIDS) - def test_bom_v1_0_services_simple(self, mock_uuid_c: Mock, mock_uuid_s: Mock) -> None: + @patch('cyclonedx.model.bom_ref.uuid4', side_effect=TEST_UUIDS) + def test_bom_v1_0_services_simple(self, mock_uuid: Mock) -> None: self._validate_xml_bom( bom=get_bom_with_services_simple(), schema_version=SchemaVersion.V1_0, fixture='bom_empty.xml' ) - mock_uuid_c.assert_called() - mock_uuid_s.assert_called() + mock_uuid.assert_called() - @patch('cyclonedx.model.component.uuid4', return_value=MOCK_UUID_6) - @patch('cyclonedx.model.service.uuid4', side_effect=TEST_UUIDS) - def test_bom_v1_4_services_complex(self, mock_uuid_c: Mock, mock_uuid_s: Mock) -> None: + @patch('cyclonedx.model.bom_ref.uuid4', side_effect=TEST_UUIDS) + def test_bom_v1_4_services_complex(self, mock_uuid4: Mock) -> None: self._validate_xml_bom( bom=get_bom_with_services_complex(), schema_version=SchemaVersion.V1_4, fixture='bom_services_complex.xml' ) - mock_uuid_c.assert_called() - mock_uuid_s.assert_called() + mock_uuid4.assert_called() - @patch('cyclonedx.model.component.uuid4', return_value=MOCK_UUID_5) - @patch('cyclonedx.model.service.uuid4', side_effect=TEST_UUIDS) - def test_bom_v1_3_services_complex(self, mock_uuid_c: Mock, mock_uuid_s: Mock) -> None: + @patch('cyclonedx.model.bom_ref.uuid4', side_effect=TEST_UUIDS) + def test_bom_v1_3_services_complex(self, mock_uuid: Mock) -> None: self._validate_xml_bom( bom=get_bom_with_services_complex(), schema_version=SchemaVersion.V1_3, fixture='bom_services_complex.xml' ) - mock_uuid_c.assert_called() - mock_uuid_s.assert_called() + mock_uuid.assert_called() - @patch('cyclonedx.model.component.uuid4', return_value=MOCK_UUID_4) - @patch('cyclonedx.model.service.uuid4', side_effect=TEST_UUIDS) - def test_bom_v1_2_services_complex(self, mock_uuid_c: Mock, mock_uuid_s: Mock) -> None: + @patch('cyclonedx.model.bom_ref.uuid4', side_effect=TEST_UUIDS) + def test_bom_v1_2_services_complex(self, mock_uuid: Mock) -> None: self._validate_xml_bom( bom=get_bom_with_services_complex(), schema_version=SchemaVersion.V1_2, fixture='bom_services_complex.xml' ) - mock_uuid_c.assert_called() - mock_uuid_s.assert_called() + mock_uuid.assert_called() - @patch('cyclonedx.model.component.uuid4', return_value=MOCK_UUID_3) - @patch('cyclonedx.model.service.uuid4', side_effect=TEST_UUIDS) - def test_bom_v1_1_services_complex(self, mock_uuid_c: Mock, mock_uuid_s: Mock) -> None: + @patch('cyclonedx.model.bom_ref.uuid4', side_effect=TEST_UUIDS) + def test_bom_v1_1_services_complex(self, mock_uuid: Mock) -> None: self._validate_xml_bom( bom=get_bom_with_services_complex(), schema_version=SchemaVersion.V1_1, fixture='bom_empty.xml' ) - mock_uuid_c.assert_called() - mock_uuid_s.assert_called() + mock_uuid.assert_called() - @patch('cyclonedx.model.component.uuid4', return_value=MOCK_UUID_6) - @patch('cyclonedx.model.service.uuid4', side_effect=TEST_UUIDS) - def test_bom_v1_4_services_nested(self, mock_uuid_c: Mock, mock_uuid_s: Mock) -> None: + @patch('cyclonedx.model.bom_ref.uuid4', side_effect=TEST_UUIDS) + def test_bom_v1_4_services_nested(self, mock_uuid: Mock) -> None: self._validate_xml_bom( bom=get_bom_with_nested_services(), schema_version=SchemaVersion.V1_4, fixture='bom_services_nested.xml' ) - mock_uuid_c.assert_called() - mock_uuid_s.assert_called() + mock_uuid.assert_called() - @patch('cyclonedx.model.component.uuid4', return_value=MOCK_UUID_5) - @patch('cyclonedx.model.service.uuid4', side_effect=TEST_UUIDS) - def test_bom_v1_3_services_nested(self, mock_uuid_c: Mock, mock_uuid_s: Mock) -> None: + @patch('cyclonedx.model.bom_ref.uuid4', side_effect=TEST_UUIDS) + def test_bom_v1_3_services_nested(self, mock_uuid: Mock) -> None: self._validate_xml_bom( bom=get_bom_with_nested_services(), schema_version=SchemaVersion.V1_3, fixture='bom_services_nested.xml' ) - mock_uuid_c.assert_called() - mock_uuid_s.assert_called() + mock_uuid.assert_called() - @patch('cyclonedx.model.component.uuid4', return_value=MOCK_UUID_4) - @patch('cyclonedx.model.service.uuid4', side_effect=TEST_UUIDS) - def test_bom_v1_2_services_nested(self, mock_uuid_c: Mock, mock_uuid_s: Mock) -> None: + @patch('cyclonedx.model.bom_ref.uuid4', side_effect=TEST_UUIDS) + def test_bom_v1_2_services_nested(self, mock_uuid: Mock) -> None: self._validate_xml_bom( bom=get_bom_with_nested_services(), schema_version=SchemaVersion.V1_2, fixture='bom_services_nested.xml' ) - mock_uuid_c.assert_called() - mock_uuid_s.assert_called() + mock_uuid.assert_called() # Helper methods def _validate_xml_bom(self, bom: Bom, schema_version: SchemaVersion, fixture: str) -> None: