# Copyright 2026 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. from artemis.utils.notes import ( append_note_content, list_notes_info, read_note_content, save_note_content, save_verified_note, update_note_content, ) import pytest @pytest.mark.parametrize("content", ["# 报告\n结果\n", "line\r\nsecond", ""]) def test_verified_note_receipt_matches_actual_file_bytes(tmp_path, content): import hashlib receipt = save_verified_note(tmp_path, "report.md", content) data = (tmp_path / receipt["path"]).read_bytes() assert receipt["path"] == "notes/report.md" assert receipt["verified"] is True and receipt["bytes"] == len(data) assert receipt["sha256"] == hashlib.sha256(data).hexdigest() def test_verified_note_does_not_confirm_incomplete_write(tmp_path, monkeypatch): from artemis.utils import notes def partial_write(base_dir, key, content): save_note_content(base_dir, key, content[:1]) monkeypatch.setattr(notes, "save_note_content", partial_write) with pytest.raises(OSError, match="读回不一致"): save_verified_note(tmp_path, "report", "完整报告") def test_list_notes_info(tmp_path): base_dir = tmp_path # Create some notes save_note_content(base_dir, "note1", "line1\nline2\nline3") save_note_content(base_dir, "note2", "line1\nline2") info = list_notes_info(base_dir) assert info == {"note1": 3, "note2": 2} def test_read_note_content_range(tmp_path): base_dir = tmp_path save_note_content(base_dir, "note1", "line1\nline2\nline3\nline4") # Read full assert read_note_content(base_dir, "note1") == "line1\nline2\nline3\nline4" # Read range assert read_note_content(base_dir, "note1", start_line=2, end_line=3) == "line2\nline3" # Read from start to line assert read_note_content(base_dir, "note1", end_line=2) == "line1\nline2" # Read from line to end assert read_note_content(base_dir, "note1", start_line=3) == "line3\nline4" def test_update_note_exact_match(tmp_path): base_dir = tmp_path original_content = 'def demo():\n print("Hello, World!")\n return True\n' save_note_content(base_dir, "task_plan", original_content) # Exact match warning = update_note_content( base_dir, "task_plan", ' print("Hello, World!")', ' print("Hello, Jetski!")', ) assert warning is None updated_content = read_note_content(base_dir, "task_plan") assert 'print("Hello, Jetski!")' in updated_content assert 'print("Hello, World!")' not in updated_content def test_update_note_relaxed_match(tmp_path): base_dir = tmp_path # Multi-line content with checkboxes and indentation original_content = ( "- [ ] Step 1: Research the issue\n - [ ] Step 2: Write code\n - [ ] Step 3: Run tests\n" ) save_note_content(base_dir, "task_plan", original_content) # Relaxed match: case difference, quote difference, checkbox bullet difference, trailing whitespace target = " * [ ] step 2: write code " replacement = " - [/] Step 2: In progress" warning = update_note_content(base_dir, "task_plan", target, replacement) # Warning should be returned assert warning is not None assert "We did our best to apply changes despite some inaccuracies" in warning updated_content = read_note_content(base_dir, "task_plan") assert "Step 2: In progress" in updated_content assert "Step 2: Write code" not in updated_content # Check indentation is preserved from replacement assert " - [/] Step 2: In progress\n" in updated_content def test_update_note_fuzzy_match(tmp_path): base_dir = tmp_path original_content = 'print("Alpha message")\nprint("Gamma message")\nprint("Delta message")\n' save_note_content(base_dir, "task_plan", original_content) # Fuzzy match: typo (Gemma vs Gamma) target = 'print("Gemma message")' replacement = 'print("Gamma updated!")' warning = update_note_content(base_dir, "task_plan", target, replacement) assert warning is not None assert "We did our best to apply changes despite some inaccuracies" in warning updated_content = read_note_content(base_dir, "task_plan") assert 'print("Gamma updated!")' in updated_content assert 'print("Gamma message")' not in updated_content def test_update_note_ambiguous_match(tmp_path): base_dir = tmp_path # Multiple lines that could match target original_content = 'print("Beta message")\nprint("Zeta message")\nprint("Eta message")\n' save_note_content(base_dir, "task_plan", original_content) # Target is highly ambiguous (Xeta is almost equally close to Beta and Zeta) target = 'print("Xeta message")' replacement = 'print("Ambiguous match!")' # Should raise ValueError due to ambiguity or inability to find unique match with pytest.raises(ValueError) as exc_info: update_note_content(base_dir, "task_plan", target, replacement) assert "Target string 'print(\"Xeta message\")' not found" in str( exc_info.value ) or "not unique" in str(exc_info.value) def test_update_note_not_found(tmp_path): base_dir = tmp_path original_content = 'print("Alpha message")\n' save_note_content(base_dir, "task_plan", original_content) # Totally different target target = 'print("Something totally different")' replacement = 'print("Hello")' with pytest.raises(ValueError) as exc_info: update_note_content(base_dir, "task_plan", target, replacement) assert "not found" in str(exc_info.value) def test_append_note_content(tmp_path): base_dir = tmp_path # Case 1: File does not exist append_note_content(base_dir, "new_note", "hello") assert read_note_content(base_dir, "new_note") == "hello\n" # Case 2: File exists and ends with a newline append_note_content(base_dir, "new_note", "world") assert read_note_content(base_dir, "new_note") == "hello\nworld\n" # Case 3: File exists and does not end with a newline file_path = base_dir / "notes" / "no_newline.md" file_path.parent.mkdir(parents=True, exist_ok=True) file_path.write_text("no newline", encoding="utf-8") append_note_content(base_dir, "no_newline", "added text") assert read_note_content(base_dir, "no_newline") == "no newline\nadded text\n"