gateway/tests/test_service_redmine_orphans.py
2026-04-21 18:14:21 +02:00

48 lines
2 KiB
Python

# Copyright (c) 2026 Patrick Motsch
# All rights reserved.
"""Pure-Python unit tests for the orphan detection in
``serviceRedmineStats._countOrphans``.
Snapshot in ``tests/fixtures/redmineSnapshot.json`` contains:
- 1x Userstory (1001) -- root
- 1x Feature (2001) related to 1001 -> reachable
- 1x Acc.Crit (3001) parent=2001 -> reachable
- 1x Bug (4001) blocks 2001 -> reachable via relation
- 2x Task (5001, 5002) -> orphan (no link to any User Story)
"""
from __future__ import annotations
from modules.features.redmine.serviceRedmineStats import _countOrphans
from tests.fixtures.loadRedmineSnapshot import loadSnapshot
class TestCountOrphans:
def test_orphansFromSnapshot(self) -> None:
schema, tickets = loadSnapshot()
orphans = _countOrphans(tickets, schema.rootTrackerId)
assert orphans == 2, "Two unrelated Tasks should be orphans"
def test_emptyListReturnsZero(self) -> None:
assert _countOrphans([], 1) == 0
def test_noRootTrackerCountsAllAsOrphan(self) -> None:
schema, tickets = loadSnapshot()
# Pretend there is no User Story tracker at all -- every ticket is orphan.
assert _countOrphans(tickets, None) == len(tickets)
def test_relationDirectionAgnostic(self) -> None:
"""A ticket reachable via the *target* side of a relation must not
be counted as orphan -- _countOrphans walks both directions."""
_, tickets = loadSnapshot()
bug = next(t for t in tickets if t.id == 4001)
# Bug 4001 -[blocks]-> 2001; it is the source. Reverse it: 2001 -[blocks]-> 4001
bug.relations = []
# Attach the relation on 2001 instead.
feature = next(t for t in tickets if t.id == 2001)
from modules.features.redmine.datamodelRedmine import RedmineRelationDto
feature.relations.append(
RedmineRelationDto(id=999, issueId=2001, issueToId=4001, relationType="blocks", delay=None)
)
orphans = _countOrphans(tickets, 1)
assert orphans == 2 # Tasks remain orphans, Bug is still reachable