test(file_type): add URL-form identity regression tests (ITL-474)#211
Conversation
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…Identity (ITL-474)
…eHasher (ITL-474)
Codecov Report✅ All modified and coverable lines are covered by tests. 📢 Thoughts on this report? Let us know! |
There was a problem hiding this comment.
Pull request overview
Adds regression coverage for ITL-474 to ensure op.File (UPath-based) preserves URL-form identity for non-local fsspec protocols (using memory:// as a stand-in), and documents the audit findings and test plan.
Changes:
- Add
TestURLFormIdentityto validateFilestring/hash()behavior andLogicalFileserialization round-trips for URL-form paths. - Add a
CachedFileHasherregression test to ensure the cache key retains the original URL-form path. - Add supporting design/spec and implementation plan documents for the audit and tests.
Reviewed changes
Copilot reviewed 4 out of 4 changed files in this pull request and generated 5 comments.
| File | Description |
|---|---|
| tests/test_extension_types/test_file_type.py | Adds memory://-backed regression tests for URL-form identity across File and LogicalFile. |
| tests/test_hashing/test_file_hashers.py | Adds a regression test ensuring CachedFileHasher cache keys preserve URL-form paths. |
| superpowers/specs/2026-07-07-itl-474-opfile-url-form-identity-design.md | Records audit findings and specifies the regression tests added in this PR. |
| docs/metamorphic/plans/2026-07-07-itl-474-opfile-url-form-identity.md | Implementation plan documenting how to add/verify the regression tests. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| def test_hash_equals_upath_protocol_tuple(self): | ||
| # UPath.__hash__ = hash((protocol, vfspath)) | ||
| # For memory://ns/x.bin: protocol="memory", vfspath="/ns/x.bin" | ||
| # This test pins the exact hash contract so any regression is immediately visible. | ||
| expected = hash(("memory", "/ns/x.bin")) | ||
| actual = hash(File("memory://ns/x.bin")) | ||
| assert actual == expected, ( | ||
| f"hash(File('memory://ns/x.bin')) should equal hash(('memory', '/ns/x.bin')), " | ||
| f"got {actual} vs {expected}" | ||
| ) |
There was a problem hiding this comment.
Fixed — replaced the hard-coded hash(("memory", "/ns/x.bin")) with hash(UPath("memory://ns/x.bin")). The test now derives the expected value directly from UPath, so it stays focused on verifying that File follows the same hash contract as its wrapped UPath rather than asserting against a specific internal representation.
| cached.hash_file(UPath("memory://ns/cache_key_test.bin")) | ||
|
|
||
| keys = list(cacher._cache.keys()) | ||
| assert len(keys) == 1, f"Expected 1 cache entry, got {len(keys)}" | ||
| key = keys[0] | ||
| assert str(key.path) == "memory://ns/cache_key_test.bin", ( | ||
| f"Cache key path must preserve URL form; got {str(key.path)!r}" | ||
| ) |
There was a problem hiding this comment.
Fixed — removed the cacher._cache inspection. The test now reconstructs the expected FileHashKey using url_path.resolve() + stat() (mirroring exactly what CachedFileHasher does internally), then verifies a cache hit via the public cacher.get() API. The URL-form assertion then checks str(expected_key.path) on that reconstructed key, which confirms the key path was not resolved to a concrete backend path.
| | Test class | File | What it covers | | ||
| |---|---|---| | ||
| | `TestURLFormIdentity` | `tests/test_extension_types/test_file_type.py` | `str()`, `hash()`, `LogicalFile` round-trip | | ||
| | `TestCachedFileHasherURLKey` (new method in existing class) | `tests/test_hashing/test_file_hashers.py` | `CachedFileHasher` cache-key URL preservation | | ||
|
|
There was a problem hiding this comment.
Fixed — updated the table entry to reference the actual test: TestCachedFileHasher.test_cache_key_preserves_url_form.
| | Test | Assertion | | ||
| |---|---| | ||
| | `test_cache_key_preserves_url_form` | After hashing `memory://ns/x.bin`, the `FileHashKey.path` in the cache has `str(key.path) == "memory://ns/x.bin"` | |
There was a problem hiding this comment.
Fixed — updated the example path to memory://ns/cache_key_test.bin to match the implemented test.
- Replace fs.store membership checks with fs.exists() public API in TestURLFormIdentity fixture (avoids fsspec internals) - Derive expected hash from UPath directly in test_hash_equals_upath_protocol_tuple (no hard-coded vfspath string) - Replace cacher._cache inspection with FileHashKey reconstruction + cacher.get() in test_cache_key_preserves_url_form (uses public API) - Fix spec doc: correct TestCachedFileHasher test name and example path Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Review round summaryAll 5 Copilot comments addressed in commit ed3f1d3.
All 6 affected tests still pass. |
…ity-preservation-for-engmfs
Summary
Audit result for ITL-474:
op.Filealready preserves URL-form identity correctly at every layer — no production code changes needed.This PR adds regression tests that lock in that behaviour:
TestURLFormIdentity(5 tests) intests/test_extension_types/test_file_type.py: coversstr(),hash()stability, hash equalshash((protocol, vfspath)),LogicalFile.python_to_storage()encoding, andLogicalFileround-trip.test_cache_key_preserves_url_formintests/test_hashing/test_file_hashers.py: confirmsCachedFileHasherdoes not resolve URL-form paths to concrete backend paths when building theFileHashKey.Both test classes use
memory://(always-available fsspec protocol) as a stand-in forengm://.Closes ITL-474