mirror of
https://github.com/crewAIInc/crewAI.git
synced 2026-01-07 15:18:29 +00:00
* adding fingerprints * fixed * fix * Fix Pydantic v2 compatibility in SecurityConfig and Fingerprint classes (#2335) * Fix Pydantic v2 compatibility in SecurityConfig and Fingerprint classes Co-Authored-By: Joe Moura <joao@crewai.com> * Fix type-checker errors in fingerprint properties Co-Authored-By: Joe Moura <joao@crewai.com> * Enhance security validation in Fingerprint and SecurityConfig classes Co-Authored-By: Joe Moura <joao@crewai.com> --------- Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Co-authored-by: Joe Moura <joao@crewai.com> * incorporate small improvements / changes * Expect different * Remove redundant null check in Crew.fingerprint property (#2342) * Remove redundant null check in Crew.fingerprint property and add security module Co-Authored-By: Joe Moura <joao@crewai.com> * Enhance security module with type hints, improved UUID namespace, metadata validation, and versioning Co-Authored-By: Joe Moura <joao@crewai.com> --------- Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Co-authored-by: Joe Moura <joao@crewai.com> Co-authored-by: João Moura <joaomdmoura@gmail.com> --------- Co-authored-by: devin-ai-integration[bot] <158243242+devin-ai-integration[bot]@users.noreply.github.com> Co-authored-by: Joe Moura <joao@crewai.com> Co-authored-by: Brandon Hancock <brandon@brandonhancock.io>
263 lines
8.2 KiB
Python
263 lines
8.2 KiB
Python
"""Test for the Fingerprint class."""
|
|
|
|
import json
|
|
import uuid
|
|
from datetime import datetime, timedelta
|
|
|
|
import pytest
|
|
from pydantic import ValidationError
|
|
|
|
from crewai.security import Fingerprint
|
|
|
|
|
|
def test_fingerprint_creation_with_defaults():
|
|
"""Test creating a Fingerprint with default values."""
|
|
fingerprint = Fingerprint()
|
|
|
|
# Check that a UUID was generated
|
|
assert fingerprint.uuid_str is not None
|
|
# Check that it's a valid UUID
|
|
uuid_obj = uuid.UUID(fingerprint.uuid_str)
|
|
assert isinstance(uuid_obj, uuid.UUID)
|
|
|
|
# Check that creation time was set
|
|
assert isinstance(fingerprint.created_at, datetime)
|
|
|
|
# Check that metadata is an empty dict
|
|
assert fingerprint.metadata == {}
|
|
|
|
|
|
def test_fingerprint_creation_with_metadata():
|
|
"""Test creating a Fingerprint with custom metadata only."""
|
|
metadata = {"version": "1.0", "author": "Test Author"}
|
|
|
|
fingerprint = Fingerprint(metadata=metadata)
|
|
|
|
# UUID and created_at should be auto-generated
|
|
assert fingerprint.uuid_str is not None
|
|
assert isinstance(fingerprint.created_at, datetime)
|
|
# Only metadata should be settable
|
|
assert fingerprint.metadata == metadata
|
|
|
|
|
|
def test_fingerprint_uuid_cannot_be_set():
|
|
"""Test that uuid_str cannot be manually set."""
|
|
original_uuid = "b723c6ff-95de-5e87-860b-467b72282bd8"
|
|
|
|
# Attempt to set uuid_str
|
|
fingerprint = Fingerprint(uuid_str=original_uuid)
|
|
|
|
# UUID should be generated, not set to our value
|
|
assert fingerprint.uuid_str != original_uuid
|
|
assert uuid.UUID(fingerprint.uuid_str) # Should be a valid UUID
|
|
|
|
|
|
def test_fingerprint_created_at_cannot_be_set():
|
|
"""Test that created_at cannot be manually set."""
|
|
original_time = datetime.now() - timedelta(days=1)
|
|
|
|
# Attempt to set created_at
|
|
fingerprint = Fingerprint(created_at=original_time)
|
|
|
|
# created_at should be auto-generated, not set to our value
|
|
assert fingerprint.created_at != original_time
|
|
assert fingerprint.created_at > original_time # Should be more recent
|
|
|
|
|
|
def test_fingerprint_uuid_property():
|
|
"""Test the uuid property returns a UUID object."""
|
|
fingerprint = Fingerprint()
|
|
|
|
assert isinstance(fingerprint.uuid, uuid.UUID)
|
|
assert str(fingerprint.uuid) == fingerprint.uuid_str
|
|
|
|
|
|
def test_fingerprint_deterministic_generation():
|
|
"""Test that the same seed string always generates the same fingerprint using generate method."""
|
|
seed = "test-seed"
|
|
|
|
# Use the generate method which supports deterministic generation
|
|
fingerprint1 = Fingerprint.generate(seed)
|
|
fingerprint2 = Fingerprint.generate(seed)
|
|
|
|
assert fingerprint1.uuid_str == fingerprint2.uuid_str
|
|
|
|
# Also test with _generate_uuid method directly
|
|
uuid_str1 = Fingerprint._generate_uuid(seed)
|
|
uuid_str2 = Fingerprint._generate_uuid(seed)
|
|
assert uuid_str1 == uuid_str2
|
|
|
|
|
|
def test_fingerprint_generate_classmethod():
|
|
"""Test the generate class method."""
|
|
# Without seed
|
|
fingerprint1 = Fingerprint.generate()
|
|
assert isinstance(fingerprint1, Fingerprint)
|
|
|
|
# With seed
|
|
seed = "test-seed"
|
|
metadata = {"version": "1.0"}
|
|
fingerprint2 = Fingerprint.generate(seed, metadata)
|
|
|
|
assert isinstance(fingerprint2, Fingerprint)
|
|
assert fingerprint2.metadata == metadata
|
|
|
|
# Same seed should generate same UUID
|
|
fingerprint3 = Fingerprint.generate(seed)
|
|
assert fingerprint2.uuid_str == fingerprint3.uuid_str
|
|
|
|
|
|
def test_fingerprint_string_representation():
|
|
"""Test the string representation of Fingerprint."""
|
|
fingerprint = Fingerprint()
|
|
uuid_str = fingerprint.uuid_str
|
|
|
|
string_repr = str(fingerprint)
|
|
assert uuid_str in string_repr
|
|
|
|
|
|
def test_fingerprint_equality():
|
|
"""Test fingerprint equality comparison."""
|
|
# Using generate with the same seed to get consistent UUIDs
|
|
seed = "test-equality"
|
|
|
|
fingerprint1 = Fingerprint.generate(seed)
|
|
fingerprint2 = Fingerprint.generate(seed)
|
|
fingerprint3 = Fingerprint()
|
|
|
|
assert fingerprint1 == fingerprint2
|
|
assert fingerprint1 != fingerprint3
|
|
|
|
|
|
def test_fingerprint_hash():
|
|
"""Test that fingerprints can be used as dictionary keys."""
|
|
# Using generate with the same seed to get consistent UUIDs
|
|
seed = "test-hash"
|
|
|
|
fingerprint1 = Fingerprint.generate(seed)
|
|
fingerprint2 = Fingerprint.generate(seed)
|
|
|
|
# Hash should be consistent for same UUID
|
|
assert hash(fingerprint1) == hash(fingerprint2)
|
|
|
|
# Can be used as dict keys
|
|
fingerprint_dict = {fingerprint1: "value"}
|
|
assert fingerprint_dict[fingerprint2] == "value"
|
|
|
|
|
|
def test_fingerprint_to_dict():
|
|
"""Test converting fingerprint to dictionary."""
|
|
metadata = {"version": "1.0"}
|
|
fingerprint = Fingerprint(metadata=metadata)
|
|
|
|
uuid_str = fingerprint.uuid_str
|
|
created_at = fingerprint.created_at
|
|
|
|
fingerprint_dict = fingerprint.to_dict()
|
|
|
|
assert fingerprint_dict["uuid_str"] == uuid_str
|
|
assert fingerprint_dict["created_at"] == created_at.isoformat()
|
|
assert fingerprint_dict["metadata"] == metadata
|
|
|
|
|
|
def test_fingerprint_from_dict():
|
|
"""Test creating fingerprint from dictionary."""
|
|
uuid_str = "b723c6ff-95de-5e87-860b-467b72282bd8"
|
|
created_at = datetime.now()
|
|
created_at_iso = created_at.isoformat()
|
|
metadata = {"version": "1.0"}
|
|
|
|
fingerprint_dict = {
|
|
"uuid_str": uuid_str,
|
|
"created_at": created_at_iso,
|
|
"metadata": metadata
|
|
}
|
|
|
|
fingerprint = Fingerprint.from_dict(fingerprint_dict)
|
|
|
|
assert fingerprint.uuid_str == uuid_str
|
|
assert fingerprint.created_at.isoformat() == created_at_iso
|
|
assert fingerprint.metadata == metadata
|
|
|
|
|
|
def test_fingerprint_json_serialization():
|
|
"""Test that Fingerprint can be JSON serialized and deserialized."""
|
|
# Create a fingerprint, get its values
|
|
metadata = {"version": "1.0"}
|
|
fingerprint = Fingerprint(metadata=metadata)
|
|
|
|
uuid_str = fingerprint.uuid_str
|
|
created_at = fingerprint.created_at
|
|
|
|
# Convert to dict and then JSON
|
|
fingerprint_dict = fingerprint.to_dict()
|
|
json_str = json.dumps(fingerprint_dict)
|
|
|
|
# Parse JSON and create new fingerprint
|
|
parsed_dict = json.loads(json_str)
|
|
new_fingerprint = Fingerprint.from_dict(parsed_dict)
|
|
|
|
assert new_fingerprint.uuid_str == uuid_str
|
|
assert new_fingerprint.created_at.isoformat() == created_at.isoformat()
|
|
assert new_fingerprint.metadata == metadata
|
|
|
|
|
|
def test_invalid_uuid_str():
|
|
"""Test handling of invalid UUID strings."""
|
|
uuid_str = "not-a-valid-uuid"
|
|
created_at = datetime.now().isoformat()
|
|
|
|
fingerprint_dict = {
|
|
"uuid_str": uuid_str,
|
|
"created_at": created_at,
|
|
"metadata": {}
|
|
}
|
|
|
|
# The Fingerprint.from_dict method accepts even invalid UUIDs
|
|
# This seems to be the current behavior
|
|
fingerprint = Fingerprint.from_dict(fingerprint_dict)
|
|
|
|
# Verify it uses the provided UUID string, even if invalid
|
|
# This might not be ideal behavior, but it's the current implementation
|
|
assert fingerprint.uuid_str == uuid_str
|
|
|
|
# But this will raise an exception when we try to access the uuid property
|
|
with pytest.raises(ValueError):
|
|
uuid_obj = fingerprint.uuid
|
|
|
|
|
|
def test_fingerprint_metadata_mutation():
|
|
"""Test that metadata can be modified after fingerprint creation."""
|
|
# Create a fingerprint with initial metadata
|
|
initial_metadata = {"version": "1.0", "status": "draft"}
|
|
fingerprint = Fingerprint(metadata=initial_metadata)
|
|
|
|
# Verify initial metadata
|
|
assert fingerprint.metadata == initial_metadata
|
|
|
|
# Modify the metadata
|
|
fingerprint.metadata["status"] = "published"
|
|
fingerprint.metadata["author"] = "Test Author"
|
|
|
|
# Verify the modifications
|
|
expected_metadata = {
|
|
"version": "1.0",
|
|
"status": "published",
|
|
"author": "Test Author"
|
|
}
|
|
assert fingerprint.metadata == expected_metadata
|
|
|
|
# Make sure the UUID and creation time remain unchanged
|
|
uuid_str = fingerprint.uuid_str
|
|
created_at = fingerprint.created_at
|
|
|
|
# Completely replace the metadata
|
|
new_metadata = {"version": "2.0", "environment": "production"}
|
|
fingerprint.metadata = new_metadata
|
|
|
|
# Verify the replacement
|
|
assert fingerprint.metadata == new_metadata
|
|
|
|
# Ensure immutable fields remain unchanged
|
|
assert fingerprint.uuid_str == uuid_str
|
|
assert fingerprint.created_at == created_at |