@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
