300 lines
9.5 KiB
Python
300 lines
9.5 KiB
Python
|
|
"""
|
||
|
|
Unit tests for Information Space models.
|
||
|
|
|
||
|
|
Tests the core domain models: InformationSpace, SpaceDocument, SpaceConfig, SpaceMetadata.
|
||
|
|
"""
|
||
|
|
|
||
|
|
import pytest
|
||
|
|
from datetime import datetime
|
||
|
|
from markitect.spaces.models import (
|
||
|
|
InformationSpace,
|
||
|
|
SpaceDocument,
|
||
|
|
SpaceConfig,
|
||
|
|
SpaceMetadata,
|
||
|
|
SpaceVariable,
|
||
|
|
TransclusionReference,
|
||
|
|
SpaceStatus,
|
||
|
|
)
|
||
|
|
|
||
|
|
|
||
|
|
class TestSpaceMetadata:
|
||
|
|
"""Tests for SpaceMetadata dataclass."""
|
||
|
|
|
||
|
|
def test_default_metadata(self):
|
||
|
|
"""Test default metadata values."""
|
||
|
|
metadata = SpaceMetadata()
|
||
|
|
assert metadata.tags == []
|
||
|
|
assert metadata.author is None
|
||
|
|
assert metadata.custom == {}
|
||
|
|
|
||
|
|
def test_metadata_with_values(self):
|
||
|
|
"""Test metadata with custom values."""
|
||
|
|
metadata = SpaceMetadata(
|
||
|
|
tags=["api", "docs"],
|
||
|
|
author="test-user",
|
||
|
|
custom={"version": "1.0"}
|
||
|
|
)
|
||
|
|
assert metadata.tags == ["api", "docs"]
|
||
|
|
assert metadata.author == "test-user"
|
||
|
|
assert metadata.custom["version"] == "1.0"
|
||
|
|
|
||
|
|
def test_metadata_to_dict(self):
|
||
|
|
"""Test metadata serialization."""
|
||
|
|
metadata = SpaceMetadata(tags=["test"], author="user")
|
||
|
|
data = metadata.to_dict()
|
||
|
|
assert data["tags"] == ["test"]
|
||
|
|
assert data["author"] == "user"
|
||
|
|
|
||
|
|
def test_metadata_from_dict(self):
|
||
|
|
"""Test metadata deserialization."""
|
||
|
|
data = {"tags": ["api"], "author": "admin", "custom": {"key": "value"}}
|
||
|
|
metadata = SpaceMetadata.from_dict(data)
|
||
|
|
assert metadata.tags == ["api"]
|
||
|
|
assert metadata.author == "admin"
|
||
|
|
assert metadata.custom["key"] == "value"
|
||
|
|
|
||
|
|
|
||
|
|
class TestSpaceConfig:
|
||
|
|
"""Tests for SpaceConfig dataclass."""
|
||
|
|
|
||
|
|
def test_default_config(self):
|
||
|
|
"""Test default configuration values."""
|
||
|
|
config = SpaceConfig()
|
||
|
|
assert config.default_variant == "hierarchical"
|
||
|
|
assert config.enable_caching is True
|
||
|
|
assert config.theme is None
|
||
|
|
assert config.history_enabled is False
|
||
|
|
assert config.history_backend == "git"
|
||
|
|
|
||
|
|
def test_config_with_history_enabled(self):
|
||
|
|
"""Test config with git history enabled."""
|
||
|
|
config = SpaceConfig(history_enabled=True, history_backend="git")
|
||
|
|
assert config.history_enabled is True
|
||
|
|
assert config.history_backend == "git"
|
||
|
|
|
||
|
|
def test_config_to_dict(self):
|
||
|
|
"""Test config serialization."""
|
||
|
|
config = SpaceConfig(theme="dark", enable_caching=False)
|
||
|
|
data = config.to_dict()
|
||
|
|
assert data["theme"] == "dark"
|
||
|
|
assert data["enable_caching"] is False
|
||
|
|
|
||
|
|
def test_config_from_dict(self):
|
||
|
|
"""Test config deserialization."""
|
||
|
|
data = {"default_variant": "flat", "history_enabled": True}
|
||
|
|
config = SpaceConfig.from_dict(data)
|
||
|
|
assert config.default_variant == "flat"
|
||
|
|
assert config.history_enabled is True
|
||
|
|
|
||
|
|
|
||
|
|
class TestSpaceDocument:
|
||
|
|
"""Tests for SpaceDocument dataclass."""
|
||
|
|
|
||
|
|
def test_default_document(self):
|
||
|
|
"""Test default document values."""
|
||
|
|
doc = SpaceDocument()
|
||
|
|
assert doc.id is not None
|
||
|
|
assert doc.space_path == ""
|
||
|
|
assert doc.order_index == 0
|
||
|
|
assert doc.metadata == {}
|
||
|
|
|
||
|
|
def test_document_with_values(self):
|
||
|
|
"""Test document with custom values."""
|
||
|
|
doc = SpaceDocument(
|
||
|
|
space_id="space-1",
|
||
|
|
document_id="doc-1",
|
||
|
|
space_path="/intro.md",
|
||
|
|
order_index=1,
|
||
|
|
content_hash="abc123"
|
||
|
|
)
|
||
|
|
assert doc.space_id == "space-1"
|
||
|
|
assert doc.space_path == "/intro.md"
|
||
|
|
assert doc.content_hash == "abc123"
|
||
|
|
|
||
|
|
def test_document_to_dict(self):
|
||
|
|
"""Test document serialization."""
|
||
|
|
doc = SpaceDocument(space_path="/test.md")
|
||
|
|
data = doc.to_dict()
|
||
|
|
assert data["space_path"] == "/test.md"
|
||
|
|
assert "id" in data
|
||
|
|
assert "added_at" in data
|
||
|
|
|
||
|
|
def test_document_from_dict(self):
|
||
|
|
"""Test document deserialization."""
|
||
|
|
data = {
|
||
|
|
"id": "doc-123",
|
||
|
|
"space_id": "space-1",
|
||
|
|
"space_path": "/api.md",
|
||
|
|
"order_index": 5
|
||
|
|
}
|
||
|
|
doc = SpaceDocument.from_dict(data)
|
||
|
|
assert doc.id == "doc-123"
|
||
|
|
assert doc.space_path == "/api.md"
|
||
|
|
assert doc.order_index == 5
|
||
|
|
|
||
|
|
|
||
|
|
class TestInformationSpace:
|
||
|
|
"""Tests for InformationSpace dataclass."""
|
||
|
|
|
||
|
|
def test_space_requires_name(self):
|
||
|
|
"""Test that space name is required."""
|
||
|
|
with pytest.raises(ValueError, match="Space name is required"):
|
||
|
|
InformationSpace(name="")
|
||
|
|
|
||
|
|
def test_space_default_values(self):
|
||
|
|
"""Test default space values."""
|
||
|
|
space = InformationSpace(name="test-space")
|
||
|
|
assert space.name == "test-space"
|
||
|
|
assert space.id is not None
|
||
|
|
assert space.status == SpaceStatus.DRAFT
|
||
|
|
assert space.description is None
|
||
|
|
assert space.parent_space_id is None
|
||
|
|
|
||
|
|
def test_space_with_config(self):
|
||
|
|
"""Test space with custom config."""
|
||
|
|
config = SpaceConfig(theme="minimal", history_enabled=True)
|
||
|
|
space = InformationSpace(
|
||
|
|
name="docs",
|
||
|
|
description="Documentation space",
|
||
|
|
config=config
|
||
|
|
)
|
||
|
|
assert space.config.theme == "minimal"
|
||
|
|
assert space.config.history_enabled is True
|
||
|
|
|
||
|
|
def test_space_activation(self):
|
||
|
|
"""Test space lifecycle transitions."""
|
||
|
|
space = InformationSpace(name="test")
|
||
|
|
assert space.status == SpaceStatus.DRAFT
|
||
|
|
|
||
|
|
space.activate()
|
||
|
|
assert space.status == SpaceStatus.ACTIVE
|
||
|
|
|
||
|
|
space.archive()
|
||
|
|
assert space.status == SpaceStatus.ARCHIVED
|
||
|
|
|
||
|
|
def test_space_touch_updates_timestamp(self):
|
||
|
|
"""Test that touch() updates the timestamp."""
|
||
|
|
space = InformationSpace(name="test")
|
||
|
|
original_updated = space.updated_at
|
||
|
|
|
||
|
|
import time
|
||
|
|
time.sleep(0.01) # Small delay to ensure timestamp changes
|
||
|
|
|
||
|
|
space.touch()
|
||
|
|
assert space.updated_at >= original_updated
|
||
|
|
|
||
|
|
def test_space_to_dict(self):
|
||
|
|
"""Test space serialization."""
|
||
|
|
space = InformationSpace(
|
||
|
|
name="api-docs",
|
||
|
|
description="API Documentation"
|
||
|
|
)
|
||
|
|
data = space.to_dict()
|
||
|
|
|
||
|
|
assert data["name"] == "api-docs"
|
||
|
|
assert data["description"] == "API Documentation"
|
||
|
|
assert data["status"] == "draft"
|
||
|
|
assert "id" in data
|
||
|
|
assert "created_at" in data
|
||
|
|
|
||
|
|
def test_space_from_dict(self):
|
||
|
|
"""Test space deserialization."""
|
||
|
|
data = {
|
||
|
|
"id": "space-123",
|
||
|
|
"name": "my-space",
|
||
|
|
"description": "Test space",
|
||
|
|
"status": "active",
|
||
|
|
"config": {"history_enabled": True},
|
||
|
|
"metadata": {"tags": ["test"]}
|
||
|
|
}
|
||
|
|
space = InformationSpace.from_dict(data)
|
||
|
|
|
||
|
|
assert space.id == "space-123"
|
||
|
|
assert space.name == "my-space"
|
||
|
|
assert space.status == SpaceStatus.ACTIVE
|
||
|
|
assert space.config.history_enabled is True
|
||
|
|
assert space.metadata.tags == ["test"]
|
||
|
|
|
||
|
|
def test_space_roundtrip_serialization(self):
|
||
|
|
"""Test that to_dict and from_dict are inverse operations."""
|
||
|
|
original = InformationSpace(
|
||
|
|
name="roundtrip-test",
|
||
|
|
description="Testing serialization",
|
||
|
|
config=SpaceConfig(theme="dark", history_enabled=True),
|
||
|
|
metadata=SpaceMetadata(tags=["test", "roundtrip"])
|
||
|
|
)
|
||
|
|
original.activate()
|
||
|
|
|
||
|
|
data = original.to_dict()
|
||
|
|
restored = InformationSpace.from_dict(data)
|
||
|
|
|
||
|
|
assert restored.name == original.name
|
||
|
|
assert restored.description == original.description
|
||
|
|
assert restored.status == original.status
|
||
|
|
assert restored.config.theme == original.config.theme
|
||
|
|
assert restored.metadata.tags == original.metadata.tags
|
||
|
|
|
||
|
|
|
||
|
|
class TestSpaceVariable:
|
||
|
|
"""Tests for SpaceVariable dataclass."""
|
||
|
|
|
||
|
|
def test_variable_creation(self):
|
||
|
|
"""Test variable creation."""
|
||
|
|
var = SpaceVariable(
|
||
|
|
space_id="space-1",
|
||
|
|
name="version",
|
||
|
|
value="1.0.0"
|
||
|
|
)
|
||
|
|
assert var.name == "version"
|
||
|
|
assert var.value == "1.0.0"
|
||
|
|
assert var.scope == "space"
|
||
|
|
|
||
|
|
def test_variable_to_dict(self):
|
||
|
|
"""Test variable serialization."""
|
||
|
|
var = SpaceVariable(
|
||
|
|
space_id="space-1",
|
||
|
|
name="config",
|
||
|
|
value={"key": "value"},
|
||
|
|
scope="document"
|
||
|
|
)
|
||
|
|
data = var.to_dict()
|
||
|
|
assert data["name"] == "config"
|
||
|
|
assert data["scope"] == "document"
|
||
|
|
|
||
|
|
|
||
|
|
class TestTransclusionReference:
|
||
|
|
"""Tests for TransclusionReference dataclass."""
|
||
|
|
|
||
|
|
def test_reference_creation(self):
|
||
|
|
"""Test transclusion reference creation."""
|
||
|
|
ref = TransclusionReference(
|
||
|
|
source_doc_id="doc-1",
|
||
|
|
target_doc_id="doc-2",
|
||
|
|
space_id="space-1"
|
||
|
|
)
|
||
|
|
assert ref.source_doc_id == "doc-1"
|
||
|
|
assert ref.target_doc_id == "doc-2"
|
||
|
|
assert ref.created_at is not None
|
||
|
|
|
||
|
|
def test_reference_to_dict(self):
|
||
|
|
"""Test reference serialization."""
|
||
|
|
ref = TransclusionReference(
|
||
|
|
source_doc_id="a",
|
||
|
|
target_doc_id="b",
|
||
|
|
space_id="s"
|
||
|
|
)
|
||
|
|
data = ref.to_dict()
|
||
|
|
assert "created_at" in data
|
||
|
|
assert data["source_doc_id"] == "a"
|
||
|
|
|
||
|
|
|
||
|
|
class TestSpaceStatus:
|
||
|
|
"""Tests for SpaceStatus enum."""
|
||
|
|
|
||
|
|
def test_status_values(self):
|
||
|
|
"""Test status enum values."""
|
||
|
|
assert SpaceStatus.DRAFT.value == "draft"
|
||
|
|
assert SpaceStatus.ACTIVE.value == "active"
|
||
|
|
assert SpaceStatus.ARCHIVED.value == "archived"
|
||
|
|
assert SpaceStatus.DELETED.value == "deleted"
|