@resource @handler @content_hash Feature: ResourceHandler content_hash method As a developer using change detection and deduplication I want resource handlers to compute deterministic content hashes So that the system can detect changes and deduplicate resources # ── Sentinel hash for empty/missing resources ────────────── Scenario: Missing resource returns sentinel hash Given a resource with no location for content_hash When I compute the content_hash via base handler Then the content_hash result should be the EMPTY sentinel Scenario: Non-existent path returns sentinel hash Given a resource with location "/nonexistent/path" for content_hash When I compute the content_hash via base handler Then the content_hash result should be the EMPTY sentinel # ── Determinism ──────────────────────────────────────────── Scenario: Same file produces same hash on repeated calls Given a temporary file with content "hello world" for content_hash When I compute the content_hash twice via base handler Then the two content_hash results should be identical Scenario: Different content produces different hash Given a temporary file with content "hello" for content_hash And a second temporary file with content "world" for content_hash When I compute content_hash for both files Then the two content_hash results should differ # ── fs-directory recursive hash ──────────────────────────── Scenario: FsDirectoryHandler hashes directory recursively Given a temporary directory with files for content_hash When I compute the content_hash via FsDirectoryHandler Then the content_hash result should not be the EMPTY sentinel Scenario: FsDirectoryHandler hash changes when file content changes Given a temporary directory with files for content_hash When I compute the content_hash via FsDirectoryHandler And I modify a file in the directory And I compute the content_hash via FsDirectoryHandler again Then the two directory hashes should differ # ── git-checkout hash ────────────────────────────────────── Scenario: GitCheckoutHandler hashes a git repo Given a temporary git repository for content_hash When I compute the content_hash via GitCheckoutHandler Then the content_hash result should not be the EMPTY sentinel # ── Configurable algorithm ───────────────────────────────── Scenario: SHA-512 algorithm produces different length hash Given a temporary file with content "test" for content_hash When I compute the content_hash with algorithm "sha512" Then the content_hash length should be 128 Scenario: Default SHA-256 produces 64-char hash Given a temporary file with content "test" for content_hash When I compute the content_hash with algorithm "sha256" Then the content_hash length should be 64 # ── Protocol compliance ──────────────────────────────────── Scenario: All handlers have content_hash method Then GitCheckoutHandler should have a content_hash method And FsDirectoryHandler should have a content_hash method And DevcontainerHandler should have a content_hash method And DatabaseResourceHandler should have a content_hash method # ── devcontainer content_hash ────────────────────────────── Scenario: DevcontainerHandler config file hash for devcontainer-file Given a temporary devcontainer project for content_hash When I compute the content_hash via DevcontainerHandler Then the content_hash result should not be the EMPTY sentinel Scenario: DevcontainerHandler returns sentinel for missing config Given a resource with location "/tmp/no-devcontainer" for content_hash When I compute the content_hash via DevcontainerHandler for missing Then the content_hash result should be the EMPTY sentinel Scenario: DevcontainerHandler exec hash falls back to config when no container Given a temporary devcontainer project for content_hash When I compute the content_hash via DevcontainerHandler Then the devcontainer hash should be from config file fallback # ── database content_hash ────────────────────────────────── Scenario: DatabaseResourceHandler hashes SQLite schema Given a temporary SQLite database for content_hash When I compute the content_hash via DatabaseResourceHandler Then the content_hash result should not be the EMPTY sentinel Scenario: DatabaseResourceHandler schema hash changes when table added Given a temporary SQLite database for content_hash When I compute the content_hash via DatabaseResourceHandler And I add a table to the database And I compute the content_hash via DatabaseResourceHandler again Then the two database hashes should differ Scenario: DatabaseResourceHandler returns identity hash for remote DB Given a remote database resource for content_hash When I compute the content_hash via DatabaseResourceHandler for remote Then the content_hash result should not be the EMPTY sentinel # ── cloud content_hash ───────────────────────────────────── Scenario: CloudResourceHandler returns identity hash with location Given a cloud resource with ARN location for content_hash When I compute the content_hash via CloudResourceHandler Then the content_hash result should not be the EMPTY sentinel Scenario: CloudResourceHandler returns sentinel without location Given a cloud resource without location for content_hash When I compute the content_hash via CloudResourceHandler for empty Then the content_hash result should be the EMPTY sentinel # ── Edge case and error path coverage ────────────────────── Scenario: Base handler hashes directory entries including subdirectory names Given a temporary directory with only subdirectories for content_hash When I compute the content_hash via base handler for dir Then the content_hash result should not be the EMPTY sentinel Scenario: FsDirectoryHandler returns sentinel for non-directory location Given a temporary file pretending to be a directory for content_hash When I compute the content_hash via FsDirectoryHandler for non-dir Then the content_hash result should be the EMPTY sentinel Scenario: FsDirectoryHandler skips unreadable files gracefully Given a temporary directory with an unreadable file for content_hash When I compute the content_hash via FsDirectoryHandler for unreadable Then the content_hash result should not be the EMPTY sentinel Scenario: GitCheckoutHandler returns sentinel for non-git directory Given a temporary non-git directory for content_hash When I compute the content_hash via GitCheckoutHandler for non-git Then the content_hash result should be the EMPTY sentinel Scenario: GitCheckoutHandler returns sentinel for file path Given a temporary file as git location for content_hash When I compute the content_hash via GitCheckoutHandler for file Then the content_hash result should be the EMPTY sentinel Scenario: DatabaseResourceHandler returns sentinel for no location Given a database resource with no location for content_hash When I compute the content_hash via DatabaseResourceHandler for noloc Then the content_hash result should be the EMPTY sentinel Scenario: DatabaseResourceHandler hashes non-db-extension SQLite file directly Given a SQLite database with non-standard extension for content_hash When I compute the content_hash via DatabaseResourceHandler for nonext Then the content_hash result should not be the EMPTY sentinel Scenario: DevcontainerHandler _find_running_container returns None without docker Given a temporary devcontainer project for content_hash When I check _find_running_container Then the container ID should be None # ── Mock-based branch coverage ───────────────────────────── Scenario: DevcontainerHandler exec hash success path via mock Given a temporary devcontainer project for content_hash When I compute content_hash with mocked docker exec returning file hashes Then the content_hash result should not be the EMPTY sentinel Scenario: DevcontainerHandler _find_running_container OSError path Given a temporary devcontainer project for content_hash When I compute content_hash with docker ps raising OSError Then the devcontainer falls back to config hash Scenario: GitCheckoutHandler returns sentinel on subprocess timeout Given a temporary git-checkout resource for content_hash timeout When I compute content_hash with git rev-parse timing out Then the content_hash result should be the EMPTY sentinel Scenario: GitCheckoutHandler returns sentinel on OSError Given a temporary git-checkout resource for content_hash timeout When I compute content_hash with git rev-parse raising OSError Then the content_hash result should be the EMPTY sentinel Scenario: Base handler returns sentinel for special file paths Given a resource pointing to a non-file non-dir path for content_hash When I compute the content_hash via base handler for special Then the content_hash result should be the EMPTY sentinel