forked from Rockachopa/Timmy-time-dashboard
141 lines
4.6 KiB
Python
141 lines
4.6 KiB
Python
"""Tests for timmy.interview — structured interview runner."""
|
|
|
|
from unittest.mock import MagicMock, patch
|
|
|
|
import pytest
|
|
|
|
from timmy.interview import (
|
|
INTERVIEW_QUESTIONS,
|
|
InterviewEntry,
|
|
format_transcript,
|
|
run_interview,
|
|
)
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# INTERVIEW_QUESTIONS sanity checks
|
|
# ---------------------------------------------------------------------------
|
|
|
|
|
|
def test_interview_questions_not_empty():
|
|
"""There should be at least one interview question defined."""
|
|
assert len(INTERVIEW_QUESTIONS) > 0
|
|
|
|
|
|
def test_interview_questions_have_required_keys():
|
|
"""Every question dict must have 'category' and 'question'."""
|
|
for item in INTERVIEW_QUESTIONS:
|
|
assert "category" in item
|
|
assert "question" in item
|
|
assert isinstance(item["category"], str)
|
|
assert isinstance(item["question"], str)
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# run_interview()
|
|
# ---------------------------------------------------------------------------
|
|
|
|
|
|
def test_run_interview_calls_chat_for_each_question():
|
|
"""run_interview should call the chat function once per question."""
|
|
mock_chat = MagicMock(return_value="Answer.")
|
|
transcript = run_interview(mock_chat)
|
|
|
|
assert mock_chat.call_count == len(INTERVIEW_QUESTIONS)
|
|
assert len(transcript) == len(INTERVIEW_QUESTIONS)
|
|
|
|
|
|
def test_run_interview_returns_interview_entries():
|
|
"""Each element in the transcript should be an InterviewEntry."""
|
|
mock_chat = MagicMock(return_value="I am Timmy.")
|
|
transcript = run_interview(mock_chat)
|
|
|
|
for entry in transcript:
|
|
assert isinstance(entry, InterviewEntry)
|
|
assert entry.answer == "I am Timmy."
|
|
|
|
|
|
def test_run_interview_with_custom_questions():
|
|
"""run_interview should accept custom question lists."""
|
|
custom_qs = [
|
|
{"category": "Test", "question": "What is 2+2?"},
|
|
]
|
|
mock_chat = MagicMock(return_value="Four.")
|
|
transcript = run_interview(mock_chat, questions=custom_qs)
|
|
|
|
assert len(transcript) == 1
|
|
assert transcript[0].category == "Test"
|
|
assert transcript[0].question == "What is 2+2?"
|
|
assert transcript[0].answer == "Four."
|
|
|
|
|
|
def test_run_interview_on_answer_callback():
|
|
"""on_answer callback should be invoked for each question."""
|
|
callback = MagicMock()
|
|
mock_chat = MagicMock(return_value="OK.")
|
|
|
|
run_interview(mock_chat, on_answer=callback)
|
|
|
|
assert callback.call_count == len(INTERVIEW_QUESTIONS)
|
|
# Each call should receive an InterviewEntry
|
|
for call in callback.call_args_list:
|
|
entry = call[0][0]
|
|
assert isinstance(entry, InterviewEntry)
|
|
|
|
|
|
def test_run_interview_handles_chat_error():
|
|
"""If the chat function raises, the answer should contain the error."""
|
|
def failing_chat(msg):
|
|
raise ConnectionError("Ollama offline")
|
|
|
|
transcript = run_interview(failing_chat)
|
|
|
|
assert len(transcript) == len(INTERVIEW_QUESTIONS)
|
|
for entry in transcript:
|
|
assert "Error" in entry.answer
|
|
assert "Ollama offline" in entry.answer
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# format_transcript()
|
|
# ---------------------------------------------------------------------------
|
|
|
|
|
|
def test_format_transcript_empty():
|
|
"""Formatting an empty transcript should return a placeholder."""
|
|
result = format_transcript([])
|
|
assert "No interview data" in result
|
|
|
|
|
|
def test_format_transcript_includes_header():
|
|
"""Formatted transcript should include the header."""
|
|
entries = [InterviewEntry(category="Identity", question="Who are you?", answer="Timmy.")]
|
|
result = format_transcript(entries)
|
|
assert "TIMMY INTERVIEW TRANSCRIPT" in result
|
|
|
|
|
|
def test_format_transcript_includes_questions_and_answers():
|
|
"""Formatted transcript should include Q and A."""
|
|
entries = [
|
|
InterviewEntry(category="Identity", question="Who are you?", answer="Timmy."),
|
|
InterviewEntry(category="Values", question="What matters?", answer="Sovereignty."),
|
|
]
|
|
result = format_transcript(entries)
|
|
|
|
assert "Q: Who are you?" in result
|
|
assert "A: Timmy." in result
|
|
assert "Q: What matters?" in result
|
|
assert "A: Sovereignty." in result
|
|
|
|
|
|
def test_format_transcript_groups_by_category():
|
|
"""Categories should appear as section headers."""
|
|
entries = [
|
|
InterviewEntry(category="Identity", question="Q1", answer="A1"),
|
|
InterviewEntry(category="Values", question="Q2", answer="A2"),
|
|
]
|
|
result = format_transcript(entries)
|
|
|
|
assert "--- Identity ---" in result
|
|
assert "--- Values ---" in result
|