@unit @repository @coverage
Feature: Repository coverage for ActionRepository and LifecyclePlanRepository
  Exercise uncovered lines and branches in repositories.py to boost
  line-rate and branch-rate for ActionRepository and LifecyclePlanRepository.

  Background:
    Given a repos-cov in-memory database with lifecycle schema
    And a repos-cov action repository
    And a repos-cov lifecycle plan repository

  # ---------------------------------------------------------------------------
  # ActionRepository.get_by_name – DatabaseError on OperationalError
  # ---------------------------------------------------------------------------

  @action @error_handling
  Scenario: ActionRepository.get_by_name raises DatabaseError on OperationalError
    Given a repos-cov action repository with a session that raises OperationalError on query
    When repos-cov get_by_name is called with "local/broken-action"
    Then repos-cov a DatabaseError should be raised containing "Failed to get action by name"

  # ---------------------------------------------------------------------------
  # ActionRepository.get_by_name – success path returns domain object
  # ---------------------------------------------------------------------------

  @action
  Scenario: ActionRepository.get_by_name returns domain object for existing action
    Given repos-cov an available action "local/findable" exists
    When repos-cov get_by_name is called with "local/findable"
    Then repos-cov the returned action should have name "local/findable"

  # ---------------------------------------------------------------------------
  # ActionRepository.get_by_namespace – with state filter returning results
  # ---------------------------------------------------------------------------

  @action
  Scenario: ActionRepository.get_by_namespace with state filter returns matching actions
    Given repos-cov an action "local/active-one" exists with state "available"
    And repos-cov an action "local/archived-one" exists with state "archived"
    When repos-cov get_by_namespace is called with namespace "local" and state "available"
    Then repos-cov exactly 1 action should be in the result list
    And repos-cov the result list should contain action named "local/active-one"

  # ---------------------------------------------------------------------------
  # ActionRepository.get_by_namespace – DatabaseError on OperationalError
  # ---------------------------------------------------------------------------

  @action @error_handling
  Scenario: ActionRepository.get_by_namespace raises DatabaseError on OperationalError
    Given a repos-cov action repository with a session that raises OperationalError on query
    When repos-cov get_by_namespace is called with namespace "local" expecting error
    Then repos-cov a DatabaseError should be raised containing "Failed to list actions in namespace"

  # ---------------------------------------------------------------------------
  # ActionRepository.list_available – without namespace filter
  # ---------------------------------------------------------------------------

  @action
  Scenario: ActionRepository.list_available returns all available actions without namespace
    Given repos-cov an action "ns1/act-a" exists with state "available"
    And repos-cov an action "ns2/act-b" exists with state "available"
    And repos-cov an action "ns1/act-c" exists with state "archived"
    When repos-cov list_available is called without a namespace filter
    Then repos-cov exactly 2 actions should be in the result list

  # ---------------------------------------------------------------------------
  # ActionRepository.list_available – with namespace filter
  # ---------------------------------------------------------------------------

  @action
  Scenario: ActionRepository.list_available returns available actions filtered by namespace
    Given repos-cov an action "nsx/avail-1" exists with state "available"
    And repos-cov an action "nsy/avail-2" exists with state "available"
    When repos-cov list_available is called with namespace "nsx"
    Then repos-cov exactly 1 action should be in the result list

  # ---------------------------------------------------------------------------
  # ActionRepository.list_available – DatabaseError on OperationalError
  # ---------------------------------------------------------------------------

  @action @error_handling
  Scenario: ActionRepository.list_available raises DatabaseError on OperationalError
    Given a repos-cov action repository with a session that raises OperationalError on query
    When repos-cov list_available is called and an error is expected
    Then repos-cov a DatabaseError should be raised containing "Failed to list available actions"

  # ---------------------------------------------------------------------------
  # ActionRepository.update – with arguments having min_value and max_value
  # ---------------------------------------------------------------------------

  @action @update
  Scenario: ActionRepository.update persists arguments with min_value and max_value
    Given repos-cov an available action "local/minmax-action" exists
    And repos-cov the action has arguments with min_value and max_value set
    When repos-cov the action is updated via the repository
    Then repos-cov the update should succeed
    And repos-cov the retrieved action arguments should have min_value and max_value

  # ---------------------------------------------------------------------------
  # ActionRepository.delete – ActionInUseError when plans reference the action
  # ---------------------------------------------------------------------------

  @action @delete @error_handling
  Scenario: ActionRepository.delete raises ActionInUseError when plans reference the action
    Given repos-cov an available action "local/in-use-action" exists
    And a repos-cov lifecycle plan references action "local/in-use-action"
    When repos-cov delete is called for action "local/in-use-action"
    Then repos-cov an ActionInUseError should be raised for "local/in-use-action"

  # ---------------------------------------------------------------------------
  # ActionRepository.delete – successful deletion when no plans reference it
  # ---------------------------------------------------------------------------

  @action @delete
  Scenario: ActionRepository.delete succeeds and returns True for unreferenced action
    Given repos-cov an available action "local/deletable" exists
    When repos-cov delete is called for action "local/deletable"
    Then repos-cov the delete result should be True
    And repos-cov get_by_id for "local/deletable" should return None

  # ---------------------------------------------------------------------------
  # ActionRepository.delete – returns False for non-existent action
  # ---------------------------------------------------------------------------

  @action @delete
  Scenario: ActionRepository.delete returns False for non-existent action
    When repos-cov delete is called for action "local/ghost"
    Then repos-cov the delete result should be False

  # ---------------------------------------------------------------------------
  # DuplicatePlanError instantiation
  # ---------------------------------------------------------------------------

  @plan @error_class
  Scenario: DuplicatePlanError stores plan_id and message
    When repos-cov a DuplicatePlanError is created for plan_id "01HGZ6FE0A00000000TESTPLAN"
    Then repos-cov the error message should contain "01HGZ6FE0A00000000TESTPLAN"
    And repos-cov the error plan_id attribute should be "01HGZ6FE0A00000000TESTPLAN"

  # ---------------------------------------------------------------------------
  # LifecyclePlanRepository.get_by_name – success path
  # ---------------------------------------------------------------------------

  @plan
  Scenario: LifecyclePlanRepository.get_by_name returns domain object for existing plan
    Given repos-cov an available action "local/plan-target" exists
    And a repos-cov lifecycle plan linked to "local/plan-target" with name "local/my-plan" has been persisted
    When repos-cov plan get_by_name is called with "local/my-plan"
    Then repos-cov the returned plan should not be None

  # ---------------------------------------------------------------------------
  # LifecyclePlanRepository.get_by_name – returns None for missing
  # ---------------------------------------------------------------------------

  @plan
  Scenario: LifecyclePlanRepository.get_by_name returns None for non-existent plan
    When repos-cov plan get_by_name is called with "local/no-such-plan"
    Then repos-cov the returned plan should be None

  # ---------------------------------------------------------------------------
  # LifecyclePlanRepository.get_by_name – DatabaseError on OperationalError
  # ---------------------------------------------------------------------------

  @plan @error_handling
  Scenario: LifecyclePlanRepository.get_by_name raises DatabaseError on OperationalError
    Given a repos-cov plan repository with a session that raises OperationalError on query
    When repos-cov plan get_by_name is called with "local/error-plan" expecting error
    Then repos-cov a DatabaseError should be raised containing "Failed to get plan by name"

  # ---------------------------------------------------------------------------
  # LifecyclePlanRepository.update – full update with project_links, arguments, invariants
  # ---------------------------------------------------------------------------

  @plan @update
  Scenario: LifecyclePlanRepository.update persists project links, arguments, and invariants
    Given repos-cov an available action "local/plan-upd-action" exists
    And a repos-cov lifecycle plan linked to "local/plan-upd-action" with name "local/upd-plan" has been persisted
    And repos-cov the plan has project_links, arguments, and invariants with text attribute
    When repos-cov the plan is updated via the repository
    Then repos-cov the plan update should succeed
    And repos-cov the retrieved plan should have project links
    And repos-cov the retrieved plan should have arguments
    And repos-cov the retrieved plan should have invariants

  # ---------------------------------------------------------------------------
  # LifecyclePlanRepository.update – invariants as plain strings (no .text attr)
  # ---------------------------------------------------------------------------

  @plan @update
  Scenario: LifecyclePlanRepository.update handles invariants as plain strings
    Given repos-cov an available action "local/plan-str-inv" exists
    And a repos-cov lifecycle plan linked to "local/plan-str-inv" with name "local/str-inv-plan" has been persisted
    And repos-cov the plan has invariants as plain strings without text attribute
    When repos-cov the plan is updated via the repository
    Then repos-cov the plan update should succeed
    And repos-cov the retrieved plan should have invariants

  # ---------------------------------------------------------------------------
  # LifecyclePlanRepository.update – invariant source with .value attribute
  # ---------------------------------------------------------------------------

  @plan @update
  Scenario: LifecyclePlanRepository.update handles invariant source with .value enum
    Given repos-cov an available action "local/plan-enum-inv" exists
    And a repos-cov lifecycle plan linked to "local/plan-enum-inv" with name "local/enum-inv-plan" has been persisted
    And repos-cov the plan has invariants with InvariantSource enum values
    When repos-cov the plan is updated via the repository
    Then repos-cov the plan update should succeed

  # ---------------------------------------------------------------------------
  # LifecyclePlanRepository.update – processing_state without .value (plain str)
  # ---------------------------------------------------------------------------

  @plan @update
  Scenario: LifecyclePlanRepository.update handles processing_state as plain string
    Given repos-cov an available action "local/plan-str-state" exists
    And a repos-cov lifecycle plan linked to "local/plan-str-state" with name "local/str-state-plan" has been persisted
    And repos-cov the plan processing_state is set to a plain string "processing"
    When repos-cov the plan is updated via the repository
    Then repos-cov the plan update should succeed

  # ---------------------------------------------------------------------------
  # LifecyclePlanRepository.update – error_details and changeset_id fields
  # ---------------------------------------------------------------------------

  @plan @update
  Scenario: LifecyclePlanRepository.update persists error_details and changeset_id
    Given repos-cov an available action "local/plan-err-detail" exists
    And a repos-cov lifecycle plan linked to "local/plan-err-detail" with name "local/err-detail-plan" has been persisted
    And repos-cov the plan has error_message, error_details, and changeset_id set
    When repos-cov the plan is updated via the repository
    Then repos-cov the plan update should succeed
    And repos-cov the retrieved plan should have the error_message set
