This repository has been archived on 2026-03-24. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files
Timmy-time-dashboard/tests/timmy/test_conversation.py
Alexander Whitestone 9d78eb31d1 ruff (#169)
* polish: streamline nav, extract inline styles, improve tablet UX

- Restructure desktop nav from 8+ flat links + overflow dropdown into
  5 grouped dropdowns (Core, Agents, Intel, System, More) matching
  the mobile menu structure to reduce decision fatigue
- Extract all inline styles from mission_control.html and base.html
  notification elements into mission-control.css with semantic classes
- Replace JS-built innerHTML with secure DOM construction in
  notification loader and chat history
- Add CONNECTING state to connection indicator (amber) instead of
  showing OFFLINE before WebSocket connects
- Add tablet breakpoint (1024px) with larger touch targets for
  Apple Pencil / stylus use and safe-area padding for iPad toolbar
- Add active-link highlighting in desktop dropdown menus
- Rename "Mission Control" page title to "System Overview" to
  disambiguate from the chat home page
- Add "Home — Timmy Time" page title to index.html

https://claude.ai/code/session_015uPUoKyYa8M2UAcyk5Gt6h

* fix(security): move auth-gate credentials to environment variables

Hardcoded username, password, and HMAC secret in auth-gate.py replaced
with os.environ lookups. Startup now refuses to run if any variable is
unset. Added AUTH_GATE_SECRET/USER/PASS to .env.example.

https://claude.ai/code/session_015uPUoKyYa8M2UAcyk5Gt6h

* refactor(tooling): migrate from black+isort+bandit to ruff

Replace three separate linting/formatting tools with a single ruff
invocation. Updates tox.ini (lint, format, pre-push, pre-commit envs),
.pre-commit-config.yaml, and CI workflow. Fixes all ruff errors
including unused imports, missing raise-from, and undefined names.
Ruff config maps existing bandit skips to equivalent S-rules.

https://claude.ai/code/session_015uPUoKyYa8M2UAcyk5Gt6h

---------

Co-authored-by: Claude <noreply@anthropic.com>
2026-03-11 12:23:35 -04:00

135 lines
4.0 KiB
Python

"""Tests for timmy.conversation — conversation context and tool routing."""
from timmy.conversation import ConversationContext, ConversationManager
class TestConversationContext:
"""Test ConversationContext dataclass."""
def test_defaults(self):
ctx = ConversationContext()
assert ctx.user_name is None
assert ctx.current_topic is None
assert ctx.turn_count == 0
def test_update_topic(self):
ctx = ConversationContext()
ctx.update_topic("Bitcoin price")
assert ctx.current_topic == "Bitcoin price"
assert ctx.turn_count == 1
def test_set_user_name(self):
ctx = ConversationContext()
ctx.set_user_name("Alice")
assert ctx.user_name == "Alice"
def test_context_summary_empty(self):
ctx = ConversationContext()
assert ctx.get_context_summary() == ""
def test_context_summary_full(self):
ctx = ConversationContext()
ctx.set_user_name("Bob")
ctx.update_topic("coding")
summary = ctx.get_context_summary()
assert "Bob" in summary
assert "coding" in summary
assert "1" in summary # turn count
class TestConversationManager:
"""Test ConversationManager."""
def test_get_context_creates_new(self):
mgr = ConversationManager()
ctx = mgr.get_context("session-1")
assert isinstance(ctx, ConversationContext)
def test_get_context_returns_same(self):
mgr = ConversationManager()
ctx1 = mgr.get_context("s1")
ctx2 = mgr.get_context("s1")
assert ctx1 is ctx2
def test_clear_context(self):
mgr = ConversationManager()
mgr.get_context("s1")
mgr.clear_context("s1")
# New context should be fresh
ctx = mgr.get_context("s1")
assert ctx.turn_count == 0
def test_clear_nonexistent(self):
mgr = ConversationManager()
mgr.clear_context("nope") # Should not raise
class TestExtractUserName:
"""Test name extraction from messages."""
def test_my_name_is(self):
mgr = ConversationManager()
assert mgr.extract_user_name("My name is Alice") == "Alice"
def test_i_am(self):
mgr = ConversationManager()
assert mgr.extract_user_name("I am Bob") == "Bob"
def test_call_me(self):
mgr = ConversationManager()
assert mgr.extract_user_name("Call me Charlie") == "Charlie"
def test_im(self):
mgr = ConversationManager()
assert mgr.extract_user_name("I'm Dave") == "Dave"
def test_no_name(self):
mgr = ConversationManager()
assert mgr.extract_user_name("What is the weather?") is None
def test_strips_punctuation(self):
mgr = ConversationManager()
assert mgr.extract_user_name("My name is Eve.") == "Eve"
class TestShouldUseTools:
"""Test tool usage detection."""
def _check(self, message, expected):
mgr = ConversationManager()
ctx = ConversationContext()
assert mgr.should_use_tools(message, ctx) is expected
def test_search_needs_tools(self):
self._check("search for Python tutorials", True)
def test_calculate_needs_tools(self):
self._check("calculate 2 + 2", True)
def test_run_command_needs_tools(self):
self._check("run ls -la", True)
def test_hello_no_tools(self):
self._check("hello", False)
def test_who_are_you_no_tools(self):
self._check("who are you?", False)
def test_thanks_no_tools(self):
self._check("thanks!", False)
def test_simple_question_no_tools(self):
self._check("what is Python?", False)
def test_current_info_needs_tools(self):
self._check("what is the current price of Bitcoin today?", True)
def test_ambiguous_defaults_false(self):
self._check("tell me something interesting", False)
def test_latest_news_needs_tools(self):
self._check("what are the latest updates?", True)
def test_weather_needs_tools(self):
self._check("weather forecast please", True)