Files
cleveragents-core/features/resource_cli_tree.feature
T
HAL9000 bbf1915d54 fix(resource): preserve atomicity in register_resource without breaking shared-session callers
The previous attempt wrapped `register_resource` in `with session.begin():`
to guarantee parent + auto-discovered children commit atomically.  That
pattern raises `sqlalchemy.exc.InvalidRequestError: A transaction is
already begun on this Session.` whenever a caller (e.g. the WF05
integration helper at `robot/helper_int_wf05_db_migration.py`) reuses a
single Session across multiple service calls — autobegin has already
opened the implicit transaction by the time `register_resource` runs.

This rewrites the flow to keep the simpler `session.commit()` pattern
that worked with shared sessions, but moves the commit to AFTER
`auto_discover_children` so any failure between `session.add(parent)`
and `session.commit()` rolls the whole transaction back via the
existing `except`/`session.rollback()` handlers.  Atomicity is
preserved (parent is never persisted on an auto-discovery failure) and
shared-session callers no longer get the `InvalidRequestError`.

Also stabilises the `Service get_children returns auto-discovered
children for directory` scenario in `features/resource_cli_tree.feature`
by adding an explicit `Given a seeded directory exists at "/tmp/gcl"`
step that creates the directory and writes a sentinel file.  Without
this seed the scenario depended on whatever happened to exist at
`/tmp/gcl` in the CI environment.

ISSUES CLOSED: #6464
2026-06-06 05:30:16 -04:00

284 lines
15 KiB
Gherkin

Feature: Resource CLI tree, inspect, link-child, and unlink-child commands
As a CleverAgents user
I want to view resource trees, inspect resources, and manage DAG edges via the CLI
So that I can navigate and manipulate resource hierarchies
Background:
Given a fresh resource tree test registry
# ---- Resource Tree ----
Scenario: Tree of a resource with no children
Given resource tree built-in types are bootstrapped
And a resource tree resource "git-checkout" named "local/root-repo" at "/tmp/root"
When I display resource tree for "local/root-repo"
Then the tree output should contain "local/root-repo"
And the tree output should contain "git-checkout"
Scenario: Tree of a resource with children shows hierarchy
Given resource tree built-in types are bootstrapped
And a resource tree resource "git-checkout" named "local/parent-repo" at "/tmp/parent"
And a resource tree resource "fs-directory" named "local/child-dir" at "/tmp/child"
And resource tree child "local/child-dir" is linked to parent "local/parent-repo"
When I display resource tree for "local/parent-repo"
Then the tree output should contain "local/parent-repo"
And the tree output should contain "local/child-dir"
Scenario: Tree with depth limit of 1
Given resource tree built-in types are bootstrapped
And a resource tree resource "git-checkout" named "local/deep-root" at "/tmp/deep"
And a resource tree resource "fs-directory" named "local/deep-child" at "/tmp/dc"
And a resource tree resource "fs-directory" named "local/deep-grandchild" at "/tmp/dgc"
And resource tree child "local/deep-child" is linked to parent "local/deep-root"
And resource tree child "local/deep-grandchild" is linked to parent "local/deep-child"
When I display resource tree depth-limited to 1 for "local/deep-root"
Then the tree output should contain "local/deep-child"
And the tree output should not contain "local/deep-grandchild"
Scenario: Tree with type filter
Given resource tree built-in types are bootstrapped
And a resource tree resource "git-checkout" named "local/filter-root" at "/tmp/fr"
And a resource tree resource "fs-directory" named "local/filter-dir" at "/tmp/fd"
And resource tree child "local/filter-dir" is linked to parent "local/filter-root"
When I display resource tree type-filtered to "fs-directory" for "local/filter-root"
Then the tree output should contain "local/filter-dir"
Scenario: Tree in JSON format
Given resource tree built-in types are bootstrapped
And a resource tree resource "git-checkout" named "local/json-tree" at "/tmp/jt"
When I display resource tree as "json" for "local/json-tree"
Then the tree output should be valid JSON
Scenario: Tree of non-existent resource fails
When I display resource tree for "nonexistent-resource"
Then the tree command should fail
And the tree output should contain "not found"
# ---- Deterministic ordering ----
Scenario: Tree children are sorted by name
Given resource tree built-in types are bootstrapped
And a resource tree resource "git-checkout" named "local/sort-root" at "/tmp/sr"
And a resource tree resource "fs-directory" named "local/z-last" at "/tmp/zl"
And a resource tree resource "fs-directory" named "local/a-first" at "/tmp/af"
And resource tree child "local/z-last" is linked to parent "local/sort-root"
And resource tree child "local/a-first" is linked to parent "local/sort-root"
When I display resource tree for "local/sort-root"
Then the tree output should show "local/a-first" before "local/z-last"
# ---- Resource Inspect ----
Scenario: Inspect a resource shows details
Given resource tree built-in types are bootstrapped
And a resource tree resource "git-checkout" named "local/inspect-repo" at "/tmp/ir"
When I run basic inspect on "local/inspect-repo"
Then the tree output should contain "local/inspect-repo"
And the tree output should contain "git-checkout"
Scenario: Inspect with --tree shows children
Given resource tree built-in types are bootstrapped
And a resource tree resource "git-checkout" named "local/inspect-parent" at "/tmp/ip"
And a resource tree resource "fs-directory" named "local/inspect-child" at "/tmp/ic"
And resource tree child "local/inspect-child" is linked to parent "local/inspect-parent"
When I run inspect with subtree on "local/inspect-parent"
Then the tree output should contain "local/inspect-child"
Scenario: Inspect with --tree and no children
Given resource tree built-in types are bootstrapped
And a resource tree resource "git-checkout" named "local/no-children" at "/tmp/nc"
When I run inspect with subtree on "local/no-children"
Then the tree output should contain "no children"
Scenario: Inspect with --file for existing file
Given resource tree built-in types are bootstrapped
And a resource tree resource "git-checkout" named "local/file-repo" at a temp directory
And a temp file "hello.txt" with content "Hello World"
When I run inspect with file "hello.txt" on "local/file-repo"
Then the tree output should contain "Hello World"
Scenario: Inspect with --file for missing file
Given resource tree built-in types are bootstrapped
And a resource tree resource "git-checkout" named "local/no-file" at "/tmp/nf"
When I run inspect with file "nonexistent.txt" on "local/no-file"
Then the tree output should contain "not found"
Scenario: Inspect in JSON format
Given resource tree built-in types are bootstrapped
And a resource tree resource "git-checkout" named "local/json-inspect" at "/tmp/ji"
When I run inspect formatted as "json" on "local/json-inspect"
Then the tree output should be valid JSON
Scenario: Inspect non-existent resource fails
When I run basic inspect on "nonexistent-resource"
Then the tree command should fail
And the tree output should contain "not found"
# ---- Resource link-child ----
Scenario: Link child successfully
Given resource tree built-in types are bootstrapped
And a resource tree resource "git-checkout" named "local/link-parent" at "/tmp/lp"
And a resource tree resource "fs-directory" named "local/link-child" at "/tmp/lc"
When I link DAG child "local/link-child" to parent "local/link-parent"
Then the tree output should contain "Linked"
Scenario: Link child in JSON format
Given resource tree built-in types are bootstrapped
And a resource tree resource "git-checkout" named "local/json-parent" at "/tmp/jp"
And a resource tree resource "fs-directory" named "local/json-child" at "/tmp/jc"
When I link as "json" DAG child "local/json-child" to parent "local/json-parent"
Then the tree output should be valid JSON
And the tree output should contain "linked"
Scenario: Link child with non-existent parent fails
Given resource tree built-in types are bootstrapped
And a resource tree resource "fs-directory" named "local/orphan" at "/tmp/orph"
When I link DAG child "local/orphan" to parent "nonexistent-parent"
Then the tree command should fail
And the tree output should contain "not found"
Scenario: Link child with non-existent child fails
Given resource tree built-in types are bootstrapped
And a resource tree resource "git-checkout" named "local/lonely-parent" at "/tmp/lonely"
When I link DAG child "nonexistent-child" to parent "local/lonely-parent"
Then the tree command should fail
And the tree output should contain "not found"
Scenario: Duplicate link fails
Given resource tree built-in types are bootstrapped
And a resource tree resource "git-checkout" named "local/dup-parent" at "/tmp/dp"
And a resource tree resource "fs-directory" named "local/dup-child" at "/tmp/dpc"
And resource tree child "local/dup-child" is linked to parent "local/dup-parent"
When I link DAG child "local/dup-child" to parent "local/dup-parent"
Then the tree command should fail
And the tree output should contain "already exists"
Scenario: Cycle detection rejects link
Given resource tree built-in types are bootstrapped
And a resource tree resource "fs-directory" named "local/cyc-a" at "/tmp/ca"
And a resource tree resource "fs-directory" named "local/cyc-b" at "/tmp/cb"
And resource tree child "local/cyc-b" is linked to parent "local/cyc-a"
When I link DAG child "local/cyc-a" to parent "local/cyc-b"
Then the tree command should fail
And the tree output should contain "ycle"
# ---- Resource unlink-child ----
Scenario: Unlink child successfully
Given resource tree built-in types are bootstrapped
And a resource tree resource "git-checkout" named "local/unlink-parent" at "/tmp/up"
And a resource tree resource "fs-directory" named "local/unlink-child" at "/tmp/uc"
And resource tree child "local/unlink-child" is linked to parent "local/unlink-parent"
When I unlink DAG child "local/unlink-child" from parent "local/unlink-parent" confirmed
Then the tree output should contain "Unlinked"
Scenario: Unlink child in JSON format
Given resource tree built-in types are bootstrapped
And a resource tree resource "git-checkout" named "local/json-unlp" at "/tmp/jul"
And a resource tree resource "fs-directory" named "local/json-unlc" at "/tmp/juc"
And resource tree child "local/json-unlc" is linked to parent "local/json-unlp"
When I unlink as "json" DAG child "local/json-unlc" from parent "local/json-unlp" confirmed
Then the tree output should be valid JSON
And the tree output should contain "unlinked"
Scenario: Unlink non-existent link fails
Given resource tree built-in types are bootstrapped
And a resource tree resource "git-checkout" named "local/nolink-parent" at "/tmp/nlp"
And a resource tree resource "fs-directory" named "local/nolink-child" at "/tmp/nlc"
When I unlink DAG child "local/nolink-child" from parent "local/nolink-parent" confirmed
Then the tree command should fail
And the tree output should contain "not found"
Scenario: Unlink aborted without yes flag
Given resource tree built-in types are bootstrapped
And a resource tree resource "git-checkout" named "local/abort-parent" at "/tmp/ap"
And a resource tree resource "fs-directory" named "local/abort-child" at "/tmp/ac"
And resource tree child "local/abort-child" is linked to parent "local/abort-parent"
When I unlink DAG child "local/abort-child" from parent "local/abort-parent" declined
Then the tree command should fail
# ---- Self-link rejection ----
Scenario: Link child to itself is rejected
Given resource tree built-in types are bootstrapped
And a resource tree resource "fs-directory" named "local/self-link" at "/tmp/sl"
When I link DAG child "local/self-link" to parent "local/self-link"
Then the tree command should fail
And the tree output should contain "itself"
# ---- get_children service method ----
Scenario: Service get_children returns children sorted by name
Given resource tree built-in types are bootstrapped
And a resource tree resource "git-checkout" named "local/gc-parent" at "/tmp/gcp"
And a resource tree resource "fs-directory" named "local/gc-z-child" at "/tmp/gcz"
And a resource tree resource "fs-directory" named "local/gc-a-child" at "/tmp/gca"
And resource tree child "local/gc-z-child" is linked to parent "local/gc-parent"
And resource tree child "local/gc-a-child" is linked to parent "local/gc-parent"
When I get children of resource "local/gc-parent"
Then the children list should have 2 items
And the first child name should be "local/gc-a-child"
Scenario: Service get_children returns auto-discovered children for directory
Given resource tree built-in types are bootstrapped
And a seeded directory exists at "/tmp/gcl"
And a resource tree resource "fs-directory" named "local/gc-leaf" at "/tmp/gcl"
When I get children of resource "local/gc-leaf"
Then the children list should have at least 1 child
# ---- Inspect with no properties ----
Scenario: Inspect resource with no properties shows none
Given resource tree built-in types are bootstrapped
And a resource tree resource "git-checkout" named "local/noprops" at "/tmp/np" with no properties
When I run basic inspect on "local/noprops"
Then the tree output should contain "none"
# ---- File read edge cases ----
Scenario: Inspect file on resource with no location shows no-location message
Given resource tree built-in types are bootstrapped
And a resource tree resource "git-checkout" named "local/noloc" with no location
When I run inspect with file "test.txt" on "local/noloc"
Then the tree output should contain "no location"
Scenario: Inspect file that cannot be read shows error message
Given resource tree built-in types are bootstrapped
And a resource tree resource "git-checkout" named "local/badfile" at a temp directory
And a temp file that is unreadable named "binary.dat"
When I run inspect with file "binary.dat" on "local/badfile"
Then the tree output should contain "error reading"
# ---- CleverAgentsError catch blocks in CLI ----
Scenario: Tree command handles generic CleverAgentsError
Given resource tree built-in types are bootstrapped
And a resource tree resource "git-checkout" named "local/tree-err" at "/tmp/te"
When I display resource tree for "local/tree-err" with a service error
Then the tree command should fail
And the tree output should contain "Error"
Scenario: Inspect command handles generic CleverAgentsError
Given resource tree built-in types are bootstrapped
And a resource tree resource "git-checkout" named "local/inspect-err" at "/tmp/ie"
When I run inspect on "local/inspect-err" with a service error
Then the tree command should fail
And the tree output should contain "Error"
Scenario: Link-child command handles generic CleverAgentsError
Given resource tree built-in types are bootstrapped
And a resource tree resource "git-checkout" named "local/link-err-p" at "/tmp/lep"
And a resource tree resource "fs-directory" named "local/link-err-c" at "/tmp/lec"
When I link DAG child "local/link-err-c" to parent "local/link-err-p" with a service error
Then the tree command should fail
And the tree output should contain "Error"
Scenario: Unlink-child command handles generic CleverAgentsError
Given resource tree built-in types are bootstrapped
And a resource tree resource "git-checkout" named "local/unlink-err-p" at "/tmp/uep"
And a resource tree resource "fs-directory" named "local/unlink-err-c" at "/tmp/uec"
And resource tree child "local/unlink-err-c" is linked to parent "local/unlink-err-p"
When I unlink DAG child "local/unlink-err-c" from parent "local/unlink-err-p" with a service error
Then the tree command should fail
And the tree output should contain "Error"