testsAndMisc/python_pkg/token_audit/tests/test_parse_events.py
Krzysztof kuhy Rudnicki b60cb4972a Clear five python_pkg packages under the cap
android_ui, app_icons, token_audit, focus_policy and random_jpg are now
entirely under 250 lines. Only wsg_grabber's six test modules remain in
python_pkg.

android_ui/driver.py 411 -> 205 took two cuts and a class split: the
element and error types plus the XML parser to _elements.py, and the
text-entry methods to a TextEntryMixin, using the ABC-with-abstract-
methods shape because the tests call them as bound attributes.
app_icons/glyphs.py 291 -> 93 by moving the twelve path fragments out.

Three intermediate failures, all module-level globals that travelled
with their code (_logger, re, _SAME_FIELD_X_TOLERANCE_PX) - the same
class of miss as _make_verifier, which ruff cannot flag.

Counts unchanged: 81, 137, 14, 44, 57.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011w2Pj4Ek3ZNMQVYcFHgXDY
2026-08-16 18:14:36 +02:00

107 lines
3.0 KiB
Python

"""Tests for token_audit event iteration and transcript discovery."""
from __future__ import annotations
import json
from pathlib import Path
from typing import TYPE_CHECKING
import pytest
from python_pkg.token_audit import parse
from python_pkg.token_audit.model import ToolCall, Turn
if TYPE_CHECKING:
from pathlib import Path
def _assistant(
usage: dict[str, int] | None = None,
content: list[dict[str, object]] | None = None,
cwd: str = "/home/kuhy/demo",
*,
sidechain: bool = False,
) -> dict[str, object]:
message: dict[str, object] = {"model": "claude-opus-5"}
if usage is not None:
message["usage"] = usage
if content is not None:
message["content"] = content
return {
"type": "assistant",
"cwd": cwd,
"isSidechain": sidechain,
"message": message,
}
def _write(tmp_path: Path, records: list[object], name: str = "sess.jsonl") -> Path:
path = tmp_path / name
path.write_text(
"\n".join(json.dumps(r) if isinstance(r, dict) else r for r in records),
encoding="utf-8",
)
return path
def test_iter_events_preserves_order(tmp_path: Path) -> None:
records = [
_assistant(
content=[
{
"type": "tool_use",
"id": "a",
"name": "Read",
"input": {"file_path": "/i.png"},
},
],
),
{
"message": {
"content": [
{"type": "tool_result", "tool_use_id": "a", "content": "xxxx"}
]
}
},
_assistant(usage={"output_tokens": 1}),
]
kinds = [k for k, _ in parse.iter_events(_write(tmp_path, records))]
assert kinds == ["tool", "turn"]
def test_events_carry_expected_types(tmp_path: Path) -> None:
records = [
_assistant(
content=[
{"type": "tool_use", "id": "a", "name": "Read", "input": {}},
],
),
{"message": {"content": [{"type": "tool_result", "tool_use_id": "a"}]}},
_assistant(usage={"output_tokens": 1}),
]
events = dict(parse.iter_events(_write(tmp_path, records)))
assert isinstance(events["tool"], ToolCall)
assert isinstance(events["turn"], Turn)
def test_find_transcripts_filters_by_window(tmp_path: Path) -> None:
project = tmp_path / "proj"
project.mkdir()
old = project / "old.jsonl"
new = project / "new.jsonl"
old.write_text("{}", encoding="utf-8")
new.write_text("{}", encoding="utf-8")
import os
os.utime(old, (1000, 1000))
os.utime(new, (50_000, 50_000))
assert parse.find_transcripts(tmp_path, since=10_000) == [new]
assert parse.find_transcripts(tmp_path, since=0, until=2000) == [old]
assert len(parse.find_transcripts(tmp_path, since=0)) == 2
@pytest.mark.parametrize("payload", [None, "abcdefgh"])
def test_result_tokens_handles_missing_content(payload: str | None) -> None:
assert parse._result_tokens({"content": payload}) >= 0