"""Tests for the relationship-seed service (T38). Per Requirements §5.2, when two bots first co-appear in a chat, the user is prompted with "Have they met before? If yes, write a short prose seed." The prose is parsed via classifier into structured directed-edge content for the ``botA -> botB`` and ``botB -> botA`` edges. These tests cover: * The happy path: a canned classifier response parses cleanly into a populated :class:`RelationshipSeed` with both directions filled. * Empty prose short-circuits before any classifier call (mock has no canned responses; an accidental call would raise ``IndexError``). * Whitespace-only prose has the same short-circuit behavior. """ from __future__ import annotations import json import pytest from chat.llm.mock import MockLLMClient from chat.services.relationship_seed import ( RelationshipSeed, seed_inter_bot_edges, ) @pytest.mark.asyncio async def test_seed_parses_canned_prose(): canned = json.dumps( { "a_to_b_summary": "old college friend who now distrusts him slightly", "a_to_b_knowledge_facts": [ "studied physics together", "lost touch after a falling out", ], "a_to_b_affinity_delta": 2, "a_to_b_trust_delta": -1, "b_to_a_summary": "former roommate; warm memories, mild resentment", "b_to_a_knowledge_facts": ["lived together junior year"], "b_to_a_affinity_delta": 3, "b_to_a_trust_delta": 0, } ) mock = MockLLMClient(canned=[canned]) result = await seed_inter_bot_edges( mock, classifier_model="x", bot_a_id="bot_a", bot_a_name="Alice", bot_b_id="bot_b", bot_b_name="Bob", relationship_prose=( "Alice and Bob met in college. They studied physics together and " "lived as roommates junior year, but drifted apart after a fight." ), ) assert isinstance(result, RelationshipSeed) assert ( result.a_to_b_summary == "old college friend who now distrusts him slightly" ) assert result.a_to_b_knowledge_facts == [ "studied physics together", "lost touch after a falling out", ] assert result.a_to_b_affinity_delta == 2 assert result.a_to_b_trust_delta == -1 assert ( result.b_to_a_summary == "former roommate; warm memories, mild resentment" ) assert result.b_to_a_knowledge_facts == ["lived together junior year"] assert result.b_to_a_affinity_delta == 3 assert result.b_to_a_trust_delta == 0 @pytest.mark.asyncio async def test_seed_empty_prose_returns_empty(): """Empty prose short-circuits — classifier must not be called.""" mock = MockLLMClient(canned=[]) result = await seed_inter_bot_edges( mock, classifier_model="x", bot_a_id="bot_a", bot_a_name="Alice", bot_b_id="bot_b", bot_b_name="Bob", relationship_prose="", ) assert result == RelationshipSeed() @pytest.mark.asyncio async def test_seed_whitespace_only_prose_returns_empty(): """Whitespace-only prose is treated the same as empty.""" mock = MockLLMClient(canned=[]) result = await seed_inter_bot_edges( mock, classifier_model="x", bot_a_id="bot_a", bot_a_name="Alice", bot_b_id="bot_b", bot_b_name="Bob", relationship_prose=" \n ", ) assert result == RelationshipSeed()