bef47f89da
Claude-Session: https://claude.ai/code/session_01Ef2ozxS7QPanGT8uoATbTe
95 lines
4.6 KiB
C#
95 lines
4.6 KiB
C#
using Microsoft.Data.SqlClient;
|
|
using Shouldly;
|
|
using Xunit;
|
|
using ZB.MOM.WW.OtOpcUa.Core.Abstractions;
|
|
|
|
namespace ZB.MOM.WW.OtOpcUa.Driver.Historian.SqlServer.IntegrationTests;
|
|
|
|
[Collection(SqlHistorianCollection.Name)]
|
|
public sealed class ProvisioningTests(SqlHistorianFixture fx)
|
|
{
|
|
private (SqlServerTagProvisioner P, string S) Make(string? schema = null)
|
|
{
|
|
var s = schema ?? SqlHistorianFixture.UniqueSchema();
|
|
var o = new SqlServerHistorianOptions { ConnectionString = fx.ConnectionString, Schema = s };
|
|
var c = new SqlHistorianConnection(o);
|
|
return (new SqlServerTagProvisioner(c, new SqlServerSchemaBootstrapper(c, o)), s);
|
|
}
|
|
|
|
private async Task<(byte Type, string? Eu, string? Desc)> TagAsync(string s, string name)
|
|
{
|
|
await using var c = new SqlConnection(fx.ConnectionString);
|
|
await c.OpenAsync();
|
|
await using var cmd = c.CreateCommand();
|
|
cmd.CommandText = $"SELECT DataType, EngineeringUnit, Description FROM [{s}].Tag WHERE Name=@n";
|
|
cmd.Parameters.AddWithValue("@n", name);
|
|
await using var r = await cmd.ExecuteReaderAsync();
|
|
(await r.ReadAsync()).ShouldBeTrue();
|
|
return (r.GetByte(0), r.IsDBNull(1) ? null : r.GetString(1), r.IsDBNull(2) ? null : r.GetString(2));
|
|
}
|
|
|
|
[Fact]
|
|
public async Task Upsert_creates_then_changes_DataType_on_rerun_and_every_type_is_accepted()
|
|
{
|
|
fx.SkipIfUnavailable();
|
|
var (p, s) = Make();
|
|
var r1 = await p.EnsureTagsAsync([new("a", DriverDataType.Boolean, "degC", "desc"), new("s", DriverDataType.String, null, null)], CancellationToken.None);
|
|
r1.ShouldBe(new HistorianProvisionResult(2, 2, 0, 0));
|
|
(await TagAsync(s, "a")).ShouldBe(((byte)DriverDataType.Boolean, "degC", "desc"));
|
|
var r2 = await p.EnsureTagsAsync([new("a", DriverDataType.Float64, null, null)], CancellationToken.None);
|
|
r2.ShouldBe(new HistorianProvisionResult(1, 1, 0, 0));
|
|
(await TagAsync(s, "a")).ShouldBe(((byte)DriverDataType.Float64, "degC", "desc")); // null keeps stored metadata
|
|
}
|
|
|
|
[Fact]
|
|
public async Task Duplicate_names_and_bad_names_are_tallied_not_thrown()
|
|
{
|
|
fx.SkipIfUnavailable();
|
|
var (p, _) = Make();
|
|
var r = await p.EnsureTagsAsync([
|
|
new("d", DriverDataType.Int32, null, null), new("d", DriverDataType.Int64, null, null),
|
|
new("", DriverDataType.Int32, null, null), new(new string('x', 401), DriverDataType.Int32, null, null)], CancellationToken.None);
|
|
r.ShouldBe(new HistorianProvisionResult(4, 2, 0, 2));
|
|
}
|
|
|
|
[Fact]
|
|
public async Task More_tags_than_one_chunk_all_land()
|
|
{
|
|
fx.SkipIfUnavailable();
|
|
var (p, s) = Make();
|
|
var reqs = Enumerable.Range(0, 1000).Select(i => new HistorianTagProvisionRequest($"bulk{i}", DriverDataType.Float32, null, null)).ToList();
|
|
(await p.EnsureTagsAsync(reqs, CancellationToken.None)).ShouldBe(new HistorianProvisionResult(1000, 1000, 0, 0));
|
|
}
|
|
|
|
[Fact]
|
|
public async Task Two_concurrent_provisioners_of_the_same_new_tags_both_report_Ensured_with_no_Failed()
|
|
{
|
|
fx.SkipIfUnavailable();
|
|
var s = SqlHistorianFixture.UniqueSchema();
|
|
var (p0, _) = Make(s);
|
|
await p0.EnsureTagsAsync([new("warm", DriverDataType.Int32, null, null)], CancellationToken.None); // bootstrap once
|
|
for (var round = 0; round < 10; round++)
|
|
{
|
|
var reqs = Enumerable.Range(0, 60).Select(i => new HistorianTagProvisionRequest($"r{round}.t{i}", DriverDataType.Float64, "u", null)).ToList();
|
|
var rs = await Task.WhenAll(Enumerable.Range(0, 4).Select(_ => Task.Run(() => Make(s).P.EnsureTagsAsync(reqs, CancellationToken.None))));
|
|
foreach (var r in rs) r.ShouldBe(new HistorianProvisionResult(60, 60, 0, 0));
|
|
}
|
|
|
|
await using var c = new SqlConnection(fx.ConnectionString);
|
|
await c.OpenAsync();
|
|
await using var cmd = c.CreateCommand();
|
|
cmd.CommandText = $"SELECT COUNT(*) FROM [{s}].Tag";
|
|
Convert.ToInt32(await cmd.ExecuteScalarAsync()).ShouldBe(601);
|
|
}
|
|
|
|
[Fact]
|
|
public async Task A_connection_failure_is_a_Failed_tally_not_an_exception()
|
|
{
|
|
fx.SkipIfUnavailable();
|
|
var o = new SqlServerHistorianOptions { ConnectionString = fx.ConnectionString + ";Connect Timeout=1", Schema = SqlHistorianFixture.UniqueSchema(), AutoCreateSchema = false };
|
|
var c = new SqlHistorianConnection(o);
|
|
var p = new SqlServerTagProvisioner(c, new SqlServerSchemaBootstrapper(c, o));
|
|
(await p.EnsureTagsAsync([new("x", DriverDataType.Int32, null, null)], CancellationToken.None)).ShouldBe(new HistorianProvisionResult(1, 0, 0, 1));
|
|
}
|
|
}
|