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
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
using BenchmarkDotNet.Attributes;
using ModularityKit.Mutator.Governance.Abstractions.Requests.Model;
using ModularityKit.Mutator.Benchmarks.Governance.Approval.Support;

namespace ModularityKit.Mutator.Benchmarks.Governance.Approval;

/// <summary>
/// Benchmarks approval request creation and assignment overhead.
/// </summary>
[BenchmarkCategory("Governance")]
[MemoryDiagnoser]
[InProcess]
public class ApprovalRequestCreationBenchmarks : ApprovalWorkflowBenchmarkBase
{
/// <summary>
/// Measures approval request creation and single approval assignment.
/// </summary>
[Benchmark(Baseline = true)]
public MutationRequest PendingApproval_SingleRequest()
=> ApprovalWorkflowBenchmarkSupport.CreatePendingApprovalRequest(RequestId);

/// <summary>
/// Measures approval request creation with two approval steps.
/// </summary>
[Benchmark]
public MutationRequest PendingApproval_TwoStepRequest()
=> ApprovalWorkflowBenchmarkSupport.CreateTwoStepApprovalRequest(RequestId);

/// <summary>
/// Measures approval request creation driven by role metadata.
/// </summary>
[Benchmark]
public MutationRequest PendingApproval_RoleBasedRequest()
=> ApprovalWorkflowBenchmarkSupport.CreateRoleBasedApprovalRequest(RequestId);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
using BenchmarkDotNet.Attributes;
using ModularityKit.Mutator.Abstractions.Context;
using ModularityKit.Mutator.Governance.Abstractions.Approval.Model;
using ModularityKit.Mutator.Governance.Abstractions.Requests.Model;
using ModularityKit.Mutator.Benchmarks.Governance.Approval.Support;

namespace ModularityKit.Mutator.Benchmarks.Governance.Approval;

/// <summary>
/// Benchmarks approval decision paths in the governance runtime.
/// </summary>
[BenchmarkCategory("Governance")]
[MemoryDiagnoser]
[InProcess]
public class ApprovalWorkflowDecisionBenchmarks : ApprovalWorkflowBenchmarkBase
{
/// <summary>
/// Measures single approval requirement being approved and the request being finalized.
/// </summary>
[Benchmark(Baseline = true)]
public async Task<MutationRequest> ApproveRequirement_Granted()
{
var (manager, request) = CreateSingleApprovalWorkflow();
var approvalId = request.ApprovalRequirements[0].ApprovalId;

return await manager.ApproveRequirement(
request.RequestId,
approvalId,
ApprovalWorkflowBenchmarkSupport.CreateDecisionContext("alice", "Approve governance benchmark request"))
.ConfigureAwait(false);
}

/// <summary>
/// Measures the first approval in two step approval workflow before the request can finalize.
/// </summary>
[Benchmark]
public async Task<MutationRequest> ApproveRequirement_FirstStepPending()
{
var (manager, request) = CreateTwoStepApprovalWorkflow();
var approvalId = request.ApprovalRequirements[0].ApprovalId;

return await manager.ApproveRequirement(
request.RequestId,
approvalId,
ApprovalWorkflowBenchmarkSupport.CreateDecisionContext("alice", "Approve first step in benchmark workflow"))
.ConfigureAwait(false);
}

/// <summary>
/// Measures the second approval in two step approval workflow that finalizes the request.
/// </summary>
[Benchmark]
public async Task<MutationRequest> ApproveRequirement_FinalizeAfterSecondStep()
{
var (manager, request) = CreateTwoStepApprovalWorkflow();
var firstApprovalId = request.ApprovalRequirements[0].ApprovalId;
var secondApprovalId = request.ApprovalRequirements[1].ApprovalId;

var firstApproved = await manager.ApproveRequirement(
request.RequestId,
firstApprovalId,
ApprovalWorkflowBenchmarkSupport.CreateDecisionContext("alice", "Approve first benchmark step"))
.ConfigureAwait(false);

return await manager.ApproveRequirement(
firstApproved.RequestId,
secondApprovalId,
ApprovalWorkflowBenchmarkSupport.CreateDecisionContext("bob", "Approve second benchmark step"))
.ConfigureAwait(false);
}

/// <summary>
/// Measures single approval requirement being rejected and the request being terminated.
/// </summary>
[Benchmark]
public async Task<MutationRequest> RejectRequirement_Rejected()
{
var (manager, request) = CreateSingleApprovalWorkflow();
var approvalId = request.ApprovalRequirements[0].ApprovalId;

return await manager.RejectRequirement(
request.RequestId,
approvalId,
ApprovalWorkflowBenchmarkSupport.CreateDecisionContext("alice", "Reject governance benchmark request"),
rejection: ApprovalWorkflowBenchmarkSupport.CreateRejectionReason())
.ConfigureAwait(false);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
using BenchmarkDotNet.Attributes;
using ModularityKit.Mutator.Abstractions.Context;
using ModularityKit.Mutator.Governance.Abstractions.Requests.Model;
using ModularityKit.Mutator.Benchmarks.Governance.Approval.Support;

namespace ModularityKit.Mutator.Benchmarks.Governance.Approval;

/// <summary>
/// Benchmarks expiration handling for pending governance approvals.
/// </summary>
[BenchmarkCategory("Governance")]
[MemoryDiagnoser]
[InProcess]
public class ApprovalWorkflowExpirationBenchmarks : ApprovalWorkflowBenchmarkBase
{
/// <summary>
/// Measures expiration of pending approval request and the resulting rejection bookkeeping.
/// </summary>
[Benchmark(Baseline = true)]
public async Task<IReadOnlyList<MutationRequest>> ExpirePendingApprovals_Sweep()
{
var (manager, _) = CreateExpiredWorkflow();

return await manager.ExpirePendingApprovals(
DateTimeOffset.UtcNow,
MutationContext.Service("approval-sweeper", "Expire pending governance approvals"))
.ConfigureAwait(false);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
using ModularityKit.Mutator.Governance.Abstractions.Approval.Contracts;
using ModularityKit.Mutator.Governance.Abstractions.Requests.Model;
using ModularityKit.Mutator.Governance.Runtime.Approval.Execution;
using ModularityKit.Mutator.Governance.Runtime.Storage;

namespace ModularityKit.Mutator.Benchmarks.Governance.Approval.Support;

/// <summary>
/// Shared setup helpers for governance approval workflow benchmarks.
/// </summary>
public abstract class ApprovalWorkflowBenchmarkBase
{
protected const string RequestId = "governance-approval-request";

protected static (IMutationRequestApprovalWorkflowManager Manager, MutationRequest Request) CreateSingleApprovalWorkflow()
{
var store = new InMemoryMutationRequestStore();
var manager = new MutationRequestApprovalWorkflowManager(store);
var request = store.Create(ApprovalWorkflowBenchmarkSupport.CreatePendingApprovalRequest(RequestId))
.GetAwaiter()
.GetResult();

return (manager, request);
}

protected static (IMutationRequestApprovalWorkflowManager Manager, MutationRequest Request) CreateTwoStepApprovalWorkflow()
{
var store = new InMemoryMutationRequestStore();
var manager = new MutationRequestApprovalWorkflowManager(store);
var request = store.Create(ApprovalWorkflowBenchmarkSupport.CreateTwoStepApprovalRequest(RequestId))
.GetAwaiter()
.GetResult();

return (manager, request);
}

protected static (IMutationRequestApprovalWorkflowManager Manager, MutationRequest Request) CreateExpiredWorkflow()
{
var store = new InMemoryMutationRequestStore();
var manager = new MutationRequestApprovalWorkflowManager(store);
var request = store.Create(ApprovalWorkflowBenchmarkSupport.CreateExpiredApprovalRequest(RequestId))
.GetAwaiter()
.GetResult();

return (manager, request);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
using ModularityKit.Mutator.Abstractions.Context;
using ModularityKit.Mutator.Abstractions.Intent;
using ModularityKit.Mutator.Abstractions.Policies;
using ModularityKit.Mutator.Governance.Abstractions.Approval.Model;
using ModularityKit.Mutator.Governance.Abstractions.Requests.Factory;
using ModularityKit.Mutator.Governance.Abstractions.Requests.Model;

namespace ModularityKit.Mutator.Benchmarks.Governance.Approval.Support;

/// <summary>
/// Builds repeatable approval workflow benchmark fixtures.
/// </summary>
internal static class ApprovalWorkflowBenchmarkSupport
{
/// <summary>
/// Creates a pending approval request with one approval requirement.
/// </summary>
public static MutationRequest CreatePendingApprovalRequest(
string requestId,
DateTimeOffset? expiresAt = null)
=> MutationRequestFactory.PendingApproval(
stateId: "governance-benchmark:approval",
stateType: "GovernanceState",
mutationType: "ApproveGovernanceMutation",
intent: CreateIntent(),
context: MutationContext.User("requester", "Requester", "Need approval"),
requirements:
[
PolicyRequirement.Approval("alice", "Manager approval")
],
expectedStateVersion: "v10",
expiresAt: expiresAt)
with
{
RequestId = requestId
};

/// <summary>
/// Creates a pending approval request with two sequential approval requirements.
/// </summary>
public static MutationRequest CreateTwoStepApprovalRequest(string requestId)
=> MutationRequestFactory.PendingApproval(
stateId: "governance-benchmark:approval",
stateType: "GovernanceState",
mutationType: "ApproveGovernanceMutation",
intent: CreateIntent(),
context: MutationContext.User("requester", "Requester", "Need approval"),
requirements:
[
PolicyRequirement.Approval("alice", "Manager approval"),
PolicyRequirement.Approval("bob", "Security approval")
],
expectedStateVersion: "v10")
with
{
RequestId = requestId
};

/// <summary>
/// Creates a request that already has an expired approval requirement.
/// </summary>
public static MutationRequest CreateExpiredApprovalRequest(string requestId)
=> CreatePendingApprovalRequest(
requestId,
DateTimeOffset.UtcNow.AddMinutes(-5));

/// <summary>
/// Creates a pending approval request driven by role metadata.
/// </summary>
public static MutationRequest CreateRoleBasedApprovalRequest(string requestId)
=> MutationRequestFactory.PendingApproval(
stateId: "governance-benchmark:approval-role",
stateType: "GovernanceState",
mutationType: "ApproveGovernanceMutation",
intent: CreateIntent(),
context: MutationContext.User("requester", "Requester", "Need role-based approval"),
requirements:
[
new PolicyRequirement
{
Type = "Approval",
Description = "Role approval",
Data = new
{
ApproverRole = "security-approver",
StepOrder = 1,
Reason = "Role-based sign-off"
}
}
],
expectedStateVersion: "v10")
with
{
RequestId = requestId
};

/// <summary>
/// Creates the intent used by approval workflow benchmark scenarios.
/// </summary>
public static MutationIntent CreateIntent()
=> new()
{
OperationName = "ApproveGovernanceChange",
Category = "Governance",
Description = "Approve governance request in benchmark workflow"
};

/// <summary>
/// Creates a user context used for benchmark approval decisions.
/// </summary>
public static MutationContext CreateDecisionContext(string actorId, string reason)
=> MutationContext.User(actorId, actorId, reason);

/// <summary>
/// Creates structured rejection payload for benchmark scenarios.
/// </summary>
public static MutationApprovalRejectionReason CreateRejectionReason()
=> new()
{
Code = "benchmark-rejection",
Category = "policy",
Message = "Benchmark rejection path"
};

/// <summary>
/// Creates a mutation context that carries approval roles for role-based resolution.
/// </summary>
public static MutationContext CreateRoleDecisionContext(
string actorId,
string actorName,
string reason,
params string[] roles)
=> MutationContext.User(actorId, actorName, reason) with
{
Metadata = new Dictionary<string, object>
{
["ActorRoles"] = roles
}
};
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
using BenchmarkDotNet.Attributes;
using ModularityKit.Mutator.Governance.Abstractions.Requests.Model;
using ModularityKit.Mutator.Benchmarks.Governance.Lifecycle.Support;

namespace ModularityKit.Mutator.Benchmarks.Governance.Lifecycle;

/// <summary>
/// Benchmarks expiration sweeps for governed requests.
/// </summary>
[BenchmarkCategory("Governance")]
[MemoryDiagnoser]
[InProcess]
public class RequestLifecycleExpirationBenchmarks : RequestLifecycleBenchmarkBase
{
/// <summary>
/// Measures expiration of due requests in the pending lifecycle.
/// </summary>
[Benchmark(Baseline = true)]
public async Task<IReadOnlyList<MutationRequest>> ExpireDueRequests_Sweep()
{
var manager = CreateExpirationWorkflow();

return await manager.ExpireDueRequests(
DateTimeOffset.UtcNow,
RequestLifecycleBenchmarkSupport.CreateSweepContext())
.ConfigureAwait(false);
}
}
Loading
Loading