using Grpc.Core; using Mishima.Nexus.Wire.V1; namespace Robotico.Nexus.Client.Tests.TestSupport; /// /// In-memory fake standing in for a real Nexus graph engine host, exercising the exact wire /// contract / NexusGraphSession /// depend on. Ping honors database-name sentinels so tests /// can drive both the happy path and the failure branches of the client. /// public sealed class FakeGraphEngineService : NexusGraphEngineService.NexusGraphEngineServiceBase { private readonly Dictionary> _vertexLabels = []; private readonly HashSet _registeredLabels = []; private readonly HashSet _registeredEdgeTypes = []; private readonly HashSet<(ulong Source, ulong Target, string EdgeType)> _edges = []; private ulong _txn; /// public override Task 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" }); } /// public override Task RegisterLabel(GraphRegisterLabelRequest request, ServerCallContext context) { _registeredLabels.Add(request.LabelName); return Task.FromResult(new GraphRegisterLabelResponse { Status = NexusStatuses.Ok }); } /// public override Task RegisterEdgeType(GraphRegisterEdgeTypeRequest request, ServerCallContext context) { _registeredEdgeTypes.Add(request.EdgeTypeName); return Task.FromResult(new GraphRegisterEdgeTypeResponse { Status = NexusStatuses.Ok }); } /// public override Task UpsertVertex(GraphUpsertVertexRequest request, ServerCallContext context) { _vertexLabels[request.VertexId] = [.. request.LabelNames]; return Task.FromResult(new GraphUpsertVertexResponse { Status = NexusStatuses.Ok, CommittedTxnId = ++_txn }); } /// public override Task DeleteVertex(GraphDeleteVertexRequest request, ServerCallContext context) { _vertexLabels.Remove(request.VertexId); return Task.FromResult(new GraphDeleteVertexResponse { Status = NexusStatuses.Ok, CommittedTxnId = ++_txn }); } /// public override Task GetVertex(GraphGetVertexRequest request, ServerCallContext context) { if (!_vertexLabels.TryGetValue(request.VertexId, out List? 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); } /// public override Task UpsertEdge(GraphUpsertEdgeRequest request, ServerCallContext context) { _edges.Add((request.SourceVertexId, request.TargetVertexId, request.EdgeTypeName)); return Task.FromResult(new GraphUpsertEdgeResponse { Status = NexusStatuses.Ok, CommittedTxnId = ++_txn }); } /// public override Task DeleteEdge(GraphDeleteEdgeRequest request, ServerCallContext context) { _edges.Remove((request.SourceVertexId, request.TargetVertexId, request.EdgeTypeName)); return Task.FromResult(new GraphDeleteEdgeResponse { Status = NexusStatuses.Ok, CommittedTxnId = ++_txn }); } /// public override Task 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, }); } /// public override Task 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); } /// public override Task ListVerticesByLabel(GraphListVerticesByLabelRequest request, ServerCallContext context) { List 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); } }