193 lines
7 KiB
Python
193 lines
7 KiB
Python
# Copyright (c) 2025 Patrick Motsch
|
|
# All rights reserved.
|
|
"""
|
|
Tests for CommCoach AI service (prompt building and response parsing).
|
|
These tests don't require AI calls -- they test the prompt construction and JSON parsing.
|
|
"""
|
|
|
|
import pytest
|
|
import json
|
|
from ..serviceCommcoachAi import (
|
|
buildCoachingSystemPrompt,
|
|
buildSummaryPrompt,
|
|
buildScoringPrompt,
|
|
buildTaskExtractionPrompt,
|
|
buildEarlierConversationSummaryPrompt,
|
|
prepareMessagesForPrompt,
|
|
parseJsonResponse,
|
|
)
|
|
|
|
|
|
class TestBuildCoachingSystemPrompt:
|
|
def test_basicPrompt(self):
|
|
context = {"title": "Conflict Resolution", "category": "conflict"}
|
|
prompt = buildCoachingSystemPrompt(context, [], [])
|
|
assert "Conflict Resolution" in prompt
|
|
assert "conflict" in prompt
|
|
assert "coach" in prompt.lower()
|
|
|
|
def test_withGoals(self):
|
|
context = {
|
|
"title": "Leadership",
|
|
"category": "leadership",
|
|
"goals": json.dumps([{"text": "Improve delegation"}]),
|
|
}
|
|
prompt = buildCoachingSystemPrompt(context, [], [])
|
|
assert "Improve delegation" in prompt
|
|
|
|
def test_withInsights(self):
|
|
context = {
|
|
"title": "Test",
|
|
"category": "custom",
|
|
"insights": json.dumps([{"text": "User shows progress in empathy"}]),
|
|
}
|
|
prompt = buildCoachingSystemPrompt(context, [], [])
|
|
assert "empathy" in prompt
|
|
|
|
def test_withPreviousMessages(self):
|
|
context = {"title": "Test", "category": "custom"}
|
|
messages = [
|
|
{"role": "user", "content": "I had a conflict with my team"},
|
|
{"role": "assistant", "content": "Tell me more about that"},
|
|
]
|
|
prompt = buildCoachingSystemPrompt(context, messages, [])
|
|
assert "conflict" in prompt.lower()
|
|
|
|
def test_withTasks(self):
|
|
context = {"title": "Test", "category": "custom"}
|
|
tasks = [
|
|
{"title": "Practice listening", "status": "open"},
|
|
{"title": "Read book", "status": "done"},
|
|
]
|
|
prompt = buildCoachingSystemPrompt(context, [], tasks)
|
|
assert "Practice listening" in prompt
|
|
|
|
def test_promptLanguageIsGerman(self):
|
|
context = {"title": "Test", "category": "custom"}
|
|
prompt = buildCoachingSystemPrompt(context, [], [])
|
|
assert "Fuehrungskraefte" in prompt or "Coach" in prompt
|
|
|
|
def test_withEarlierSummary(self):
|
|
context = {"title": "Test", "category": "custom"}
|
|
messages = [{"role": "user", "content": "Recent question"}]
|
|
earlierSummary = "User discussed delegation. Coach suggested practice."
|
|
prompt = buildCoachingSystemPrompt(context, messages, [], earlierSummary=earlierSummary)
|
|
assert "Aelterer Gespraechsverlauf" in prompt
|
|
assert "delegation" in prompt.lower()
|
|
assert "Recent question" in prompt
|
|
|
|
def test_withRollingOverview(self):
|
|
context = {"title": "Test", "category": "custom"}
|
|
prompt = buildCoachingSystemPrompt(
|
|
context, [], [], rollingOverview="User arbeitet an Delegation. Fortschritt sichtbar."
|
|
)
|
|
assert "Gesamtueberblick" in prompt
|
|
assert "Delegation" in prompt
|
|
|
|
def test_withRetrievedSession(self):
|
|
context = {"title": "Test", "category": "custom"}
|
|
retrieved = {"summary": "Delegation und Feedback besprochen", "startedAt": "2026-02-01T10:00:00Z"}
|
|
prompt = buildCoachingSystemPrompt(context, [], [], retrievedSession=retrieved)
|
|
assert "angefragte Session" in prompt
|
|
assert "Delegation" in prompt
|
|
|
|
|
|
class TestPrepareMessagesForPrompt:
|
|
def test_underThreshold(self):
|
|
messages = [{"role": "user", "content": f"msg {i}"} for i in range(10)]
|
|
earlier, recent = prepareMessagesForPrompt(messages, None, None)
|
|
assert earlier is None
|
|
assert len(recent) == 10
|
|
|
|
def test_overThresholdWithCachedSummary(self):
|
|
messages = [{"role": "user", "content": f"msg {i}"} for i in range(40)]
|
|
cached = "Summary of first 25 messages"
|
|
earlier, recent = prepareMessagesForPrompt(messages, cached, 25)
|
|
assert earlier == cached
|
|
assert len(recent) == 15
|
|
|
|
def test_overThresholdNeedsRegenerate(self):
|
|
messages = [{"role": "user", "content": f"msg {i}"} for i in range(40)]
|
|
earlier, recent = prepareMessagesForPrompt(messages, "old summary", 20)
|
|
assert earlier is None
|
|
assert len(recent) == 40
|
|
|
|
|
|
class TestBuildEarlierConversationSummaryPrompt:
|
|
def test_basic(self):
|
|
messages = [
|
|
{"role": "user", "content": "I have a conflict"},
|
|
{"role": "assistant", "content": "Tell me more"},
|
|
]
|
|
prompt = buildEarlierConversationSummaryPrompt(messages)
|
|
assert "Fasse" in prompt
|
|
assert "conflict" in prompt
|
|
|
|
|
|
class TestBuildSummaryPrompt:
|
|
def test_basic(self):
|
|
messages = [
|
|
{"role": "user", "content": "I need help with delegation"},
|
|
{"role": "assistant", "content": "Let's work on that"},
|
|
]
|
|
prompt = buildSummaryPrompt(messages, "Delegation")
|
|
assert "Delegation" in prompt
|
|
assert "Zusammenfassung" in prompt
|
|
|
|
def test_emptyMessages(self):
|
|
prompt = buildSummaryPrompt([], "Test")
|
|
assert "Test" in prompt
|
|
|
|
|
|
class TestBuildScoringPrompt:
|
|
def test_basic(self):
|
|
messages = [{"role": "user", "content": "I tried active listening today"}]
|
|
prompt = buildScoringPrompt(messages, "leadership")
|
|
assert "empathy" in prompt
|
|
assert "clarity" in prompt
|
|
assert "JSON" in prompt
|
|
|
|
def test_containsScaleInfo(self):
|
|
prompt = buildScoringPrompt([{"role": "user", "content": "test"}], "custom")
|
|
assert "0-100" in prompt
|
|
|
|
|
|
class TestBuildTaskExtractionPrompt:
|
|
def test_basic(self):
|
|
messages = [
|
|
{"role": "assistant", "content": "You should try practicing active listening this week"},
|
|
]
|
|
prompt = buildTaskExtractionPrompt(messages)
|
|
assert "JSON" in prompt
|
|
assert "Aufgaben" in prompt or "Aufgabe" in prompt
|
|
|
|
def test_limitedMessages(self):
|
|
messages = [{"role": "user", "content": f"msg {i}"} for i in range(35)]
|
|
prompt = buildTaskExtractionPrompt(messages)
|
|
assert "msg 34" in prompt
|
|
assert "msg 0" not in prompt
|
|
|
|
|
|
class TestParseJsonResponse:
|
|
def test_validJson(self):
|
|
result = parseJsonResponse('[{"dimension": "empathy", "score": 70}]')
|
|
assert isinstance(result, list)
|
|
assert result[0]["score"] == 70
|
|
|
|
def test_codeBlockWrapped(self):
|
|
result = parseJsonResponse('```json\n[{"title": "task1"}]\n```')
|
|
assert isinstance(result, list)
|
|
assert result[0]["title"] == "task1"
|
|
|
|
def test_invalidJson(self):
|
|
result = parseJsonResponse('not json at all', fallback=[])
|
|
assert result == []
|
|
|
|
def test_emptyString(self):
|
|
result = parseJsonResponse('', fallback=None)
|
|
assert result is None
|
|
|
|
def test_nestedCodeBlock(self):
|
|
text = '```\n{"key": "value"}\n```'
|
|
result = parseJsonResponse(text)
|
|
assert result["key"] == "value"
|