Files
lmxopcua/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Historian.QuestDb.Tests/QuestDbConnectionTests.cs
T

141 lines
5.1 KiB
C#

using System.Net.Sockets;
using Npgsql;
using NpgsqlTypes;
using Shouldly;
using Xunit;
namespace ZB.MOM.WW.OtOpcUa.Driver.Historian.QuestDb.Tests;
public sealed class QuestDbConnectionTests
{
private static DateTime Bound(DateTime v, bool clamp = false)
{
var cmd = new NpgsqlCommand();
var p = QuestDbConnection.BindTimestamp(cmd, v, clamp);
p.NpgsqlDbType.ShouldBe(NpgsqlDbType.TimestampTz);
var bound = (DateTime)p.Value!;
bound.Kind.ShouldBe(DateTimeKind.Utc);
return bound;
}
[Fact]
public void Round_truncates_sub_microsecond_ticks()
{
var t = new DateTime(2026, 3, 1, 12, 0, 0, DateTimeKind.Utc).AddTicks(1234567);
QuestDbConnection.Round(t).ShouldBe(new DateTime(2026, 3, 1, 12, 0, 0, DateTimeKind.Utc).AddTicks(1234560));
}
[Fact]
public void Round_truncates_rather_than_rounds_before_2000()
{
var t = new DateTime(1990, 5, 5, 0, 0, 0, DateTimeKind.Utc).AddTicks(1234565);
QuestDbConnection.Round(t).Ticks.ShouldBe(t.Ticks - 5);
}
[Fact]
public void Round_treats_Unspecified_as_UTC()
{
var u = new DateTime(2026, 3, 1, 12, 0, 0, DateTimeKind.Unspecified);
var r = QuestDbConnection.Round(u);
r.Kind.ShouldBe(DateTimeKind.Utc);
r.Ticks.ShouldBe(u.Ticks);
}
[Fact]
public void Round_converts_Local_to_UTC()
{
var local = new DateTime(2026, 3, 1, 12, 0, 0, DateTimeKind.Local);
var r = QuestDbConnection.Round(local);
r.Kind.ShouldBe(DateTimeKind.Utc);
r.ShouldBe(local.ToUniversalTime());
}
[Fact]
public void Round_keeps_a_UTC_value_that_is_already_aligned()
{
var t = new DateTime(2026, 3, 1, 12, 0, 0, DateTimeKind.Utc);
QuestDbConnection.Round(t).ShouldBe(t);
}
[Theory]
[InlineData(0)] // MinValue itself
[InlineData(5)] // truncates to MinValue
public void BindTimestamp_never_emits_MinValue(long ticks)
{
var b = Bound(new DateTime(ticks, DateTimeKind.Utc));
b.ShouldNotBe(DateTime.MinValue);
b.Ticks.ShouldBe(10);
b.ShouldBe(new DateTime(10, DateTimeKind.Utc)); // 0001-01-01T00:00:00.000001 (T0-confirmed)
}
[Fact]
public void BindTimestamp_never_emits_MaxValue()
{
var b = Bound(DateTime.MaxValue);
b.ShouldNotBe(DateTime.MaxValue);
b.ShouldBe(new DateTime(9999, 12, 31, 23, 59, 59, DateTimeKind.Utc).AddTicks(9999990)); // T0-confirmed bound
Bound(DateTime.MinValue).ShouldNotBe(DateTime.MinValue);
}
[Fact]
public void BindTimestamp_never_emits_either_extreme_for_any_kind()
{
foreach (var kind in new[] { DateTimeKind.Utc, DateTimeKind.Unspecified, DateTimeKind.Local })
foreach (var t in new[] { new DateTime(DateTime.MinValue.Ticks, kind), new DateTime(DateTime.MaxValue.Ticks, kind) })
{
var b = Bound(t);
b.ShouldNotBe(DateTime.MinValue);
b.ShouldNotBe(DateTime.MaxValue);
}
}
[Fact]
public void BindTimestamp_keeps_a_pre_1970_range_bound_unless_asked_to_clamp()
{
var pre = new DateTime(1960, 1, 1, 0, 0, 0, DateTimeKind.Utc);
Bound(pre).ShouldBe(pre);
Bound(pre, clamp: true).ShouldBe(DateTime.UnixEpoch);
Bound(new DateTime(2026, 1, 1, 0, 0, 0, DateTimeKind.Utc), clamp: true).Year.ShouldBe(2026);
}
[Fact]
public void IsStorable_is_false_only_before_1970()
{
QuestDbConnection.IsStorable(DateTime.UnixEpoch).ShouldBeTrue();
QuestDbConnection.IsStorable(DateTime.UnixEpoch.AddTicks(-1)).ShouldBeFalse();
QuestDbConnection.IsStorable(new DateTime(1601, 1, 1, 0, 0, 0, DateTimeKind.Utc)).ShouldBeFalse();
QuestDbConnection.IsStorable(DateTime.MinValue).ShouldBeFalse();
QuestDbConnection.IsStorable(DateTime.MaxValue).ShouldBeTrue();
QuestDbConnection.IsStorable(new DateTime(2026, 1, 1, 0, 0, 0, DateTimeKind.Utc)).ShouldBeTrue();
}
[Fact]
public void BindByte_binds_smallint()
{
var p = QuestDbConnection.BindByte(new NpgsqlCommand(), 200);
p.NpgsqlDbType.ShouldBe(NpgsqlDbType.Smallint);
p.Value.ShouldBe((short)200);
}
[Fact]
public void IsTransient_classifies_transport_failures_not_server_rejections()
{
QuestDbConnection.IsTransient(new SocketException()).ShouldBeTrue();
QuestDbConnection.IsTransient(new IOException("x")).ShouldBeTrue();
QuestDbConnection.IsTransient(new TimeoutException()).ShouldBeTrue();
QuestDbConnection.IsTransient(new NpgsqlException("x", new IOException())).ShouldBeTrue();
QuestDbConnection.IsTransient(new InvalidOperationException("x")).ShouldBeFalse();
QuestDbConnection.IsTransient(new OperationCanceledException()).ShouldBeFalse();
}
[Fact]
public void T_prefixes_the_table_base_name()
{
var c = new QuestDbConnection(new QuestDbHistorianOptions { ConnectionString = "Host=127.0.0.1;Port=1", TablePrefix = "p_" });
c.T("sample").ShouldBe("p_sample");
c.SampleTable.ShouldBe("p_sample");
c.EventTable.ShouldBe("p_event");
c.TagTable.ShouldBe("p_tag");
}
}