- WS /ws/session: modes, summon, anomaly fragments, streaming direct contact,
passive wire-ghost ambient loop, per-user rate limits, event transcript
- entities: anomaly-signature fingerprinting, LLM minting + procedural
fallback, Codex matching with contact counts and sightings
- tts: 8 local Piper voices (EN/ES), per-entity voice profiles, numpy
effects chain (pitch/rate/bitcrush/echo/static)
- llm: streaming client, submit_stream in bounded queue, SpiritService
with offline fallbacks for every channel
- routes: public /api/codex, /api/codex/{id}, /api/stats; /audio static mount
- models: Entity, EntitySighting, Event, ContactSession(entity_id, language)
84 lines
2.6 KiB
Python
84 lines
2.6 KiB
Python
import uuid
|
|
|
|
import pytest
|
|
from sqlalchemy import select
|
|
|
|
from app.models.entity import Entity
|
|
|
|
|
|
def _make_entity(name="Vesper Wren", rarity="rare", signature="abc123def4567890"):
|
|
return Entity(
|
|
name=name,
|
|
epithet="the Static Widow",
|
|
persona="A voice worn smooth as sea glass.",
|
|
rarity_tier=rarity,
|
|
signature=signature,
|
|
voice_profile={"voice_id": "lessac", "pitch": -2, "rate": 0.95, "noise": 0.04},
|
|
visual_profile={"hue": 265, "form": "wisp"},
|
|
sample_quotes=["I am closer than the dial suggests."],
|
|
contact_count=3,
|
|
)
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_codex_lists_entities(client, db_session):
|
|
db_session.add(_make_entity())
|
|
db_session.add(_make_entity("Hollow Briar", "common", "0123456789abcdef"))
|
|
await db_session.commit()
|
|
|
|
response = await client.get("/api/codex")
|
|
assert response.status_code == 200
|
|
names = {entity["name"] for entity in response.json()["entities"]}
|
|
assert names == {"Vesper Wren", "Hollow Briar"}
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_codex_filters_by_rarity(client, db_session):
|
|
db_session.add(_make_entity())
|
|
db_session.add(_make_entity("Hollow Briar", "common", "0123456789abcdef"))
|
|
await db_session.commit()
|
|
|
|
response = await client.get("/api/codex?rarity=rare")
|
|
assert response.status_code == 200
|
|
entities = response.json()["entities"]
|
|
assert len(entities) == 1
|
|
assert entities[0]["name"] == "Vesper Wren"
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_codex_detail_and_404(client, db_session):
|
|
entity = _make_entity()
|
|
db_session.add(entity)
|
|
await db_session.commit()
|
|
await db_session.refresh(entity)
|
|
|
|
response = await client.get(f"/api/codex/{entity.id}")
|
|
assert response.status_code == 200
|
|
body = response.json()
|
|
assert body["name"] == "Vesper Wren"
|
|
assert body["persona"].startswith("A voice")
|
|
assert body["sightings"] == 0
|
|
|
|
missing = await client.get(f"/api/codex/{uuid.uuid4()}")
|
|
assert missing.status_code == 404
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_stats_counts_veil_activity(client, db_session):
|
|
db_session.add(_make_entity())
|
|
await db_session.commit()
|
|
|
|
response = await client.get("/api/stats")
|
|
assert response.status_code == 200
|
|
body = response.json()
|
|
assert body["entities"] == 1
|
|
assert body["sessions"] == 0
|
|
assert body["utterances"] == 0
|
|
assert body["anomalies"] == 0
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_codex_is_public_without_auth(client):
|
|
assert (await client.get("/api/codex")).status_code == 200
|
|
assert (await client.get("/api/stats")).status_code == 200
|