Skip to content

fix: ArchiveAsync returns true for already-archived issues (MatchedCount vs ModifiedCount)#27

Merged
mpaulosky merged 2 commits into
feature/dto-refactor-and-soft-deletefrom
copilot/sub-pr-23-yet-again
Feb 21, 2026
Merged

fix: ArchiveAsync returns true for already-archived issues (MatchedCount vs ModifiedCount)#27
mpaulosky merged 2 commits into
feature/dto-refactor-and-soft-deletefrom
copilot/sub-pr-23-yet-again

Conversation

Copy link
Copy Markdown
Contributor

Copilot AI commented Feb 20, 2026

ArchiveAsync used ModifiedCount > 0 to signal success, causing it to return false when an issue existed but was already archived — indistinguishable from "not found".

Changes

  • IssueRepository.ArchiveAsync: Switch return condition from ModifiedCount > 0 to MatchedCount > 0
// Before — breaks on already-archived issues (ModifiedCount == 0, no change made)
return result.ModifiedCount > 0;

// After — correctly distinguishes not-found from no-op
return result.MatchedCount > 0;
Scenario MatchedCount ModifiedCount Before After
Not found 0 0 false false
Already archived 1 0 false true
Newly archived 1 1 true true
  • DeleteIssueHandlerTests: Add integration tests covering all three cases above using a real MongoDB container (Testcontainers).

💬 We'd love your input! Share your thoughts on Copilot coding agent in our 2 minute survey.

…tinguish not-found from no-op

Co-authored-by: mpaulosky <60372079+mpaulosky@users.noreply.github.com>
Copy link
Copy Markdown
Contributor Author

Copilot AI commented Feb 21, 2026

Just as a heads up, I was blocked by some firewall rules while working on your feedback. Expand below for details.

Warning

Firewall rules blocked me from connecting to one or more addresses (expand for details)

I tried to connect to the following addresses, but was blocked by firewall rules:

  • api.segment.io
    • Triggering command: /usr/bin/mongosh mongosh --host 127.0.0.1 --port 27017 --quiet admin bash 3d5 --log-format t&#34;, 198d5c00f38c592abb50c2239ed9700d48c06d091a97173fe0531fa7b803bfe2 a08a9fe94b47748755e/log.json /usr�� mongod /run/containerd/io.containerd.runtime.v2.task/mo--log-format es eca2edd7e6854f1ebash 0181b0c81457e6fe/usr/bin/runc 0a26c80d563c76c6--root es (dns block)
  • compass.mongodb.com
    • Triggering command: /usr/bin/mongosh mongosh --host 127.0.0.1 --port 27017 --quiet admin (dns block)
    • Triggering command: /usr/bin/mongosh mongosh --host 127.0.0.1 --port 27017 --quiet admin ndor/bin/git (dns block)
    • Triggering command: /usr/bin/mongosh mongosh --host 127.0.0.1 --port 27017 --quiet admin e440ede2b5cba5dde5262c949e93afdc3d5/log.json DROP (dns block)

If you need me to access, download, or install something from one of these locations, you can either:

Copilot AI changed the title [WIP] Update DTOs refactor and soft-delete audit trail improvements fix: ArchiveAsync returns true for already-archived issues (MatchedCount vs ModifiedCount) Feb 21, 2026
Copilot AI requested a review from mpaulosky February 21, 2026 00:02
@github-actions
Copy link
Copy Markdown

Test Results Summary

0 tests   0 ✅  0s ⏱️
0 suites  0 💤
0 files    0 ❌

Results for commit fc44b02.

@mpaulosky mpaulosky marked this pull request as ready for review February 21, 2026 00:09
Copilot AI review requested due to automatic review settings February 21, 2026 00:09
@mpaulosky mpaulosky merged commit 53c4968 into feature/dto-refactor-and-soft-delete Feb 21, 2026
11 of 12 checks passed
@mpaulosky mpaulosky deleted the copilot/sub-pr-23-yet-again branch February 21, 2026 00:09
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR aims to make IssueRepository.ArchiveAsync report success when an issue exists but no update is applied (distinguishing “not found” from “already archived/no-op”) by switching the success condition from ModifiedCount to MatchedCount, and adds an integration test class intended to cover the relevant scenarios.

Changes:

  • Update IssueRepository.ArchiveAsync to return true when the update matched an existing issue (MatchedCount > 0).
  • Add integration tests for ArchiveAsync covering existing, already-archived, and non-existent issues.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 3 comments.

File Description
src/Api/Data/IssueRepository.cs Switches archive success criteria from ModifiedCount to MatchedCount.
tests/Integration/Handlers/DeleteIssueHandlerTests.cs Adds integration tests intended to validate archive behavior across three scenarios.

Comment on lines +63 to +74
public async Task ArchiveAsync_AlreadyArchivedIssue_ReturnsTrueIdempotent()
{
// Arrange
var issue = Issue.Create("Already Archived Issue", "Description");
await _repository.CreateAsync(issue);
await _repository.ArchiveAsync(issue.Id, "firstuser");

// Act - archive again (already archived, ModifiedCount will be 0)
var result = await _repository.ArchiveAsync(issue.Id, "seconduser");

// Assert - should return true (issue was found), not false (issue not found)
result.Should().BeTrue();
Copy link

Copilot AI Feb 21, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ArchiveAsync_AlreadyArchivedIssue_ReturnsTrueIdempotent doesn’t currently exercise the “already archived => ModifiedCount == 0” scenario described in the PR. IssueRepository.ArchiveAsync always sets ArchivedBy, ArchivedAt, and UpdatedAt to new values, so a second call will typically still modify the document (meaning the old ModifiedCount > 0 check would also have returned true). To validate the regression this PR claims to fix, either adjust the repository method to be a true no-op when IsArchived is already true (and assert ArchivedBy/ArchivedAt remain unchanged), or update the test/PR description to reflect that re-archiving mutates metadata and therefore isn’t idempotent.

Suggested change
public async Task ArchiveAsync_AlreadyArchivedIssue_ReturnsTrueIdempotent()
{
// Arrange
var issue = Issue.Create("Already Archived Issue", "Description");
await _repository.CreateAsync(issue);
await _repository.ArchiveAsync(issue.Id, "firstuser");
// Act - archive again (already archived, ModifiedCount will be 0)
var result = await _repository.ArchiveAsync(issue.Id, "seconduser");
// Assert - should return true (issue was found), not false (issue not found)
result.Should().BeTrue();
public async Task ArchiveAsync_AlreadyArchivedIssue_RearchivesAndReturnsTrue()
{
// Arrange
var issue = Issue.Create("Already Archived Issue", "Description");
await _repository.CreateAsync(issue);
await _repository.ArchiveAsync(issue.Id, "firstuser");
// Act - archive again (already archived; repository updates metadata)
var result = await _repository.ArchiveAsync(issue.Id, "seconduser");
// Assert - should return true (issue was found) and reflect latest archive metadata
result.Should().BeTrue();
var retrieved = await _repository.GetByIdAsync(issue.Id);
retrieved!.IsArchived.Should().BeTrue();
retrieved.ArchivedBy.Should().Be("seconduser");
retrieved.ArchivedAt.Should().NotBeNull();

Copilot uses AI. Check for mistakes.
Comment on lines 104 to 110
var result = await _collection.UpdateOneAsync(
x => x.Id == issueId,
update,
cancellationToken: cancellationToken);

return result.ModifiedCount > 0;
return result.MatchedCount > 0;
}
Copy link

Copilot AI Feb 21, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This change assumes ArchiveAsync can match an issue but perform a no-op update (so ModifiedCount == 0 while MatchedCount == 1). However, the current update unconditionally sets ArchivedBy, ArchivedAt, and UpdatedAt to new values, so an existing document will typically be modified on every call and the previous ModifiedCount > 0 check would also return true. Either (a) adjust the update to be idempotent for already-archived issues (so ModifiedCount can legitimately be 0), or (b) update the PR description/tests because the current implementation doesn’t align with the claimed MatchedCount vs ModifiedCount behavior.

Copilot uses AI. Check for mistakes.
/// Integration tests for IssueRepository.ArchiveAsync (soft-delete).
/// Verifies correct behavior when archiving existing, already-archived, and non-existent issues.
/// </summary>
public class DeleteIssueHandlerTests : IAsyncLifetime
Copy link

Copilot AI Feb 21, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The class/file name suggests these are tests for a delete handler, but the tests (and XML summary) exercise IssueRepository.ArchiveAsync directly and never instantiate/call DeleteIssueHandler. Renaming/moving this test to match what it covers will make test intent and failures easier to understand (e.g., an IssueRepositoryArchiveAsyncTests-style name and/or placement under a repository/data test folder).

Suggested change
public class DeleteIssueHandlerTests : IAsyncLifetime
public class IssueRepositoryArchiveAsyncTests : IAsyncLifetime

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants