Address review nit: the older getTagTree tests relied on Mockito's default
empty-list return for findSubtreeDocumentCountsPerTag. Stub it explicitly so
the two-query contract is self-documenting.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Characterization tests for AC#8: the merge preview and the delete-impact
warning describe direct-document operations, so they must report the tag's
direct documentCount, never a subtree rollup. Both tests pass a stray
subtreeDocumentCount and assert it does not leak into the preview, so a future
change can't silently desync a destructive-action preview.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
TagTreeNodeDTO now requires subtreeDocumentCount, so the admin sidebar test
fixtures (TagTreeNode, TagsListPanel) need the field to type-check. The admin
sidebar still renders the direct documentCount — these fixtures only gain the
new field, no behaviour change.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The /themen page (box header, child rows, aria-labels) and the dashboard
ThemenWidget now display subtreeDocumentCount instead of the direct
documentCount, so a topic's number reflects its whole sub-topic tree and
matches what /documents?tag=X actually returns. A parent with 0 direct
documents but documents under its children now shows a non-zero total.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Regenerate the TagTreeNodeDTO type with subtreeDocumentCount and switch
hasAnyDocuments to read it directly — the backend rollup already includes all
descendants, so the recursive children walk is no longer needed. Reader
surfaces now hide a topic only when its whole subtree is empty.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Record that getTagTree returns both documentCount (direct, read by admin
surfaces) and subtreeDocumentCount (rollup, read by the reader surfaces),
matching the corrected getTagTree JavaDoc.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Cover AC#1-4 (leaf=direct, distinct overlap counted once, full descendant
depth), REQ-THEMEN-05 (empty subtree absent), REQ-THEMEN-06 (cycle terminates
via the 50-level guard) and AC#7 (rollup equals distinct documents found by the
real tag-search expansion — count↔destination parity). Testcontainers
postgres:16-alpine since the recursive CTE + COUNT(DISTINCT) needs real PG.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Add subtreeDocumentCount to TagTreeNodeDTO, populated by a new recursive-CTE
aggregate query that builds a tag closure and counts distinct documents per
ancestor subtree. The direct documentCount is unchanged; getTagTree now maps
both counts onto each node from two aggregate queries (no N+1).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Move the inline {#if data?.user && data.canWrite} condition into a named
$derived, matching the existing isAdmin / isAuthPage derivations in the
same file. No behaviour change — the 11 layout specs stay green.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Hiding the header upload button is UI polish; the real control is endpoint
authz. Add explicit READ_ALL-only 403 boundary tests for POST /api/documents
and POST /api/documents/quick-upload, matching the reader-only convention
already used elsewhere in this suite.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Documents that the gate keys on lack of WRITE_ALL, not on being READ_ALL:
an ANNOTATE_ALL-only user (canWrite=false) must still not see the upload
link. The writer-sees-it contract is already covered by the existing
upload-link tests.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
The header "Hochladen" link was gated only on {#if data?.user}, so a
reader without WRITE_ALL saw it, clicked it, and got bounced by the
server-side redirect in documents/new — confusing friction on the main
read journey. Gate it on data.canWrite (already on the layout data).
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.