Skip to content

Commit d482cb6

Browse files
fix: macOS js-tests rollup install and fuzz tmp_path cleanup
Install platform Rollup binary after npm ci; drop Linux-only optionalDep. Use pytest tmp_path in test_parser_fuzz.py instead of leaking mkdtemp dirs.
1 parent 52db5dc commit d482cb6

4 files changed

Lines changed: 36 additions & 23 deletions

File tree

.github/workflows/ci.yml

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -188,4 +188,19 @@ jobs:
188188
cache: npm
189189

190190
- run: npm ci
191+
# npm optional-deps bug: platform rollup binaries are sometimes skipped after npm ci
192+
# (https://github.com/npm/cli/issues/4828). Install the native package explicitly.
193+
- name: Ensure Rollup native binary
194+
shell: bash
195+
run: |
196+
ROLLUP_VERSION=$(node -p "require('./node_modules/rollup/package.json').version")
197+
case "$(node -p "process.platform + '-' + process.arch")" in
198+
darwin-arm64) PKG="@rollup/rollup-darwin-arm64" ;;
199+
darwin-x64) PKG="@rollup/rollup-darwin-x64" ;;
200+
linux-x64) PKG="@rollup/rollup-linux-x64-gnu" ;;
201+
win32-x64) PKG="@rollup/rollup-win32-x64-msvc" ;;
202+
win32-arm64) PKG="@rollup/rollup-win32-arm64-msvc" ;;
203+
*) echo "Unsupported Node platform: $(node -p "process.platform + '-' + process.arch")"; exit 1 ;;
204+
esac
205+
npm install --no-save "${PKG}@${ROLLUP_VERSION}"
191206
- run: npm test

package-lock.json

Lines changed: 1 addition & 3 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,5 @@
1212
"jsdom": "^26.1.0",
1313
"marked": "^12.0.1",
1414
"vitest": "^3.2.4"
15-
},
16-
"optionalDependencies": {
17-
"@rollup/rollup-linux-x64-gnu": "4.60.4"
1815
}
1916
}

tests/test_parser_fuzz.py

Lines changed: 20 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -5,22 +5,25 @@
55
import json
66
import os
77
import sys
8-
import tempfile
98
from pathlib import Path
109

11-
from hypothesis import given, settings, strategies as st
10+
from hypothesis import HealthCheck, given, settings, strategies as st
1211

1312
sys.path.insert(0, os.path.join(os.path.dirname(__file__), ".."))
1413

1514
from utils.jsonl_parser import parse_session
1615

17-
FUZZ_SETTINGS = settings(max_examples=200, deadline=5000)
16+
FUZZ_SETTINGS = settings(
17+
max_examples=200,
18+
deadline=5000,
19+
suppress_health_check=[HealthCheck.function_scoped_fixture],
20+
)
1821

1922
ALLOWED_EXCEPTIONS: tuple[type[BaseException], ...] = ()
2023

2124

22-
def _fuzz_jsonl_path(name: str) -> Path:
23-
return Path(tempfile.mkdtemp()) / name
25+
def _fuzz_jsonl_path(tmp_path: Path, name: str) -> Path:
26+
return tmp_path / name
2427

2528

2629
def _parse_file_without_crash(path: str) -> None:
@@ -145,59 +148,59 @@ def structured_entry(draw: st.DrawFn) -> dict:
145148

146149
@FUZZ_SETTINGS
147150
@given(st.lists(st.text(min_size=0, max_size=500), min_size=0, max_size=30))
148-
def test_raw_line_soup_does_not_crash(lines: list[str]) -> None:
151+
def test_raw_line_soup_does_not_crash(tmp_path: Path, lines: list[str]) -> None:
149152
"""Malformed JSON lines, garbage text, and empty lines."""
150-
path = _write_jsonl(_fuzz_jsonl_path("soup.jsonl"), lines)
153+
path = _write_jsonl(_fuzz_jsonl_path(tmp_path, "soup.jsonl"), lines)
151154
_parse_file_without_crash(path)
152155

153156

154157
@FUZZ_SETTINGS
155158
@given(st.text(min_size=1, max_size=500))
156-
def test_truncated_json_line(prefix: str) -> None:
159+
def test_truncated_json_line(tmp_path: Path, prefix: str) -> None:
157160
"""Partial JSON simulating concurrent writes."""
158161
half = json.dumps(prefix)[: max(1, len(prefix) // 2)]
159162
line = '{"type": "user", "message": {"content": ' + half
160-
path = _write_jsonl(_fuzz_jsonl_path("trunc.jsonl"), [line])
163+
path = _write_jsonl(_fuzz_jsonl_path(tmp_path, "trunc.jsonl"), [line])
161164
_parse_file_without_crash(path)
162165

163166

164167
@FUZZ_SETTINGS
165168
@given(st.lists(structured_entry(), min_size=0, max_size=15))
166-
def test_structured_entries_with_fuzzed_fields(entries: list[dict]) -> None:
169+
def test_structured_entries_with_fuzzed_fields(tmp_path: Path, entries: list[dict]) -> None:
167170
"""Unknown types, missing/extra fields, wrong-typed nested values."""
168171
lines = [json.dumps(e, default=str) for e in entries]
169-
path = _write_jsonl(_fuzz_jsonl_path("structured.jsonl"), lines)
172+
path = _write_jsonl(_fuzz_jsonl_path(tmp_path, "structured.jsonl"), lines)
170173
_parse_file_without_crash(path)
171174

172175

173176
@FUZZ_SETTINGS
174177
@given(st.lists(_json_value, min_size=1, max_size=5))
175-
def test_deep_nesting_in_message_content(nested_values: list) -> None:
178+
def test_deep_nesting_in_message_content(tmp_path: Path, nested_values: list) -> None:
176179
entry = {
177180
"type": "user",
178181
"timestamp": "2026-06-11T00:00:00Z",
179182
"message": {"content": nested_values},
180183
}
181-
path = _write_jsonl(_fuzz_jsonl_path("nest.jsonl"), [json.dumps(entry, default=str)])
184+
path = _write_jsonl(_fuzz_jsonl_path(tmp_path, "nest.jsonl"), [json.dumps(entry, default=str)])
182185
_parse_file_without_crash(path)
183186

184187

185188
@FUZZ_SETTINGS
186189
@given(st.integers(min_value=10_000, max_value=50_000))
187-
def test_long_line_payload(length: int) -> None:
190+
def test_long_line_payload(tmp_path: Path, length: int) -> None:
188191
payload = "x" * length
189192
entry = {
190193
"type": "user",
191194
"timestamp": "2026-06-11T00:00:00Z",
192195
"message": {"content": [{"type": "text", "text": payload}]},
193196
}
194-
path = _write_jsonl(_fuzz_jsonl_path("long.jsonl"), [json.dumps(entry)])
197+
path = _write_jsonl(_fuzz_jsonl_path(tmp_path, "long.jsonl"), [json.dumps(entry)])
195198
_parse_file_without_crash(path)
196199

197200

198201
@FUZZ_SETTINGS
199202
@given(st.lists(st.text(max_size=100), min_size=1, max_size=10))
200-
def test_empty_lines_between_records(texts: list[str]) -> None:
203+
def test_empty_lines_between_records(tmp_path: Path, texts: list[str]) -> None:
201204
lines: list[str] = []
202205
for text in texts:
203206
lines.append("")
@@ -211,7 +214,7 @@ def test_empty_lines_between_records(texts: list[str]) -> None:
211214
)
212215
)
213216
lines.append(" ")
214-
path = _write_jsonl(_fuzz_jsonl_path("empty.jsonl"), lines)
217+
path = _write_jsonl(_fuzz_jsonl_path(tmp_path, "empty.jsonl"), lines)
215218
_parse_file_without_crash(path)
216219

217220

0 commit comments

Comments
 (0)