Recount closed blockers when a blocked issue is closed or reopened

What does this MR do and why?

Closing or reopening an issue refreshes issues.blocking_issues_count on the issue and on its blockers in refresh_blocking_and_blocked_issues_cache!, but the blockers came from blocking_issues_ids, which returns only open ones. A closed blocker kept counting the issue: its count stayed too high when the blocked issue was closed, and too low when the blocked issue was reopened. GraphQL blockingCount, the work items REST API's blocking_count and the BLOCKING_ISSUES_* sorts read the column for closed issues too.

This MR reads the blockers from the blocks links that point at the issue, bounded by MAX_LINKS_COUNT as the refresh on delete is, so closed blockers are recounted as well: closed issues are to be kept up to date, as Heinrich Lee Yu confirmed in !266012 (comment 4190451933). The fix and its two specs come from Abdul Wadood's database review in !266012 (comment 4148836140), and the commit credits Abdul as co-author. The only change to them is described_class for Issue in the specs, which RuboCop asks for.

Both new examples fail on master without the model change and pass with it.

What I ran (Ruby 3.3.11)

On master at 5670247b:

  • the context when changing state of blocking issues of ee/spec/models/issue_spec.rb with the model change reverted: 4 examples, 2 failures, the two new ones ("did not change");
  • the same context with the change: 4 examples, 0 failures;
  • the whole ee/spec/models/issue_spec.rb without the :elastic examples, which need Elasticsearch: 202 examples, 0 failures;
  • the specs that close, reopen or move issues, spec/services/issues/close_service_spec.rb, spec/services/issues/reopen_service_spec.rb, ee/spec/services/ee/issues/close_service_spec.rb, ee/spec/services/ee/issues/reopen_service_spec.rb, ee/spec/models/work_item_spec.rb, both work_items/data_sync/move_service_spec.rb and spec/services/issuable/bulk_update_service_spec.rb: 563 examples, 2 failures, both returns a new work item with the same attributes in the move service spec, on updated_at;
  • both move service specs again, with and without the model change: 135 examples, 0 failures with it, and the same 2 failures without it. On a slow host the example runs more than a minute after let_it_be created the original, so ThrottledTouch touches the new work item;
  • RuboCop on both files and the commit linter: no offenses.

References

Closes #639147 (closed)

  • !266012 (merged) repairs the counts that are already wrong, and merges after this MR.
  • !265408 (merged) added the refresh on delete whose lookup this MR reuses.

Screenshots or screen recordings

Not applicable: backend change, no UI change of its own.

How to set up and validate locally

  1. On Premium or Ultimate, create issues A and B, and add B to A's linked items as blocks.
  2. Close A, then close B. In the Rails console, Issue.find(<A id>).blocking_issues_count is 0 on this branch and 1 on master.
  3. With a new pair where A blocks B, close B, close A, then reopen B. A's count is 1 on this branch and 0 on master.

I followed these steps through the model specs above, not on a GDK.

MR acceptance checklist

Evaluate this MR against the MR acceptance checklist. It helps you analyze changes to reduce risks in quality, performance, reliability, security, and maintainability.

Merge request reports

Loading
Loading