Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
117 changes: 117 additions & 0 deletions .github/scripts/ci/Get-CodeCoverage.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
####################################################################################
# To execute
# 1. In powershell, set security policy for this script:
# Set-ExecutionPolicy Unrestricted -Scope Process -Force
# 2. Change directory to the script folder:
# CD src (wherever your script is)
# 3. In powershell, run script:
# .\Get-CodeCoverage.ps1 -TestProjectFilter '*Tests*.csproj'
# This script uses native .NET 10 code coverage (Microsoft.Testing.Platform)
# Note: Due to MSTest 4.1.0 incompatibility with 'dotnet test' on .NET 10, this runs tests as executables
####################################################################################

Param(
[string]$TestProjectFilter = '*Tests*.csproj',
[string]$Configuration = 'Release',
[string]$TestRootPath = ''
)
####################################################################################
if ($IsWindows) {Set-ExecutionPolicy Unrestricted -Scope Process -Force}
$VerbosePreference = 'SilentlyContinue' # 'Continue'
####################################################################################

function Resolve-TestRootPath {
param(
[Parameter(Mandatory = $true)]
[System.IO.DirectoryInfo]$ScriptDir,
[Parameter(Mandatory = $false)]
[string]$OverridePath
)

if (-not [string]::IsNullOrWhiteSpace($OverridePath)) {
return Get-Item -Path (Resolve-Path -Path $OverridePath)
}

$current = $ScriptDir
while ($null -ne $current) {
$srcCandidate = Join-Path $current.FullName 'src'
if (Test-Path -Path $srcCandidate) {
return Get-Item -Path $srcCandidate
}

$current = $current.Parent
}

return $ScriptDir
}

# Install required tools
& dotnet tool install -g dotnet-reportgenerator-globaltool
& dotnet tool install -g dotnet-coverage

$timestamp = Get-Date -Format "yyyyMMdd-HHmmss"
$scriptPath = Get-Item -Path $PSScriptRoot
$testRootPath = Resolve-TestRootPath -ScriptDir $scriptPath -OverridePath $TestRootPath
$reportOutputPath = Join-Path $testRootPath "TestResults\Reports\$timestamp"

New-Item -ItemType Directory -Force -Path $reportOutputPath

# Find test projects
$testProjects = Get-ChildItem $testRootPath -Filter $TestProjectFilter -Recurse
Write-Host "Found $($testProjects.Count) test projects."

foreach ($project in $testProjects) {
$testProjectPath = $project.FullName
Write-Host "Running tests with coverage for project: $($project.BaseName)"

Write-Host "Building test project: $($project.BaseName)"
& dotnet build $testProjectPath --configuration $Configuration

# Use 'dotnet run' instead of 'dotnet test' for MSTest runner projects
# This bypasses the VSTest target that's incompatible with .NET 10 SDK
Push-Location $project.DirectoryName
& dotnet run --configuration $Configuration --no-build -- --coverage
Pop-Location
}

# Collect all coverage files (Microsoft.Testing.Platform outputs .coverage files)
$coverageFiles = Get-ChildItem -Path $testRootPath -Filter "*.coverage" -Recurse | Select-Object -ExpandProperty FullName

if ($coverageFiles.Count -eq 0) {
Write-Warning "No coverage files found. Make sure your test projects have code coverage enabled."
exit 0
}

Write-Host "Found $($coverageFiles.Count) coverage file(s)"

# Convert binary .coverage files to XML format
$coverageXmlFiles = @()
foreach ($coverageFile in $coverageFiles) {
$xmlFile = $coverageFile -replace '\.coverage$', '.cobertura.xml'
Write-Host "Converting $coverageFile to XML format..."
& dotnet-coverage merge $coverageFile --output $xmlFile --output-format cobertura
if (Test-Path $xmlFile) {
$coverageXmlFiles += $xmlFile
}
}

if ($coverageXmlFiles.Count -eq 0) {
Write-Warning "No XML coverage files were generated."
exit 0
}

Write-Host "Generated $($coverageXmlFiles.Count) XML coverage file(s)"

# Generate HTML report
$coverageFilesArg = ($coverageXmlFiles -join ";")
& reportgenerator -reports:$coverageFilesArg -targetdir:$reportOutputPath -reporttypes:Html

Write-Host "Code coverage report generated at: $reportOutputPath"

$reportIndexHtml = Join-Path $reportOutputPath "index.html"
if (Test-Path $reportIndexHtml) {
Invoke-Item -Path $reportIndexHtml
}
else {
Write-Warning "Report index.html not found at: $reportIndexHtml"
}
19 changes: 16 additions & 3 deletions .github/workflows/gtc-agent-standalone-web-api-sql.yml
Original file line number Diff line number Diff line change
Expand Up @@ -154,11 +154,21 @@ jobs:
with:
name: ${{ env.MIGRATION_ARTIFACT_NAME }}
path: ${{ env.MIGRATION_ARTIFACT_PATH }}
if: github.event_name == 'push' && github.ref == 'refs/heads/main' || github.event_name == 'workflow_dispatch'

- name: Test
run: |
cd ${{ env.SRC_PATH }}/${{ env.TEST_PATH }}
dotnet run --configuration ${{ env.CONFIGURATION }} --no-build
shell: pwsh

- name: Copy test results
if: ${{ always() }}
run: |
mkdir -p TestResults-${{ matrix.DOTNET_VERSION }}
dotnet test ${{ env.SRC_PATH }}/${{ env.TEST_PATH }}/${{ env.TEST_PROJECT }} --configuration ${{ env.CONFIGURATION }} --results-directory TestResults-${{ matrix.DOTNET_VERSION }} --collect:"Code Coverage" --verbosity normal
if (Test-Path "${{ env.SRC_PATH }}/${{ env.TEST_PATH }}/bin/Release/net10.0/TestResults") {
Copy-Item -Path "${{ env.SRC_PATH }}/${{ env.TEST_PATH }}/bin/Release/net10.0/TestResults/*" -Destination "TestResults-${{ matrix.DOTNET_VERSION }}" -Recurse -Force
}
shell: pwsh

- name: Upload test results
Expand All @@ -178,6 +188,7 @@ jobs:
with:
name: ${{ env.API_ARTIFACT_NAME }}
path: ${{ env.API_ARTIFACT_OUTPUT }}/**
if: github.event_name == 'push' && github.ref == 'refs/heads/main' || github.event_name == 'workflow_dispatch'

- name: Pack Blazor artifact
run: |
Expand All @@ -189,16 +200,18 @@ jobs:
with:
name: ${{ env.WEB_ARTIFACT_NAME }}
path: ${{ env.WEB_ARTIFACT_OUTPUT }}/**
if: github.event_name == 'push' && github.ref == 'refs/heads/main' || github.event_name == 'workflow_dispatch'

- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v4
if: ${{ !github.event.repository.private }}

- name: Run Coverage Script
run: |
pwsh ${{ env.SRC_PATH }}/Get-CodeCoverage.ps1 `
pwsh ${{ env.SCRIPTS_PATH }}/ci/Get-CodeCoverage.ps1 `
-TestProjectFilter '${{ env.TEST_PROJECT }}' `
-ProdPackagesOnly
-Configuration '${{ env.CONFIGURATION }}' `
-TestRootPath '${{ env.SRC_PATH }}'
shell: pwsh

- name: Upload Coverage Report
Expand Down
4 changes: 0 additions & 4 deletions src/.github/copilot-instructions.md

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ namespace Goodtocode.AgentFramework.Core.Application.Abstractions;
public interface IAgentFrameworkContext
{
DbSet<ChatMessageEntity> ChatMessages { get; }
DbSet<ChatSessionEntity> ChatSessions {get; }
DbSet<ChatSessionEntity> ChatSessions { get; }
DbSet<ActorEntity> Actors { get; }

Task<int> SaveChangesAsync(CancellationToken cancellationToken = default);
Expand Down
2 changes: 1 addition & 1 deletion src/Core.Application/Actor/SaveMyActorCommand.cs
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ public async Task<ActorDto> Handle(SaveMyActorCommand request, CancellationToken
}
else
{
actor = ActorEntity.Create(Guid.NewGuid(), request?.FirstName, request?.LastName, request?.Email);
actor = ActorEntity.Create(Guid.NewGuid(), request?.FirstName, request?.LastName, request?.Email);
_context.Actors.Add(actor);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,6 @@ private static void GuardAgainstUnauthorizedUser(ChatSessionEntity chatSession,
private static void GuardAgainstNullAgentResponse(ChatMessage? response)
{
if (response == null)
throw new CustomValidationException([new("ChatMessage","Agent response cannot be null")]);
throw new CustomValidationException([new("ChatMessage", "Agent response cannot be null")]);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -30,15 +30,15 @@ public async Task<ChatSessionDto> Handle(CreateMyChatSessionCommand request, Can
GuardAgainstEmptyUser(request?.UserContext);

var actor = await _context.Actors
.FirstOrDefaultAsync(a => a.OwnerId == request!.UserContext!.OwnerId
.FirstOrDefaultAsync(a => a.OwnerId == request!.UserContext!.OwnerId
&& a.TenantId == request.UserContext.TenantId, cancellationToken);

if (actor == null)
{
actor = ActorEntity.Create(
Guid.NewGuid(),
request?.UserContext?.FirstName,
request?.UserContext?.LastName,
request?.UserContext?.FirstName,
request?.UserContext?.LastName,
request?.UserContext?.Email
);
_context.Actors.Add(actor);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
public class CreateMyChatSessionCommandValidator : Validator<CreateMyChatSessionCommand>
{
public CreateMyChatSessionCommandValidator()
{
{
RuleFor(x => x.Message)
.NotEmpty("Message is required");

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ public async Task<PaginatedList<ChatMessageDto>> Handle(GetMyChatMessagesPaginat
{
GuardAgainstEmptyUser(request?.UserContext);

var userContext = request!.UserContext!;
var userContext = request!.UserContext!;

var returnData = await _context.ChatMessages
.Where(x => x.ChatSession != null && x.ChatSession.OwnerId == userContext.OwnerId)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ public async Task<ICollection<ChatSessionDto>> Handle(GetMyChatSessionsQuery req

var startDate = request?.StartDate;
var endDate = request?.EndDate;
var userContext = request?.UserContext;
var userContext = request?.UserContext;

var returnData = await _context.ChatSessions
.Where(x => userContext != null && x.OwnerId == userContext.OwnerId)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
using Microsoft.Extensions.Logging;
using System.Diagnostics;
using System.Diagnostics;
using Microsoft.Extensions.Logging;

namespace Goodtocode.AgentFramework.Core.Application.Common.Behaviors;

Expand Down
4 changes: 2 additions & 2 deletions src/Core.Application/GlobalUsings.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
global using Goodtocode.Validation;
global using System.Reflection;
global using Goodtocode.Mediator;
global using Goodtocode.Validation;
global using Microsoft.EntityFrameworkCore;
global using System.Reflection;
8 changes: 4 additions & 4 deletions src/Core.Domain/Actor/ActorEntity.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ public static ActorEntity Create(Guid id, string? firstName, string? lastName, s
{
return new ActorEntity
{
Id = id == Guid.Empty ? Guid.NewGuid() : id,
Id = id == Guid.Empty ? Guid.NewGuid() : id,
FirstName = firstName,
LastName = lastName,
Email = email
Expand All @@ -37,8 +37,8 @@ public static ActorEntity Create(IUserContext userInfo)

public void Update(string? firstName, string? lastName, string? email)
{
FirstName = firstName ?? FirstName;
LastName = lastName ?? LastName;
Email = email ?? Email;
FirstName = firstName ?? FirstName;
LastName = lastName ?? LastName;
Email = email ?? Email;
}
}
2 changes: 1 addition & 1 deletion src/Core.Domain/Auth/IUserContext.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ public interface IUserContext
string FirstName { get; }
string LastName { get; }
string Email { get; }
IEnumerable<string> Roles { get; }
IEnumerable<string> Roles { get; }
bool CanView { get; }
bool CanEdit { get; }
bool CanDelete { get; }
Expand Down
2 changes: 1 addition & 1 deletion src/Core.Domain/Auth/UserContext.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ public struct UserRoles
{
public const string ChatOwner = "AssetOwner";
public const string ChatEditor = "AssetEditor";
public const string ChatViewer = "AssetViewer";
public const string ChatViewer = "AssetViewer";
}

/// <summary>
Expand Down
6 changes: 3 additions & 3 deletions src/Core.Domain/ChatCompletion/ChatSessionEntity.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
using Goodtocode.Domain.Entities;
using Goodtocode.AgentFramework.Core.Domain.Actor;
using Goodtocode.AgentFramework.Core.Domain.Actor;
using Goodtocode.Domain.Entities;

namespace Goodtocode.AgentFramework.Core.Domain.ChatCompletion;

Expand Down Expand Up @@ -27,6 +27,6 @@ public static ChatSessionEntity Create(Guid id, Guid actorId, string? title, Cha

public void Update(string? title)
{
Title = title ?? Title;
Title = title ?? Title;
}
}
64 changes: 0 additions & 64 deletions src/Get-CodeCoverage.ps1

This file was deleted.

2 changes: 1 addition & 1 deletion src/Infrastructure.AgentFramework/ConfigureServices.cs
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ public static IServiceCollection AddAgentFrameworkOpenAIServices(this IServiceCo
};

var agentOptions = new ChatClientAgentOptions
{
{
Name = "CopilotAgent",
Description = "Microsoft Agent Framework Quick-start Copilot",
};
Expand Down
Loading
Loading