"""Tests for ml/features/context.py""" import pytest import sys, os; sys.path.insert(0, os.path.dirname(__file__)) from context import build_context, TaskSignal, PromptContext, CONTEXT_FEATURES def test_empty_tasks(): ctx = build_context([], hour_of_day=9, day_of_week=1) assert ctx.tasks == [] assert ctx.hour_of_day == 9 assert ctx.day_of_week == 1 def test_overdue_tasks_sorted_first(): tasks = [ TaskSignal(id="a", content="Normal task", priority=1, is_overdue=False), TaskSignal(id="b", content="Overdue task", priority=2, is_overdue=True, task_age_days=3.0), ] ctx = build_context(tasks) assert ctx.tasks[0]["id"] == "b" def test_high_priority_within_non_overdue(): tasks = [ TaskSignal(id="lo", content="Low prio", priority=1, is_overdue=False), TaskSignal(id="hi", content="High prio", priority=4, is_overdue=False), ] ctx = build_context(tasks) assert ctx.tasks[0]["id"] == "hi" def test_extra_fields_passed_through(): ctx = build_context([], extra={"mood": "focused"}) assert ctx.extra["mood"] == "focused" def test_task_age_rounded(): tasks = [TaskSignal(id="x", content="Task", task_age_days=1.23456)] ctx = build_context(tasks) assert ctx.tasks[0]["task_age_days"] == 1.2 def test_overdue_sorted_by_priority(): tasks = [ TaskSignal(id="lo", content="Low", priority=1, is_overdue=True), TaskSignal(id="hi", content="High", priority=4, is_overdue=True), ] ctx = build_context(tasks) assert ctx.tasks[0]["id"] == "hi" def test_overdue_same_priority_sorted_by_age(): tasks = [ TaskSignal(id="new", content="New", priority=2, is_overdue=True, task_age_days=1.0), TaskSignal(id="old", content="Old", priority=2, is_overdue=True, task_age_days=5.0), ] ctx = build_context(tasks) assert ctx.tasks[0]["id"] == "old" def test_due_date_none_preserved(): tasks = [TaskSignal(id="x", content="No due", due_date=None)] ctx = build_context(tasks) assert ctx.tasks[0]["due_date"] is None # ── CONTEXT_FEATURES spec tests (issue #61) ────────────────────────────────── def test_context_features_expected_names(): names = {f.name for f in CONTEXT_FEATURES} assert names == {"hour_of_day", "day_of_week", "tasks"} def test_context_features_all_jit(): for f in CONTEXT_FEATURES: assert f.freshness == "jit", f"{f.name}: expected freshness='jit', got {f.freshness!r}" def test_context_features_source_set(): for f in CONTEXT_FEATURES: assert f.source, f"{f.name}: source must not be empty" def test_context_features_fallback_set(): for f in CONTEXT_FEATURES: assert f.fallback, f"{f.name}: fallback must not be empty" def test_context_features_no_duplicates(): names = [f.name for f in CONTEXT_FEATURES] assert len(names) == len(set(names)), f"duplicate names: {names}"