using Microsoft.Data.Sqlite; using Shouldly; using Xunit; namespace ZB.MOM.WW.OtOpcUa.Core.AlarmHistorian.Tests; /// /// Verifies creates its table and is idempotent — /// the DDL depends on nothing but a , so a bare in-memory /// connection is a faithful harness (no zb_hlc_next() UDF is involved in CREATE TABLE). /// [Trait("Category", "Unit")] public sealed class AlarmConditionStateSchemaTests { private static SqliteConnection OpenInMemory() { var connection = new SqliteConnection("Data Source=:memory:"); connection.Open(); return connection; } /// A single Apply creates the table. [Fact] public void Apply_creates_the_state_table() { using var connection = OpenInMemory(); AlarmConditionStateSchema.Apply(connection); using var cmd = connection.CreateCommand(); cmd.CommandText = "SELECT COUNT(*) FROM sqlite_master WHERE type = 'table' AND name = @Name"; cmd.Parameters.AddWithValue("@Name", AlarmConditionStateSchema.StateTable); ((long)cmd.ExecuteScalar()!).ShouldBe(1); } /// Apply also creates the append-only comment table, keyed by alarm id + ordinal. [Fact] public void Apply_creates_the_comment_table_with_a_composite_key() { using var connection = OpenInMemory(); AlarmConditionStateSchema.Apply(connection); using var cmd = connection.CreateCommand(); cmd.CommandText = "SELECT name FROM pragma_table_info(@Name) WHERE pk > 0 ORDER BY pk"; cmd.Parameters.AddWithValue("@Name", AlarmConditionStateSchema.CommentsTable); var pk = new List(); using (var reader = cmd.ExecuteReader()) while (reader.Read()) pk.Add(reader.GetString(0)); pk.ShouldBe(["scripted_alarm_id", "seq"]); } /// Applying twice on the same connection does not throw (CREATE TABLE IF NOT EXISTS). [Fact] public void Apply_is_idempotent() { using var connection = OpenInMemory(); AlarmConditionStateSchema.Apply(connection); Should.NotThrow(() => AlarmConditionStateSchema.Apply(connection)); } }