|
5 | 5 | import pytest |
6 | 6 | from ..helpers import IntegrityError, Location, bin_to_hex |
7 | 7 | from ..hashindex import ChunkIndex |
8 | | -from ..constants import UNKNOWN_BYTES32 |
9 | 8 | from ..repository import Repository, MAX_DATA_SIZE, rest_serve_command, PackWriter, PackReader |
10 | 9 | from ..repoobj import RepoObj, OBJ_MAGIC, OBJ_VERSION |
11 | 10 | from .hashindex_test import H |
@@ -187,11 +186,11 @@ def test_multi_object_pack_roundtrip(repo_fixtures, request): |
187 | 186 | with get_repository_from_fixture(repo_fixtures, request) as repository: |
188 | 187 | repository._pack_writer.max_count = 2 # this test is written for exactly two objects per pack |
189 | 188 | repository.put(H(0), chunk0) |
190 | | - assert repository.chunks[H(0)].pack_id == UNKNOWN_BYTES32 # buffered: the pack is not full yet |
| 189 | + assert repository.chunks.is_pending(H(0)) # buffered: the pack is not full yet |
191 | 190 | repository.put(H(1), chunk1) # fills the pack, flushing both objects at once |
192 | 191 | # both objects share one pack, written exactly once, laid out in put() order |
193 | 192 | pack_id = repository.chunks[H(0)].pack_id |
194 | | - assert pack_id != UNKNOWN_BYTES32 |
| 193 | + assert not repository.chunks.is_pending(H(0)) |
195 | 194 | assert repository.chunks[H(1)].pack_id == pack_id |
196 | 195 | assert [info.name for info in repository.store_list("packs")] == [bin_to_hex(pack_id)] |
197 | 196 | assert repository.chunks[H(0)].obj_offset == 0 |
@@ -509,20 +508,57 @@ def test_get_uses_chunk_index_location(tmp_path): |
509 | 508 |
|
510 | 509 |
|
511 | 510 | def test_put_marks_id_in_chunk_index(tmp_path): |
512 | | - # put() marks the id pending (pack_id=UNKNOWN_BYTES32); flush() then fills in the |
513 | | - # real pack location for the current session. |
| 511 | + # put() marks the id pending; flush() sets the real pack location and clears the pending flag. |
514 | 512 | with Repository(str(tmp_path / "repo"), exclusive=True, create=True) as repository: |
515 | 513 | id1 = H(1) |
516 | 514 | repository.put(id1, fchunk(b"ZEROS")) |
517 | 515 | entry = repository._chunks.get(id1) |
518 | 516 | assert entry is not None |
519 | | - assert entry.pack_id == UNKNOWN_BYTES32 # buffered, not yet flushed |
| 517 | + assert repository._chunks.is_pending(id1) # buffered, not yet flushed |
520 | 518 | repository.flush() |
521 | 519 | entry = repository._chunks.get(id1) |
| 520 | + assert not repository._chunks.is_pending(id1) |
522 | 521 | assert entry.pack_id == sha256(fchunk(b"ZEROS")).digest() |
523 | 522 | assert entry.size == 0 # uncompressed size filled in by cache layer |
524 | 523 |
|
525 | 524 |
|
| 525 | +def test_list_skips_pending_chunk(tmp_path): |
| 526 | + # list() skips a pending chunk and yields it once flushed. |
| 527 | + with Repository(str(tmp_path / "repo"), exclusive=True, create=True) as repository: |
| 528 | + repository.put(H(1), fchunk(b"BUFFERED")) # buffered: the pack is not full yet |
| 529 | + assert repository._chunks.is_pending(H(1)) |
| 530 | + assert repository.list() == [] |
| 531 | + repository.flush() |
| 532 | + assert [chunk_id for chunk_id, _ in repository.list()] == [H(1)] |
| 533 | + |
| 534 | + |
| 535 | +def test_get_pending_chunk_raises(tmp_path): |
| 536 | + # get() on a pending chunk raises PackLocationUnknown, also with raise_missing=False. |
| 537 | + with Repository(str(tmp_path / "repo"), exclusive=True, create=True) as repository: |
| 538 | + repository.put(H(1), fchunk(b"BUFFERED")) # buffered: the pack is not full yet |
| 539 | + assert repository._chunks.is_pending(H(1)) |
| 540 | + with pytest.raises(Repository.PackLocationUnknown): |
| 541 | + repository.get(H(1)) |
| 542 | + with pytest.raises(Repository.PackLocationUnknown): |
| 543 | + repository.get(H(1), raise_missing=False) |
| 544 | + repository.flush() # close() requires the buffer to be empty |
| 545 | + |
| 546 | + |
| 547 | +def test_flush_store_failure_drops_pending_entries(tmp_path): |
| 548 | + # flush() removes the pending index entries when storing the pack fails. |
| 549 | + with Repository(str(tmp_path / "repo"), exclusive=True, create=True) as repository: |
| 550 | + repository.put(H(1), fchunk(b"BUFFERED")) |
| 551 | + assert repository._chunks.is_pending(H(1)) |
| 552 | + |
| 553 | + def boom(*args, **kwargs): |
| 554 | + raise OSError("store failed") |
| 555 | + |
| 556 | + repository._pack_writer.store.store = boom |
| 557 | + with pytest.raises(OSError): |
| 558 | + repository.flush() |
| 559 | + assert H(1) not in repository._chunks |
| 560 | + |
| 561 | + |
526 | 562 | def test_check_detects_corruption_in_later_object(tmp_path): |
527 | 563 | # Corruption anywhere in a multi-object pack must be caught, not just in the first object: the pack |
528 | 564 | # is named by sha256(content), so flipping any byte makes its stored hash differ from its name. |
|
0 commit comments