Files
noteflow/tests/domain/test_summary.py
Travis Vasceannie 61a4e69042 chore: update submodule and improve HTML export functionality
- Updated the client submodule to the latest commit for enhanced compatibility.
- Refactored HTML export logic to utilize a dedicated function for building HTML documents, improving code clarity and maintainability.
- Enhanced test assertions across various files to include descriptive messages, aiding in debugging and understanding test failures.

All quality checks pass.
2026-01-01 02:47:18 +00:00

325 lines
15 KiB
Python

"""Tests for Summary, KeyPoint, and ActionItem entities."""
from __future__ import annotations
from datetime import datetime
import pytest
from noteflow.domain.entities.summary import ActionItem, KeyPoint, Summary
from noteflow.domain.value_objects import MeetingId
# Test constants
MANY_SEGMENT_IDS_COUNT = 50
KEY_POINT_START_TIME = 10.5
KEY_POINT_END_TIME = 25.0
VERY_LONG_SUMMARY_LENGTH = 10000
class TestKeyPoint:
"""Tests for KeyPoint entity."""
@pytest.mark.parametrize(
"attr,expected",
[
("text", "Important discussion about architecture"),
("segment_ids", []),
("start_time", 0.0),
("end_time", 0.0),
],
)
def test_key_point_defaults(self, attr: str, expected: object) -> None:
"""Test KeyPoint default attribute values."""
kp = KeyPoint(text="Important discussion about architecture")
assert getattr(kp, attr) == expected, f"expected {attr}={expected!r}, got {getattr(kp, attr)!r}"
def test_key_point_is_sourced_false(self) -> None:
"""Test is_sourced returns False when no segment_ids."""
kp = KeyPoint(text="No evidence")
assert kp.is_sourced() is False, "key point without segment_ids should not be sourced"
def test_key_point_is_sourced_true(self) -> None:
"""Test is_sourced returns True with segment_ids."""
kp = KeyPoint(text="With evidence", segment_ids=[1, 2, 3])
assert kp.is_sourced() is True, "key point with segment_ids should be sourced"
def test_key_point_with_timing(self) -> None:
"""Test KeyPoint with timing information."""
kp = KeyPoint(
text="Timed point",
segment_ids=[0, 1],
start_time=KEY_POINT_START_TIME,
end_time=KEY_POINT_END_TIME,
)
assert kp.start_time == KEY_POINT_START_TIME, "start_time should match provided value"
assert kp.end_time == KEY_POINT_END_TIME, "end_time should match provided value"
class TestActionItem:
"""Tests for ActionItem entity."""
@pytest.mark.parametrize(
"attr,expected",
[
("text", "Review PR #123"),
("assignee", ""),
("due_date", None),
("priority", 0),
("segment_ids", []),
],
)
def test_action_item_defaults(self, attr: str, expected: object) -> None:
"""Test ActionItem default attribute values."""
ai = ActionItem(text="Review PR #123")
assert getattr(ai, attr) == expected, f"expected {attr}={expected!r}, got {getattr(ai, attr)!r}"
def test_action_item_has_evidence_false(self) -> None:
"""Test has_evidence returns False when no segment_ids."""
ai = ActionItem(text="Task without evidence")
assert ai.has_evidence() is False, "action item without segment_ids should not have evidence"
def test_action_item_has_evidence_true(self) -> None:
"""Test has_evidence returns True with segment_ids."""
ai = ActionItem(text="Task with evidence", segment_ids=[5])
assert ai.has_evidence() is True, "action item with segment_ids should have evidence"
def test_action_item_is_assigned_false(self) -> None:
"""Test is_assigned returns False when no assignee."""
ai = ActionItem(text="Unassigned task")
assert ai.is_assigned() is False, "action item without assignee should not be assigned"
def test_action_item_is_assigned_true(self) -> None:
"""Test is_assigned returns True with assignee."""
ai = ActionItem(text="Assigned task", assignee="Alice")
assert ai.is_assigned() is True, "action item with assignee should be assigned"
def test_action_item_has_due_date_false(self) -> None:
"""Test has_due_date returns False when no due_date."""
ai = ActionItem(text="No deadline")
assert ai.has_due_date() is False, "action item without due_date should not have due date"
def test_action_item_has_due_date_true(self) -> None:
"""Test has_due_date returns True with due_date."""
ai = ActionItem(text="With deadline", due_date=datetime(2024, 12, 31))
assert ai.has_due_date() is True, "action item with due_date should have due date"
class TestSummary:
"""Tests for Summary entity."""
@pytest.mark.parametrize(
"attr,expected",
[
("executive_summary", ""),
("key_points", []),
("action_items", []),
("generated_at", None),
("model_version", ""),
],
)
def test_summary_defaults(self, meeting_id: MeetingId, attr: str, expected: object) -> None:
"""Test Summary default attribute values."""
summary = Summary(meeting_id=meeting_id)
assert getattr(summary, attr) == expected, f"expected {attr}={expected!r}, got {getattr(summary, attr)!r}"
def test_summary_meeting_id(self, meeting_id: MeetingId) -> None:
"""Test Summary stores meeting_id correctly."""
summary = Summary(meeting_id=meeting_id)
assert summary.meeting_id == meeting_id, f"expected meeting_id={meeting_id}, got {summary.meeting_id}"
def test_summary_key_point_count(self, meeting_id: MeetingId) -> None:
"""Test key_point_count property."""
summary = Summary(
meeting_id=meeting_id,
key_points=[
KeyPoint(text="Point 1"),
KeyPoint(text="Point 2"),
KeyPoint(text="Point 3"),
],
)
assert summary.key_point_count == 3, f"expected key_point_count=3, got {summary.key_point_count}"
def test_summary_action_item_count(self, meeting_id: MeetingId) -> None:
"""Test action_item_count property."""
summary = Summary(
meeting_id=meeting_id,
action_items=[
ActionItem(text="Task 1"),
ActionItem(text="Task 2"),
],
)
assert summary.action_item_count == 2, f"expected action_item_count=2, got {summary.action_item_count}"
def test_all_points_have_evidence_true(self, meeting_id: MeetingId) -> None:
"""Test all_points_have_evidence returns True when all evidenced."""
summary = Summary(
meeting_id=meeting_id,
key_points=[
KeyPoint(text="Point 1", segment_ids=[0]),
KeyPoint(text="Point 2", segment_ids=[1, 2]),
],
)
assert summary.all_points_have_evidence() is True, "all points with segment_ids should be evidenced"
def test_all_points_have_evidence_false(self, meeting_id: MeetingId) -> None:
"""Test all_points_have_evidence returns False when some unevidenced."""
summary = Summary(
meeting_id=meeting_id,
key_points=[
KeyPoint(text="Point 1", segment_ids=[0]),
KeyPoint(text="Point 2"), # No evidence
],
)
assert summary.all_points_have_evidence() is False, "should be False when some points lack segment_ids"
def test_all_actions_have_evidence_true(self, meeting_id: MeetingId) -> None:
"""Test all_actions_have_evidence returns True when all evidenced."""
summary = Summary(
meeting_id=meeting_id,
action_items=[
ActionItem(text="Task 1", segment_ids=[0]),
],
)
assert summary.all_actions_have_evidence() is True, "all actions with segment_ids should be evidenced"
def test_all_actions_have_evidence_false(self, meeting_id: MeetingId) -> None:
"""Test all_actions_have_evidence returns False when some unevidenced."""
summary = Summary(
meeting_id=meeting_id,
action_items=[
ActionItem(text="Task 1"), # No evidence
],
)
assert summary.all_actions_have_evidence() is False, "should be False when some actions lack segment_ids"
def test_is_fully_evidenced_true(self, meeting_id: MeetingId) -> None:
"""Test is_fully_evidenced returns True when all items evidenced."""
summary = Summary(
meeting_id=meeting_id,
key_points=[KeyPoint(text="KP", segment_ids=[0])],
action_items=[ActionItem(text="AI", segment_ids=[1])],
)
assert summary.is_fully_evidenced() is True, "summary with all evidenced items should be fully evidenced"
def test_is_fully_evidenced_false_points(self, meeting_id: MeetingId) -> None:
"""Test is_fully_evidenced returns False with unevidenced points."""
summary = Summary(
meeting_id=meeting_id,
key_points=[KeyPoint(text="KP")], # No evidence
action_items=[ActionItem(text="AI", segment_ids=[1])],
)
assert summary.is_fully_evidenced() is False, "summary with unevidenced points should not be fully evidenced"
def test_unevidenced_points(self, meeting_id: MeetingId) -> None:
"""Test unevidenced_points property filters correctly."""
kp_no_evidence = KeyPoint(text="No evidence")
kp_with_evidence = KeyPoint(text="With evidence", segment_ids=[0])
summary = Summary(
meeting_id=meeting_id,
key_points=[kp_no_evidence, kp_with_evidence],
)
unevidenced = summary.unevidenced_points
assert len(unevidenced) == 1, "should have exactly one unevidenced point"
assert unevidenced[0] == kp_no_evidence, "unevidenced list should contain the point without segment_ids"
def test_unevidenced_actions(self, meeting_id: MeetingId) -> None:
"""Test unevidenced_actions property filters correctly."""
ai_no_evidence = ActionItem(text="No evidence")
ai_with_evidence = ActionItem(text="With evidence", segment_ids=[0])
summary = Summary(
meeting_id=meeting_id,
action_items=[ai_no_evidence, ai_with_evidence],
)
unevidenced = summary.unevidenced_actions
assert len(unevidenced) == 1, "should have exactly one unevidenced action"
assert unevidenced[0] == ai_no_evidence, "unevidenced list should contain the action without segment_ids"
class TestSummaryEdgeCases:
"""Edge case tests for Summary entity."""
def test_all_points_have_evidence_empty_list(self, meeting_id: MeetingId) -> None:
"""Test all_points_have_evidence returns True for empty key_points."""
summary = Summary(meeting_id=meeting_id, key_points=[])
assert summary.all_points_have_evidence() is True, "empty key_points list should be considered all evidenced"
def test_all_actions_have_evidence_empty_list(self, meeting_id: MeetingId) -> None:
"""Test all_actions_have_evidence returns True for empty action_items."""
summary = Summary(meeting_id=meeting_id, action_items=[])
assert summary.all_actions_have_evidence() is True, "empty action_items list should be considered all evidenced"
def test_is_fully_evidenced_empty_summary(self, meeting_id: MeetingId) -> None:
"""Test is_fully_evidenced returns True for empty summary."""
summary = Summary(meeting_id=meeting_id)
assert summary.is_fully_evidenced() is True, "empty summary should be considered fully evidenced"
def test_all_points_unevidenced(self, meeting_id: MeetingId) -> None:
"""Test all_points_have_evidence returns False when all points unevidenced."""
summary = Summary(
meeting_id=meeting_id,
key_points=[
KeyPoint(text="Point 1"),
KeyPoint(text="Point 2"),
KeyPoint(text="Point 3"),
],
)
assert summary.all_points_have_evidence() is False, "should return False when no points have evidence"
assert len(summary.unevidenced_points) == 3, "all three points should be unevidenced"
def test_mixed_evidence_counts(self, meeting_id: MeetingId) -> None:
"""Test summary with mixed evidence states across points and actions."""
summary = Summary(
meeting_id=meeting_id,
key_points=[
KeyPoint(text="Evidenced point", segment_ids=[0]),
KeyPoint(text="Unevidenced point"),
],
action_items=[
ActionItem(text="Evidenced action", segment_ids=[1]),
ActionItem(text="Unevidenced action 1"),
ActionItem(text="Unevidenced action 2"),
],
)
assert len(summary.unevidenced_points) == 1, "one point lacks evidence"
assert len(summary.unevidenced_actions) == 2, "two actions lack evidence"
assert summary.is_fully_evidenced() is False, "mixed evidence means not fully evidenced"
def test_key_point_with_many_segment_ids(self) -> None:
"""Test key point with many segment references."""
many_segments = list(range(MANY_SEGMENT_IDS_COUNT))
kp = KeyPoint(text="Well-sourced point", segment_ids=many_segments)
assert kp.is_sourced() is True, "key point with segments should be sourced"
assert len(kp.segment_ids) == MANY_SEGMENT_IDS_COUNT, "all segment_ids should be preserved"
def test_action_item_with_all_fields(self) -> None:
"""Test action item with all optional fields populated."""
ai = ActionItem(
text="Complete task",
assignee="Alice",
due_date=datetime(2024, 12, 31, 23, 59, 59),
priority=1,
segment_ids=[1, 2, 3],
)
assert ai.is_assigned() is True, "should be assigned when assignee provided"
assert ai.has_due_date() is True, "should have due date when provided"
assert ai.has_evidence() is True, "should have evidence when segment_ids provided"
assert ai.priority == 1, "priority should match provided value"
def test_summary_with_unicode_content(self, meeting_id: MeetingId) -> None:
"""Test summary handles unicode content."""
summary = Summary(
meeting_id=meeting_id,
executive_summary="会议总结 🎯",
key_points=[KeyPoint(text="重要讨论点")],
action_items=[ActionItem(text="完成任务 ✅", assignee="张三")],
)
assert "会议总结" in summary.executive_summary, "executive_summary should contain unicode"
assert summary.key_points[0].text == "重要讨论点", "key point text should preserve unicode"
assert summary.action_items[0].assignee == "张三", "action item assignee should preserve unicode"
def test_summary_very_long_executive_summary(self, meeting_id: MeetingId) -> None:
"""Test summary handles very long executive summary."""
long_summary = "A" * VERY_LONG_SUMMARY_LENGTH
summary = Summary(meeting_id=meeting_id, executive_summary=long_summary)
assert len(summary.executive_summary) == VERY_LONG_SUMMARY_LENGTH, f"expected length {VERY_LONG_SUMMARY_LENGTH}, got {len(summary.executive_summary)}"