feat: Add Alateya, Clan, Eonarch agents + fix gateway-router connection
## Agents Added - Alateya: R&D, biotech, innovations - Clan (Spirit): Community spirit agent - Eonarch: Consciousness evolution agent ## Changes - docker-compose.node1.yml: Added tokens for all 3 new agents - gateway-bot/http_api.py: Added configs and webhook endpoints - gateway-bot/clan_prompt.txt: New prompt file - gateway-bot/eonarch_prompt.txt: New prompt file ## Fixes - Fixed ROUTER_URL from :9102 to :8000 (internal container port) - All 9 Telegram agents now working ## Documentation - Created PROJECT-MASTER-INDEX.md - single entry point - Added various status documents and scripts Tokens configured: - Helion, NUTRA, Agromatrix (existing) - Alateya, Clan, Eonarch (new) - Druid, GreenFood, DAARWIZZ (configured)
This commit is contained in:
282
services/memory/qdrant/tests/test_payload_validation.py
Normal file
282
services/memory/qdrant/tests/test_payload_validation.py
Normal file
@@ -0,0 +1,282 @@
|
||||
"""
|
||||
Unit tests for Co-Memory payload validation.
|
||||
"""
|
||||
|
||||
import pytest
|
||||
from datetime import datetime
|
||||
|
||||
from services.memory.qdrant.payload_validation import (
|
||||
validate_payload,
|
||||
PayloadValidationError,
|
||||
create_minimal_payload,
|
||||
)
|
||||
|
||||
|
||||
class TestPayloadValidation:
|
||||
"""Tests for payload validation."""
|
||||
|
||||
def test_valid_minimal_payload(self):
|
||||
"""Test that minimal valid payload passes validation."""
|
||||
payload = create_minimal_payload(
|
||||
tenant_id="t_daarion",
|
||||
source_id="doc_01HQ8K9X2NPQR3FGJKLM5678",
|
||||
chunk_id="chk_01HQ8K9X3MPQR3FGJKLM9012",
|
||||
chunk_idx=0,
|
||||
fingerprint="sha256:abc123",
|
||||
team_id="team_core",
|
||||
)
|
||||
|
||||
# Should not raise
|
||||
result = validate_payload(payload)
|
||||
assert result["schema_version"] == "cm_payload_v1"
|
||||
assert result["tenant_id"] == "t_daarion"
|
||||
|
||||
def test_valid_full_payload(self):
|
||||
"""Test that full payload with all fields passes validation."""
|
||||
payload = {
|
||||
"schema_version": "cm_payload_v1",
|
||||
"tenant_id": "t_daarion",
|
||||
"team_id": "team_core",
|
||||
"project_id": "proj_helion",
|
||||
"agent_id": "agt_helion",
|
||||
"owner_kind": "agent",
|
||||
"owner_id": "agt_helion",
|
||||
"scope": "docs",
|
||||
"visibility": "confidential",
|
||||
"indexed": True,
|
||||
"source_kind": "document",
|
||||
"source_id": "doc_01HQ8K9X2NPQR3FGJKLM5678",
|
||||
"chunk": {
|
||||
"chunk_id": "chk_01HQ8K9X3MPQR3FGJKLM9012",
|
||||
"chunk_idx": 0,
|
||||
},
|
||||
"fingerprint": "sha256:abc123def456",
|
||||
"created_at": "2026-01-26T12:00:00Z",
|
||||
"acl": {
|
||||
"read_team_ids": ["team_core"],
|
||||
"read_agent_ids": ["agt_nutra"],
|
||||
},
|
||||
"tags": ["product", "features"],
|
||||
"lang": "uk",
|
||||
"importance": 0.8,
|
||||
"embedding": {
|
||||
"model": "cohere-embed-v3",
|
||||
"dim": 1024,
|
||||
"metric": "cosine",
|
||||
}
|
||||
}
|
||||
|
||||
result = validate_payload(payload)
|
||||
assert result["agent_id"] == "agt_helion"
|
||||
assert result["scope"] == "docs"
|
||||
|
||||
def test_missing_required_field(self):
|
||||
"""Test that missing required field raises error."""
|
||||
payload = {
|
||||
"schema_version": "cm_payload_v1",
|
||||
"tenant_id": "t_daarion",
|
||||
# Missing other required fields
|
||||
}
|
||||
|
||||
with pytest.raises(PayloadValidationError) as exc_info:
|
||||
validate_payload(payload)
|
||||
|
||||
assert "Missing required field" in str(exc_info.value)
|
||||
|
||||
def test_invalid_schema_version(self):
|
||||
"""Test that invalid schema version raises error."""
|
||||
payload = create_minimal_payload(
|
||||
tenant_id="t_daarion",
|
||||
source_id="doc_01HQ8K9X2NPQR3FGJKLM5678",
|
||||
chunk_id="chk_01HQ8K9X3MPQR3FGJKLM9012",
|
||||
chunk_idx=0,
|
||||
fingerprint="sha256:abc123",
|
||||
)
|
||||
payload["schema_version"] = "v2" # Invalid
|
||||
|
||||
with pytest.raises(PayloadValidationError) as exc_info:
|
||||
validate_payload(payload)
|
||||
|
||||
assert "schema_version" in str(exc_info.value)
|
||||
|
||||
def test_invalid_tenant_id_format(self):
|
||||
"""Test that invalid tenant_id format raises error."""
|
||||
payload = create_minimal_payload(
|
||||
tenant_id="invalid-tenant", # Wrong format
|
||||
source_id="doc_01HQ8K9X2NPQR3FGJKLM5678",
|
||||
chunk_id="chk_01HQ8K9X3MPQR3FGJKLM9012",
|
||||
chunk_idx=0,
|
||||
fingerprint="sha256:abc123",
|
||||
)
|
||||
|
||||
with pytest.raises(PayloadValidationError) as exc_info:
|
||||
validate_payload(payload)
|
||||
|
||||
assert "tenant_id" in str(exc_info.value)
|
||||
|
||||
def test_invalid_agent_id_format(self):
|
||||
"""Test that invalid agent_id format raises error."""
|
||||
payload = create_minimal_payload(
|
||||
tenant_id="t_daarion",
|
||||
source_id="doc_01HQ8K9X2NPQR3FGJKLM5678",
|
||||
chunk_id="chk_01HQ8K9X3MPQR3FGJKLM9012",
|
||||
chunk_idx=0,
|
||||
fingerprint="sha256:abc123",
|
||||
)
|
||||
payload["agent_id"] = "helion" # Missing prefix
|
||||
|
||||
with pytest.raises(PayloadValidationError) as exc_info:
|
||||
validate_payload(payload)
|
||||
|
||||
assert "agent_id" in str(exc_info.value)
|
||||
|
||||
def test_invalid_scope(self):
|
||||
"""Test that invalid scope raises error."""
|
||||
payload = create_minimal_payload(
|
||||
tenant_id="t_daarion",
|
||||
source_id="doc_01HQ8K9X2NPQR3FGJKLM5678",
|
||||
chunk_id="chk_01HQ8K9X3MPQR3FGJKLM9012",
|
||||
chunk_idx=0,
|
||||
fingerprint="sha256:abc123",
|
||||
scope="invalid_scope",
|
||||
)
|
||||
|
||||
with pytest.raises(PayloadValidationError) as exc_info:
|
||||
validate_payload(payload)
|
||||
|
||||
assert "scope" in str(exc_info.value)
|
||||
|
||||
def test_invalid_visibility(self):
|
||||
"""Test that invalid visibility raises error."""
|
||||
payload = create_minimal_payload(
|
||||
tenant_id="t_daarion",
|
||||
source_id="doc_01HQ8K9X2NPQR3FGJKLM5678",
|
||||
chunk_id="chk_01HQ8K9X3MPQR3FGJKLM9012",
|
||||
chunk_idx=0,
|
||||
fingerprint="sha256:abc123",
|
||||
visibility="secret", # Invalid
|
||||
)
|
||||
|
||||
with pytest.raises(PayloadValidationError) as exc_info:
|
||||
validate_payload(payload)
|
||||
|
||||
assert "visibility" in str(exc_info.value)
|
||||
|
||||
def test_invalid_importance_range(self):
|
||||
"""Test that importance outside 0-1 raises error."""
|
||||
payload = create_minimal_payload(
|
||||
tenant_id="t_daarion",
|
||||
source_id="doc_01HQ8K9X2NPQR3FGJKLM5678",
|
||||
chunk_id="chk_01HQ8K9X3MPQR3FGJKLM9012",
|
||||
chunk_idx=0,
|
||||
fingerprint="sha256:abc123",
|
||||
)
|
||||
payload["importance"] = 1.5 # Invalid
|
||||
|
||||
with pytest.raises(PayloadValidationError) as exc_info:
|
||||
validate_payload(payload)
|
||||
|
||||
assert "importance" in str(exc_info.value)
|
||||
|
||||
def test_invalid_chunk_idx_negative(self):
|
||||
"""Test that negative chunk_idx raises error."""
|
||||
payload = create_minimal_payload(
|
||||
tenant_id="t_daarion",
|
||||
source_id="doc_01HQ8K9X2NPQR3FGJKLM5678",
|
||||
chunk_id="chk_01HQ8K9X3MPQR3FGJKLM9012",
|
||||
chunk_idx=-1, # Invalid
|
||||
fingerprint="sha256:abc123",
|
||||
)
|
||||
|
||||
with pytest.raises(PayloadValidationError) as exc_info:
|
||||
validate_payload(payload)
|
||||
|
||||
assert "chunk_idx" in str(exc_info.value)
|
||||
|
||||
def test_non_strict_mode(self):
|
||||
"""Test that non-strict mode returns payload with errors."""
|
||||
payload = {
|
||||
"schema_version": "cm_payload_v1",
|
||||
"tenant_id": "invalid", # Invalid format
|
||||
}
|
||||
|
||||
# Should not raise in non-strict mode
|
||||
result = validate_payload(payload, strict=False)
|
||||
assert result["schema_version"] == "cm_payload_v1"
|
||||
|
||||
def test_all_valid_scopes(self):
|
||||
"""Test that all valid scopes pass validation."""
|
||||
valid_scopes = ["docs", "messages", "memory", "artifacts", "signals"]
|
||||
|
||||
for scope in valid_scopes:
|
||||
payload = create_minimal_payload(
|
||||
tenant_id="t_daarion",
|
||||
source_id="doc_01HQ8K9X2NPQR3FGJKLM5678",
|
||||
chunk_id="chk_01HQ8K9X3MPQR3FGJKLM9012",
|
||||
chunk_idx=0,
|
||||
fingerprint="sha256:abc123",
|
||||
scope=scope,
|
||||
)
|
||||
result = validate_payload(payload)
|
||||
assert result["scope"] == scope
|
||||
|
||||
def test_all_valid_visibilities(self):
|
||||
"""Test that all valid visibilities pass validation."""
|
||||
valid_visibilities = ["public", "confidential", "private"]
|
||||
|
||||
for visibility in valid_visibilities:
|
||||
payload = create_minimal_payload(
|
||||
tenant_id="t_daarion",
|
||||
source_id="doc_01HQ8K9X2NPQR3FGJKLM5678",
|
||||
chunk_id="chk_01HQ8K9X3MPQR3FGJKLM9012",
|
||||
chunk_idx=0,
|
||||
fingerprint="sha256:abc123",
|
||||
visibility=visibility,
|
||||
)
|
||||
result = validate_payload(payload)
|
||||
assert result["visibility"] == visibility
|
||||
|
||||
|
||||
class TestCollectionNameMapping:
|
||||
"""Tests for legacy collection name to payload mapping."""
|
||||
|
||||
def test_parse_docs_collection(self):
|
||||
"""Test parsing *_docs collection names."""
|
||||
from scripts.qdrant_migrate_to_canonical import parse_collection_name
|
||||
|
||||
result = parse_collection_name("helion_docs")
|
||||
assert result is not None
|
||||
assert result["agent_id"] == "agt_helion"
|
||||
assert result["scope"] == "docs"
|
||||
assert result["tags"] == []
|
||||
|
||||
def test_parse_messages_collection(self):
|
||||
"""Test parsing *_messages collection names."""
|
||||
from scripts.qdrant_migrate_to_canonical import parse_collection_name
|
||||
|
||||
result = parse_collection_name("nutra_messages")
|
||||
assert result is not None
|
||||
assert result["agent_id"] == "agt_nutra"
|
||||
assert result["scope"] == "messages"
|
||||
|
||||
def test_parse_special_kb_collection(self):
|
||||
"""Test parsing special knowledge base collections."""
|
||||
from scripts.qdrant_migrate_to_canonical import parse_collection_name
|
||||
|
||||
result = parse_collection_name("druid_legal_kb")
|
||||
assert result is not None
|
||||
assert result["agent_id"] == "agt_druid"
|
||||
assert result["scope"] == "docs"
|
||||
assert "legal_kb" in result["tags"]
|
||||
|
||||
def test_parse_unknown_collection(self):
|
||||
"""Test parsing unknown collection returns None."""
|
||||
from scripts.qdrant_migrate_to_canonical import parse_collection_name
|
||||
|
||||
result = parse_collection_name("random_collection_xyz")
|
||||
# Should still try to match generic patterns or return None
|
||||
# Based on implementation, this might match *_xyz or return None
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
pytest.main([__file__, "-v"])
|
||||
Reference in New Issue
Block a user