diff --git a/.github/workflows/platform-integration-test.yaml b/.github/workflows/platform-integration-test.yaml index 23c2f25..f4d8420 100644 --- a/.github/workflows/platform-integration-test.yaml +++ b/.github/workflows/platform-integration-test.yaml @@ -163,8 +163,8 @@ jobs: OIDC_TOKEN_ENDPOINT: "http://localhost:8888/auth/realms/opentdf/protocol/openid-connect/token" OPENTDF_KAS_URL: "http://localhost:8080/kas" INSECURE_SKIP_VERIFY: "TRUE" - TEST_OPENTDF_ATTRIBUTE_1: "https://example.com/attr/attr1/value/value1" - TEST_OPENTDF_ATTRIBUTE_2: "https://example.com/attr/attr1/value/value2" + TEST_OPENTDF_ATTRIBUTE_1: "https://example.net/attr/attr1/value/value1" + TEST_OPENTDF_ATTRIBUTE_2: "https://example.com/attr/attr1/value/value1" run: | uv sync # Skip the tests marked "integration" @@ -180,8 +180,8 @@ jobs: OIDC_OP_TOKEN_ENDPOINT: "http://localhost:8888/auth/realms/opentdf/protocol/openid-connect/token" OPENTDF_KAS_URL: "http://localhost:8080/kas" INSECURE_SKIP_VERIFY: "TRUE" - TEST_OPENTDF_ATTRIBUTE_1: "https://example.com/attr/attr1/value/value1" - TEST_OPENTDF_ATTRIBUTE_2: "https://example.com/attr/attr1/value/value2" + TEST_OPENTDF_ATTRIBUTE_1: "https://example.net/attr/attr1/value/value1" + TEST_OPENTDF_ATTRIBUTE_2: "https://example.com/attr/attr1/value/value1" run: | # Run check_entitlements.sh ./.github/check_entitlements.sh diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index de489ad..dba9fe7 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -32,7 +32,7 @@ repos: rev: v0.12.12 hooks: # Run the linter. - - id: ruff + - id: ruff-check # Run the formatter. - id: ruff-format - repo: https://github.com/compilerla/conventional-pre-commit diff --git a/conftest.py b/conftest.py index a9bc2ab..c816ae6 100644 --- a/conftest.py +++ b/conftest.py @@ -6,6 +6,7 @@ """ import pytest + from tests.server_logs import log_server_logs_on_failure @@ -43,7 +44,6 @@ def pytest_runtest_makereport(item, call): log_server_logs_on_failure(test_name) -# Optional: Add a fixture to manually collect logs @pytest.fixture def collect_server_logs(): """ diff --git a/pyproject.toml b/pyproject.toml index aa396f2..810b110 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -68,6 +68,8 @@ lint.select = [ "C4", # McCabe complexity "C90", + # isort + "I", # Performance-related rules "PERF", # Ruff's performance rules # Additional useful rules diff --git a/src/otdf_python/__init__.py b/src/otdf_python/__init__.py index df07890..f8dcd49 100644 --- a/src/otdf_python/__init__.py +++ b/src/otdf_python/__init__.py @@ -5,10 +5,10 @@ Provides both programmatic APIs and command-line interface for encryption and decryption. """ +from .cli import main as cli_main +from .config import KASInfo, NanoTDFConfig, TDFConfig from .sdk import SDK from .sdk_builder import SDKBuilder -from .config import TDFConfig, NanoTDFConfig, KASInfo -from .cli import main as cli_main __all__ = [ "SDK", diff --git a/src/otdf_python/aesgcm.py b/src/otdf_python/aesgcm.py index a7d7446..ced6427 100644 --- a/src/otdf_python/aesgcm.py +++ b/src/otdf_python/aesgcm.py @@ -1,6 +1,7 @@ -from cryptography.hazmat.primitives.ciphers.aead import AESGCM import os +from cryptography.hazmat.primitives.ciphers.aead import AESGCM + class AesGcm: GCM_NONCE_LENGTH = 12 diff --git a/src/otdf_python/asym_crypto.py b/src/otdf_python/asym_crypto.py index 78e3f8d..932bfa1 100644 --- a/src/otdf_python/asym_crypto.py +++ b/src/otdf_python/asym_crypto.py @@ -2,10 +2,9 @@ Asymmetric encryption and decryption utilities for RSA keys in PEM format. """ -from cryptography.hazmat.primitives import serialization -from cryptography.hazmat.primitives.asymmetric import rsa, padding -from cryptography.hazmat.primitives import hashes from cryptography.hazmat.backends import default_backend +from cryptography.hazmat.primitives import hashes, serialization +from cryptography.hazmat.primitives.asymmetric import padding, rsa from cryptography.x509 import load_pem_x509_certificate from .sdk_exceptions import SDKException diff --git a/src/otdf_python/asym_decryption.py b/src/otdf_python/asym_decryption.py index 2ea7611..af11414 100644 --- a/src/otdf_python/asym_decryption.py +++ b/src/otdf_python/asym_decryption.py @@ -1,9 +1,9 @@ -from cryptography.hazmat.primitives import serialization -from cryptography.hazmat.primitives.asymmetric import padding -from cryptography.hazmat.primitives import hashes -from cryptography.hazmat.backends import default_backend import base64 +from cryptography.hazmat.backends import default_backend +from cryptography.hazmat.primitives import hashes, serialization +from cryptography.hazmat.primitives.asymmetric import padding + from .sdk_exceptions import SDKException diff --git a/src/otdf_python/asym_encryption.py b/src/otdf_python/asym_encryption.py index e385817..7e5e27a 100644 --- a/src/otdf_python/asym_encryption.py +++ b/src/otdf_python/asym_encryption.py @@ -1,11 +1,11 @@ -from cryptography.hazmat.primitives import serialization -from cryptography.hazmat.primitives.asymmetric import padding -from cryptography.hazmat.primitives import hashes -from cryptography.x509 import load_pem_x509_certificate -from cryptography.hazmat.backends import default_backend import base64 import re +from cryptography.hazmat.backends import default_backend +from cryptography.hazmat.primitives import hashes, serialization +from cryptography.hazmat.primitives.asymmetric import padding +from cryptography.x509 import load_pem_x509_certificate + from .sdk_exceptions import SDKException diff --git a/src/otdf_python/cli.py b/src/otdf_python/cli.py index 0eb0dd1..18b7b9c 100644 --- a/src/otdf_python/cli.py +++ b/src/otdf_python/cli.py @@ -11,9 +11,9 @@ import json import logging import sys +from dataclasses import asdict from io import BytesIO from pathlib import Path -from dataclasses import asdict from otdf_python.config import KASInfo, NanoTDFConfig, TDFConfig from otdf_python.sdk import SDK diff --git a/src/otdf_python/config.py b/src/otdf_python/config.py index 0531458..646acec 100644 --- a/src/otdf_python/config.py +++ b/src/otdf_python/config.py @@ -1,7 +1,7 @@ from dataclasses import dataclass, field from enum import Enum -from urllib.parse import urlparse, urlunparse from typing import Any +from urllib.parse import urlparse, urlunparse class TDFFormat(Enum): diff --git a/src/otdf_python/crypto_utils.py b/src/otdf_python/crypto_utils.py index 2b80e79..b32a5e9 100644 --- a/src/otdf_python/crypto_utils.py +++ b/src/otdf_python/crypto_utils.py @@ -1,8 +1,9 @@ -import hmac import hashlib -from cryptography.hazmat.primitives.asymmetric import rsa, ec -from cryptography.hazmat.primitives import serialization +import hmac + from cryptography.hazmat.backends import default_backend +from cryptography.hazmat.primitives import serialization +from cryptography.hazmat.primitives.asymmetric import ec, rsa class CryptoUtils: diff --git a/src/otdf_python/dpop.py b/src/otdf_python/dpop.py index d27bc0f..c442a5e 100644 --- a/src/otdf_python/dpop.py +++ b/src/otdf_python/dpop.py @@ -2,9 +2,10 @@ DPoP (Demonstration of Proof-of-Possession) token generation utilities. """ -import time -import hashlib import base64 +import hashlib +import time + import jwt from .crypto_utils import CryptoUtils diff --git a/src/otdf_python/eckeypair.py b/src/otdf_python/eckeypair.py index f463abc..3dee0aa 100644 --- a/src/otdf_python/eckeypair.py +++ b/src/otdf_python/eckeypair.py @@ -1,14 +1,14 @@ +from cryptography.exceptions import InvalidSignature +from cryptography.hazmat.backends import default_backend +from cryptography.hazmat.primitives import hashes, serialization from cryptography.hazmat.primitives.asymmetric import ec -from cryptography.hazmat.primitives import serialization, hashes from cryptography.hazmat.primitives.kdf.hkdf import HKDF from cryptography.hazmat.primitives.serialization import ( Encoding, - PublicFormat, - PrivateFormat, NoEncryption, + PrivateFormat, + PublicFormat, ) -from cryptography.hazmat.backends import default_backend -from cryptography.exceptions import InvalidSignature class ECKeyPair: diff --git a/src/otdf_python/header.py b/src/otdf_python/header.py index ceae3f6..df7186d 100644 --- a/src/otdf_python/header.py +++ b/src/otdf_python/header.py @@ -1,8 +1,8 @@ -from otdf_python.resource_locator import ResourceLocator +from otdf_python.constants import MAGIC_NUMBER_AND_VERSION from otdf_python.ecc_mode import ECCMode -from otdf_python.symmetric_and_payload_config import SymmetricAndPayloadConfig from otdf_python.policy_info import PolicyInfo -from otdf_python.constants import MAGIC_NUMBER_AND_VERSION +from otdf_python.resource_locator import ResourceLocator +from otdf_python.symmetric_and_payload_config import SymmetricAndPayloadConfig class Header: diff --git a/src/otdf_python/kas_client.py b/src/otdf_python/kas_client.py index 39bba64..43b3c6a 100644 --- a/src/otdf_python/kas_client.py +++ b/src/otdf_python/kas_client.py @@ -2,21 +2,22 @@ KASClient: Handles communication with the Key Access Service (KAS). """ -import time -import logging +import base64 import hashlib +import logging import secrets -import base64 +import time from base64 import b64decode from dataclasses import dataclass + import jwt -from .kas_key_cache import KASKeyCache -from .sdk_exceptions import SDKException -from .crypto_utils import CryptoUtils from .asym_decryption import AsymDecryption -from .key_type_constants import RSA_KEY_TYPE, EC_KEY_TYPE +from .crypto_utils import CryptoUtils from .kas_connect_rpc_client import KASConnectRPCClient +from .kas_key_cache import KASKeyCache +from .key_type_constants import EC_KEY_TYPE, RSA_KEY_TYPE +from .sdk_exceptions import SDKException @dataclass diff --git a/src/otdf_python/kas_connect_rpc_client.py b/src/otdf_python/kas_connect_rpc_client.py index c8b7319..3b39021 100644 --- a/src/otdf_python/kas_connect_rpc_client.py +++ b/src/otdf_python/kas_connect_rpc_client.py @@ -4,12 +4,13 @@ """ import logging -import urllib3 -from .sdk_exceptions import SDKException +import urllib3 from otdf_python_proto.kas import kas_pb2 from otdf_python_proto.kas.kas_pb2_connect import AccessServiceClient +from .sdk_exceptions import SDKException + class KASConnectRPCClient: """ diff --git a/src/otdf_python/manifest.py b/src/otdf_python/manifest.py index 1d771da..1ebbae3 100644 --- a/src/otdf_python/manifest.py +++ b/src/otdf_python/manifest.py @@ -1,6 +1,6 @@ -from dataclasses import dataclass, field, asdict -from typing import Any import json +from dataclasses import asdict, dataclass, field +from typing import Any @dataclass diff --git a/src/otdf_python/nanotdf.py b/src/otdf_python/nanotdf.py index 9e896fa..d8a063e 100644 --- a/src/otdf_python/nanotdf.py +++ b/src/otdf_python/nanotdf.py @@ -1,20 +1,22 @@ -from cryptography.hazmat.primitives.ciphers.aead import AESGCM -from otdf_python.asym_crypto import AsymDecryption +import hashlib +import json import secrets -from typing import BinaryIO from io import BytesIO +from typing import BinaryIO + +from cryptography.hazmat.primitives.ciphers.aead import AESGCM + +from otdf_python.asym_crypto import AsymDecryption from otdf_python.collection_store import CollectionStore, NoOpCollectionStore -from otdf_python.policy_stub import NULL_POLICY_UUID -from otdf_python.sdk_exceptions import SDKException +from otdf_python.config import KASInfo, NanoTDFConfig from otdf_python.constants import MAGIC_NUMBER_AND_VERSION -from otdf_python.resource_locator import ResourceLocator -from otdf_python.policy_object import PolicyObject, PolicyBody, AttributeObject -from otdf_python.symmetric_and_payload_config import SymmetricAndPayloadConfig from otdf_python.ecc_mode import ECCMode -import json -import hashlib from otdf_python.policy_info import PolicyInfo -from otdf_python.config import NanoTDFConfig, KASInfo +from otdf_python.policy_object import AttributeObject, PolicyBody, PolicyObject +from otdf_python.policy_stub import NULL_POLICY_UUID +from otdf_python.resource_locator import ResourceLocator +from otdf_python.sdk_exceptions import SDKException +from otdf_python.symmetric_and_payload_config import SymmetricAndPayloadConfig class NanoTDFException(SDKException): @@ -54,7 +56,7 @@ def _create_policy_object(self, attributes: list[str]) -> PolicyObject: def _serialize_policy_object(self, obj): """Custom NanoTDF serializer to convert to compatible JSON format.""" - from otdf_python.policy_object import PolicyBody, AttributeObject + from otdf_python.policy_object import AttributeObject, PolicyBody if isinstance(obj, PolicyBody): # Convert data_attributes to dataAttributes and use null instead of empty array @@ -224,10 +226,9 @@ def _wrap_key_if_needed( break if kas_public_key: - from cryptography.hazmat.primitives import serialization - from cryptography.hazmat.primitives.asymmetric import padding - from cryptography.hazmat.primitives import hashes from cryptography.hazmat.backends import default_backend + from cryptography.hazmat.primitives import hashes, serialization + from cryptography.hazmat.primitives.asymmetric import padding public_key = serialization.load_pem_public_key( kas_public_key.encode(), backend=default_backend() diff --git a/src/otdf_python/sdk.py b/src/otdf_python/sdk.py index 7734519..407db14 100644 --- a/src/otdf_python/sdk.py +++ b/src/otdf_python/sdk.py @@ -2,14 +2,14 @@ Python port of the main SDK class for OpenTDF platform interaction. """ -from typing import Any, BinaryIO -from io import BytesIO from contextlib import AbstractContextManager +from io import BytesIO +from typing import Any, BinaryIO -from otdf_python.tdf import TDF, TDFReaderConfig, TDFReader +from otdf_python.config import NanoTDFConfig, TDFConfig from otdf_python.nanotdf import NanoTDF from otdf_python.sdk_exceptions import SDKException -from otdf_python.config import NanoTDFConfig, TDFConfig +from otdf_python.tdf import TDF, TDFReader, TDFReaderConfig # Stubs for service client interfaces (to be implemented) diff --git a/src/otdf_python/sdk_builder.py b/src/otdf_python/sdk_builder.py index 87e34e0..ead42f5 100644 --- a/src/otdf_python/sdk_builder.py +++ b/src/otdf_python/sdk_builder.py @@ -3,14 +3,15 @@ Provides methods to configure and build SDK instances. """ -from typing import Any -import os import logging +import os import ssl -import httpx from dataclasses import dataclass +from typing import Any + +import httpx -from otdf_python.sdk import SDK, KAS +from otdf_python.sdk import KAS, SDK from otdf_python.sdk_exceptions import AutoConfigureException # Configure logging diff --git a/src/otdf_python/tdf.py b/src/otdf_python/tdf.py index 5eec00e..3dd35af 100644 --- a/src/otdf_python/tdf.py +++ b/src/otdf_python/tdf.py @@ -1,31 +1,32 @@ -from typing import BinaryIO, TYPE_CHECKING -import io -import os +import base64 import hashlib import hmac -import base64 -import zipfile +import io import logging +import os +import zipfile +from typing import TYPE_CHECKING, BinaryIO if TYPE_CHECKING: from otdf_python.kas_client import KASClient +from dataclasses import dataclass + +from otdf_python.aesgcm import AesGcm +from otdf_python.config import TDFConfig +from otdf_python.key_type_constants import RSA_KEY_TYPE from otdf_python.manifest import ( Manifest, - ManifestSegment, - ManifestIntegrityInformation, - ManifestRootSignature, ManifestEncryptionInformation, - ManifestPayload, - ManifestMethod, + ManifestIntegrityInformation, ManifestKeyAccess, + ManifestMethod, + ManifestPayload, + ManifestRootSignature, + ManifestSegment, ) from otdf_python.policy_stub import NULL_POLICY_UUID from otdf_python.tdf_writer import TDFWriter -from otdf_python.aesgcm import AesGcm -from dataclasses import dataclass -from otdf_python.key_type_constants import RSA_KEY_TYPE -from otdf_python.config import TDFConfig @dataclass @@ -83,10 +84,11 @@ def _validate_kas_infos(self, kas_infos): return validated_kas_infos def _wrap_key_for_kas(self, key, kas_infos, policy_json=None): - from otdf_python.asym_crypto import AsymEncryption import hashlib import hmac + from otdf_python.asym_crypto import AsymEncryption + key_access_objs = [] for kas in kas_infos: asym = AsymEncryption(kas.public_key) @@ -161,7 +163,7 @@ def _build_policy_json(self, config: TDFConfig) -> str: def _serialize_policy_object(self, obj): """Custom TDF serializer to convert to compatible JSON format.""" - from otdf_python.policy_object import PolicyBody, AttributeObject + from otdf_python.policy_object import AttributeObject, PolicyBody if isinstance(obj, PolicyBody): # Convert data_attributes to dataAttributes and use null instead of empty array @@ -277,7 +279,7 @@ def create_tdf( self, payload: bytes | BinaryIO, config: TDFConfig, - output_stream: BinaryIO | None = None, + output_stream: io.BytesIO | None = None, ): if output_stream is None: output_stream = io.BytesIO() @@ -376,13 +378,13 @@ def create_tdf( return manifest, size, output_stream def load_tdf( - self, tdf_data: bytes | BinaryIO, config: TDFReaderConfig + self, tdf_data: bytes | io.BytesIO, config: TDFReaderConfig ) -> TDFReader: # Extract manifest, unwrap payload key using KAS client # Handle both bytes and BinaryIO input tdf_bytes_io = io.BytesIO(tdf_data) if isinstance(tdf_data, bytes) else tdf_data - with zipfile.ZipFile(tdf_bytes_io, "r") as z: # type: ignore + with zipfile.ZipFile(tdf_bytes_io, "r") as z: manifest_json = z.read("0.manifest.json").decode() manifest = Manifest.from_json(manifest_json) @@ -424,10 +426,11 @@ def read_payload( """ Reads and verifies TDF segments, decrypts if needed, and writes the payload to output_stream. """ + import base64 import zipfile + from otdf_python.aesgcm import AesGcm from otdf_python.asym_crypto import AsymDecryption - import base64 with zipfile.ZipFile(io.BytesIO(tdf_bytes), "r") as z: manifest_json = z.read("0.manifest.json").decode() diff --git a/src/otdf_python/tdf_reader.py b/src/otdf_python/tdf_reader.py index 8e797b7..a414f16 100644 --- a/src/otdf_python/tdf_reader.py +++ b/src/otdf_python/tdf_reader.py @@ -2,10 +2,10 @@ TDFReader is responsible for reading and processing Trusted Data Format (TDF) files. """ -from .zip_reader import ZipReader -from .sdk_exceptions import SDKException -from .policy_object import PolicyObject from .manifest import Manifest +from .policy_object import PolicyObject +from .sdk_exceptions import SDKException +from .zip_reader import ZipReader # Constants from TDFWriter TDF_MANIFEST_FILE_NAME = "0.manifest.json" @@ -119,9 +119,9 @@ def read_policy_object(self) -> PolicyObject: # Convert to PolicyObject from otdf_python.policy_object import ( - PolicyObject, - PolicyBody, AttributeObject, + PolicyBody, + PolicyObject, ) # Parse data attributes - handle case where body might be None or have None values diff --git a/src/otdf_python/tdf_writer.py b/src/otdf_python/tdf_writer.py index 93ad9a9..6dcd7d5 100644 --- a/src/otdf_python/tdf_writer.py +++ b/src/otdf_python/tdf_writer.py @@ -1,4 +1,5 @@ import io + from otdf_python.zip_writer import ZipWriter diff --git a/src/otdf_python/token_source.py b/src/otdf_python/token_source.py index f3610bc..0c60c3a 100644 --- a/src/otdf_python/token_source.py +++ b/src/otdf_python/token_source.py @@ -3,6 +3,7 @@ """ import time + import httpx diff --git a/src/otdf_python/zip_reader.py b/src/otdf_python/zip_reader.py index ddc8e82..78d7ecb 100644 --- a/src/otdf_python/zip_reader.py +++ b/src/otdf_python/zip_reader.py @@ -1,5 +1,6 @@ -import zipfile import io +import zipfile + from otdf_python.invalid_zip_exception import InvalidZipException diff --git a/src/otdf_python/zip_writer.py b/src/otdf_python/zip_writer.py index a96b551..e548d97 100644 --- a/src/otdf_python/zip_writer.py +++ b/src/otdf_python/zip_writer.py @@ -1,5 +1,5 @@ -import zipfile import io +import zipfile import zlib diff --git a/tests/config_pydantic.py b/tests/config_pydantic.py index 076a7f7..457f01c 100644 --- a/tests/config_pydantic.py +++ b/tests/config_pydantic.py @@ -14,8 +14,8 @@ """ -from pydantic_settings import BaseSettings, SettingsConfigDict from pydantic import Field +from pydantic_settings import BaseSettings, SettingsConfigDict class ConfigureTdf(BaseSettings): diff --git a/tests/integration/conftest.py b/tests/integration/conftest.py index 2b17fd8..997a3f0 100644 --- a/tests/integration/conftest.py +++ b/tests/integration/conftest.py @@ -3,13 +3,14 @@ """ import json +import logging import os import subprocess import tempfile from pathlib import Path import pytest -import logging + from tests.support_cli_args import get_otdfctl_flags, get_platform_url logger = logging.getLogger(__name__) diff --git a/tests/integration/test_fixture_structure.py b/tests/integration/otdfctl_only/test_fixture_structure.py similarity index 100% rename from tests/integration/test_fixture_structure.py rename to tests/integration/otdfctl_only/test_fixture_structure.py diff --git a/tests/integration/test_cli_comparison.py b/tests/integration/otdfctl_to_python/test_cli_comparison.py similarity index 100% rename from tests/integration/test_cli_comparison.py rename to tests/integration/otdfctl_to_python/test_cli_comparison.py diff --git a/tests/integration/otdfctl_to_python/test_cli_decrypt.py b/tests/integration/otdfctl_to_python/test_cli_decrypt.py new file mode 100644 index 0000000..b1eedcc --- /dev/null +++ b/tests/integration/otdfctl_to_python/test_cli_decrypt.py @@ -0,0 +1,196 @@ +""" +Tests using target mode fixtures, for CLI integration testing. +""" + +import logging +import subprocess +import sys +import tempfile +from pathlib import Path + +import pytest + +from tests.config_pydantic import CONFIG_TDF +from tests.support_cli_args import ( + get_cli_flags, +) + +logger = logging.getLogger(__name__) + + +@pytest.mark.integration +def test_cli_decrypt_v4_2_2_vs_v4_3_1(all_target_mode_tdf_files, temp_credentials_file): + """ + Test Python CLI decrypt with various TDF versions created by otdfctl. + """ + + v4_2_2_files = all_target_mode_tdf_files["v4.2.2"] + v4_3_1_files = all_target_mode_tdf_files["v4.3.1"] + + # Test decrypt on both versions of the same file type + for file_type in ["text", "binary"]: + v4_2_2_tdf = v4_2_2_files[file_type] + v4_3_1_tdf = v4_3_1_files[file_type] + + # Decrypt v4.2.2 TDF + v4_2_2_output = _run_cli_decrypt(v4_2_2_tdf, temp_credentials_file) + + # Decrypt v4.3.1 TDF + v4_3_1_output = _run_cli_decrypt(v4_3_1_tdf, temp_credentials_file) + + # Both should succeed and produce output files + assert v4_2_2_output is not None, f"Failed to decrypt v4.2.2 {file_type} TDF" + assert v4_3_1_output is not None, f"Failed to decrypt v4.3.1 {file_type} TDF" + + assert v4_2_2_output.exists(), ( + f"v4.2.2 {file_type} decrypt output file not created" + ) + assert v4_3_1_output.exists(), ( + f"v4.3.1 {file_type} decrypt output file not created" + ) + + # Both output files should have content (not empty) + assert v4_2_2_output.stat().st_size > 0, ( + f"v4.2.2 {file_type} decrypt produced empty file" + ) + assert v4_3_1_output.stat().st_size > 0, ( + f"v4.3.1 {file_type} decrypt produced empty file" + ) + + # Log the decryption results for comparison + logger.info(f"\n=== {file_type.upper()} TDF Decryption Comparison ===") + logger.info(f"v4.2.2 output size: {v4_2_2_output.stat().st_size} bytes") + logger.info(f"v4.3.1 output size: {v4_3_1_output.stat().st_size} bytes") + + # For text files, we can compare the content directly + if file_type == "text": + v4_2_2_content = v4_2_2_output.read_text() + v4_3_1_content = v4_3_1_output.read_text() + + logger.info(f"v4.2.2 content preview: {v4_2_2_content[:50]}...") + logger.info(f"v4.3.1 content preview: {v4_3_1_content[:50]}...") + + # Clean up output files + v4_2_2_output.unlink() + v4_3_1_output.unlink() + + +@pytest.mark.integration +def test_cli_decrypt_different_file_types( + all_target_mode_tdf_files, temp_credentials_file +): + """ + Test CLI decrypt with different file types. + """ + + assert "v4.2.2" in all_target_mode_tdf_files + assert "v4.3.1" in all_target_mode_tdf_files + + # Check each version has the expected file types + for version in ["v4.2.2", "v4.3.1"]: + tdf_files = all_target_mode_tdf_files[version] + + file_types_to_test = [ + "text", + "binary", + "with_attributes", + ] # TODO: Consider adding "empty" file type as well + + for file_type in file_types_to_test: + tdf_path = tdf_files[file_type] + + # Decrypt the TDF + output_file = _run_cli_decrypt(tdf_path, temp_credentials_file) + + assert output_file is not None, f"Failed to decrypt {file_type} TDF" + assert output_file.exists(), ( + f"{file_type} TDF decrypt output file not created" + ) + + # Check file-type specific expectations + if file_type == "empty": + # Empty files should produce empty output files + assert output_file.stat().st_size == 0, ( + f"{file_type} TDF should produce empty output" + ) + else: + # Non-empty files should produce non-empty output + assert output_file.stat().st_size > 0, ( + f"{file_type} TDF produced empty decrypt output" + ) + + # For attributed files, just ensure they decrypt successfully + if file_type == "with_attributes": + logger.info( + f"Successfully decrypted attributed TDF, output size: {output_file.stat().st_size}" + ) + + # For text files, verify the content is readable + if file_type == "text": + try: + content = output_file.read_text() + assert len(content) > 0, "Text file should have readable content" + logger.info(f"Text content preview: {content[:100]}...") + except UnicodeDecodeError: + pytest.fail(f"Decrypted {file_type} file should be valid text") + + # Clean up output file + output_file.unlink() + + +def _run_cli_decrypt(tdf_path: Path, creds_file: Path) -> Path | None: + """ + Helper function to run Python CLI decrypt command and return the output file path. + + Returns the Path to the decrypted output file if successful, None if failed. + """ + # Determine platform flags + platform_url = CONFIG_TDF.OPENTDF_PLATFORM_URL + cli_flags = get_cli_flags() + + # Create a temporary output file + with tempfile.NamedTemporaryFile(delete=False, suffix=".decrypted") as temp_file: + output_path = Path(temp_file.name) + + try: + # Build CLI command + cmd = [ + sys.executable, + "-m", + "otdf_python.cli", + "--platform-url", + platform_url, + "--with-client-creds-file", + str(creds_file), + *cli_flags, + "decrypt", + str(tdf_path), + "-o", + str(output_path), + ] + + # Run the CLI command + result = subprocess.run( + cmd, + capture_output=True, + text=True, + check=True, + cwd=Path(__file__).parent.parent.parent, # Project root + ) + + logger.debug(f"CLI decrypt succeeded for {tdf_path}") + if result.stdout: + logger.debug(f"CLI stdout: {result.stdout}") + + return output_path + + except subprocess.CalledProcessError as e: + logger.error(f"CLI decrypt failed for {tdf_path}: {e}") + logger.error(f"CLI stderr: {e.stderr}") + logger.error(f"CLI stdout: {e.stdout}") + + # Clean up the output file if it was created but command failed + if output_path.exists(): + output_path.unlink() + + raise Exception(f"Failed to decrypt TDF {tdf_path}: {e}") from e diff --git a/tests/integration/test_cli_inspect.py b/tests/integration/otdfctl_to_python/test_cli_inspect.py similarity index 69% rename from tests/integration/test_cli_inspect.py rename to tests/integration/otdfctl_to_python/test_cli_inspect.py index 4791432..3acd0fb 100644 --- a/tests/integration/test_cli_inspect.py +++ b/tests/integration/otdfctl_to_python/test_cli_inspect.py @@ -3,12 +3,11 @@ """ import json +import logging import subprocess import sys from pathlib import Path -import logging - import pytest from tests.config_pydantic import CONFIG_TDF @@ -22,7 +21,7 @@ @pytest.mark.integration def test_cli_inspect_v4_2_2_vs_v4_3_1(all_target_mode_tdf_files, temp_credentials_file): """ - Test CLI inspect with various TDF versions. + Test Python CLI inspect with various TDF versions created by otdfctl. """ v4_2_2_files = all_target_mode_tdf_files["v4.2.2"] @@ -74,51 +73,64 @@ def test_cli_inspect_v4_2_2_vs_v4_3_1(all_target_mode_tdf_files, temp_credential f"v4.3.1 {file_type} inspection missing size" ) - print(f"\n=== {file_type.upper()} TDF Comparison (Limited Inspection) ===") - print( + logger.info( + f"\n=== {file_type.upper()} TDF Comparison (Limited Inspection) ===" + ) + logger.info( f"v4.2.2 type: {v4_2_2_result['type']}, size: {v4_2_2_result['size']}" ) - print( + logger.info( f"v4.3.1 type: {v4_3_1_result['type']}, size: {v4_3_1_result['size']}" ) @pytest.mark.integration -def test_cli_inspect_different_file_types(tdf_v4_3_1_files, temp_credentials_file): +def test_cli_inspect_different_file_types( + all_target_mode_tdf_files, + temp_credentials_file, +): """ Test CLI inspect with different file types. """ + assert "v4.2.2" in all_target_mode_tdf_files + assert "v4.3.1" in all_target_mode_tdf_files + + # Check each version has the expected file types + for version in ["v4.2.2", "v4.3.1"]: + tdf_files = all_target_mode_tdf_files[version] + + file_types_to_test = [ + "text", + "binary", + "with_attributes", + ] # TODO: Consider adding "empty" file type as well - file_types_to_test = [ - "text", - "binary", - "with_attributes", - ] # TODO: Consider adding "empty" file type as well - - for file_type in file_types_to_test: - tdf_path = tdf_v4_3_1_files[file_type] - - # Inspect the TDF - result = _run_cli_inspect(tdf_path, temp_credentials_file) - - assert result is not None, f"Failed to inspect {file_type} TDF" - assert "manifest" in result, f"{file_type} TDF inspection missing manifest" - - # Check file-type specific expectations - if file_type == "empty": - # Empty files should still have valid manifests - assert "encryptionInformation" in result["manifest"] - elif file_type == "with_attributes": - # Attributed files should have keyAccess information - assert ( - "keyAccess" in result["manifest"] - or "encryptionInformation" in result["manifest"] + for file_type in file_types_to_test: + tdf_path = tdf_files[file_type] + + # Inspect the TDF + result = _run_cli_inspect(tdf_path, temp_credentials_file) + + assert result is not None, ( + f"Failed to inspect {file_type} TDF, TDF version {version}" ) + assert "manifest" in result, f"{file_type} TDF inspection missing manifest" + + # Check file-type specific expectations + if file_type == "empty": + # Empty files should still have valid manifests + assert "encryptionInformation" in result["manifest"] + elif file_type == "with_attributes": + # Attributed files should have keyAccess information + assert ( + "keyAccess" in result["manifest"] + or "encryptionInformation" in result["manifest"] + ) def _run_cli_inspect(tdf_path: Path, creds_file: Path) -> dict | None: """ - Helper function to run CLI inspect command and return parsed JSON result. + Helper function to run Python CLI inspect command and return parsed JSON result. This demonstrates how the CLI inspect functionality could be tested with the new fixtures. diff --git a/tests/integration/test_tdf_reader_integration.py b/tests/integration/otdfctl_to_python/test_tdf_reader_integration.py similarity index 96% rename from tests/integration/test_tdf_reader_integration.py rename to tests/integration/otdfctl_to_python/test_tdf_reader_integration.py index 341aa08..6c58c32 100644 --- a/tests/integration/test_tdf_reader_integration.py +++ b/tests/integration/otdfctl_to_python/test_tdf_reader_integration.py @@ -55,13 +55,15 @@ def test_read_otdfctl_created_tdf_structure(self, temp_credentials_file): str(otdfctl_output), ] - otdfctl_result = subprocess.run( + otdfctl_encrypt_result = subprocess.run( otdfctl_cmd, capture_output=True, text=True, cwd=temp_path ) # If otdfctl fails, skip the test (might be server issues) - if otdfctl_result.returncode != 0: - pytest.skip(f"otdfctl encrypt failed: {otdfctl_result.stderr}") + if otdfctl_encrypt_result.returncode != 0: + raise Exception( + f"otdfctl encrypt failed: {otdfctl_encrypt_result.stderr}" + ) # Verify the TDF file was created assert otdfctl_output.exists(), "otdfctl did not create TDF file" @@ -153,13 +155,9 @@ def test_read_otdfctl_tdf_with_attributes(self, temp_credentials_file): # If otdfctl fails, skip the test # assert otdfctl_result.returncode == 0, "otdfctl encrypt failed" if otdfctl_result.returncode != 0: - print(f"otdfctl encrypt failed: {otdfctl_result.stderr}") - # Skip the test - pytest.skip( + raise Exception( f"otdfctl encrypt with attributes failed: {otdfctl_result.stderr}" ) - else: - print("otdfctl encrypt with attributes succeeded") # Verify the TDF file was created assert otdfctl_output.exists(), "otdfctl did not create TDF file" diff --git a/tests/integration/test_kas_client_integration.py b/tests/integration/python_only/test_kas_client_integration.py similarity index 99% rename from tests/integration/test_kas_client_integration.py rename to tests/integration/python_only/test_kas_client_integration.py index c904437..97fc723 100644 --- a/tests/integration/test_kas_client_integration.py +++ b/tests/integration/python_only/test_kas_client_integration.py @@ -3,6 +3,7 @@ """ import pytest + from otdf_python.kas_client import KASClient, KeyAccess from otdf_python.kas_key_cache import KASKeyCache from otdf_python.sdk_exceptions import SDKException diff --git a/tests/integration/support_sdk.py b/tests/integration/support_sdk.py index 84dd1e1..0d93ba3 100644 --- a/tests/integration/support_sdk.py +++ b/tests/integration/support_sdk.py @@ -1,7 +1,8 @@ -from otdf_python.sdk_builder import SDKBuilder +import httpx + from otdf_python.sdk import SDK +from otdf_python.sdk_builder import SDKBuilder from tests.config_pydantic import CONFIG_TDF -import httpx def _get_sdk_builder() -> SDKBuilder: diff --git a/tests/integration/test_cli_integration.py b/tests/integration/test_cli_integration.py index f2b3acb..3a3765e 100644 --- a/tests/integration/test_cli_integration.py +++ b/tests/integration/test_cli_integration.py @@ -10,7 +10,6 @@ import pytest -from tests.config_pydantic import CONFIG_TDF from tests.support_cli_args import get_platform_url original_env = os.environ.copy() @@ -65,7 +64,7 @@ def test_cli_decrypt_otdfctl_tdf(temp_credentials_file): env=original_env, ) - # If otdfctl fails, skip the test (might be server issues) + # If otdfctl fails to encrypt, fail fast if otdfctl_result.returncode != 0: raise Exception(f"otdfctl encrypt failed: {otdfctl_result.stderr}") @@ -165,7 +164,7 @@ def test_otdfctl_decrypt_comparison(collect_server_logs, temp_credentials_file): env=original_env, ) - # If otdfctl encrypt fails, skip the test (might be server issues) + # If otdfctl fails to encrypt, fail fast if otdfctl_encrypt_result.returncode != 0: raise Exception(f"otdfctl encrypt failed: {otdfctl_encrypt_result.stderr}") @@ -226,23 +225,9 @@ def test_otdfctl_decrypt_comparison(collect_server_logs, temp_credentials_file): # Check that our CLI succeeded if cli_decrypt_result.returncode != 0: - # Collect server logs for debugging logs = collect_server_logs() print(f"Server logs when Python CLI decrypt failed:\n{logs}") - - # Check if this is a server connectivity issue - if ( - "401 Unauthorized" in cli_decrypt_result.stderr - or "token endpoint discovery" in cli_decrypt_result.stderr - or "Issuer endpoint must be configured" in cli_decrypt_result.stderr - ): - pytest.skip( - f"Server connectivity or authentication issue: {cli_decrypt_result.stderr}" - ) - else: - assert cli_decrypt_result.returncode == 0, ( - f"Python CLI decrypt failed: {cli_decrypt_result.stderr}" - ) + raise Exception(f"Python CLI decrypt failed: {cli_decrypt_result.stderr}") # Verify both decrypted files were created assert otdfctl_decrypt_output.exists(), "otdfctl did not create decrypted file" @@ -327,25 +312,9 @@ def test_otdfctl_encrypt_decrypt_roundtrip(collect_server_logs, temp_credentials env=original_env, ) - # If otdfctl encrypt fails, skip the test (might be server issues) + # If otdfctl fails to encrypt, fail fast if otdfctl_encrypt_result.returncode != 0: - # Collect server logs for debugging - logs = collect_server_logs() - print(f"Server logs when otdfctl encrypt failed:\n{logs}") - - # Check if this is a server connectivity issue - if ( - "401 Unauthorized" in otdfctl_encrypt_result.stderr - or "token endpoint discovery" in otdfctl_encrypt_result.stderr - or "Issuer endpoint must be configured" in otdfctl_encrypt_result.stderr - ): - pytest.skip( - f"Server connectivity or authentication issue: {otdfctl_encrypt_result.stderr}" - ) - else: - assert otdfctl_encrypt_result.returncode == 0, ( - f"otdfctl encrypt failed: {otdfctl_encrypt_result.stderr}" - ) + raise Exception(f"otdfctl encrypt failed: {otdfctl_encrypt_result.stderr}") # Verify the TDF file was created assert otdfctl_tdf_output.exists(), "otdfctl did not create TDF file" @@ -378,25 +347,11 @@ def test_otdfctl_encrypt_decrypt_roundtrip(collect_server_logs, temp_credentials env=original_env, ) - # Check that otdfctl decrypt succeeded + # If otdfctl fails to decrypt, fail fast if otdfctl_decrypt_result.returncode != 0: - # Collect server logs for debugging logs = collect_server_logs() print(f"Server logs when otdfctl decrypt failed:\n{logs}") - - # Check if this is a server connectivity issue - if ( - "401 Unauthorized" in otdfctl_decrypt_result.stderr - or "token endpoint discovery" in otdfctl_decrypt_result.stderr - or "Issuer endpoint must be configured" in otdfctl_decrypt_result.stderr - ): - pytest.skip( - f"Server connectivity or authentication issue: {otdfctl_decrypt_result.stderr}" - ) - else: - assert otdfctl_decrypt_result.returncode == 0, ( - f"otdfctl decrypt failed: {otdfctl_decrypt_result.stderr}" - ) + raise Exception(f"otdfctl decrypt failed: {otdfctl_decrypt_result.stderr}") # Verify the decrypted file was created assert otdfctl_decrypt_output.exists(), "otdfctl did not create decrypted file" @@ -429,14 +384,8 @@ def test_otdfctl_encrypt_decrypt_roundtrip(collect_server_logs, temp_credentials @pytest.mark.integration -def test_cli_encrypt_integration(temp_credentials_file): +def test_cli_encrypt_integration(collect_server_logs, temp_credentials_file): """Integration test comparing our CLI with otdfctl""" - # Skip if OPENTDF_PLATFORM_URL is not set - platform_url = CONFIG_TDF.OPENTDF_PLATFORM_URL - if not platform_url: - raise Exception( - "OPENTDF_PLATFORM_URL must be set in config for integration tests" - ) # Create temporary directory for work with tempfile.TemporaryDirectory() as temp_dir: @@ -502,20 +451,9 @@ def test_cli_encrypt_integration(temp_credentials_file): # Check that our CLI succeeded if cli_result.returncode != 0: - # Check if this is a server connectivity issue - if ( - "401 Unauthorized" in cli_result.stderr - or "token endpoint discovery" in cli_result.stderr - or "Issuer endpoint must be configured" in cli_result.stderr - ): - pytest.skip( - f"Server connectivity or authentication issue: {cli_result.stderr}" - ) - - else: - assert cli_result.returncode == 0, ( - f"Python CLI failed: {cli_result.stderr}" - ) + logs = collect_server_logs() + print(f"Server logs when Python CLI encrypt failed:\n{logs}") + raise Exception(f"Python CLI failed: {cli_result.stderr}") # Both output files should exist assert otdfctl_output.exists(), "otdfctl output file does not exist" diff --git a/tests/integration/test_cli_tdf_validation.py b/tests/integration/test_cli_tdf_validation.py index 51421cd..9e9f971 100644 --- a/tests/integration/test_cli_tdf_validation.py +++ b/tests/integration/test_cli_tdf_validation.py @@ -13,9 +13,9 @@ from otdf_python.tdf_reader import TDF_MANIFEST_FILE_NAME, TDF_PAYLOAD_FILE_NAME from tests.support_cli_args import ( + get_cli_flags, get_otdfctl_flags, get_platform_url, - get_cli_flags, ) original_env = os.environ.copy() diff --git a/tests/integration/test_pe_interaction.py b/tests/integration/test_pe_interaction.py index 02418a3..00e4447 100644 --- a/tests/integration/test_pe_interaction.py +++ b/tests/integration/test_pe_interaction.py @@ -5,11 +5,12 @@ import logging import tempfile from pathlib import Path + import pytest from otdf_python.sdk import SDK -from tests.config_pydantic import CONFIG_TDF from otdf_python.sdk_exceptions import SDKException +from tests.config_pydantic import CONFIG_TDF from tests.integration.support_sdk import get_sdk_for_pe # Test files (adjust paths as needed) diff --git a/tests/mock_crypto.py b/tests/mock_crypto.py index 0209f67..006963b 100644 --- a/tests/mock_crypto.py +++ b/tests/mock_crypto.py @@ -1,5 +1,5 @@ -from cryptography.hazmat.primitives.asymmetric import rsa from cryptography.hazmat.primitives import serialization +from cryptography.hazmat.primitives.asymmetric import rsa def generate_rsa_keypair(): diff --git a/tests/server_logs.py b/tests/server_logs.py index 34d424d..0bc846d 100644 --- a/tests/server_logs.py +++ b/tests/server_logs.py @@ -2,8 +2,8 @@ Server log collection utility for debugging test failures. """ -import subprocess import logging +import subprocess from tests.config_pydantic import CONFIG_TESTING diff --git a/tests/test_aesgcm.py b/tests/test_aesgcm.py index b7a7e73..965f62d 100644 --- a/tests/test_aesgcm.py +++ b/tests/test_aesgcm.py @@ -1,6 +1,7 @@ +import os import unittest + from otdf_python.aesgcm import AesGcm -import os class TestAesGcm(unittest.TestCase): diff --git a/tests/test_assertion_config.py b/tests/test_assertion_config.py index 15b085c..f7c1a30 100644 --- a/tests/test_assertion_config.py +++ b/tests/test_assertion_config.py @@ -1,13 +1,14 @@ import unittest + from otdf_python.assertion_config import ( - Type, - Scope, - AssertionKeyAlg, AppliesToState, - BindingMethod, + AssertionConfig, AssertionKey, + AssertionKeyAlg, + BindingMethod, + Scope, Statement, - AssertionConfig, + Type, ) diff --git a/tests/test_asym_encryption.py b/tests/test_asym_encryption.py index e54eac9..617eb35 100644 --- a/tests/test_asym_encryption.py +++ b/tests/test_asym_encryption.py @@ -1,5 +1,5 @@ -from otdf_python.asym_encryption import AsymEncryption from otdf_python.asym_decryption import AsymDecryption +from otdf_python.asym_encryption import AsymEncryption from tests.mock_crypto import generate_rsa_keypair diff --git a/tests/test_autoconfigure_utils.py b/tests/test_autoconfigure_utils.py index 4916712..39fa412 100644 --- a/tests/test_autoconfigure_utils.py +++ b/tests/test_autoconfigure_utils.py @@ -1,10 +1,11 @@ import unittest + from otdf_python.autoconfigure_utils import ( - RuleType, - KeySplitStep, AttributeNameFQN, AttributeValueFQN, AutoConfigureException, + KeySplitStep, + RuleType, ) diff --git a/tests/test_cli.py b/tests/test_cli.py index e056ad6..999bdd7 100644 --- a/tests/test_cli.py +++ b/tests/test_cli.py @@ -2,13 +2,14 @@ Test CLI functionality """ -import pytest +import os import subprocess import sys import tempfile -import os from pathlib import Path +import pytest + def test_cli_help(): """Test that CLI help command works""" diff --git a/tests/test_collection_store.py b/tests/test_collection_store.py index 3131d87..5d18c24 100644 --- a/tests/test_collection_store.py +++ b/tests/test_collection_store.py @@ -1,8 +1,9 @@ import unittest + from otdf_python.collection_store import ( CollectionKey, - NoOpCollectionStore, CollectionStoreImpl, + NoOpCollectionStore, ) diff --git a/tests/test_config.py b/tests/test_config.py index 442f0d1..421691b 100644 --- a/tests/test_config.py +++ b/tests/test_config.py @@ -1,4 +1,4 @@ -from otdf_python.config import TDFConfig, KASInfo, get_kas_address +from otdf_python.config import KASInfo, TDFConfig, get_kas_address def test_tdf_config_defaults(): diff --git a/tests/test_crypto_utils.py b/tests/test_crypto_utils.py index 5f5fefa..2a802f5 100644 --- a/tests/test_crypto_utils.py +++ b/tests/test_crypto_utils.py @@ -1,7 +1,9 @@ import unittest -from otdf_python.crypto_utils import CryptoUtils + from cryptography.hazmat.primitives.asymmetric import ec +from otdf_python.crypto_utils import CryptoUtils + class TestCryptoUtils(unittest.TestCase): def test_hmac(self): diff --git a/tests/test_eckeypair.py b/tests/test_eckeypair.py index 81dddf5..b193dce 100644 --- a/tests/test_eckeypair.py +++ b/tests/test_eckeypair.py @@ -1,4 +1,5 @@ import unittest + from otdf_python.eckeypair import ECKeyPair diff --git a/tests/test_header.py b/tests/test_header.py index 62fcb79..678f931 100644 --- a/tests/test_header.py +++ b/tests/test_header.py @@ -1,9 +1,10 @@ +import unittest + +from otdf_python.ecc_mode import ECCMode from otdf_python.header import Header +from otdf_python.policy_info import PolicyInfo from otdf_python.resource_locator import ResourceLocator -from otdf_python.ecc_mode import ECCMode from otdf_python.symmetric_and_payload_config import SymmetricAndPayloadConfig -from otdf_python.policy_info import PolicyInfo -import unittest class TestHeader(unittest.TestCase): diff --git a/tests/test_inner_classes.py b/tests/test_inner_classes.py index 378abc7..3b5ea89 100644 --- a/tests/test_inner_classes.py +++ b/tests/test_inner_classes.py @@ -1,4 +1,5 @@ import unittest + from otdf_python.auth_headers import AuthHeaders from otdf_python.kas_info import KASInfo from otdf_python.policy_binding_serializer import PolicyBinding, PolicyBindingSerializer diff --git a/tests/test_kas_client.py b/tests/test_kas_client.py index 9620d45..b4c3b17 100644 --- a/tests/test_kas_client.py +++ b/tests/test_kas_client.py @@ -2,9 +2,11 @@ Unit tests for KASClient. """ -import pytest -from unittest.mock import patch, MagicMock from base64 import b64decode +from unittest.mock import MagicMock, patch + +import pytest + from otdf_python.kas_client import KASClient, KeyAccess from otdf_python.kas_key_cache import KASKeyCache from otdf_python.sdk_exceptions import SDKException diff --git a/tests/test_kas_key_cache.py b/tests/test_kas_key_cache.py index e665beb..b4e6cc7 100644 --- a/tests/test_kas_key_cache.py +++ b/tests/test_kas_key_cache.py @@ -2,9 +2,10 @@ Unit tests for KASKeyCache. """ -from otdf_python.kas_key_cache import KASKeyCache from dataclasses import dataclass +from otdf_python.kas_key_cache import KASKeyCache + @dataclass class MockKasInfo: diff --git a/tests/test_kas_key_management.py b/tests/test_kas_key_management.py index d1f1527..cb964df 100644 --- a/tests/test_kas_key_management.py +++ b/tests/test_kas_key_management.py @@ -1,11 +1,12 @@ -import unittest -from unittest.mock import Mock, patch import base64 import os +import unittest +from unittest.mock import Mock, patch + import pytest from otdf_python.kas_client import KASClient, KeyAccess -from otdf_python.key_type_constants import RSA_KEY_TYPE, EC_KEY_TYPE +from otdf_python.key_type_constants import EC_KEY_TYPE, RSA_KEY_TYPE class TestKASKeyManagement(unittest.TestCase): diff --git a/tests/test_key_type.py b/tests/test_key_type.py index 872b2c8..e899b90 100644 --- a/tests/test_key_type.py +++ b/tests/test_key_type.py @@ -1,4 +1,5 @@ import unittest + from otdf_python.key_type import KeyType diff --git a/tests/test_log_collection.py b/tests/test_log_collection.py index 2de4b75..1845290 100644 --- a/tests/test_log_collection.py +++ b/tests/test_log_collection.py @@ -5,8 +5,8 @@ This script tests the server log collection without running full pytest. """ -from tests.server_logs import collect_server_logs, log_server_logs_on_failure from tests.config_pydantic import CONFIG_TESTING +from tests.server_logs import collect_server_logs, log_server_logs_on_failure def test_log_collection(): diff --git a/tests/test_manifest.py b/tests/test_manifest.py index 49fd5fa..f9e36d1 100644 --- a/tests/test_manifest.py +++ b/tests/test_manifest.py @@ -1,11 +1,11 @@ from otdf_python.manifest import ( Manifest, - ManifestEncryptionInformation, - ManifestPayload, ManifestAssertion, - ManifestMethod, - ManifestKeyAccess, + ManifestEncryptionInformation, ManifestIntegrityInformation, + ManifestKeyAccess, + ManifestMethod, + ManifestPayload, ManifestRootSignature, ManifestSegment, ) diff --git a/tests/test_manifest_format.py b/tests/test_manifest_format.py index 38353e7..674aa26 100644 --- a/tests/test_manifest_format.py +++ b/tests/test_manifest_format.py @@ -3,10 +3,9 @@ """ import json -from otdf_python.tdf import TDF -from otdf_python.config import TDFConfig, KASInfo - +from otdf_python.config import KASInfo, TDFConfig +from otdf_python.tdf import TDF from tests.mock_crypto import generate_rsa_keypair diff --git a/tests/test_nanotdf.py b/tests/test_nanotdf.py index dd36e09..1517d1e 100644 --- a/tests/test_nanotdf.py +++ b/tests/test_nanotdf.py @@ -1,7 +1,9 @@ -import pytest import secrets -from otdf_python.nanotdf import NanoTDF, NanoTDFMaxSizeLimit, InvalidNanoTDFConfig + +import pytest + from otdf_python.config import NanoTDFConfig +from otdf_python.nanotdf import InvalidNanoTDFConfig, NanoTDF, NanoTDFMaxSizeLimit def test_nanotdf_roundtrip(): @@ -39,8 +41,8 @@ def test_nanotdf_invalid_magic(): @pytest.mark.integration def test_nanotdf_integration_encrypt_decrypt(): # Load environment variables for integration - from tests.config_pydantic import CONFIG_TDF from otdf_python.config import KASInfo + from tests.config_pydantic import CONFIG_TDF # Create KAS info from configuration kas_info = KASInfo(url=CONFIG_TDF.KAS_ENDPOINT) diff --git a/tests/test_nanotdf_ecdsa_struct.py b/tests/test_nanotdf_ecdsa_struct.py index c8b71eb..d83eb16 100644 --- a/tests/test_nanotdf_ecdsa_struct.py +++ b/tests/test_nanotdf_ecdsa_struct.py @@ -5,8 +5,8 @@ import pytest from otdf_python.nanotdf_ecdsa_struct import ( - NanoTDFECDSAStruct, IncorrectNanoTDFECDSASignatureSize, + NanoTDFECDSAStruct, ) diff --git a/tests/test_nanotdf_integration.py b/tests/test_nanotdf_integration.py index cb1d47e..943cfaf 100644 --- a/tests/test_nanotdf_integration.py +++ b/tests/test_nanotdf_integration.py @@ -1,9 +1,11 @@ +import io + import pytest -from otdf_python.nanotdf import NanoTDF -from cryptography.hazmat.primitives.asymmetric import rsa from cryptography.hazmat.primitives import serialization -import io -from otdf_python.config import NanoTDFConfig, KASInfo +from cryptography.hazmat.primitives.asymmetric import rsa + +from otdf_python.config import KASInfo, NanoTDFConfig +from otdf_python.nanotdf import NanoTDF @pytest.mark.integration diff --git a/tests/test_nanotdf_type.py b/tests/test_nanotdf_type.py index f3b614d..c93c8b8 100644 --- a/tests/test_nanotdf_type.py +++ b/tests/test_nanotdf_type.py @@ -1,10 +1,11 @@ import unittest + from otdf_python.nanotdf_type import ( + Cipher, ECCurve, - Protocol, IdentifierType, PolicyType, - Cipher, + Protocol, ) diff --git a/tests/test_policy_object.py b/tests/test_policy_object.py index f75e13d..a0ceb01 100644 --- a/tests/test_policy_object.py +++ b/tests/test_policy_object.py @@ -1,4 +1,5 @@ import unittest + from otdf_python.policy_object import AttributeObject, PolicyBody, PolicyObject diff --git a/tests/test_sdk_builder.py b/tests/test_sdk_builder.py index d3b039f..0e9f835 100644 --- a/tests/test_sdk_builder.py +++ b/tests/test_sdk_builder.py @@ -3,10 +3,11 @@ """ import os +import tempfile +from unittest.mock import MagicMock, patch + import pytest import respx -import tempfile -from unittest.mock import patch, MagicMock from otdf_python.sdk import SDK from otdf_python.sdk_builder import SDKBuilder diff --git a/tests/test_sdk_exceptions.py b/tests/test_sdk_exceptions.py index 9a5307e..92ddf4b 100644 --- a/tests/test_sdk_exceptions.py +++ b/tests/test_sdk_exceptions.py @@ -1,5 +1,6 @@ import unittest -from otdf_python.sdk_exceptions import SDKException, AutoConfigureException + +from otdf_python.sdk_exceptions import AutoConfigureException, SDKException class TestSDKExceptions(unittest.TestCase): diff --git a/tests/test_sdk_mock.py b/tests/test_sdk_mock.py index 531e309..088a452 100644 --- a/tests/test_sdk_mock.py +++ b/tests/test_sdk_mock.py @@ -1,12 +1,12 @@ from otdf_python.sdk import ( - SDK, KAS, + SDK, AttributesServiceClientInterface, - NamespaceServiceClientInterface, - SubjectMappingServiceClientInterface, - ResourceMappingServiceClientInterface, AuthorizationServiceClientInterface, KeyAccessServerRegistryServiceClientInterface, + NamespaceServiceClientInterface, + ResourceMappingServiceClientInterface, + SubjectMappingServiceClientInterface, ) diff --git a/tests/test_tdf.py b/tests/test_tdf.py index 664f0ac..699ba32 100644 --- a/tests/test_tdf.py +++ b/tests/test_tdf.py @@ -1,11 +1,12 @@ -from otdf_python.tdf import TDF, TDFReaderConfig -from otdf_python.config import TDFConfig, KASInfo -from otdf_python.manifest import Manifest import io -import zipfile import json +import zipfile + import pytest +from otdf_python.config import KASInfo, TDFConfig +from otdf_python.manifest import Manifest +from otdf_python.tdf import TDF, TDFReaderConfig from tests.mock_crypto import generate_rsa_keypair diff --git a/tests/test_tdf_key_management.py b/tests/test_tdf_key_management.py index 25cfc0c..03d1fff 100644 --- a/tests/test_tdf_key_management.py +++ b/tests/test_tdf_key_management.py @@ -1,20 +1,20 @@ -import unittest -from unittest.mock import Mock, patch import base64 import io +import unittest import zipfile +from unittest.mock import Mock, patch -from otdf_python.tdf import TDF, TDFReaderConfig from otdf_python.manifest import ( Manifest, ManifestEncryptionInformation, + ManifestIntegrityInformation, + ManifestKeyAccess, ManifestMethod, ManifestPayload, - ManifestKeyAccess, - ManifestIntegrityInformation, ManifestRootSignature, ManifestSegment, ) +from otdf_python.tdf import TDF, TDFReaderConfig class TestTDFKeyManagement(unittest.TestCase): diff --git a/tests/test_tdf_reader.py b/tests/test_tdf_reader.py index 12e09f0..5e7c634 100644 --- a/tests/test_tdf_reader.py +++ b/tests/test_tdf_reader.py @@ -4,15 +4,16 @@ import io import json -import pytest from unittest.mock import MagicMock, patch +import pytest + +from otdf_python.policy_object import PolicyObject from otdf_python.tdf_reader import ( - TDFReader, TDF_MANIFEST_FILE_NAME, TDF_PAYLOAD_FILE_NAME, + TDFReader, ) -from otdf_python.policy_object import PolicyObject class TestTDFReader: diff --git a/tests/test_tdf_writer.py b/tests/test_tdf_writer.py index e586446..4d6ff79 100644 --- a/tests/test_tdf_writer.py +++ b/tests/test_tdf_writer.py @@ -1,6 +1,7 @@ -import unittest import io +import unittest import zipfile + from otdf_python.tdf_writer import TDFWriter diff --git a/tests/test_token_source.py b/tests/test_token_source.py index 972254d..5bc9e28 100644 --- a/tests/test_token_source.py +++ b/tests/test_token_source.py @@ -3,7 +3,8 @@ """ import time -from unittest.mock import patch, MagicMock +from unittest.mock import MagicMock, patch + from otdf_python.token_source import TokenSource diff --git a/tests/test_url_normalization.py b/tests/test_url_normalization.py index 9c71351..6c6eab9 100644 --- a/tests/test_url_normalization.py +++ b/tests/test_url_normalization.py @@ -7,8 +7,8 @@ """ # Allow importing from src directory -import sys import os +import sys sys.path.insert(0, os.path.join(os.path.dirname(__file__), "..")) diff --git a/tests/test_use_plaintext_flow.py b/tests/test_use_plaintext_flow.py index dfc09e6..3019bed 100644 --- a/tests/test_use_plaintext_flow.py +++ b/tests/test_use_plaintext_flow.py @@ -2,7 +2,7 @@ Test to verify that the use_plaintext parameter flows correctly from SDKBuilder to KASClient. """ -from unittest.mock import patch, MagicMock +from unittest.mock import MagicMock, patch from otdf_python.sdk_builder import SDKBuilder diff --git a/tests/test_validate_otdf_python.py b/tests/test_validate_otdf_python.py index b2bfc3c..10b3860 100644 --- a/tests/test_validate_otdf_python.py +++ b/tests/test_validate_otdf_python.py @@ -6,13 +6,14 @@ uv run pytest tests/test_validate_otdf_python.py """ +import logging import sys import tempfile -import logging from pathlib import Path -from otdf_python.tdf import TDFReaderConfig + import pytest +from otdf_python.tdf import TDFReaderConfig from tests.integration.support_sdk import get_sdk # Set up detailed logging diff --git a/tests/test_version.py b/tests/test_version.py index 4232608..c4740cf 100644 --- a/tests/test_version.py +++ b/tests/test_version.py @@ -1,4 +1,5 @@ import unittest + from otdf_python.version import Version diff --git a/tests/test_zip_reader.py b/tests/test_zip_reader.py index 1d98120..11af01c 100644 --- a/tests/test_zip_reader.py +++ b/tests/test_zip_reader.py @@ -1,8 +1,9 @@ -import unittest import io import random -from otdf_python.zip_writer import ZipWriter +import unittest + from otdf_python.zip_reader import ZipReader +from otdf_python.zip_writer import ZipWriter class TestZipReader(unittest.TestCase): diff --git a/tests/test_zip_writer.py b/tests/test_zip_writer.py index cb1112d..bf7dccd 100644 --- a/tests/test_zip_writer.py +++ b/tests/test_zip_writer.py @@ -1,8 +1,9 @@ -import unittest import io -from otdf_python.zip_writer import ZipWriter +import unittest import zipfile +from otdf_python.zip_writer import ZipWriter + class TestZipWriter(unittest.TestCase): def test_data_and_stream(self):