This is an automated email from the ASF dual-hosted git repository.
sbp pushed a commit to branch sbp
in repository https://gitbox.apache.org/repos/asf/tooling-trusted-releases.git
The following commit(s) were added to refs/heads/sbp by this push:
new 45f2bfd0 Add tests for file metadata, including testing an alternative
design
45f2bfd0 is described below
commit 45f2bfd0580e93e4a2e3eb9e196902193416c608
Author: Sean B. Palmer <[email protected]>
AuthorDate: Tue Mar 17 15:39:05 2026 +0000
Add tests for file metadata, including testing an alternative design
---
tests/unit/test_create_revision.py | 67 +++++++++++++++++
tests/unit/test_release_file_state.py | 132 ++++++++++++++++++++++++++++++++++
2 files changed, 199 insertions(+)
diff --git a/tests/unit/test_create_revision.py
b/tests/unit/test_create_revision.py
index 1b8a7ff0..ffb0f01e 100644
--- a/tests/unit/test_create_revision.py
+++ b/tests/unit/test_create_revision.py
@@ -54,6 +54,7 @@ class FakeRevision:
self.name = ""
self.number = ""
self.parent_name: str | None = None
+ self.seq: int = 0
self.phase = phase
self.release = release
self.release_name = release_name
@@ -85,6 +86,7 @@ class MockSafeData:
raise RuntimeError("Expected data.add to set _new_revision before
flush")
self._new_revision.name = f"{self._new_revision.release_name}
{self._new_number}"
self._new_revision.number = self._new_number
+ self._new_revision.seq = int(self._new_number)
self._new_revision.parent_name = self._parent_name
async def _merge(self, obj: object) -> object:
@@ -284,6 +286,71 @@ async def
test_modify_failed_error_propagates_and_cleans_up(tmp_path: pathlib.Pa
assert not os.listdir(tmp_path)
[email protected]
+async def test_v1_previous_attestable_suppresses_file_state_rows(tmp_path:
pathlib.Path):
+ release = mock.MagicMock()
+ release.phase = sql.ReleasePhase.RELEASE_CANDIDATE_DRAFT
+ release.project = mock.MagicMock()
+ release.project.release_policy = None
+ release.release_policy = None
+ release_name = sql.release_name("proj", "1.0")
+
+ old_revision = mock.MagicMock()
+ old_revision.name = f"{release_name} 00001"
+ old_revision.number = "00001"
+ old_revision.safe_number = safe.RevisionNumber("00001")
+
+ import contextlib
+
+ import atr.models.attestable as attestable
+
+ v1_attestable = attestable.AttestableV1(paths={"example.txt": "hash1"})
+
+ mock_session = _mock_db_session(release)
+ participant = _make_participant()
+ safe_data = MockSafeData(parent_name=old_revision.name, new_number="00002")
+
+ patches = [
+ mock.patch.object(revision.aiofiles.os, "makedirs",
new_callable=mock.AsyncMock),
+ mock.patch.object(revision.aiofiles.os, "rename",
new_callable=mock.AsyncMock),
+ mock.patch.object(revision.attestable, "load",
new_callable=mock.AsyncMock, return_value=v1_attestable),
+ mock.patch.object(
+ revision.attestable,
+ "paths_to_hashes_and_sizes",
+ new_callable=mock.AsyncMock,
+ return_value=({"example.txt": "hash2"}, {"example.txt": 100}),
+ ),
+ mock.patch.object(revision.attestable, "write_files_data",
new_callable=mock.AsyncMock),
+ mock.patch.object(revision.attestable, "compute_classifications",
return_value={"example.txt": "binary"}),
+ mock.patch.object(revision.db, "session", return_value=mock_session),
+ mock.patch.object(revision.detection,
"detect_archives_requiring_quarantine", return_value=[]),
+ mock.patch.object(revision.detection, "validate_directory",
return_value=[]),
+ mock.patch.object(
+ revision.interaction, "latest_revision",
new_callable=mock.AsyncMock, return_value=old_revision
+ ),
+ mock.patch.object(revision.merge, "merge",
new_callable=mock.AsyncMock),
+ mock.patch.object(revision.sql, "Revision",
side_effect=_make_fake_revision),
+ mock.patch.object(revision, "SafeSession",
return_value=MockSafeSession(safe_data)),
+ mock.patch.object(revision.tasks, "draft_checks",
new_callable=mock.AsyncMock),
+ mock.patch.object(revision.util, "chmod_directories"),
+ mock.patch.object(revision.util, "chmod_files"),
+ mock.patch.object(revision.util, "create_hard_link_clone",
new_callable=mock.AsyncMock),
+ mock.patch.object(revision.paths, "get_tmp_dir",
return_value=tmp_path),
+ mock.patch.object(revision.util, "paths_to_inodes", return_value={}),
+ mock.patch.object(revision.paths, "release_directory",
return_value=tmp_path / "releases" / "00002"),
+ mock.patch.object(revision.paths, "release_directory_base",
return_value=tmp_path / "releases"),
+ ]
+
+ with contextlib.ExitStack() as stack:
+ for patch in patches:
+ stack.enter_context(patch)
+ await participant.create_revision_with_quarantine("proj", "1.0",
"test")
+
+ added_objects = [call.args[0] for call in safe_data.add.call_args_list]
+ file_state_rows = [obj for obj in added_objects if isinstance(obj,
sql.ReleaseFileState)]
+ assert file_state_rows == []
+
+
def _make_fake_revision(**kwargs) -> FakeRevision:
return FakeRevision(**kwargs)
diff --git a/tests/unit/test_release_file_state.py
b/tests/unit/test_release_file_state.py
index b84c8945..034d9a5e 100644
--- a/tests/unit/test_release_file_state.py
+++ b/tests/unit/test_release_file_state.py
@@ -15,9 +15,141 @@
# specific language governing permissions and limitations
# under the License.
+import atr.attestable as attestable
+import atr.models.attestable as models
import atr.models.sql as sql
+def test_can_write_file_state_rows_first_revision():
+ assert attestable.can_write_file_state_rows(previous=None,
parent_name=None) is True
+
+
+def test_can_write_file_state_rows_missing_attestable_with_parent():
+ assert attestable.can_write_file_state_rows(previous=None,
parent_name="proj-1.0 00005") is False
+
+
+def test_can_write_file_state_rows_v1_previous():
+ previous = models.AttestableV1(paths={"a.tar.gz": "h1"})
+ assert attestable.can_write_file_state_rows(previous=previous,
parent_name="proj-1.0 00002") is False
+
+
+def test_can_write_file_state_rows_v2_previous():
+ previous = models.AttestableV2(
+ paths={
+ "a.tar.gz": models.PathEntryV2(content_hash="h1",
classification="source"),
+ }
+ )
+ assert attestable.can_write_file_state_rows(previous=previous,
parent_name="proj-1.0 00002") is True
+
+
+def test_compute_file_state_rows_changed_classification():
+ previous = models.AttestableV2(
+ paths={
+ "app.tar.gz": models.PathEntryV2(content_hash="hash1",
classification="metadata"),
+ },
+ )
+ rows = attestable.compute_file_state_rows(
+ "example-0.0.1",
+ 2,
+ {"app.tar.gz": "hash1"},
+ {"app.tar.gz": "source"},
+ previous,
+ )
+
+ assert len(rows) == 1
+ assert rows[0].path == "app.tar.gz"
+ assert rows[0].present is True
+ assert rows[0].content_hash == "hash1"
+ assert rows[0].classification == "source"
+
+
+def test_compute_file_state_rows_changed_hash():
+ previous = models.AttestableV2(
+ paths={
+ "README.md": models.PathEntryV2(content_hash="hash1",
classification="metadata"),
+ },
+ )
+ rows = attestable.compute_file_state_rows(
+ "example-0.0.1",
+ 2,
+ {"README.md": "hash2"},
+ {"README.md": "metadata"},
+ previous,
+ )
+
+ assert len(rows) == 1
+ assert rows[0].path == "README.md"
+ assert rows[0].present is True
+ assert rows[0].content_hash == "hash2"
+ assert rows[0].classification == "metadata"
+
+
+def test_compute_file_state_rows_deleted_path():
+ previous = models.AttestableV2(
+ paths={
+ "old-file.tar.gz": models.PathEntryV2(content_hash="hash1",
classification="source"),
+ },
+ )
+ rows = attestable.compute_file_state_rows("example-0.0.1", 2, {}, {},
previous)
+
+ assert len(rows) == 1
+ assert rows[0].path == "old-file.tar.gz"
+ assert rows[0].present is False
+ assert rows[0].content_hash is None
+ assert rows[0].classification is None
+
+
+def test_compute_file_state_rows_new_path():
+ rows = attestable.compute_file_state_rows(
+ "example-0.0.1",
+ 1,
+ {"README.md": "hash1"},
+ {"README.md": "metadata"},
+ None,
+ )
+
+ assert len(rows) == 1
+ assert rows[0].release_name == "example-0.0.1"
+ assert rows[0].path == "README.md"
+ assert rows[0].since_revision_seq == 1
+ assert rows[0].present is True
+ assert rows[0].content_hash == "hash1"
+ assert rows[0].classification == "metadata"
+
+
+def test_compute_file_state_rows_unchanged_path():
+ previous = models.AttestableV2(
+ paths={
+ "README.md": models.PathEntryV2(content_hash="hash1",
classification="metadata"),
+ },
+ )
+ rows = attestable.compute_file_state_rows(
+ "example-0.0.1",
+ 2,
+ {"README.md": "hash1"},
+ {"README.md": "metadata"},
+ previous,
+ )
+
+ assert len(rows) == 0
+
+
+def test_compute_file_state_rows_v1_previous():
+ previous = models.AttestableV1(paths={"README.md": "hash1"})
+ rows = attestable.compute_file_state_rows(
+ "example-0.0.1",
+ 2,
+ {"README.md": "hash1"},
+ {"README.md": "metadata"},
+ previous,
+ )
+
+ assert len(rows) == 1
+ assert rows[0].present is True
+ assert rows[0].content_hash == "hash1"
+ assert rows[0].classification == "metadata"
+
+
def test_release_file_state_deleted():
state = sql.ReleaseFileState(
release_name="example-0.0.1",
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]