Skip to content

test: failing test for #6293 -- generator instance orphaned when target object deleted - #10370

Draft
opsmill-bug-pipeline[bot] wants to merge 1 commit into
stablefrom
ai-bug-pipeline-6293-generator-orphan-4ddb24ec820caea7
Draft

test: failing test for #6293 -- generator instance orphaned when target object deleted#10370
opsmill-bug-pipeline[bot] wants to merge 1 commit into
stablefrom
ai-bug-pipeline-6293-generator-orphan-4ddb24ec820caea7

Conversation

@opsmill-bug-pipeline

@opsmill-bug-pipeline opsmill-bug-pipeline Bot commented Aug 21, 2026

Copy link
Copy Markdown
Contributor

Analyst's findings (summary)

Root cause: When a generator target object is deleted, the CoreGeneratorInstance.object relationship is left dangling (the target node disappears, but the generator instance is not cascade-deleted or blocked), and when request_generator_definition_run subsequently iterates over existing instances it unconditionally calls instance.object.peer.id, which raises ValueError because the SDK's RelatedNode.get() finds neither an ID nor an HFID on the orphaned relationship edge.

Affected files:

  • backend/infrahub/generators/tasks.py — unconditional instance.object.peer.id access with no guard for orphaned (dangling) relationships
  • backend/infrahub/core/schema/definitions/core/generator.pyCoreGeneratorInstance.object relationship peers to CoreNode with no on_delete cascade; the delete validator never sees this as a dependency
  • python_sdk/infrahub_sdk/node/related_node.pyget() raises ValueError when both self.id and self.hfid_str are None

Replication test

Test file: backend/tests/unit/core/node/test_delete_validator.py
Test name: test_deleting_generator_target_cascades_to_generator_instance

What it tests: Building the real Core schema, the kind that CoreGeneratorInstance.object targets must carry a relationship with on_delete=CASCADE back to CoreGeneratorInstance, so deleting a target object removes its generator instance instead of orphaning it.

Verification: Test confirmed FAILING on current code.
Failure reason: On stable the object relationship peers to CoreNode, a generic that carries no cascade relationship, so the set of cascade-delete peers is empty and CoreGeneratorInstance is absent — proving the target object has no cascade path to its generator instance (the orphaning bug). Once CoreGeneratorInstance.object peers to a CoreGeneratorTarget generic carrying an instances relationship with on_delete=CASCADE (mirroring CoreArtifactTarget), the peer set becomes {CoreGeneratorInstance} and the test passes.

Failure output (last 20 lines)
    instance_schema = all_schemas[InfrahubKind.GENERATORINSTANCE]
    target_kind = instance_schema.get_relationship(name="object").peer
    target_schema = all_schemas[target_kind]

    cascade_delete_peers = {
        relationship.peer
        for relationship in target_schema.relationships
        if relationship.on_delete == RelationshipDeleteBehavior.CASCADE
    }

>       assert InfrahubKind.GENERATORINSTANCE in cascade_delete_peers
E       AssertionError: assert 'CoreGeneratorInstance' in set()
E        +  where 'CoreGeneratorInstance' = InfrahubKind.GENERATORINSTANCE

backend/tests/unit/core/node/test_delete_validator.py:113: AssertionError
FAILED backend/tests/unit/core/node/test_delete_validator.py::test_deleting_generator_target_cascades_to_generator_instance

Test expectations

The test asserts the schema-level cascade contract that fix 2 in the analysis establishes: the node kind targeted by CoreGeneratorInstance.object must have a CASCADE on_delete relationship pointing back to CoreGeneratorInstance, exactly as CoreArtifact.object peers to CoreArtifactTarget (which has an artifacts CASCADE relationship). It reads the target kind dynamically from the processed Core schema rather than hard-coding a not-yet-existing kind, so it fails today (target is CoreNode, no cascade) and passes once the generator-target generic is added and the object peer is repointed to it.

Notes:

  • The defensive guard (analysis "fix 1") is already present in map_subscriber_ids_by_member, so the original ValueError crash no longer reproduces; the remaining, architecturally-correct behavior is the cascade delete this test targets.
  • The test is placed alongside the existing test_repository_cascade_reaches_exactly_expected_kinds and reuses the same Core-schema / delete-index setup pattern. It is a unit test (no DB) and runs in ~2s.

AGENT_TEST_COMPLETE

Generated by Bug test-writer agent for #6293 · 440.2 AIC · ⌖ 32.3 AIC · ⊞ 13.1K ·
Comment /bug-tdd to run again

Review in cubic

@github-actions github-actions Bot added the group/backend Issue related to the backend (API Server, Git Agent) label Aug 21, 2026
@opsmill-bug-pipeline

Copy link
Copy Markdown
Contributor Author

AGENT_REVIEW_VERDICT: TEST_APPROVED

Overall verdict: APPROVED WITH SUGGESTIONS

The failing test correctly encodes the architectural fix identified in the analysis (fix 2: a cascade path from the generator target to its CoreGeneratorInstance). It fails on stable today for the right reason and will pass once CoreGeneratorInstance.object is repointed to a generator-target generic carrying an on_delete=CASCADE relationship back to the instance — mirroring CoreArtifact.objectCoreArtifactTarget.artifacts. Two non-blocking suggestions below would make it a stronger regression guard, but neither prevents merge.

A. Test realism

Not a client-input test — it operates purely on the processed Core schema. Every value used is a real system value traced to production code:

  • InfrahubKind.GENERATORINSTANCE and the "object" relationship name → backend/infrahub/core/schema/definitions/core/generator.py:142-148.
  • RelationshipDeleteBehavior.CASCADEbackend/infrahub/core/constants/__init__.py:354-356.

Crucially, the test resolves the target kind dynamically (instance_schema.get_relationship(name="object").peer) rather than hard-coding a not-yet-existing CoreGeneratorTarget kind. That is the right call: it fails today (peer is CoreNode, no cascade) and passes after the fix without needing an edit. No realism concerns.

B. Test correctness

  • Asserts the expected post-fix behavior, not the buggy state. ✓
  • Direction is correct: it looks for a CASCADE relationship on the target schema whose peer is GENERATORINSTANCE (deleting the target cascades to the instance), matching the artifact pattern where CoreArtifactTarget.artifacts carries the cascade. ✓
  • Cannot false-pass on current code: the PR's own failure output shows cascade_delete_peers == set() because the peer is the generic CoreNode. ✓

One weakness worth noting (see suggestion 1): the test inspects the raw relationship.on_delete attribute directly, one step removed from the analyst's stated root cause — "the delete validator never sees this as a dependency." The sibling test test_repository_cascade_reaches_exactly_expected_kinds (test_delete_validator.py:50-86) proves reachability through NodeDeleteIndex / _cascade_closure, which is the actual mechanism the delete path uses. Asserting the schema attribute is a valid proxy (it is what NodeDeleteIndex consumes), but it does not prove the delete validator picks the edge up.

C. Test quality

  • Isolated, deterministic, no DB — correct tier for schema-only logic. ✓
  • Well placed alongside the existing delete-validator cascade tests and reuses the same SchemaBranch(...).load_schema(...).process() / get_all(duplicate=False) setup. ✓
  • Clear docstring explaining the artifact analogy and the orphaning failure mode, with no issue-number or URL references (per project rules). ✓

Minor convention deviation (see suggestion 2): the assertion uses membership (InfrahubKind.GENERATORINSTANCE in cascade_delete_peers) rather than exact set equality. .claude/rules/testing-python.md ("Assert exact expectations") prefers full set equality over in, and the sibling test in the same file already does exactly that (assert reachable == expected_cascade).

D. Alignment with analysis

  • Targets the analyst's fix 2 (schema cascade), which is the architecturally-correct remedy. ✓
  • Scope is appropriate — narrowly on the cascade contract, neither too broad nor too narrow. ✓
  • The PR body is transparent that fix 1 (the defensive guard) is already present. I verified this at backend/infrahub/core/regeneration/members.py:24-32, where map_subscriber_ids_by_member catches ValueError/NodeNotFoundError and skips orphan subscribers — so the original runtime crash no longer reproduces, and locking in the cascade via this test is the correct remaining objective. ✓

Recommended next steps

Approved as-is; the fixer may proceed. Two optional strengthenings the fixer (or a follow-up) can fold in:

  1. Assert delete-path reachability, not just the schema attribute. Build a NodeDeleteIndex from all_schemas, index from target_schema, and assert GENERATORINSTANCE is in the cascade closure via _cascade_closure — the same approach as test_repository_cascade_reaches_exactly_expected_kinds (test_delete_validator.py:50-86). This proves the delete validator actually sees the dependency (the analyst's root cause), rather than only that the on_delete flag is declared.
  2. Prefer exact set equality per .claude/rules/testing-python.md, e.g. assert the target generic's cascade-peer set equals {InfrahubKind.GENERATORINSTANCE} instead of using in, consistent with the sibling test.

AGENT_REVIEW_ITERATION: test-1

Generated by Bug reviewer agent for #10370 · 112.3 AIC · ⌖ 28.8 AIC · ⊞ 12.6K ·

@codspeed-hq

codspeed-hq Bot commented Aug 21, 2026

Copy link
Copy Markdown

Merging this PR will not alter performance

✅ 13 untouched benchmarks


Comparing ai-bug-pipeline-6293-generator-orphan-4ddb24ec820caea7 (8216419) with stable (6b4199a)1

Open in CodSpeed

Footnotes

  1. No successful run was found on stable (97fd44b) during the generation of this report, so d899f72 was used instead as the comparison base. There might be some changes unrelated to this pull request in this report.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

group/backend Issue related to the backend (API Server, Git Agent)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

bug: Generator instance corrupted when deleting target object

0 participants