using Robotico.Nexus.Client.Document; using Robotico.Nexus.Client.Relational; using Robotico.Nexus.Client.Tests.TestSupport; using Robotico.Result.Errors; using Xunit; namespace Robotico.Nexus.Client.Tests; public class NexusClientTests : IAsyncLifetime { private NexusTestHost _host = null!; public async Task InitializeAsync() => _host = await NexusTestHost.StartAsync(); public async Task DisposeAsync() => await _host.DisposeAsync(); [Fact] public void Connect_Fails_ForInvalidUri() { Robotico.Result.Result result = NexusClient.Connect("not-a-uri"); Assert.True(result.IsError(out IError? error)); Assert.Equal(NexusErrors.UriInvalid, error!.Code); } [Fact] public void Connect_Succeeds_AndBindsDefaultDatabase_WhenPathOmitted() { Robotico.Result.Result result = NexusClient.Connect(_host.ConnectionUri(NexusUriSchemes.Graph)); Assert.True(result.IsSuccess(out NexusClient? client, out _)); using NexusClient c = client!; Assert.Equal(NexusUriSchemes.Graph, c.Scheme); Assert.Equal("pathways", c.Database); } [Fact] public void Connect_Succeeds_AndBindsExplicitDatabase() { Robotico.Result.Result result = NexusClient.Connect(_host.ConnectionUri(NexusUriSchemes.Time, "custom-db")); Assert.True(result.IsSuccess(out NexusClient? client, out _)); using NexusClient c = client!; Assert.Equal("custom-db", c.Database); } [Fact] public void CheckHealth_Succeeds_WhenServerReady() { using NexusClient client = Connect(NexusUriSchemes.Graph); _host.Health.Ready = true; Robotico.Result.Result result = client.CheckHealth(); Assert.True(result.IsSuccess()); } [Fact] public void CheckHealth_Fails_WhenServerNotReady() { using NexusClient client = Connect(NexusUriSchemes.Graph); _host.Health.Ready = false; Robotico.Result.Result result = client.CheckHealth(); Assert.True(result.IsError(out IError? error)); Assert.Equal(NexusErrors.Unavailable, error!.Code); } [Fact] public void CheckHealth_Fails_AfterDispose() { NexusClient client = Connect(NexusUriSchemes.Graph); client.Dispose(); Robotico.Result.Result result = client.CheckHealth(); Assert.True(result.IsError(out IError? error)); Assert.Equal(NexusErrors.Disposed, error!.Code); } [Fact] public void Dispose_IsIdempotent() { NexusClient client = Connect(NexusUriSchemes.Graph); client.Dispose(); client.Dispose(); } [Fact] public async Task DisposeAsync_IsIdempotent_AndAfterSyncDispose() { NexusClient client = Connect(NexusUriSchemes.Graph); await client.DisposeAsync(); await client.DisposeAsync(); NexusClient client2 = Connect(NexusUriSchemes.Time); client2.Dispose(); await client2.DisposeAsync(); } [Fact] public void CreateDocumentClient_Succeeds_WhenNotDisposed() { using NexusClient client = Connect(NexusUriSchemes.Document); Robotico.Result.Result result = client.CreateDocumentClient(); Assert.True(result.IsSuccess(out NexusDocumentClient? documentClient, out _)); Assert.NotNull(documentClient); } [Fact] public void CreateDocumentClient_Fails_AfterDispose() { NexusClient client = Connect(NexusUriSchemes.Document); client.Dispose(); Robotico.Result.Result result = client.CreateDocumentClient(); Assert.True(result.IsError(out IError? error)); Assert.Equal(NexusErrors.Disposed, error!.Code); } [Fact] public void CreateRelationalClient_Succeeds_WhenNotDisposed() { using NexusClient client = Connect(NexusUriSchemes.Relational); Robotico.Result.Result result = client.CreateRelationalClient(); Assert.True(result.IsSuccess(out NexusRelationalClient? relationalClient, out _)); Assert.NotNull(relationalClient); } [Fact] public void CreateRelationalClient_Fails_AfterDispose() { NexusClient client = Connect(NexusUriSchemes.Relational); client.Dispose(); Robotico.Result.Result result = client.CreateRelationalClient(); Assert.True(result.IsError(out IError? error)); Assert.Equal(NexusErrors.Disposed, error!.Code); } [Fact] public void CreateDocumentWireClient_Succeeds_WhenNotDisposed() { using NexusClient client = Connect(NexusUriSchemes.Document); Robotico.Result.Result result = client.CreateDocumentWireClient(); Assert.True(result.IsSuccess()); } [Fact] public void CreateDocumentWireClient_Fails_AfterDispose() { NexusClient client = Connect(NexusUriSchemes.Document); client.Dispose(); Robotico.Result.Result result = client.CreateDocumentWireClient(); Assert.True(result.IsError(out IError? error)); Assert.Equal(NexusErrors.Disposed, error!.Code); } [Fact] public void CreateRelationalWireClient_Succeeds_WhenNotDisposed() { using NexusClient client = Connect(NexusUriSchemes.Relational); Robotico.Result.Result result = client.CreateRelationalWireClient(); Assert.True(result.IsSuccess()); } [Fact] public void CreateRelationalWireClient_Fails_AfterDispose() { NexusClient client = Connect(NexusUriSchemes.Relational); client.Dispose(); Robotico.Result.Result result = client.CreateRelationalWireClient(); Assert.True(result.IsError(out IError? error)); Assert.Equal(NexusErrors.Disposed, error!.Code); } [Fact] public void BuildCallMetadata_UsesBoundDatabase_WhenNoOverride() { using NexusClient client = Connect(NexusUriSchemes.Graph, "boundDb"); Grpc.Core.Metadata metadata = client.BuildCallMetadata(); Assert.Equal("boundDb", metadata.GetValue(NexusCallMetadata.DatabaseKey)); } [Fact] public void BuildCallMetadata_UsesOverride_WhenProvided() { using NexusClient client = Connect(NexusUriSchemes.Graph, "boundDb"); Grpc.Core.Metadata metadata = client.BuildCallMetadata("overrideDb"); Assert.Equal("overrideDb", metadata.GetValue(NexusCallMetadata.DatabaseKey)); } private NexusClient Connect(string scheme, string? database = null) { Robotico.Result.Result result = NexusClient.Connect(_host.ConnectionUri(scheme, database)); Assert.True(result.IsSuccess(out NexusClient? client, out _)); return client!; } }