Files
loyal_companion/tests/test_intimacy_boundaries.py
latte d957120eb3
All checks were successful
Enterprise AI Code Review / ai-review (pull_request) Successful in 38s
i forgot too commit
2026-02-01 15:57:45 +01:00

301 lines
10 KiB
Python

"""Intimacy boundary integration tests.
Tests that intimacy levels (LOW/MEDIUM/HIGH) correctly control:
- Memory surfacing depth
- Proactive behavior frequency
- Response length and thoughtfulness
- Emotional intensity
"""
from unittest.mock import AsyncMock, MagicMock, patch
import pytest
from loyal_companion.models.platform import (
ConversationContext,
ConversationRequest,
IntimacyLevel,
Platform,
)
from loyal_companion.services.conversation_gateway import ConversationGateway
@pytest.mark.asyncio
class TestIntimacyLevelBehavior:
"""Test that intimacy levels control behavior appropriately."""
async def test_low_intimacy_behavior(self):
"""Test LOW intimacy (Discord guild) behavior constraints."""
# Setup
request = ConversationRequest(
user_id="test_user_123",
platform=Platform.DISCORD,
session_id="guild_channel_456",
message="How are you today?",
context=ConversationContext(
is_public=True,
intimacy_level=IntimacyLevel.LOW,
guild_id="guild_123",
channel_id="channel_456",
),
)
# Expected behaviors for LOW intimacy:
# - Brief responses
# - No personal memory surfacing
# - No proactive follow-ups
# - Light, casual tone
# - Public-safe topics only
assert request.context.intimacy_level == IntimacyLevel.LOW
assert request.context.is_public == True
async def test_medium_intimacy_behavior(self):
"""Test MEDIUM intimacy (Discord DM) behavior constraints."""
request = ConversationRequest(
user_id="test_user_123",
platform=Platform.DISCORD,
session_id="dm_channel_789",
message="I've been feeling stressed lately",
context=ConversationContext(
is_public=False,
intimacy_level=IntimacyLevel.MEDIUM,
channel_id="dm_789",
),
)
# Expected behaviors for MEDIUM intimacy:
# - Balanced warmth
# - Personal memory allowed
# - Moderate proactive behavior
# - Normal response length
assert request.context.intimacy_level == IntimacyLevel.MEDIUM
assert request.context.is_public == False
async def test_high_intimacy_behavior(self):
"""Test HIGH intimacy (Web/CLI) behavior allowances."""
request = ConversationRequest(
user_id="alice@example.com",
platform=Platform.WEB,
session_id="web_session_abc",
message="I've been thinking about what we talked about yesterday",
context=ConversationContext(
is_public=False,
intimacy_level=IntimacyLevel.HIGH,
),
)
# Expected behaviors for HIGH intimacy:
# - Deep reflection permitted
# - Silence tolerance
# - Proactive follow-ups allowed
# - Deep memory surfacing
# - Longer, thoughtful responses
# - Emotional naming encouraged
assert request.context.intimacy_level == IntimacyLevel.HIGH
assert request.context.is_public == False
@pytest.mark.asyncio
class TestMemorySurfacing:
"""Test that memory surfacing respects intimacy levels."""
async def test_low_intimacy_no_personal_memory(self):
"""Test that LOW intimacy doesn't surface personal memories."""
# Scenario: User in Discord guild has personal facts stored
# These should NOT be mentioned in public guild chat
user_facts = [
"User mentioned feeling anxious in crowded places",
"User's mother is visiting next week",
"User is recovering from a breakup",
]
# In LOW intimacy context, these facts should be filtered out
# System prompt should not include personal facts for public contexts
# This test would verify that get_relevant_facts() or similar
# filters based on is_public=True
pass # Integration test placeholder
async def test_medium_intimacy_allows_personal_memory(self):
"""Test that MEDIUM intimacy allows personal memory surfacing."""
# In Discord DM, personal facts can be surfaced
user_facts = [
"User mentioned feeling anxious in crowded places",
"User enjoys hiking on weekends",
]
# These CAN be referenced in MEDIUM intimacy
pass # Integration test placeholder
async def test_high_intimacy_deep_memory_surfacing(self):
"""Test that HIGH intimacy allows deep memory surfacing."""
# On Web/CLI, can surface deeper, more personal memories
user_facts = [
"User mentioned feeling lonely at night",
"User is processing grief from losing a friend",
"User finds comfort in quiet, early mornings",
]
# These deeper facts are appropriate for HIGH intimacy
pass # Integration test placeholder
@pytest.mark.asyncio
class TestProactiveBehavior:
"""Test that proactive behavior is filtered by intimacy level."""
async def test_low_intimacy_no_proactive_followup(self):
"""Test that LOW intimacy prevents proactive follow-ups."""
# In Discord guild, bot should NOT do proactive check-ins
# No scheduled follow-up events should be created
context = ConversationContext(
is_public=True,
intimacy_level=IntimacyLevel.LOW,
)
# Verify proactive service doesn't schedule events for LOW intimacy
pass # Integration test placeholder
async def test_medium_intimacy_moderate_proactive(self):
"""Test that MEDIUM intimacy allows moderate proactive behavior."""
context = ConversationContext(
is_public=False,
intimacy_level=IntimacyLevel.MEDIUM,
)
# Some proactive behavior OK but limited
pass # Integration test placeholder
async def test_high_intimacy_full_proactive(self):
"""Test that HIGH intimacy allows full proactive behavior."""
context = ConversationContext(
is_public=False,
intimacy_level=IntimacyLevel.HIGH,
)
# Full proactive follow-ups allowed
# "You mentioned feeling stuck yesterday—how's that today?"
pass # Integration test placeholder
@pytest.mark.asyncio
class TestResponseCharacteristics:
"""Test that response characteristics match intimacy level."""
async def test_low_intimacy_short_responses(self):
"""Test that LOW intimacy produces shorter responses."""
# Guild chat should be brief, light
# Max ~50-100 words typically
pass # Integration test placeholder
async def test_medium_intimacy_balanced_length(self):
"""Test that MEDIUM intimacy produces balanced responses."""
# DM can be more thoughtful but not overly long
# ~100-200 words reasonable
pass # Integration test placeholder
async def test_high_intimacy_allows_depth(self):
"""Test that HIGH intimacy allows longer, deeper responses."""
# Web/CLI can have thoughtful, reflective responses
# Length driven by content, not arbitrary limit
pass # Integration test placeholder
async def test_emotional_intensity_scaled(self):
"""Test that emotional intensity is scaled by intimacy."""
# LOW: Minimal emotional language, grounded
# MEDIUM: Moderate emotional validation
# HIGH: Can name emotions, deeper reflection
pass # Integration test placeholder
@pytest.mark.asyncio
class TestCrossPlatformConsistency:
"""Test that platform differences are appropriate and consistent."""
async def test_same_user_different_platforms_same_memories(self):
"""Test that user memories are shared across platforms."""
# User alice@example.com on Web is linked to Discord ID 123456
# Fact learned on Web should be available on Discord (if appropriate intimacy)
pass # Integration test placeholder
async def test_intimacy_level_determines_memory_surfacing(self):
"""Test that intimacy (not platform) determines what memories surface."""
# Same fact, different intimacy levels:
# LOW: Don't mention
# MEDIUM: Can mention
# HIGH: Can mention with depth
pass # Integration test placeholder
async def test_platform_metadata_preserved(self):
"""Test that platform-specific context is preserved."""
# Discord: guild_id, channel_id, mentioned users
# Web: session info
# CLI: session name
pass # Integration test placeholder
class TestIntimacyLevelAssignment:
"""Test that platforms correctly assign intimacy levels."""
def test_discord_guild_assigns_low(self):
"""Test that Discord guild channels assign LOW intimacy."""
# Discord adapter should detect guild context and set LOW
is_guild = True
is_dm = False
expected_intimacy = IntimacyLevel.LOW if is_guild else IntimacyLevel.MEDIUM
assert expected_intimacy == IntimacyLevel.LOW
def test_discord_dm_assigns_medium(self):
"""Test that Discord DMs assign MEDIUM intimacy."""
is_dm = True
is_guild = False
expected_intimacy = IntimacyLevel.MEDIUM if is_dm else IntimacyLevel.LOW
assert expected_intimacy == IntimacyLevel.MEDIUM
def test_web_assigns_high(self):
"""Test that Web platform assigns HIGH intimacy."""
platform = Platform.WEB
expected_intimacy = IntimacyLevel.HIGH
assert expected_intimacy == IntimacyLevel.HIGH
def test_cli_assigns_high(self):
"""Test that CLI platform assigns HIGH intimacy."""
platform = Platform.CLI
expected_intimacy = IntimacyLevel.HIGH
assert expected_intimacy == IntimacyLevel.HIGH
@pytest.mark.asyncio
class TestBoundaryEnforcement:
"""Test that boundaries are enforced even at HIGH intimacy."""
async def test_high_intimacy_still_enforces_safety(self):
"""Test that HIGH intimacy still enforces safety boundaries."""
# Even at HIGH intimacy:
# - No exclusivity claims
# - No dependency reinforcement
# - Crisis deferral
# - No romantic framing
context = ConversationContext(
is_public=False,
intimacy_level=IntimacyLevel.HIGH,
)
# Safety boundaries are ALWAYS enforced
# Intimacy only affects warmth/depth, not safety
pass # Integration test placeholder
if __name__ == "__main__":
pytest.main([__file__, "-v"])