diff --git a/backend/tests/test_read_file_line_range.py b/backend/tests/test_read_file_line_range.py index ccae1295fc..0c86d78cdf 100644 --- a/backend/tests/test_read_file_line_range.py +++ b/backend/tests/test_read_file_line_range.py @@ -33,7 +33,7 @@ def _local_runtime(tmp_path: Path) -> SimpleNamespace: def _read(tmp_path, monkeypatch, **kwargs) -> str: runtime = _local_runtime(tmp_path) - (tmp_path / "uploads" / "five.txt").write_text(_FIVE_LINES, encoding="utf-8") + (tmp_path / "uploads" / "five.txt").write_text(_FIVE_LINES, encoding="utf-8", newline="\n") monkeypatch.setattr("deerflow.sandbox.tools.ensure_sandbox_initialized", lambda runtime: LocalSandbox("t1")) monkeypatch.setattr("deerflow.sandbox.tools.ensure_thread_directories_exist", lambda runtime: None) return read_file_tool.func( diff --git a/backend/tests/test_read_file_tool_binary.py b/backend/tests/test_read_file_tool_binary.py index 31f32a5eab..c773daf486 100644 --- a/backend/tests/test_read_file_tool_binary.py +++ b/backend/tests/test_read_file_tool_binary.py @@ -51,7 +51,7 @@ def test_read_file_tool_binary_file_returns_actionable_hint(tmp_path, monkeypatc def test_read_file_tool_text_file_unaffected(tmp_path, monkeypatch) -> None: runtime = _local_runtime(tmp_path) - (tmp_path / "uploads" / "notes.txt").write_text("hello 你好\nsecond line", encoding="utf-8") + (tmp_path / "uploads" / "notes.txt").write_text("hello 你好\nsecond line", encoding="utf-8", newline="\n") monkeypatch.setattr("deerflow.sandbox.tools.ensure_sandbox_initialized", lambda runtime: LocalSandbox("t1")) monkeypatch.setattr("deerflow.sandbox.tools.ensure_thread_directories_exist", lambda runtime: None) diff --git a/backend/tests/test_read_file_truncation_continuation.py b/backend/tests/test_read_file_truncation_continuation.py index a0a7bf86bb..94d9f1e6e5 100644 --- a/backend/tests/test_read_file_truncation_continuation.py +++ b/backend/tests/test_read_file_truncation_continuation.py @@ -40,7 +40,7 @@ def test_following_the_markers_reads_the_whole_file_without_gap_or_overlap(tmp_p runtime = _local_runtime(tmp_path) lines = [f"{i:05d} " + "x" * (50 + i % 7) for i in range(1, 2601)] # 2,600 lines of ~57 chars, > 150k chars content = "\n".join(lines) + "\n" - (tmp_path / "uploads" / "long.txt").write_text(content, encoding="utf-8") + (tmp_path / "uploads" / "long.txt").write_text(content, encoding="utf-8", newline="\n") monkeypatch.setattr("deerflow.sandbox.tools.ensure_sandbox_initialized", lambda runtime: LocalSandbox("t1")) monkeypatch.setattr("deerflow.sandbox.tools.ensure_thread_directories_exist", lambda runtime: None) @@ -117,7 +117,7 @@ def test_long_lines_near_the_budget_are_followed_without_gap_or_overlap(tmp_path tail = "".join(f"{i:05d} tail line\n" for i in range(1, 3001)) for length in (49600, 49743, 49750, 49760, 50000): content = "a\n" + "y" * length + "\n" + tail - (tmp_path / "uploads" / "long.txt").write_text(content, encoding="utf-8") + (tmp_path / "uploads" / "long.txt").write_text(content, encoding="utf-8", newline="\n") rebuilt, forms = _follow_markers(runtime, content) assert rebuilt == content, (length, forms) assert "bash" not in forms, (length, forms) @@ -128,7 +128,7 @@ def test_a_line_longer_than_max_chars_is_pointed_at_bash_not_at_a_read(tmp_path, monkeypatch.setattr("deerflow.sandbox.tools.ensure_sandbox_initialized", lambda runtime: LocalSandbox("t1")) monkeypatch.setattr("deerflow.sandbox.tools.ensure_thread_directories_exist", lambda runtime: None) content = "a\n" + "y" * 50001 + "\n" + "".join(f"{i:05d} tail line\n" for i in range(1, 301)) - (tmp_path / "uploads" / "long.txt").write_text(content, encoding="utf-8") + (tmp_path / "uploads" / "long.txt").write_text(content, encoding="utf-8", newline="\n") result = _read(runtime) assert "cut inside line 2 of 302 lines" in result assert "Continue with start_line" not in result and "Read that line whole" not in result @@ -142,7 +142,7 @@ def test_a_ranged_read_ending_in_a_blank_line_reports_the_full_span(tmp_path, mo monkeypatch.setattr("deerflow.sandbox.tools.ensure_sandbox_initialized", lambda runtime: LocalSandbox("t1")) monkeypatch.setattr("deerflow.sandbox.tools.ensure_thread_directories_exist", lambda runtime: None) content = "".join(f"{i:05d} " + "x" * 51 + "\n" for i in range(1, 3001)) + "\n" # 3,001 lines, the last one blank - (tmp_path / "uploads" / "long.txt").write_text(content, encoding="utf-8") + (tmp_path / "uploads" / "long.txt").write_text(content, encoding="utf-8", newline="\n") assert "of 3001 lines" in _read(runtime) assert "of 2-3001 lines" in _read(runtime, start_line=2) assert "of 3001 lines" in _read(runtime, start_line=1, end_line=3001) @@ -153,7 +153,7 @@ def test_a_last_line_read_whole_is_the_end_of_the_walk(tmp_path, monkeypatch) -> monkeypatch.setattr("deerflow.sandbox.tools.ensure_sandbox_initialized", lambda runtime: LocalSandbox("t1")) monkeypatch.setattr("deerflow.sandbox.tools.ensure_thread_directories_exist", lambda runtime: None) content = "a\n" + "y" * 50000 + "\n" - (tmp_path / "uploads" / "long.txt").write_text(content, encoding="utf-8") + (tmp_path / "uploads" / "long.txt").write_text(content, encoding="utf-8", newline="\n") rebuilt, forms = _follow_markers(runtime, content) assert forms == ["whole_line"] assert rebuilt == content @@ -165,7 +165,7 @@ def test_a_bounded_read_cut_inside_its_last_line_still_names_the_line_after_it(t monkeypatch.setattr("deerflow.sandbox.tools.ensure_thread_directories_exist", lambda runtime: None) lines = [f"{i:05d} line" for i in range(1, 1002)] + ["y" * 49900] + [f"{i:05d} after" for i in range(1, 301)] content = "\n".join(lines) + "\n" - (tmp_path / "uploads" / "long.txt").write_text(content, encoding="utf-8") + (tmp_path / "uploads" / "long.txt").write_text(content, encoding="utf-8", newline="\n") result = _read(runtime, start_line=1, end_line=1002) assert "cut inside line 1002 of 1002 lines" in result assert "Read that line whole with start_line=1002, end_line=1002, then continue with start_line=1003]" in result @@ -180,7 +180,7 @@ def test_a_start_line_only_read_cut_inside_the_files_last_line_names_nothing_fur monkeypatch.setattr("deerflow.sandbox.tools.ensure_sandbox_initialized", lambda runtime: LocalSandbox("t1")) monkeypatch.setattr("deerflow.sandbox.tools.ensure_thread_directories_exist", lambda runtime: None) content = "a\n" + "x" * 40000 + "\n" + "y" * 49900 + "\n" - (tmp_path / "uploads" / "long.txt").write_text(content, encoding="utf-8") + (tmp_path / "uploads" / "long.txt").write_text(content, encoding="utf-8", newline="\n") result = _read(runtime, start_line=2) # no end_line: the read runs to the end of the file assert "cut inside line 3 of 2-3 lines" in result assert "Read that line whole with start_line=3, end_line=3]" in result @@ -193,7 +193,7 @@ def test_a_blank_line_named_by_a_marker_reads_as_empty_not_as_past_the_end(tmp_p monkeypatch.setattr("deerflow.sandbox.tools.ensure_thread_directories_exist", lambda runtime: None) lines = [f"{i:05d} line" for i in range(1, 1099)] + ["y" * 49800, ""] + [f"{i:05d} after" for i in range(1, 401)] content = "\n".join(lines) + "\n" # line 1100 is blank, 400 lines follow it - (tmp_path / "uploads" / "long.txt").write_text(content, encoding="utf-8") + (tmp_path / "uploads" / "long.txt").write_text(content, encoding="utf-8", newline="\n") result = _read(runtime, start_line=958, end_line=1100) assert "Read that line whole with start_line=1099, end_line=1099, then continue with start_line=1100]" in result assert _read(runtime, start_line=1100, end_line=1100) == "(empty)"