|
6 | 6 | import os |
7 | 7 | import sys |
8 | 8 | import unittest |
| 9 | +from unittest.mock import patch |
9 | 10 |
|
10 | 11 | REPO_ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) |
11 | 12 | if REPO_ROOT not in sys.path: |
12 | 13 | sys.path.insert(0, REPO_ROOT) |
13 | 14 |
|
14 | 15 | from models.conversation import Bubble, Composer |
| 16 | +from services.workspace_tabs import _bubble_entry_timestamp_ms |
15 | 17 | from models.raw_access import ( |
16 | 18 | composer_newly_created_files, |
17 | 19 | conversation_header_bubble_id, |
@@ -86,6 +88,45 @@ def test_bubble_timestamp_ms_prefers_created_at_and_handles_zero(self) -> None: |
86 | 88 | with self.assertNoLogs("models.conversation", level="WARNING"): |
87 | 89 | self.assertIsNone(bad_bool.bubble_timestamp_ms()) |
88 | 90 |
|
| 91 | + def test_bubble_thinking_silent_when_missing(self) -> None: |
| 92 | + bubble = Bubble.from_dict({"text": "hi"}, bubble_id="b-1") |
| 93 | + with self.assertNoLogs("models.conversation", level="WARNING"): |
| 94 | + self.assertIsNone(bubble.thinking) |
| 95 | + |
| 96 | + def test_bubble_thinking_warns_on_wrong_type(self) -> None: |
| 97 | + bubble = Bubble.from_dict({"thinking": 42}, bubble_id="b-bad") |
| 98 | + with self.assertLogs("models.conversation", level="WARNING") as logs: |
| 99 | + self.assertIsNone(bubble.thinking) |
| 100 | + self.assertTrue(any("thinking" in m for m in logs.output), logs.output) |
| 101 | + |
| 102 | + def test_bubble_thinking_accepts_str_or_dict(self) -> None: |
| 103 | + s = Bubble.from_dict({"thinking": "trace"}, bubble_id="b-str") |
| 104 | + self.assertEqual(s.thinking, "trace") |
| 105 | + d = Bubble.from_dict({"thinking": {"text": "nested"}}, bubble_id="b-dict") |
| 106 | + self.assertEqual(d.thinking, {"text": "nested"}) |
| 107 | + |
| 108 | + def test_bubble_entry_timestamp_ms_preserves_epoch_zero(self) -> None: |
| 109 | + """Regression: falsy ``or now()`` must not replace a valid ``createdAt`` of 0.""" |
| 110 | + bubble = Bubble.from_dict({"createdAt": 0}, bubble_id="b-zero") |
| 111 | + sentinel_now_ms = 9_999_000_000_000 |
| 112 | + with patch( |
| 113 | + "services.workspace_tabs.datetime", |
| 114 | + ) as mock_datetime: |
| 115 | + mock_datetime.now.return_value.timestamp.return_value = sentinel_now_ms / 1000 |
| 116 | + ts = _bubble_entry_timestamp_ms(bubble) |
| 117 | + self.assertEqual(ts, 0) |
| 118 | + self.assertNotEqual(ts, sentinel_now_ms) |
| 119 | + |
| 120 | + def test_bubble_entry_timestamp_ms_falls_back_when_no_timestamp(self) -> None: |
| 121 | + bubble = Bubble.from_dict({"text": "hi"}, bubble_id="b-none") |
| 122 | + sentinel_now_ms = 1_700_000_000_000 |
| 123 | + with patch( |
| 124 | + "services.workspace_tabs.datetime", |
| 125 | + ) as mock_datetime: |
| 126 | + mock_datetime.now.return_value.timestamp.return_value = sentinel_now_ms / 1000 |
| 127 | + ts = _bubble_entry_timestamp_ms(bubble) |
| 128 | + self.assertEqual(ts, sentinel_now_ms) |
| 129 | + |
89 | 130 | def test_dict_bridge_newly_created_files_matches_composer_property(self) -> None: |
90 | 131 | data = {**GOOD_COMPOSER_RAW, "newlyCreatedFiles": [{"uri": {"path": "/a"}}]} |
91 | 132 | composer = Composer.from_dict(data, composer_id="cid-bridge") |
|
0 commit comments