Skip to content

Commit 88b7b10

Browse files
committed
fix: review comments
1 parent 011f6e8 commit 88b7b10

1 file changed

Lines changed: 54 additions & 20 deletions

File tree

tests/test_normalize_file_path.py

Lines changed: 54 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,20 @@
1-
"""Tests for utils.path_helpers.normalize_file_path.
2-
3-
Covers the shared implementation that was previously duplicated in
4-
scripts/export.py (closes #46). All call-sites in both the web app and the
5-
CLI export script now use this single copy.
6-
7-
Edge-case matrix:
8-
- file:/// and file:// URI schemes
9-
- Percent-encoded characters: spaces (%20), colons (%3A), hashes (%23)
10-
- Windows-style drive paths (backslash and forward-slash) on all platforms
11-
- Drive-letter lowercasing on win32
12-
- Plain POSIX paths pass through unchanged
13-
- Empty / None-like input
1+
"""Tests for utils.path_helpers path/timestamp helpers (closes #46).
2+
3+
Covers ``normalize_file_path`` and ``to_epoch_ms``, both previously duplicated
4+
in scripts/export.py. All call-sites in the web app and CLI export script now
5+
use the shared implementations in utils.path_helpers.
6+
7+
Test inventory (this module only): 21 cases — 12 ``normalize_file_path``,
8+
9 ``to_epoch_ms``. On win32, 2 cases skip (POSIX passthrough in
9+
``TestNormalizeFilePathPosixPassthrough`` only). A full-suite run may report
10+
more skips (e.g. ``skipped=4``) from other test modules, not this file.
1411
"""
1512

1613
import sys
1714
import unittest
15+
from datetime import datetime, timezone
1816

19-
from utils.path_helpers import normalize_file_path
17+
from utils.path_helpers import normalize_file_path, to_epoch_ms
2018

2119

2220
class TestNormalizeFilePathUriStripping(unittest.TestCase):
@@ -48,9 +46,14 @@ def test_hash_decoded(self) -> None:
4846
def test_percent_encoded_colon_in_uri_prefix(self) -> None:
4947
"""URI-style /d%3A/... path: %3A is decoded to ':'.
5048
51-
On win32 the backslash branch is entered (leading slash removed
52-
and path lowercased). On other platforms the leading slash prevents
53-
the Windows-drive branch, so the path is returned as decoded only.
49+
Only test that exercises the leading-``/`` + drive-letter shape end-to-end
50+
(Cursor sometimes stores ``/d%3A/...`` URIs). Other drive-path tests use
51+
``D:/...`` or ``D:\\...`` without a leading slash.
52+
53+
On win32 the win32 branch strips the leading slash, lowercases, and
54+
normalises to backslashes. On other platforms the leading ``/`` prevents
55+
the ``^[a-zA-Z]:[/\\]`` cross-platform branch in ``path_helpers``, so the
56+
path is returned as percent-decoded only (no slash flip / lowercasing).
5457
"""
5558
out = normalize_file_path("/d%3A/_Work/project")
5659
self.assertNotIn("%3A", out)
@@ -79,9 +82,8 @@ def test_forward_slash_drive_path_converted(self) -> None:
7982

8083
def test_file_uri_with_windows_drive(self) -> None:
8184
out = normalize_file_path("file:///C:/Users/Dev/project")
82-
self.assertIn("users", out)
83-
self.assertIn("dev", out)
84-
self.assertTrue(out.startswith("c:"))
85+
# file:/// stripped, then same drive-letter branch as D:/ and D:\ inputs.
86+
self.assertEqual(out, r"c:\users\dev\project")
8587

8688
def test_mixed_case_drive_lowercased(self) -> None:
8789
out = normalize_file_path(r"E:\Mixed\Case\Path")
@@ -103,5 +105,37 @@ def test_path_without_scheme_unchanged(self) -> None:
103105
self.assertEqual(out, "relative/path/file.py")
104106

105107

108+
class TestToEpochMs(unittest.TestCase):
109+
def test_none_returns_zero(self) -> None:
110+
self.assertEqual(to_epoch_ms(None), 0)
111+
112+
def test_ms_int_passthrough(self) -> None:
113+
self.assertEqual(to_epoch_ms(1_700_000_000_000), 1_700_000_000_000)
114+
115+
def test_seconds_int_converted_to_ms(self) -> None:
116+
self.assertEqual(to_epoch_ms(1_700_000_000), 1_700_000_000_000)
117+
118+
def test_seconds_float_converted_to_ms(self) -> None:
119+
self.assertEqual(to_epoch_ms(1_700_000_000.5), 1_700_000_000_500)
120+
121+
def test_zero_returns_zero(self) -> None:
122+
self.assertEqual(to_epoch_ms(0), 0)
123+
124+
def test_iso8601_zulu(self) -> None:
125+
expected = int(
126+
datetime(2026, 2, 3, 20, 39, 54, 17_000, tzinfo=timezone.utc).timestamp() * 1000
127+
)
128+
self.assertEqual(to_epoch_ms("2026-02-03T20:39:54.017Z"), expected)
129+
130+
def test_numeric_string_already_ms(self) -> None:
131+
self.assertEqual(to_epoch_ms("1700000000000"), 1_700_000_000_000)
132+
133+
def test_numeric_string_seconds(self) -> None:
134+
self.assertEqual(to_epoch_ms("1700000000"), 1_700_000_000_000)
135+
136+
def test_unrecognised_string_returns_zero(self) -> None:
137+
self.assertEqual(to_epoch_ms("not-a-timestamp"), 0)
138+
139+
106140
if __name__ == "__main__":
107141
unittest.main()

0 commit comments

Comments
 (0)