mishima-nexus-client-csharp/tests/Robotico.Nexus.Client.Tests/TestSupport/FakeGraphEngineService.cs

138 lines
5.9 KiB
C#

using Grpc.Core;
using Mishima.Nexus.Wire.V1;
namespace Robotico.Nexus.Client.Tests.TestSupport;
/// <summary>
/// In-memory fake standing in for a real Nexus graph engine host, exercising the exact wire
/// contract <see cref="Robotico.Nexus.Client.Graph.NexusGraphStore"/> / <c>NexusGraphSession</c>
/// depend on. <c>Ping</c> honors <see cref="FakeNexusTriggers"/> database-name sentinels so tests
/// can drive both the happy path and the failure branches of the client.
/// </summary>
public sealed class FakeGraphEngineService : NexusGraphEngineService.NexusGraphEngineServiceBase
{
private readonly Dictionary<ulong, List<string>> _vertexLabels = [];
private readonly HashSet<string> _registeredLabels = [];
private readonly HashSet<string> _registeredEdgeTypes = [];
private readonly HashSet<(ulong Source, ulong Target, string EdgeType)> _edges = [];
private ulong _txn;
/// <inheritdoc />
public override Task<GraphPingResponse> Ping(GraphPingRequest request, ServerCallContext context)
{
NexusStatus? failure = FakeNexusTriggers.TryStatusFailure(context);
if (failure is not null)
{
return Task.FromResult(new GraphPingResponse { Status = failure, EngineVersion = "fake" });
}
return Task.FromResult(new GraphPingResponse { Status = NexusStatuses.Ok, EngineVersion = "fake" });
}
/// <inheritdoc />
public override Task<GraphRegisterLabelResponse> RegisterLabel(GraphRegisterLabelRequest request, ServerCallContext context)
{
_registeredLabels.Add(request.LabelName);
return Task.FromResult(new GraphRegisterLabelResponse { Status = NexusStatuses.Ok });
}
/// <inheritdoc />
public override Task<GraphRegisterEdgeTypeResponse> RegisterEdgeType(GraphRegisterEdgeTypeRequest request, ServerCallContext context)
{
_registeredEdgeTypes.Add(request.EdgeTypeName);
return Task.FromResult(new GraphRegisterEdgeTypeResponse { Status = NexusStatuses.Ok });
}
/// <inheritdoc />
public override Task<GraphUpsertVertexResponse> UpsertVertex(GraphUpsertVertexRequest request, ServerCallContext context)
{
_vertexLabels[request.VertexId] = [.. request.LabelNames];
return Task.FromResult(new GraphUpsertVertexResponse { Status = NexusStatuses.Ok, CommittedTxnId = ++_txn });
}
/// <inheritdoc />
public override Task<GraphDeleteVertexResponse> DeleteVertex(GraphDeleteVertexRequest request, ServerCallContext context)
{
_vertexLabels.Remove(request.VertexId);
return Task.FromResult(new GraphDeleteVertexResponse { Status = NexusStatuses.Ok, CommittedTxnId = ++_txn });
}
/// <inheritdoc />
public override Task<GraphGetVertexResponse> GetVertex(GraphGetVertexRequest request, ServerCallContext context)
{
if (!_vertexLabels.TryGetValue(request.VertexId, out List<string>? labels))
{
return Task.FromResult(new GraphGetVertexResponse { Status = NexusStatuses.NotFound($"Vertex {request.VertexId} not found.") });
}
GraphGetVertexResponse response = new() { Status = NexusStatuses.Ok, VertexId = request.VertexId };
response.LabelNames.AddRange(labels);
return Task.FromResult(response);
}
/// <inheritdoc />
public override Task<GraphUpsertEdgeResponse> UpsertEdge(GraphUpsertEdgeRequest request, ServerCallContext context)
{
_edges.Add((request.SourceVertexId, request.TargetVertexId, request.EdgeTypeName));
return Task.FromResult(new GraphUpsertEdgeResponse { Status = NexusStatuses.Ok, CommittedTxnId = ++_txn });
}
/// <inheritdoc />
public override Task<GraphDeleteEdgeResponse> DeleteEdge(GraphDeleteEdgeRequest request, ServerCallContext context)
{
_edges.Remove((request.SourceVertexId, request.TargetVertexId, request.EdgeTypeName));
return Task.FromResult(new GraphDeleteEdgeResponse { Status = NexusStatuses.Ok, CommittedTxnId = ++_txn });
}
/// <inheritdoc />
public override Task<GraphGetEdgeResponse> GetEdge(GraphGetEdgeRequest request, ServerCallContext context)
{
(ulong, ulong, string) key = (request.SourceVertexId, request.TargetVertexId, request.EdgeTypeName);
if (!_edges.Contains(key))
{
return Task.FromResult(new GraphGetEdgeResponse { Status = NexusStatuses.NotFound("Edge not found.") });
}
return Task.FromResult(new GraphGetEdgeResponse
{
Status = NexusStatuses.Ok,
SourceVertexId = request.SourceVertexId,
TargetVertexId = request.TargetVertexId,
EdgeTypeName = request.EdgeTypeName,
});
}
/// <inheritdoc />
public override Task<GraphBreadthFirstResponse> BreadthFirst(GraphBreadthFirstRequest request, ServerCallContext context)
{
GraphBreadthFirstResponse response = new() { Status = NexusStatuses.Ok };
response.VisitOrder.Add(request.StartVertexId);
response.DistanceByVertex.Add(request.StartVertexId, 0);
return Task.FromResult(response);
}
/// <inheritdoc />
public override Task<GraphListVerticesByLabelResponse> ListVerticesByLabel(GraphListVerticesByLabelRequest request, ServerCallContext context)
{
List<GraphVertexWire> matches = [.. _vertexLabels
.Where(pair => pair.Value.Contains(request.LabelName))
.Select(pair =>
{
GraphVertexWire wire = new() { VertexId = pair.Key };
wire.LabelNames.AddRange(pair.Value);
return wire;
})];
GraphListVerticesByLabelResponse response = new()
{
Status = NexusStatuses.Ok,
LabelName = request.LabelName,
TotalMatching = matches.Count,
Skip = request.Skip,
Limit = request.Limit,
};
response.Vertices.AddRange(matches.Skip(request.Skip).Take(request.Limit is 0 ? matches.Count : request.Limit));
return Task.FromResult(response);
}
}