Skip to content

Fix: CreateIssueCommand drops CategoryId and StatusId selected in form#130

Merged
mpaulosky merged 2 commits intomainfrom
copilot/fix-create-issue-command-fields
Apr 14, 2026
Merged

Fix: CreateIssueCommand drops CategoryId and StatusId selected in form#130
mpaulosky merged 2 commits intomainfrom
copilot/fix-create-issue-command-fields

Conversation

Copy link
Copy Markdown
Contributor

Copilot AI commented Apr 14, 2026

HandleSubmit in CreateIssuePage.razor built CreateIssueCommand with only Title and Description, silently discarding the category and status the user selected. The handler then hardcoded CategoryDto.Empty and StatusDto.Empty on the created issue.

Changes

  • CreateIssueCommand — Added CategoryId and StatusId (string?) properties.
  • CreateIssuePage.razorHandleSubmit now maps request.CategoryId and request.StatusId into the command.
  • CreateIssueHandler — Injects ICategoryRepository and IStatusRepository; resolves the full DTO by ID at creation time, falling back to Empty if the ID is absent, unparseable, or not found. Lookup pattern extracted into a generic LookupByIdAsync<T> helper to avoid duplication.
// Before
var command = new CreateIssueCommand { Title = request.Title, Description = request.Description };

// After
var command = new CreateIssueCommand
{
    Title = request.Title,
    Description = request.Description,
    CategoryId = request.CategoryId,
    StatusId = request.StatusId
};
// Handler now resolves full DTOs before persisting
var category = await LookupByIdAsync(command.CategoryId, CategoryDto.Empty, _categoryRepository.GetByIdAsync, cancellationToken);
var status   = await LookupByIdAsync(command.StatusId,   StatusDto.Empty,   _statusRepository.GetByIdAsync,  cancellationToken);
  • Tests — Unit and integration test constructors updated for the two new handler dependencies; two new unit tests added covering the lookup-present and lookup-absent paths.

- Add CategoryId and StatusId properties to CreateIssueCommand
- Inject ICategoryRepository and IStatusRepository into CreateIssueHandler
- Look up category/status by ID in handler; fall back to Empty if null/not found
- Extract reusable LookupByIdAsync<T> helper to eliminate duplication
- Update CreateIssuePage.razor to pass CategoryId and StatusId from form
- Update unit and integration test constructors for new dependencies
- Add two new unit tests for the category/status lookup behaviour

Closes #118

Agent-Logs-Url: https://github.com/mpaulosky/IssueManager/sessions/c4f50e37-fcd4-4e02-a375-d0b15f289a72

Co-authored-by: mpaulosky <60372079+mpaulosky@users.noreply.github.com>
Copilot AI changed the title [WIP] Fix CreateIssueCommand to include CategoryId and StatusId Fix: CreateIssueCommand drops CategoryId and StatusId selected in form Apr 14, 2026
Copilot AI requested a review from mpaulosky April 14, 2026 23:00
@mpaulosky mpaulosky marked this pull request as ready for review April 14, 2026 23:26
Copilot AI review requested due to automatic review settings April 14, 2026 23:26
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

Fixes issue creation so the Category and Status selected in the Blazor form are not dropped, and ensures the API persists the resolved DTOs rather than hardcoded Empty values.

Changes:

  • Extended CreateIssueCommand with optional CategoryId and StatusId.
  • Updated the Blazor create page to map selected IDs into the command.
  • Updated CreateIssueHandler to resolve CategoryDto/StatusDto from repositories via a shared lookup helper, with safe fallbacks.

Reviewed changes

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

Show a summary per file
File Description
tests/Api.Tests.Unit/Handlers/Issues/CreateIssueHandlerTests.cs Updates handler construction for new dependencies; adds unit tests for lookup-found and lookup-null paths.
tests/Api.Tests.Integration/Handlers/CreateIssueHandlerTests.cs Updates handler construction to satisfy new dependencies in integration test setup.
src/Web/package-lock.json Lockfile reformat (whitespace/indentation) with no functional dependency changes shown in the excerpt.
src/Web/Components/Features/Issues/CreateIssuePage.razor Passes CategoryId/StatusId from the form request into CreateIssueCommand.
src/Shared/Contracts/CreateIssueValidator.cs Adds CategoryId/StatusId fields to the shared command contract.
src/Api/Handlers/Issues/CreateIssueHandler.cs Injects category/status repositories and resolves DTOs by ID during issue creation.

result.Value!.Category.CategoryName.Should().Be("Bug");
result.Value!.Status.StatusName.Should().Be("Open");
await _categoryRepository.Received(1).GetByIdAsync(categoryId, Arg.Any<CancellationToken>());
await _statusRepository.Received(1).GetByIdAsync(statusId, Arg.Any<CancellationToken>());
Copy link

Copilot AI Apr 14, 2026

Choose a reason for hiding this comment

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

The assertions in this test can pass even if the handler doesn’t apply the looked-up category/status to the IssueDto sent to CreateAsync, because CreateAsync is stubbed to return an IssueDto that already contains expectedCategory/expectedStatus. To make the test actually validate behavior, assert that _repository.CreateAsync(...) was called with an IssueDto whose Category and Status match the looked-up DTOs (e.g., using Arg.Is<IssueDto>(...) or capturing the argument).

Suggested change
await _statusRepository.Received(1).GetByIdAsync(statusId, Arg.Any<CancellationToken>());
await _statusRepository.Received(1).GetByIdAsync(statusId, Arg.Any<CancellationToken>());
await _repository.Received(1).CreateAsync(
Arg.Is<IssueDto>(issue =>
issue.Category == expectedCategory &&
issue.Status == expectedStatus),
Arg.Any<CancellationToken>());

Copilot uses AI. Check for mistakes.
Comment on lines +83 to +89
if (!string.IsNullOrEmpty(id) && ObjectId.TryParse(id, out var objectId))
{
var result = await getById(objectId, cancellationToken);
if (result.Success && result.Value is not null)
return result.Value;
}
return defaultValue;
Copy link

Copilot AI Apr 14, 2026

Choose a reason for hiding this comment

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

LookupByIdAsync has important branches for (a) non-empty but unparseable IDs and (b) parseable IDs where the repository returns NotFound/Fail. There’s currently coverage for the “present and found” and “null IDs” paths, but not for these new fallback branches. Consider adding unit tests to ensure invalid/not-found IDs don’t call CreateAsync with unintended values and correctly fall back to CategoryDto.Empty/StatusDto.Empty.

Copilot uses AI. Check for mistakes.
@github-actions
Copy link
Copy Markdown

Test Results Summary

  6 files  ±0   33 suites  ±0   10s ⏱️ -1s
837 tests +2  837 ✅ +2  0 💤 ±0  0 ❌ ±0 
848 runs  +2  848 ✅ +2  0 💤 ±0  0 ❌ ±0 

Results for commit 171c2f7. ± Comparison against base commit 7180786.

@codecov
Copy link
Copy Markdown

codecov Bot commented Apr 14, 2026

Codecov Report

❌ Patch coverage is 86.95652% with 3 lines in your changes missing coverage. Please review.
✅ Project coverage is 55.74%. Comparing base (7180786) to head (171c2f7).
⚠️ Report is 3 commits behind head on main.

Files with missing lines Patch % Lines
...b/Components/Features/Issues/CreateIssuePage.razor 0.00% 3 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main     #130      +/-   ##
==========================================
+ Coverage   55.52%   55.74%   +0.22%     
==========================================
  Files         124      124              
  Lines        2826     2845      +19     
  Branches      313      315       +2     
==========================================
+ Hits         1569     1586      +17     
- Misses       1027     1029       +2     
  Partials      230      230              
Files with missing lines Coverage Δ
src/Api/Handlers/Issues/CreateIssueHandler.cs 92.68% <100.00%> (+4.22%) ⬆️
src/Shared/Contracts/CreateIssueValidator.cs 100.00% <100.00%> (ø)
...b/Components/Features/Issues/CreateIssuePage.razor 0.00% <0.00%> (ø)
🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@mpaulosky
Copy link
Copy Markdown
Owner

Ralph (Work Monitor): All test suites pass (unit, integration, bunit, architecture). The E2E failure is a pre-existing CI infrastructure issue (DB init failure, Redis SSL errors) unrelated to this PR's changes. Merging.

@mpaulosky mpaulosky merged commit df96df5 into main Apr 14, 2026
15 of 16 checks passed
@mpaulosky mpaulosky deleted the copilot/fix-create-issue-command-fields branch April 14, 2026 23:51
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.

[Bug] CreateIssueCommand drops CategoryId and StatusId from form

3 participants