bbf1915d54
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
284 lines
15 KiB
Gherkin
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"
|