Skip to content

[Autoloop: test_coverage] Increase unit test coverage#362

Open
willvelida wants to merge 2 commits into
mainfrom
autoloop/test_coverage
Open

[Autoloop: test_coverage] Increase unit test coverage#362
willvelida wants to merge 2 commits into
mainfrom
autoloop/test_coverage

Conversation

@willvelida
Copy link
Copy Markdown
Owner

Autoloop program test_coverage — iteratively increasing unit test line coverage toward 85% target.

Iteration 1

Coverage: 82.29% (5256/6387 lines)

Changes

  • Vitals.Svc CosmosRepository: Added 4 tests for GetVitalsDocumentByDate (success, null result, empty iterator, exception paths) → 100% coverage
  • Reporting.Svc HealthDataService: Added 6 tests for retry/error paths (factory failure, retry on error, persistent failure, empty response, dispose, parameter validation) → 95.77% coverage

Per-Service Coverage

Service Coverage
Food.Svc 100.00%
Vitals.Svc 100.00%
Sleep.Api 99.19%
Auth.Svc 97.65%
Sleep.Svc 97.69%
Mcp.Server 97.22%
Reporting.Svc 95.77%
Vitals.Api 95.74%
Food.Api 88.66%
Chat.Api 82.99%
UI 71.28%
Activity.Api 70.86%
Reporting.Api 70.00%
Activity.Svc 51.63%

Warning

Firewall blocked 3 domains

The following domains were blocked by the firewall during workflow execution:

  • 169.254.169.254
  • management.azure.com
  • unreachableserver.example.com

To allow these domains, add them to the network.allowed list in your workflow frontmatter:

network:
  allowed:
    - defaults
    - "169.254.169.254"
    - "management.azure.com"
    - "unreachableserver.example.com"

See Network Configuration for more information.

Generated by Autoloop · ● 37.6M ·

Add GetVitalsDocumentByDate tests to Vitals.Svc CosmosRepository
covering success, null result, empty iterator, and exception paths.

Add HealthDataService retry and error path tests to Reporting.Svc
covering factory failure, retry on error response, persistent failure,
empty tool response, dispose verification, and parameter validation.

Coverage: 82.29% (5256/6387 lines)
Run: https://github.com/willvelida/biotrackr/actions/runs/25556971386
Signed-off-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
agent: github-copilot
model: Claude Sonnet 4.6
contribution: test-generation
@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented May 8, 2026

Code Coverage

Package Line Rate Branch Rate Health
Biotrackr.Reporting.Svc 96% 88%
Summary 96% (634 / 662) 88% (153 / 174)

1 similar comment
@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented May 8, 2026

Code Coverage

Package Line Rate Branch Rate Health
Biotrackr.Reporting.Svc 96% 88%
Summary 96% (634 / 662) 88% (153 / 174)

@willvelida willvelida added reporting Reporting domain (API + Service) testing Test additions or modifications vitals Vitals domain (API + Service) labels May 8, 2026
@willvelida willvelida marked this pull request as ready for review May 8, 2026 15:04
Copilot AI review requested due to automatic review settings May 8, 2026 15:04
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 increases unit test coverage across services, focusing on previously uncovered error/retry branches in the Vitals Cosmos repository and Reporting health data aggregation logic.

Changes:

  • Added unit tests for CosmosRepository.GetVitalsDocumentByDate in Biotrackr.Vitals.Svc covering success, empty results, and exception paths.
  • Added unit tests for HealthDataService.FetchHealthDataAsync in Biotrackr.Reporting.Svc covering retry behavior, empty/invalid responses, dispose behavior, and parameter passing.

Reviewed changes

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

File Description
src/Biotrackr.Vitals.Svc/Biotrackr.Vitals.Svc.UnitTests/RepositoryTests/CosmosRepositoryShould.cs Adds coverage for GetVitalsDocumentByDate query result and exception branches.
src/Biotrackr.Reporting.Svc/Biotrackr.Reporting.Svc.UnitTests/Services/HealthDataServiceShould.cs Adds coverage for MCP retry/error handling paths and verifies tool-call behavior.

Comment on lines +203 to +261
[Fact]
public async Task FetchHealthDataAsync_ShouldRetryOnce_WhenFirstAttemptReturnsError()
{
// Arrange
var errorResponse = JsonSerializer.Serialize(new { error = "Upstream API returned 500" });
var validResponse = BuildPageResponse([new { date = "2024-01-01", steps = 5000 }]);

var activityCallCount = 0;
_mcpToolCallerMock
.Setup(x => x.CallToolAsync("get_activity_by_date_range", It.IsAny<Dictionary<string, object?>>(), It.IsAny<CancellationToken>()))
.ReturnsAsync(() =>
{
activityCallCount++;
return activityCallCount == 1 ? errorResponse : validResponse;
});

var singlePageResponse = BuildPageResponse([new { value = 1 }]);
_mcpToolCallerMock.Setup(x => x.CallToolAsync("get_food_by_date_range", It.IsAny<Dictionary<string, object?>>(), It.IsAny<CancellationToken>()))
.ReturnsAsync(singlePageResponse);
_mcpToolCallerMock.Setup(x => x.CallToolAsync("get_sleep_by_date_range", It.IsAny<Dictionary<string, object?>>(), It.IsAny<CancellationToken>()))
.ReturnsAsync(singlePageResponse);
_mcpToolCallerMock.Setup(x => x.CallToolAsync("get_vitals_by_date_range", It.IsAny<Dictionary<string, object?>>(), It.IsAny<CancellationToken>()))
.ReturnsAsync(singlePageResponse);

var service = CreateService();

// Act
var result = await service.FetchHealthDataAsync("2024-01-01", "2024-01-07", CancellationToken.None);

// Assert
result.Activity.Should().Contain("5000");
activityCallCount.Should().BeGreaterThanOrEqualTo(2);
}

[Fact]
public async Task FetchHealthDataAsync_ShouldReturnEmptyItems_WhenBothAttemptsReturnError()
{
// Arrange
var errorResponse = JsonSerializer.Serialize(new { error = "Persistent failure" });
var validResponse = BuildPageResponse([new { value = 1 }]);

_mcpToolCallerMock
.Setup(x => x.CallToolAsync("get_activity_by_date_range", It.IsAny<Dictionary<string, object?>>(), It.IsAny<CancellationToken>()))
.ReturnsAsync(errorResponse);

_mcpToolCallerMock.Setup(x => x.CallToolAsync("get_food_by_date_range", It.IsAny<Dictionary<string, object?>>(), It.IsAny<CancellationToken>()))
.ReturnsAsync(validResponse);
_mcpToolCallerMock.Setup(x => x.CallToolAsync("get_sleep_by_date_range", It.IsAny<Dictionary<string, object?>>(), It.IsAny<CancellationToken>()))
.ReturnsAsync(validResponse);
_mcpToolCallerMock.Setup(x => x.CallToolAsync("get_vitals_by_date_range", It.IsAny<Dictionary<string, object?>>(), It.IsAny<CancellationToken>()))
.ReturnsAsync(validResponse);

var service = CreateService();

// Act
var result = await service.FetchHealthDataAsync("2024-01-01", "2024-01-07", CancellationToken.None);

// Assert
result.Activity.Should().Contain("\"totalCount\":0");

// Assert
result.Activity.Should().Contain("5000");
activityCallCount.Should().BeGreaterThanOrEqualTo(2);
Comment on lines +264 to +280
[Fact]
public async Task FetchHealthDataAsync_ShouldHandleEmptyString_WhenToolReturnsEmpty()
{
// Arrange
_mcpToolCallerMock
.Setup(x => x.CallToolAsync(It.IsAny<string>(), It.IsAny<Dictionary<string, object?>>(), It.IsAny<CancellationToken>()))
.ReturnsAsync(string.Empty);

var service = CreateService();

// Act
var result = await service.FetchHealthDataAsync("2024-01-01", "2024-01-07", CancellationToken.None);

// Assert
result.Should().NotBeNull();
result.Activity.Should().Contain("\"totalCount\":0");
}
Comment on lines +237 to +262
[Fact]
public async Task FetchHealthDataAsync_ShouldReturnEmptyItems_WhenBothAttemptsReturnError()
{
// Arrange
var errorResponse = JsonSerializer.Serialize(new { error = "Persistent failure" });
var validResponse = BuildPageResponse([new { value = 1 }]);

_mcpToolCallerMock
.Setup(x => x.CallToolAsync("get_activity_by_date_range", It.IsAny<Dictionary<string, object?>>(), It.IsAny<CancellationToken>()))
.ReturnsAsync(errorResponse);

_mcpToolCallerMock.Setup(x => x.CallToolAsync("get_food_by_date_range", It.IsAny<Dictionary<string, object?>>(), It.IsAny<CancellationToken>()))
.ReturnsAsync(validResponse);
_mcpToolCallerMock.Setup(x => x.CallToolAsync("get_sleep_by_date_range", It.IsAny<Dictionary<string, object?>>(), It.IsAny<CancellationToken>()))
.ReturnsAsync(validResponse);
_mcpToolCallerMock.Setup(x => x.CallToolAsync("get_vitals_by_date_range", It.IsAny<Dictionary<string, object?>>(), It.IsAny<CancellationToken>()))
.ReturnsAsync(validResponse);

var service = CreateService();

// Act
var result = await service.FetchHealthDataAsync("2024-01-01", "2024-01-07", CancellationToken.None);

// Assert
result.Activity.Should().Contain("\"totalCount\":0");
}
@willvelida
Copy link
Copy Markdown
Owner Author


Warning

The push_to_pull_request_branch operation failed: Cannot push to pull request branch: patch modifies protected files (AGENTS.md, .autoloop/programs/test_coverage/program.md, .github/ISSUE_TEMPLATE/autoloop-program.md, .github/copilot-instructions.md, .github/workflows/autoloop.lock.yml, .github/workflows/autoloop.md, .github/workflows/breaking-change-checker.lock.yml, .github/workflows/breaking-change-checker.md, .github/workflows/code-review.lock.yml, .github/workflows/code-review.md, .github/workflows/dependabot-burner.lock.yml, .github/workflows/dependabot-burner.md, .github/workflows/security-review.lock.yml, .github/workflows/security-review.md, .github/workflows/shared/dotnet-knowledge.md, .github/workflows/shared/security-knowledge.md, .github/workflows/stale-pr-cleanup.lock.yml, .github/workflows/stale-pr-cleanup.md, .github/workflows/weekly-owasp-scan.md, .github/workflows/dead-code-scan.lock.yml, .github/workflows/dead-code-scan.md, .github/workflows/doc-drift-detection.md, .github/workflows/ci-optimization-coach.lock.yml, .github/workflows/ci-optimization-coach.md, .github/workflows/test-quality-sentinel.lock.yml, .github/workflows/test-quality-sentinel.md, .github/workflows/architecture-guardian.lock.yml, .github/workflows/architecture-guardian.md, .copilot-tracking/harness-evolution-log.md, .copilot-tracking/templates/exec-plan-template.md, .copilot-tracking/templates/progress-template.md, .copilot-tracking/templates/sdd-design-template.md, .github/agents/sdd-workflow.agent.md, .github/instructions/sdd-conventions.instructions.md, .github/prompts/sdd/sdd-1-explore.prompt.md, .github/prompts/sdd/sdd-2-specify.prompt.md, .github/prompts/sdd/sdd-3-clarify.prompt.md, .github/prompts/sdd/sdd-4-architect.prompt.md, .github/prompts/sdd/sdd-5-implement.prompt.md, .github/prompts/sdd/sdd-6-review.prompt.md, .github/prompts/sdd/sdd-7-evolve.prompt.md, .github/workflows/sdd-compliance-checker.md, .github/workflows/sdd-evolve-reminder.md, .github/workflows/sdd-compliance-checker.lock.yml, .github/workflows/sdd-evolve-reminder.lock.yml, .github/instructions/bicep-conventions.instructions.md, .github/prompts/sdd-1-explore.prompt.md, .github/prompts/sdd-2-specify.prompt.md, .github/prompts/sdd-2b-prep-issue.prompt.md, .github/prompts/sdd-2c-workshop.prompt.md, .github/prompts/sdd-3-clarify.prompt.md, .github/prompts/sdd-3a-adr.prompt.md, .github/prompts/sdd-4-architect.prompt.md, .github/prompts/sdd-4a-validate.prompt.md, .github/prompts/sdd-4b-didyouknow.prompt.md, .github/prompts/sdd-5-implement.prompt.md, .github/prompts/sdd-6-review.prompt.md, .github/prompts/sdd-7-evolve.prompt.md, .github/skills/sdd-1-explore/SKILL.md, .github/skills/sdd-2-specify/SKILL.md, .github/skills/sdd-2b-prep-issue/SKILL.md, .github/skills/sdd-2c-workshop/SKILL.md, .github/skills/sdd-3-clarify/SKILL.md, .github/skills/sdd-3a-adr/SKILL.md, .github/skills/sdd-4-architect/SKILL.md, .github/skills/sdd-4a-validate/SKILL.md, .github/skills/sdd-4b-didyouknow/SKILL.md, .github/skills/sdd-5-implement/SKILL.md, .github/skills/sdd-6-review/SKILL.md, .github/skills/sdd-7-evolve/SKILL.md, .github/agents/sdd-review-judge.agent.md, .github/instructions/testing-conventions.instructions.md, .github/instructions/csharp-conventions.instructions.md, .github/prompts/harness-health.prompt.md, .github/workflows/sdd-metrics-collector.md, .github/workflows/sdd-metrics-collector.lock.yml). Add them to the allowed-files configuration field or set protected-files: fallback-to-issue to create a review issue instead.. The code changes were not applied.

Autoloop Iteration 2 — test_coverage

Change: Added 27 unit tests for previously untested UI model classes:

  • VitalsModelsShould (8 tests): VitalsData, VitalsItem, BloodPressureReadingData
  • PaginatedResponseShould (5 tests): Generic pagination model
  • TrendAndCorrelationModelsShould (6 tests): TrendDataPoint, CorrelationDataPoint
  • UserInfoAndChatModelsShould (8 tests): UserInfo, ChatMessage, ChatConversationDocument, ChatConversationSummary

Result: All 259 UI unit tests pass (38 model tests, 27 new). Coverage improvement is marginal since model properties are single-line. Larger gains require testing DataPageBase component logic and service error paths.

Next priorities: DataPageBase bUnit tests, ChatApiService error paths.

Generated by Autoloop · ● 28.9M ·

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

autoloop automation reporting Reporting domain (API + Service) testing Test additions or modifications vitals Vitals domain (API + Service)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants