Added MCp support

This commit is contained in:
2026-10-30 10:16:27 +02:00
parent d7806ff784
commit 58f8006ab2
57 changed files with 1832 additions and 22 deletions
+330
View File
@@ -0,0 +1,330 @@
using Microsoft.Extensions.Logging.Abstractions;
using PostFundManagement.Application.Abstractions.Services;
using PostFundManagement.Application.Services.Governance;
using PostFundManagement.Domain;
using PostFundManagement.Domain.Models;
using PostFundManagement.Domain.Models.Security;
using PostFundManagement.Application.Mcp;
using PostFundManagement.Application.Abstractions.Mcp;
using PostFundManagement.Application.Extensions;
namespace PostFundManagement.Tests;
public class McpToolingTests
{
private readonly IServiceProvider _serviceProvider;
public McpToolingTests()
{
var services = new ServiceCollection();
services.AddSingleton<ILoggerFactory, NullLoggerFactory>();
services.AddLogging();
// Register dummy/mock services for testing MCP proxying
services.AddSingleton<IGrantsService, TestGrantsService>();
services.AddSingleton<IProjectService, TestProjectService>();
services.AddSingleton<IExecutionService, TestExecutionService>();
services.AddSingleton<IGovernanceService, TestGovernanceService>();
services.AddSingleton<IEvidenceService, TestEvidenceService>();
services.AddSingleton<ICommunicationsService, TestCommunicationsService>();
services.AddSingleton<IUserService, TestUserService>();
// Register MCP tooling & InApp client
services.AddApplicationMcpTools();
_serviceProvider = services.BuildServiceProvider();
}
[Fact]
public void AddApplicationMcpTools_RegistersAllToolTypesAndInAppClient()
{
using var scope = _serviceProvider.CreateScope();
var sp = scope.ServiceProvider;
Assert.NotNull(sp.GetService<GrantsMcpTools>());
Assert.NotNull(sp.GetService<ProjectsMcpTools>());
Assert.NotNull(sp.GetService<ExecutionMcpTools>());
Assert.NotNull(sp.GetService<GovernanceMcpTools>());
Assert.NotNull(sp.GetService<EvidenceMcpTools>());
Assert.NotNull(sp.GetService<CommunicationsMcpTools>());
Assert.NotNull(sp.GetService<UsersMcpTools>());
Assert.NotNull(sp.GetService<IInAppMcpClient>());
}
[Fact]
public void InAppMcpClient_DiscoversToolsAcrossAllServiceGroupings()
{
using var scope = _serviceProvider.CreateScope();
var client = scope.ServiceProvider.GetRequiredService<IInAppMcpClient>();
var tools = client.GetAvailableTools();
Assert.NotEmpty(tools);
Assert.True(tools.Count >= 40, $"Expected at least 40 tools, but found {tools.Count}");
var groups = tools.Select(t => t.Group).Distinct().OrderBy(g => g).ToList();
Assert.Contains("Grants", groups);
Assert.Contains("Projects", groups);
Assert.Contains("Execution", groups);
Assert.Contains("Governance", groups);
Assert.Contains("Evidence", groups);
Assert.Contains("Communications", groups);
Assert.Contains("Users", groups);
}
[Fact]
public async Task InAppMcpClient_ExecutesToolInProcess_WithoutNetworkHops()
{
using var scope = _serviceProvider.CreateScope();
var client = scope.ServiceProvider.GetRequiredService<IInAppMcpClient>();
var result = await client.ExecuteToolAsync("grants_get_award", new Dictionary<string, object?>
{
["awardIdHash"] = "awd_test_123"
});
Assert.True(result.Success, $"Execution failed: {result.Error}");
Assert.NotNull(result.Data);
Assert.NotNull(result.SerializedResult);
var mcpResult = result.Data as Records<Award>;
Assert.NotNull(mcpResult);
Assert.True(mcpResult.Success);
Assert.Equal(1L, mcpResult.Data?.Id);
Assert.Equal(1_000_000m, mcpResult.Data?.Amount);
}
[Fact]
public async Task InAppMcpClient_WhenUnknownTool_ReturnsFailure()
{
using var scope = _serviceProvider.CreateScope();
var client = scope.ServiceProvider.GetRequiredService<IInAppMcpClient>();
var result = await client.ExecuteToolAsync("non_existent_tool");
Assert.False(result.Success);
Assert.Contains("not recognized", result.Error);
}
[Fact]
public void McpToolResult_OptimizesTokenUsage_ForSuccessAndFailure()
{
var okResult = Records<string>.Ok("TestData");
Assert.True(okResult.Success);
Assert.Equal("TestData", okResult.Data);
Assert.Null(okResult.Errors);
var failResult = Records<string>.Fail("Something went wrong", ["Error detail 1"]);
Assert.False(failResult.Success);
Assert.Equal("Something went wrong", failResult.Message);
Assert.NotNull(failResult.Errors);
Assert.Single(failResult.Errors);
}
#region Test Service Implementations
private sealed class TestGrantsService : IGrantsService
{
public Task<Result<Award>> GetAwardAsync(string awardIdHash, CancellationToken cancellationToken = default)
{
var award = new Award
{
Id = 1,
Amount = 1_000_000m
};
return Task.FromResult(Result.Ok(award));
}
public Task<Result<Award[]>> GetAwardsAsync(Pagination pagination, CancellationToken cancellationToken = default) =>
Task.FromResult(Result.Ok(Array.Empty<Award>()));
public Task<Result> CreateAwardAsync(Application.Services.Grants.CreateAwardRequest request, Guid userId, CancellationToken cancellationToken = default) =>
Task.FromResult(Result.Ok());
public Task<Result> UpdateAwardAsync(Application.Services.Grants.UpdateAwardRequest request, Guid userId, CancellationToken cancellationToken = default) =>
Task.FromResult(Result.Ok());
public Task<Result<Contract>> GetContractAsync(string contractIdHash, CancellationToken cancellationToken = default) =>
Task.FromResult(Result.Ok(new Contract { Id = 1 }));
public Task<Result<Contract[]>> GetContractsAsync(Pagination pagination, CancellationToken cancellationToken = default) =>
Task.FromResult(Result.Ok(Array.Empty<Contract>()));
public Task<Result> CreateContractAsync(Application.Services.Grants.CreateContractRequest request, Guid userId, CancellationToken cancellationToken = default) =>
Task.FromResult(Result.Ok());
public Task<Result> UpdateContractAsync(Application.Services.Grants.UpdateContractRequest request, CancellationToken cancellationToken = default) =>
Task.FromResult(Result.Ok());
public Task<Result> ExecuteContractAsync(Application.Services.Grants.ExecuteContractRequest request, Guid userId, CancellationToken cancellationToken = default) =>
Task.FromResult(Result.Ok());
public Task<Result<string>> GetContractUrlAsync(string contractIdHash, CancellationToken cancellationToken = default) =>
Task.FromResult(Result.Ok("https://test.cdn/contract.pdf"));
public Task<Result<Budget>> GetBudgetAsync(string budgetIdHash, CancellationToken cancellationToken = default) =>
Task.FromResult(Result.Ok(new Budget { Id = 1 }));
public Task<Result<Budget[]>> GetBudgetsAsync(Pagination pagination, CancellationToken cancellationToken = default) =>
Task.FromResult(Result.Ok(Array.Empty<Budget>()));
public Task<Result> CreateBudgetAsync(Application.Services.Grants.CreateBudgetRequest request, Guid userId, CancellationToken cancellationToken = default) =>
Task.FromResult(Result.Ok());
public Task<Result> UpdateBudgetAsync(Application.Services.Grants.UpdateBudgetRequest request, Guid userId, CancellationToken cancellationToken = default) =>
Task.FromResult(Result.Ok());
public Task<Result> UploadContractAsync(string contractIdHash, Guid userId, Microsoft.AspNetCore.Http.IFormFile file, CancellationToken cancellationToken = default) =>
Task.FromResult(Result.Ok());
}
private sealed class TestProjectService : IProjectService
{
public Task<Result> CreateActivityAsync(Application.Services.Projects.CreateActivityRequest request, Guid userId, CancellationToken cancellationToken = default) => Task.FromResult(Result.Ok());
public Task<Result> CreateBeneficiaryAsync(Application.Services.Projects.CreateBeneficiaryRequest request, Guid userId, CancellationToken cancellationToken = default) => Task.FromResult(Result.Ok());
public Task<Result> CreateChangeRequestAsync(Application.Services.Projects.CreateChangeRequest request, Guid userId, CancellationToken cancellationToken = default) => Task.FromResult(Result.Ok());
public Task<Result> CreateComplianceItemAsync(Application.Services.Projects.CreateComplianceItemRequest request, Guid userId, CancellationToken cancellationToken = default) => Task.FromResult(Result.Ok());
public Task<Result> CreateIssueAsync(Application.Services.Projects.CreateIssueRequest request, Guid userId, CancellationToken cancellationToken = default) => Task.FromResult(Result.Ok());
public Task<Result> CreateOutcomeAsync(Application.Services.Projects.CreateOutcomeRequest request, Guid userId, CancellationToken cancellationToken = default) => Task.FromResult(Result.Ok());
public Task<Result> CreateProgrammeAsync(Application.Services.Projects.CreateProgrammeRequest request, Guid userId, CancellationToken cancellationToken = default) => Task.FromResult(Result.Ok());
public Task<Result> CreateProjectAsync(Application.Services.Projects.CreateProjectRequest request, Guid userId, CancellationToken cancellationToken = default) => Task.FromResult(Result.Ok());
public Task<Result> CreateSiteVisitAsync(Application.Services.Projects.CreateSiteVisitRequest request, Guid userId, CancellationToken cancellationToken = default) => Task.FromResult(Result.Ok());
public Task<Result<Domain.Models.Activity[]>> GetActivitiesAsync(string projectIdHash, CancellationToken cancellationToken = default) => Task.FromResult(Result.Ok(Array.Empty<Domain.Models.Activity>()));
public Task<Result<Domain.Models.Activity>> GetActivityAsync(string activityIdHash, CancellationToken cancellationToken = default) => Task.FromResult(Result.Ok(new Domain.Models.Activity()));
public Task<Result<Beneficiary[]>> GetBeneficiariesAsync(string projectIdHash, CancellationToken cancellationToken = default) => Task.FromResult(Result.Ok(Array.Empty<Beneficiary>()));
public Task<Result<Beneficiary>> GetBeneficiaryAsync(string beneficiaryIdHash, CancellationToken cancellationToken = default) => Task.FromResult(Result.Ok(new Beneficiary()));
public Task<Result<ChangeRequest[]>> GetChangeRequestsAsync(string projectIdHash, CancellationToken cancellationToken = default) => Task.FromResult(Result.Ok(Array.Empty<ChangeRequest>()));
public Task<Result<ChangeRequest>> GetChangeRequestAsync(string changeRequestIdHash, CancellationToken cancellationToken = default) => Task.FromResult(Result.Ok(new ChangeRequest()));
public Task<Result<ComplianceItem[]>> GetComplianceItemsAsync(string projectIdHash, CancellationToken cancellationToken = default) => Task.FromResult(Result.Ok(Array.Empty<ComplianceItem>()));
public Task<Result<ComplianceItem>> GetComplianceItemAsync(string complianceItemIdHash, CancellationToken cancellationToken = default) => Task.FromResult(Result.Ok(new ComplianceItem()));
public Task<Result<Issue[]>> GetIssuesAsync(string projectIdHash, CancellationToken cancellationToken = default) => Task.FromResult(Result.Ok(Array.Empty<Issue>()));
public Task<Result<Issue>> GetIssueAsync(string issueIdHash, CancellationToken cancellationToken = default) => Task.FromResult(Result.Ok(new Issue()));
public Task<Result<Milestone>> GetMilestoneAsync(string milestoneIdHash, CancellationToken cancellationToken = default) => Task.FromResult(Result.Ok(new Milestone()));
public Task<Result<Milestone[]>> GetMilestonesAsync(Pagination pagination, CancellationToken cancellationToken = default) => Task.FromResult(Result.Ok(Array.Empty<Milestone>()));
public Task<Result<Outcome[]>> GetOutcomesAsync(string beneficiaryIdHash, CancellationToken cancellationToken = default) => Task.FromResult(Result.Ok(Array.Empty<Outcome>()));
public Task<Result<Outcome>> GetOutcomeAsync(string outcomeIdHash, CancellationToken cancellationToken = default) => Task.FromResult(Result.Ok(new Outcome()));
public Task<Result<Programme>> GetProgrammeAsync(string programmeIdHash, CancellationToken cancellationToken = default) => Task.FromResult(Result.Ok(new Programme()));
public Task<Result<Programme[]>> GetProgrammesAsync(Pagination pagination, CancellationToken cancellationToken = default) => Task.FromResult(Result.Ok(Array.Empty<Programme>()));
public Task<Result<Project[]>> GetProgrammeProjectsAsync(string programmeIdHash, CancellationToken cancellationToken = default) => Task.FromResult(Result.Ok(Array.Empty<Project>()));
public Task<Result<Project>> GetProjectAsync(string projectIdHash, CancellationToken cancellationToken = default) => Task.FromResult(Result.Ok(new Project()));
public Task<Result<Indicator[]>> GetProjectIndicatorsAsync(string projectIdHash, CancellationToken cancellationToken = default) => Task.FromResult(Result.Ok(Array.Empty<Indicator>()));
public Task<Result<Milestone[]>> GetProjectMilestonesAsync(string projectIdHash, CancellationToken cancellationToken = default) => Task.FromResult(Result.Ok(Array.Empty<Milestone>()));
public Task<Result<Outcome[]>> GetProjectOutcomesAsync(string projectIdHash, CancellationToken cancellationToken = default) => Task.FromResult(Result.Ok(Array.Empty<Outcome>()));
public Task<Result<Risk[]>> GetProjectRisksAsync(string projectIdHash, CancellationToken cancellationToken = default) => Task.FromResult(Result.Ok(Array.Empty<Risk>()));
public Task<Result<Project[]>> GetProjectsAsync(Pagination pagination, CancellationToken cancellationToken = default) => Task.FromResult(Result.Ok(Array.Empty<Project>()));
public Task<Result<SiteVisit[]>> GetSiteVisitsAsync(string projectIdHash, CancellationToken cancellationToken = default) => Task.FromResult(Result.Ok(Array.Empty<SiteVisit>()));
public Task<Result<SiteVisit>> GetSiteVisitAsync(string siteVisitIdHash, CancellationToken cancellationToken = default) => Task.FromResult(Result.Ok(new SiteVisit()));
public Task<Result> UpdateActivityAsync(Application.Services.Projects.UpdateActivityRequest request, Guid userId, CancellationToken cancellationToken = default) => Task.FromResult(Result.Ok());
public Task<Result> UpdateBeneficiaryAsync(Application.Services.Projects.UpdateBeneficiaryRequest request, Guid userId, CancellationToken cancellationToken = default) => Task.FromResult(Result.Ok());
public Task<Result> UpdateChangeRequestAsync(Application.Services.Projects.UpdateChangeRequest request, Guid userId, CancellationToken cancellationToken = default) => Task.FromResult(Result.Ok());
public Task<Result> UpdateComplianceItemAsync(Application.Services.Projects.UpdateComplianceItemRequest request, Guid userId, CancellationToken cancellationToken = default) => Task.FromResult(Result.Ok());
public Task<Result> UpdateIssueAsync(Application.Services.Projects.UpdateIssueRequest request, Guid userId, CancellationToken cancellationToken = default) => Task.FromResult(Result.Ok());
public Task<Result> UpdateMilestoneAsync(Application.Services.Projects.UpdateMilestoneRequest request, Guid userId, CancellationToken cancellationToken = default) => Task.FromResult(Result.Ok());
public Task<Result> UpdateOutcomeAsync(Application.Services.Projects.UpdateOutcomeRequest request, Guid userId, CancellationToken cancellationToken = default) => Task.FromResult(Result.Ok());
public Task<Result> UpdateProgrammeAsync(Application.Services.Projects.UpdateProgrammeRequest request, Guid userId, CancellationToken cancellationToken = default) => Task.FromResult(Result.Ok());
public Task<Result> UpdateProjectAsync(Application.Services.Projects.UpdateProjectRequest request, Guid userId, CancellationToken cancellationToken = default) => Task.FromResult(Result.Ok());
public Task<Result> UpdateProjectStatusAsync(Application.Services.Projects.UpdateProjectStatusRequest request, Guid userId, CancellationToken cancellationToken = default) => Task.FromResult(Result.Ok());
public Task<Result> UpdateSiteVisitAsync(Application.Services.Projects.UpdateSiteVisitRequest request, Guid userId, CancellationToken cancellationToken = default) => Task.FromResult(Result.Ok());
}
private sealed class TestExecutionService : IExecutionService
{
public Task<Result> ApproveDisbursementAsync(string disbursementIdHash, Guid userId, CancellationToken cancellationToken = default) => Task.FromResult(Result.Ok());
public Task<Result> CreateDisbursementAsync(Application.Services.Execution.CreateDisbursementRequest request, Guid userId, CancellationToken cancellationToken = default) => Task.FromResult(Result.Ok());
public Task<Result> CreateRiskAsync(Application.Services.Execution.CreateRiskRequest request, Guid userId, CancellationToken cancellationToken = default) => Task.FromResult(Result.Ok());
public Task<Result> CreateRuleAsync(Application.Services.Execution.CreateRuleRequest request, Guid userId, CancellationToken cancellationToken = default) => Task.FromResult(Result.Ok());
public Task<Result<Disbursement>> GetDisbursementAsync(string disbursementIdHash, CancellationToken cancellationToken = default) => Task.FromResult(Result.Ok(new Disbursement()));
public Task<Result<Disbursement[]>> GetDisbursementsAsync(Pagination pagination, CancellationToken cancellationToken = default) => Task.FromResult(Result.Ok(Array.Empty<Disbursement>()));
public Task<Result<Risk>> GetRiskAsync(string riskIdHash, CancellationToken cancellationToken = default) => Task.FromResult(Result.Ok(new Risk()));
public Task<Result<Risk[]>> GetRisksAsync(Pagination pagination, CancellationToken cancellationToken = default) => Task.FromResult(Result.Ok(Array.Empty<Risk>()));
public Task<Result<Rule>> GetRuleAsync(string ruleIdHash, CancellationToken cancellationToken = default) => Task.FromResult(Result.Ok(new Rule()));
public Task<Result<Rule[]>> GetRulesAsync(Pagination pagination, CancellationToken cancellationToken = default) => Task.FromResult(Result.Ok(Array.Empty<Rule>()));
public Task<Result> UpdateDisbursementAsync(Application.Services.Execution.UpdateDisbursementRequest request, Guid userId, CancellationToken cancellationToken = default) => Task.FromResult(Result.Ok());
public Task<Result> UpdateRiskAsync(Application.Services.Execution.UpdateRiskRequest request, CancellationToken cancellationToken = default) => Task.FromResult(Result.Ok());
public Task<Result> UpdateRuleAsync(Application.Services.Execution.UpdateRuleRequest request, Guid userId, CancellationToken cancellationToken = default) => Task.FromResult(Result.Ok());
}
private sealed class TestGovernanceService : IGovernanceService
{
public Task<Result> CreateInstrumentAsync(Application.Services.Governance.CreateInstrumentRequest request, Guid userId, CancellationToken cancellationToken = default) => Task.FromResult(Result.Ok());
public Task<Result> CreateOrganisationAsync(Application.Services.Governance.CreateOrganisationRequest request, Guid userId, CancellationToken cancellationToken = default) => Task.FromResult(Result.Ok());
public Task<Result> CreatePortfolioAsync(Application.Services.Governance.CreatePortfolioRequest request, Guid userId, CancellationToken cancellationToken = default) => Task.FromResult(Result.Ok());
public Task<Result> CreateTenantAsync(Application.Services.Governance.CreateTenantRequest request, Guid userId, CancellationToken cancellationToken = default) => Task.FromResult(Result.Ok());
public Task<Result> CreateTenantOrganisationAsync(string organisationIdHash, string tenantIdHash, Guid userId, CancellationToken cancellationToken = default) => Task.FromResult(Result.Ok());
public Task<Result<Dashboard>> GetDashboardAsync(CancellationToken cancellationToken = default) => Task.FromResult(Result.Ok(new Dashboard(0, 0, 0, 0, 0, 0)));
public Task<Result<Instrument[]>> GetInstrumentsAsync(string programmeIdHash, CancellationToken cancellationToken = default) => Task.FromResult(Result.Ok(Array.Empty<Instrument>()));
public Task<Result<Organisation>> GetOrganisationAsync(string organisationIdHash, CancellationToken cancellationToken = default) => Task.FromResult(Result.Ok(new Organisation()));
public Task<Result<Portfolio[]>> GetOrganisationPorfoliosAsync(string organisationIdHash, CancellationToken cancellationToken = default) => Task.FromResult(Result.Ok(Array.Empty<Portfolio>()));
public Task<Result<Organisation[]>> GetOrganisationsAsync(Pagination pagination, CancellationToken cancellationToken = default) => Task.FromResult(Result.Ok(Array.Empty<Organisation>()));
public Task<Result<Portfolio>> GetPortfolioAsync(string portfolioIdHash, CancellationToken cancellationToken = default) => Task.FromResult(Result.Ok(new Portfolio()));
public Task<Result<Programme[]>> GetPortfolioProgrammesAsync(string portfolioIdHash, CancellationToken cancellationToken = default) => Task.FromResult(Result.Ok(Array.Empty<Programme>()));
public Task<Result<decimal>> GetPorfolioValueAsync(string portfolioIdHash, CancellationToken cancellationToken = default) => Task.FromResult(Result.Ok(0m));
public Task<Result<Portfolio[]>> GetPorfoliosAsync(Pagination pagination, CancellationToken cancellationToken = default) => Task.FromResult(Result.Ok(Array.Empty<Portfolio>()));
public Task<Result<Programme[]>> GetProgrammesAsync(string portfolioIdHash, CancellationToken cancellationToken = default) => Task.FromResult(Result.Ok(Array.Empty<Programme>()));
public Task<Result<decimal>> GetProgrammeValueAsync(string programmeIdHash, CancellationToken cancellationToken = default) => Task.FromResult(Result.Ok(0m));
public Task<Result<Tenant>> GetTenantAsync(string tenantIdHash, CancellationToken cancellationToken = default) => Task.FromResult(Result.Ok(new Tenant()));
public Task<Result<TenantOrganisation>> GetTenantOrganisationAsync(string tenantOrganisationIdHash, CancellationToken cancellationToken = default) => Task.FromResult(Result.Ok(new TenantOrganisation()));
public Task<Result<TenantOrganisation[]>> GetTenantOrganisationsAsync(string tenantIdHash, CancellationToken cancellationToken = default) => Task.FromResult(Result.Ok(Array.Empty<TenantOrganisation>()));
public Task<Result<Tenant[]>> GetTenantsAsync(CancellationToken cancellationToken = default) => Task.FromResult(Result.Ok(Array.Empty<Tenant>()));
public Task<Result<TenantUser[]>> GetTenantUsersAsync(string tenantIdHash, CancellationToken cancellationToken = default) => Task.FromResult(Result.Ok(Array.Empty<TenantUser>()));
public Task<Result> UpdateInstrumentAsync(Application.Services.Governance.UpdateInstrumentRequest request, Guid userId, CancellationToken cancellationToken = default) => Task.FromResult(Result.Ok());
public Task<Result<Organisation>> UpdateOrganisationAsync(Application.Services.Governance.UpdateOrganisationRequest request, Guid userId, CancellationToken cancellationToken = default) => Task.FromResult(Result.Ok(new Organisation()));
public Task<Result> UpdatePortfolioAsync(Application.Services.Governance.UpdatePortfolioRequest request, Guid userId, CancellationToken cancellationToken = default) => Task.FromResult(Result.Ok());
public Task<Result> UpdateTenantAsync(Application.Services.Governance.UpdateTenantRequest request, Guid userId, CancellationToken cancellationToken = default) => Task.FromResult(Result.Ok());
public Task<Result> UpdateTenantOrganisationAsync(string tenantOrganisationIdHash, ActivityStatus status, Guid userId, CancellationToken cancellationToken = default) => Task.FromResult(Result.Ok());
public Task<Result> UpdateTenantUserAsync(Application.Services.Governance.UpdateTenantUserRequest request, Guid userId, CancellationToken cancellationToken = default) => Task.FromResult(Result.Ok());
}
private sealed class TestEvidenceService : IEvidenceService
{
public Task<Result> CreateEvidenceAsync(Guid userId, Microsoft.AspNetCore.Http.IFormCollection form, Microsoft.AspNetCore.Http.IFormFile file, CancellationToken cancellationToken = default) => Task.FromResult(Result.Ok());
public Task<Result> CreateIndicatorAsync(Application.Services.Evidence.CreateIndicatorRequest request, Guid userId, CancellationToken cancellationToken = default) => Task.FromResult(Result.Ok());
public Task<Result> CreateMilestoneAsync(Application.Services.Evidence.CreateMilestoneRequest request, Guid userId, CancellationToken cancellationToken = default) => Task.FromResult(Result.Ok());
public Task<Result<Domain.Models.Evidence>> GetEvidenceAsync(string evidenceId, CancellationToken cancellationToken = default) => Task.FromResult(Result.Ok(new Domain.Models.Evidence()));
public Task<Result<Domain.Models.Evidence[]>> GetEvidenceFilesAsync(Pagination pagination, CancellationToken cancellationToken = default) => Task.FromResult(Result.Ok(Array.Empty<Domain.Models.Evidence>()));
public Task<Result<string>> GetEvidenceUrlAsync(string evidenceIdHash, CancellationToken cancellationToken = default) => Task.FromResult(Result.Ok("https://test.cdn/evidence.pdf"));
public Task<Result<Indicator>> GetIndicatorAsync(string indicatorId, CancellationToken cancellationToken) => Task.FromResult(Result.Ok(new Indicator()));
public Task<Result<Indicator[]>> GetIndicatorsAsync(Pagination pagination, CancellationToken cancellationToken = default) => Task.FromResult(Result.Ok(Array.Empty<Indicator>()));
public Task<Result<Domain.Models.Evidence[]>> GetMilestoneEvidencesAsync(string milestoneIdHash, CancellationToken cancellationToken = default) => Task.FromResult(Result.Ok(Array.Empty<Domain.Models.Evidence>()));
public Task<Result<Domain.Models.Evidence[]>> GetProjectEvidencesAsync(string projectIdHash, CancellationToken cancellationToken = default) => Task.FromResult(Result.Ok(Array.Empty<Domain.Models.Evidence>()));
public Task<Result> RecordIndicatorActualAmountAsync(string indicatorIdHash, decimal amount, Guid userId, CancellationToken cancellationToken = default) => Task.FromResult(Result.Ok());
public Task<Result> RecordIndicatorActualAmountAsync(string indicatorIdHash, decimal amount, decimal? actualCost, Guid userId, CancellationToken cancellationToken = default) => Task.FromResult(Result.Ok());
public Task<Result> UpdateEvidenceAsync(Application.Services.Evidence.UpdateEvidenceRequest request, Guid userId, CancellationToken cancellationToken = default) => Task.FromResult(Result.Ok());
}
private sealed class TestCommunicationsService : ICommunicationsService
{
public Task<Result> CreateNotificationAsync(Application.Services.Communications.CreateNotificationRequest request, Guid userId, CancellationToken cancellationToken = default) => Task.FromResult(Result.Ok());
public Task<Result<ApplicantData[]>> GetApplicantDataAsync(Guid applicantUserId, CancellationToken cancellationToken = default) => Task.FromResult(Result.Ok(Array.Empty<ApplicantData>()));
public Task<Result<Invite>> GetApplicantInviteAsync(Guid applicantUserId, CancellationToken cancellationToken = default) => Task.FromResult(Result.Ok(new Invite()));
public Task<Result<Invite[]>> GetApplicantInvitesAsync(Guid applicantUserId, CancellationToken cancellationToken = default) => Task.FromResult(Result.Ok(Array.Empty<Invite>()));
public Task<Result<Invite[]>> GetInvitesAsync(Pagination pagination, CancellationToken cancellationToken = default) => Task.FromResult(Result.Ok(Array.Empty<Invite>()));
public Task<Result<Notification>> GetNotificationAsync(string notificationIdHash, CancellationToken cancellationToken = default) => Task.FromResult(Result.Ok(new Notification()));
public Task<Result<Notification[]>> GetNotificationsAsync(string organisationIdHash, CancellationToken cancellationToken = default) => Task.FromResult(Result.Ok(Array.Empty<Notification>()));
public Task<Result<Notification[]>> GetUserNotificationsAsync(Guid userId, CancellationToken cancellationToken = default) => Task.FromResult(Result.Ok(Array.Empty<Notification>()));
public Task<Result<Invite>> SendInviteAsync(Application.Services.Communications.InviteRequest request, Guid userId, CancellationToken cancellationToken = default) => Task.FromResult(Result.Ok(new Invite()));
public Task<Result> UpdateNotificationAsync(Application.Services.Communications.UpdateNotificationRequest request, CancellationToken cancellationToken = default) => Task.FromResult(Result.Ok());
}
private sealed class TestUserService : IUserService
{
public Task<Result> AddUserClaimAsync(AddUserClaimRequest request, Guid userId, CancellationToken cancellationToken = default) => Task.FromResult(Result.Ok());
public Task<Result> AddUserRoleAsync(Guid targetUserId, Guid roleId, Guid userId, CancellationToken cancellationToken = default) => Task.FromResult(Result.Ok());
public Task<Result> CreateUserAsync(string tenantIdHash, string idNumberHash, CreateUserRequest request, Guid userId, CancellationToken cancellationToken = default) => Task.FromResult(Result.Ok());
public Task<Result> DeleteUserClaimAsync(string claimIdHash, Guid targetUserId, Guid userId, CancellationToken cancellationToken = default) => Task.FromResult(Result.Ok());
public Task<Result> DeleteUserRoleAsync(Guid targetUserId, Guid roleId, Guid userId, CancellationToken cancellationToken = default) => Task.FromResult(Result.Ok());
public Task<Result<Domain.Models.Security.Claim[]>> GetRoleClaimsAsync(Guid roleId, Guid userId, CancellationToken cancellationToken = default) => Task.FromResult(Result.Ok(Array.Empty<Domain.Models.Security.Claim>()));
public Task<Result<Role[]>> GetSystemRolesAsync(Guid userId, CancellationToken cancellationToken = default) => Task.FromResult(Result.Ok(Array.Empty<Role>()));
public Task<Result<User>> GetUserAsync(Guid userId, CancellationToken cancellationToken = default) => Task.FromResult(Result.Ok(new User { Id = userId }));
public Task<Result<Domain.Models.Security.Claim[]>> GetUserClaimsAsync(Guid targetUserId, Guid userId, CancellationToken cancellationToken = default) => Task.FromResult(Result.Ok(Array.Empty<Domain.Models.Security.Claim>()));
public Task<Result<Role[]>> GetUserRolesAsync(Guid targetUserId, Guid userId, CancellationToken cancellationToken = default) => Task.FromResult(Result.Ok(Array.Empty<Role>()));
public Task<Result<User[]>> GetUsersAsync(Pagination pagination, Application.Services.Users.UserFilterRequest request, CancellationToken cancellationToken = default) => Task.FromResult(Result.Ok(Array.Empty<User>()));
public Task<Result> LogoutUserAsync(Guid targetUserId, Guid userId, CancellationToken cancellationToken = default) => Task.FromResult(Result.Ok());
public Task<Result> SetUserLockoutAsync(Guid targetUserId, Guid userId, bool disable, CancellationToken cancellationToken = default) => Task.FromResult(Result.Ok());
public Task<Result> SyncUserAsync(Guid userId, string email, string role, CancellationToken cancellationToken = default) => Task.FromResult(Result.Ok());
public Task<Result> UpdateUserStatusAsync(Guid userId, ActivityStatus status, CancellationToken cancellationToken = default) => Task.FromResult(Result.Ok());
}
#endregion
}