markitect-main/tests/unit/prompts/test_artifact_models.py

314 lines
11 KiB
Python
Raw Normal View History

"""
Unit tests for artifact models.
Tests Artifact, ArtifactMetadata, ArtifactReference, and content digest
calculation functions.
"""
import pytest
from datetime import datetime
from markitect.prompts.models import (
Artifact,
ArtifactMetadata,
ArtifactReference,
ArtifactType,
calculate_content_digest,
calculate_bundle_digest,
)
class TestArtifactMetadata:
"""Tests for ArtifactMetadata."""
def test_create_empty_metadata(self):
"""Test creating metadata with defaults."""
metadata = ArtifactMetadata()
assert metadata.description is None
assert metadata.tags == []
assert metadata.author is None
assert metadata.version is None
assert metadata.custom == {}
def test_create_metadata_with_values(self):
"""Test creating metadata with values."""
metadata = ArtifactMetadata(
description="Test artifact",
tags=["test", "example"],
author="test-author",
version="1.0.0",
custom={"key": "value"}
)
assert metadata.description == "Test artifact"
assert metadata.tags == ["test", "example"]
assert metadata.author == "test-author"
assert metadata.version == "1.0.0"
assert metadata.custom == {"key": "value"}
def test_metadata_to_dict(self):
"""Test metadata serialization to dict."""
metadata = ArtifactMetadata(
description="Test",
tags=["tag1"],
author="author",
)
data = metadata.to_dict()
assert data["description"] == "Test"
assert data["tags"] == ["tag1"]
assert data["author"] == "author"
assert data["version"] is None
assert data["custom"] == {}
def test_metadata_from_dict(self):
"""Test metadata deserialization from dict."""
data = {
"description": "Test",
"tags": ["tag1", "tag2"],
"author": "author",
"version": "2.0",
"custom": {"extra": "data"}
}
metadata = ArtifactMetadata.from_dict(data)
assert metadata.description == "Test"
assert metadata.tags == ["tag1", "tag2"]
assert metadata.author == "author"
assert metadata.version == "2.0"
assert metadata.custom == {"extra": "data"}
class TestArtifact:
"""Tests for Artifact model."""
def test_create_artifact(self):
"""Test artifact creation with automatic digest calculation."""
content = "# Test Content\n\nThis is a test."
artifact = Artifact.create(
space_id="space-1",
name="test-artifact",
content=content,
)
assert artifact.id # UUID generated
assert artifact.space_id == "space-1"
assert artifact.name == "test-artifact"
assert artifact.artifact_type == ArtifactType.CONTENT
assert artifact.content_digest == calculate_content_digest(content)
assert artifact.content_size == len(content.encode('utf-8'))
assert isinstance(artifact.metadata, ArtifactMetadata)
assert isinstance(artifact.created_at, datetime)
assert isinstance(artifact.updated_at, datetime)
def test_create_artifact_with_type(self):
"""Test creating artifact with specific type."""
artifact = Artifact.create(
space_id="space-1",
name="my-template",
content="Template content",
artifact_type=ArtifactType.TEMPLATE,
)
assert artifact.artifact_type == ArtifactType.TEMPLATE
def test_create_artifact_with_metadata(self):
"""Test creating artifact with metadata."""
metadata = ArtifactMetadata(description="Test", tags=["tag1"])
artifact = Artifact.create(
space_id="space-1",
name="test",
content="content",
metadata=metadata,
)
assert artifact.metadata.description == "Test"
assert artifact.metadata.tags == ["tag1"]
def test_update_content(self):
"""Test updating artifact content."""
artifact = Artifact.create(
space_id="space-1",
name="test",
content="original content",
)
original_digest = artifact.content_digest
original_updated_at = artifact.updated_at
# Small delay to ensure timestamp difference
import time
time.sleep(0.01)
new_content = "updated content"
artifact.update_content(new_content)
assert artifact.content_digest != original_digest
assert artifact.content_digest == calculate_content_digest(new_content)
assert artifact.content_size == len(new_content.encode('utf-8'))
assert artifact.updated_at > original_updated_at
def test_has_changed(self):
"""Test change detection."""
content = "original"
artifact = Artifact.create(
space_id="space-1",
name="test",
content=content,
)
# Same content - no change
assert not artifact.has_changed(calculate_content_digest(content))
# Different content - changed
assert artifact.has_changed(calculate_content_digest("different"))
def test_artifact_to_dict(self):
"""Test artifact serialization."""
artifact = Artifact.create(
space_id="space-1",
name="test",
content="content",
artifact_type=ArtifactType.TEMPLATE,
)
data = artifact.to_dict()
assert data["id"] == artifact.id
assert data["space_id"] == "space-1"
assert data["name"] == "test"
assert data["artifact_type"] == "template"
assert data["content_digest"] == artifact.content_digest
assert data["content_size"] == artifact.content_size
assert "metadata" in data
assert "created_at" in data
assert "updated_at" in data
def test_artifact_from_dict(self):
"""Test artifact deserialization."""
original = Artifact.create(
space_id="space-1",
name="test",
content="content",
)
data = original.to_dict()
restored = Artifact.from_dict(data)
assert restored.id == original.id
assert restored.space_id == original.space_id
assert restored.name == original.name
assert restored.artifact_type == original.artifact_type
assert restored.content_digest == original.content_digest
assert restored.content_size == original.content_size
class TestArtifactReference:
"""Tests for ArtifactReference."""
def test_simple_reference(self):
"""Test simple artifact name reference."""
ref = ArtifactReference(name="my-artifact")
assert ref.name == "my-artifact"
assert ref.space_id is None
assert ref.version is None
assert str(ref) == "my-artifact"
def test_reference_with_space(self):
"""Test reference with space ID."""
ref = ArtifactReference(name="artifact", space_id="space-1")
assert ref.name == "artifact"
assert ref.space_id == "space-1"
assert str(ref) == "space-1:artifact"
def test_reference_with_version(self):
"""Test reference with version."""
ref = ArtifactReference(name="artifact", version="1.0.0")
assert ref.name == "artifact"
assert ref.version == "1.0.0"
assert str(ref) == "artifact@1.0.0"
def test_reference_with_space_and_version(self):
"""Test fully qualified reference."""
ref = ArtifactReference(
name="artifact",
space_id="space-1",
version="2.0.0"
)
assert str(ref) == "space-1:artifact@2.0.0"
def test_parse_simple_reference(self):
"""Test parsing simple reference."""
ref = ArtifactReference.parse("my-artifact")
assert ref.name == "my-artifact"
assert ref.space_id is None
assert ref.version is None
def test_parse_space_reference(self):
"""Test parsing space:artifact reference."""
ref = ArtifactReference.parse("space-1:my-artifact")
assert ref.name == "my-artifact"
assert ref.space_id == "space-1"
assert ref.version is None
def test_parse_version_reference(self):
"""Test parsing artifact@version reference."""
ref = ArtifactReference.parse("my-artifact@1.0.0")
assert ref.name == "my-artifact"
assert ref.space_id is None
assert ref.version == "1.0.0"
def test_parse_full_reference(self):
"""Test parsing space:artifact@version reference."""
ref = ArtifactReference.parse("space-1:my-artifact@2.0.0")
assert ref.name == "my-artifact"
assert ref.space_id == "space-1"
assert ref.version == "2.0.0"
def test_parse_invalid_reference(self):
"""Test parsing invalid reference raises error."""
with pytest.raises(ValueError, match="Invalid artifact reference"):
ArtifactReference.parse("space-1:")
with pytest.raises(ValueError, match="Invalid artifact reference"):
ArtifactReference.parse("@1.0.0")
class TestContentDigest:
"""Tests for content digest functions."""
def test_calculate_content_digest(self):
"""Test SHA-256 digest calculation."""
content = "Hello, World!"
digest = calculate_content_digest(content)
# SHA-256 produces 64 hex characters
assert len(digest) == 64
assert digest.isalnum()
def test_digest_consistency(self):
"""Test digest is consistent for same content."""
content = "Test content"
digest1 = calculate_content_digest(content)
digest2 = calculate_content_digest(content)
assert digest1 == digest2
def test_digest_changes_with_content(self):
"""Test digest changes when content changes."""
digest1 = calculate_content_digest("content 1")
digest2 = calculate_content_digest("content 2")
assert digest1 != digest2
def test_calculate_bundle_digest(self):
"""Test bundle digest calculation."""
components = {
"template": "abc123",
"dep1": "def456",
"dep2": "ghi789",
}
digest = calculate_bundle_digest(components)
assert len(digest) == 64
def test_bundle_digest_order_independent(self):
"""Test bundle digest is deterministic regardless of dict order."""
components1 = {"a": "1", "b": "2", "c": "3"}
components2 = {"c": "3", "a": "1", "b": "2"}
assert calculate_bundle_digest(components1) == calculate_bundle_digest(components2)
def test_bundle_digest_sensitive_to_values(self):
"""Test bundle digest changes when values change."""
digest1 = calculate_bundle_digest({"a": "1", "b": "2"})
digest2 = calculate_bundle_digest({"a": "1", "b": "3"})
assert digest1 != digest2