namespace AVEVA.Historian.Client.Tests; public sealed class HistorianClientIntegrationTests { [Fact] public async Task ProbeAsync_ReturnsTrueForConfiguredHistorian() { string? host = Environment.GetEnvironmentVariable("HISTORIAN_HOST"); if (string.IsNullOrWhiteSpace(host)) { return; } int port = int.TryParse(Environment.GetEnvironmentVariable("HISTORIAN_PORT"), out int parsedPort) ? parsedPort : HistorianClientOptions.DefaultPort; HistorianClient client = new(new HistorianClientOptions { Host = host, Port = port }); Assert.True(await client.ProbeAsync(CancellationToken.None)); } [Fact] public async Task BrowseTagNamesAsync_ReturnsConfiguredTestTag() { string? host = Environment.GetEnvironmentVariable("HISTORIAN_HOST"); string? testTag = Environment.GetEnvironmentVariable("HISTORIAN_TEST_TAG"); string? filter = Environment.GetEnvironmentVariable("HISTORIAN_TAG_FILTER") ?? testTag; if (string.IsNullOrWhiteSpace(host) || string.IsNullOrWhiteSpace(testTag) || string.IsNullOrWhiteSpace(filter)) { return; } int port = int.TryParse(Environment.GetEnvironmentVariable("HISTORIAN_PORT"), out int parsedPort) ? parsedPort : HistorianClientOptions.DefaultPort; HistorianClient client = new(new HistorianClientOptions { Host = host, Port = port, IntegratedSecurity = true, UserName = Environment.GetEnvironmentVariable("HISTORIAN_USER") ?? string.Empty, Password = Environment.GetEnvironmentVariable("HISTORIAN_PASSWORD") ?? string.Empty }); List tagNames = []; await foreach (string tagName in client.BrowseTagNamesAsync(filter, CancellationToken.None)) { tagNames.Add(tagName); } Assert.Contains(testTag, tagNames); } [Fact] public async Task ReadRawAsync_AgainstLocalHistorian_ReturnsAtLeastOneRow() { string? host = Environment.GetEnvironmentVariable("HISTORIAN_HOST"); string? testTag = Environment.GetEnvironmentVariable("HISTORIAN_TEST_TAG"); if (string.IsNullOrWhiteSpace(host) || string.IsNullOrWhiteSpace(testTag)) { return; } if (!string.Equals(host, "localhost", StringComparison.OrdinalIgnoreCase)) { // The managed read flow currently only supports the LocalPipe transport. return; } if (!OperatingSystem.IsWindows()) { return; } HistorianClient client = new(new HistorianClientOptions { Host = host, IntegratedSecurity = true, Transport = HistorianTransport.LocalPipe }); DateTime endUtc = DateTime.UtcNow; DateTime startUtc = endUtc - TimeSpan.FromDays(7); List samples = []; await foreach (AVEVA.Historian.Client.Models.HistorianSample sample in client.ReadRawAsync(testTag, startUtc, endUtc, maxValues: 8, CancellationToken.None)) { samples.Add(sample); } Assert.NotEmpty(samples); Assert.All(samples, s => Assert.Equal(testTag, s.TagName)); } [Fact] public async Task ReadAggregateAsync_AgainstLocalHistorian_ReturnsTimeWeightedAverageRows() { string? host = Environment.GetEnvironmentVariable("HISTORIAN_HOST"); string? testTag = Environment.GetEnvironmentVariable("HISTORIAN_TEST_TAG"); if (string.IsNullOrWhiteSpace(host) || string.IsNullOrWhiteSpace(testTag)) { return; } if (!string.Equals(host, "localhost", StringComparison.OrdinalIgnoreCase) || !OperatingSystem.IsWindows()) { return; } HistorianClient client = new(new HistorianClientOptions { Host = host, IntegratedSecurity = true, Transport = HistorianTransport.LocalPipe }); DateTime endUtc = DateTime.UtcNow; DateTime startUtc = endUtc - TimeSpan.FromMinutes(10); List samples = []; await foreach (AVEVA.Historian.Client.Models.HistorianAggregateSample sample in client.ReadAggregateAsync( testTag, startUtc, endUtc, AVEVA.Historian.Client.Models.RetrievalMode.TimeWeightedAverage, TimeSpan.FromMinutes(1), CancellationToken.None)) { samples.Add(sample); } Assert.NotEmpty(samples); Assert.All(samples, s => Assert.Equal(testTag, s.TagName)); Assert.All(samples, s => Assert.Equal(AVEVA.Historian.Client.Models.RetrievalMode.TimeWeightedAverage, s.RetrievalMode)); } [Fact] public async Task ReadAtTimeAsync_AgainstLocalHistorian_ReturnsRequestedTimestamps() { string? host = Environment.GetEnvironmentVariable("HISTORIAN_HOST"); string? testTag = Environment.GetEnvironmentVariable("HISTORIAN_TEST_TAG"); if (string.IsNullOrWhiteSpace(host) || string.IsNullOrWhiteSpace(testTag)) { return; } if (!string.Equals(host, "localhost", StringComparison.OrdinalIgnoreCase) || !OperatingSystem.IsWindows()) { return; } HistorianClient client = new(new HistorianClientOptions { Host = host, IntegratedSecurity = true, Transport = HistorianTransport.LocalPipe }); DateTime nowUtc = DateTime.UtcNow; DateTime[] timestamps = [ nowUtc - TimeSpan.FromMinutes(5), nowUtc - TimeSpan.FromMinutes(2), nowUtc - TimeSpan.FromMinutes(1) ]; IReadOnlyList samples = await client.ReadAtTimeAsync(testTag, timestamps, CancellationToken.None); Assert.NotEmpty(samples); Assert.All(samples, s => Assert.Equal(testTag, s.TagName)); } [Fact] public async Task ReadEventsAsync_AgainstLocalHistorian_DoesNotThrow() { string? host = Environment.GetEnvironmentVariable("HISTORIAN_HOST"); if (string.IsNullOrWhiteSpace(host) || !string.Equals(host, "localhost", StringComparison.OrdinalIgnoreCase) || !OperatingSystem.IsWindows()) { return; } HistorianClient client = new(new HistorianClientOptions { Host = host, IntegratedSecurity = true, Transport = HistorianTransport.LocalPipe }); DateTime endUtc = DateTime.UtcNow; DateTime startUtc = endUtc - TimeSpan.FromDays(7); // The event-row WCF wire format is not yet decoded; this test verifies the chain // (ValCl + Open2 + Retr.IsOriginalAllowed + Retr.StartEventQuery) reaches the server // without throwing. An empty event list is acceptable until row parsing is wired. List events = []; await foreach (AVEVA.Historian.Client.Models.HistorianEvent evt in client.ReadEventsAsync(startUtc, endUtc, CancellationToken.None)) { events.Add(evt); } Assert.NotNull(events); } [Fact] public async Task GetSystemParameterAsync_AgainstLocalHistorian_ReturnsHistorianVersion() { string? host = Environment.GetEnvironmentVariable("HISTORIAN_HOST"); if (string.IsNullOrWhiteSpace(host) || !string.Equals(host, "localhost", StringComparison.OrdinalIgnoreCase) || !OperatingSystem.IsWindows()) { return; } HistorianClient client = new(new HistorianClientOptions { Host = host, IntegratedSecurity = true, Transport = HistorianTransport.LocalPipe }); string? value = await client.GetSystemParameterAsync("HistorianVersion", CancellationToken.None); // The server returns a non-empty version string for the documented HistorianVersion parameter. Assert.False(string.IsNullOrWhiteSpace(value)); } [Fact] public async Task GetConnectionStatusAsync_AgainstLocalHistorian_ReportsConnectedToServer() { string? host = Environment.GetEnvironmentVariable("HISTORIAN_HOST"); if (string.IsNullOrWhiteSpace(host) || !string.Equals(host, "localhost", StringComparison.OrdinalIgnoreCase) || !OperatingSystem.IsWindows()) { return; } HistorianClient client = new(new HistorianClientOptions { Host = host, IntegratedSecurity = true, Transport = HistorianTransport.LocalPipe }); AVEVA.Historian.Client.Models.HistorianConnectionStatus status = await client.GetConnectionStatusAsync(CancellationToken.None); Assert.True(status.ConnectedToServer); Assert.False(status.ErrorOccurred); Assert.False(status.Pending); Assert.Equal(host, status.ServerName); } [Fact] public async Task GetStoreForwardStatusAsync_AgainstLocalHistorian_ReturnsDefaults() { string? host = Environment.GetEnvironmentVariable("HISTORIAN_HOST"); if (string.IsNullOrWhiteSpace(host) || !string.Equals(host, "localhost", StringComparison.OrdinalIgnoreCase) || !OperatingSystem.IsWindows()) { return; } HistorianClient client = new(new HistorianClientOptions { Host = host, IntegratedSecurity = true, Transport = HistorianTransport.LocalPipe }); AVEVA.Historian.Client.Models.HistorianStoreForwardStatus status = await client.GetStoreForwardStatusAsync(CancellationToken.None); // The synthesized status returns defaults — no store-forward sidecar to probe in this build. Assert.False(status.ErrorOccurred); Assert.False(status.Pending); Assert.Equal(host, status.ServerName); } [Fact] public async Task GetTagMetadataAsync_ReturnsConfiguredTestTagMetadata() { string? host = Environment.GetEnvironmentVariable("HISTORIAN_HOST"); string? testTag = Environment.GetEnvironmentVariable("HISTORIAN_TEST_TAG"); if (string.IsNullOrWhiteSpace(host) || string.IsNullOrWhiteSpace(testTag)) { return; } int port = int.TryParse(Environment.GetEnvironmentVariable("HISTORIAN_PORT"), out int parsedPort) ? parsedPort : HistorianClientOptions.DefaultPort; HistorianClient client = new(new HistorianClientOptions { Host = host, Port = port, IntegratedSecurity = true, UserName = Environment.GetEnvironmentVariable("HISTORIAN_USER") ?? string.Empty, Password = Environment.GetEnvironmentVariable("HISTORIAN_PASSWORD") ?? string.Empty }); AVEVA.Historian.Client.Models.HistorianTagMetadata? metadata = await client.GetTagMetadataAsync(testTag, CancellationToken.None); Assert.NotNull(metadata); Assert.Equal(testTag, metadata.Name); Assert.NotNull(metadata.Key); } }