using Admin.NET.Plugin.AiDOP.DataPlatform.Schema; using Xunit; namespace Admin.NET.Plugin.AiDOP.Tests.DataPlatform; public class MdpSchemaAlignerTests { private const string Create = """ CREATE TABLE IF NOT EXISTS mdp_std_demo ( id BIGINT AUTO_INCREMENT PRIMARY KEY, tenant_id BIGINT NOT NULL, approved_flag TINYINT NOT NULL DEFAULT 1, UNIQUE KEY uk_demo (tenant_id), KEY idx_demo (approved_flag) ) ENGINE=InnoDB """; [Fact] public void MissingTable_PlansCreateOnly() { var planned = MdpSchemaAligner.Plan(Create, SchemaSnapshot.Missing); Assert.Single(planned); Assert.StartsWith("CREATE TABLE", planned[0], StringComparison.OrdinalIgnoreCase); Assert.DoesNotContain("DROP", planned[0], StringComparison.OrdinalIgnoreCase); } [Fact] public void MissingColumnAndIndex_PlansAdditiveAlterOnly() { var snapshot = new SchemaSnapshot(true, ["id", "tenant_id"], ["PRIMARY"]); var planned = MdpSchemaAligner.Plan(Create, snapshot); Assert.Equal(3, planned.Count); Assert.Contains(planned, x => x.Contains("ADD COLUMN approved_flag", StringComparison.OrdinalIgnoreCase)); Assert.Contains(planned, x => x.Contains("ADD UNIQUE KEY `uk_demo`", StringComparison.OrdinalIgnoreCase)); Assert.Contains(planned, x => x.Contains("ADD KEY `idx_demo`", StringComparison.OrdinalIgnoreCase)); Assert.DoesNotContain(planned, x => x.Contains("DROP", StringComparison.OrdinalIgnoreCase)); } [Fact] public void ReadyTable_PlansNothing() { var snapshot = new SchemaSnapshot( true, ["id", "tenant_id", "approved_flag"], ["PRIMARY", "uk_demo", "idx_demo"]); Assert.Empty(MdpSchemaAligner.Plan(Create, snapshot)); } [Fact] public void LikeTable_SkippedWhenPresent() { const string like = "CREATE TABLE IF NOT EXISTS mdp_stg_s4_shipment LIKE mdp_stg_s4_iqc"; Assert.Empty(MdpSchemaAligner.Plan(like, new SchemaSnapshot(true, [], []))); var created = MdpSchemaAligner.Plan(like, SchemaSnapshot.Missing); Assert.Single(created); Assert.Contains("LIKE", created[0], StringComparison.OrdinalIgnoreCase); } /// /// dwd_requirement_examine_detail 的 need_count 前面有一段说明注释。 /// Plan 过去不剥行注释,ColumnName 取到的是「--」而不是 need_count,于是这一列 /// 每次都被当成缺列再 ADD 一次;MySQL 执行 ALTER 时自己吃掉注释,抛 /// 「Duplicate column name 'need_count'」,S1 重算因此每次都在对齐阶段失败。 /// [Fact] public void CommentedColumns_AreNotReAdded() { const string create = """ CREATE TABLE IF NOT EXISTS mdp_std_demo ( id BIGINT AUTO_INCREMENT PRIMARY KEY, tenant_id BIGINT NOT NULL, -- 说明:这一列前面有注释,注释里还有逗号、括号 (a,b) 和 don't 这种单引号 -- 第二行注释 need_count DECIMAL(18,4) NULL COMMENT '标准列·需求量', approved_flag TINYINT NOT NULL DEFAULT 1 ) ENGINE=InnoDB """; var ready = new SchemaSnapshot( true, ["id", "tenant_id", "need_count", "approved_flag"], ["PRIMARY"]); Assert.Empty(MdpSchemaAligner.Plan(create, ready)); var missingOne = new SchemaSnapshot(true, ["id", "tenant_id", "approved_flag"], ["PRIMARY"]); var planned = MdpSchemaAligner.Plan(create, missingOne); Assert.Single(planned); Assert.Contains("ADD COLUMN need_count", planned[0], StringComparison.OrdinalIgnoreCase); Assert.DoesNotContain("--", planned[0], StringComparison.Ordinal); } /// COMMENT 字符串里的 -- 不是注释,不能被剥掉。 [Fact] public void StripLineComments_KeepsDashesInsideQuotedText() { var stripped = MdpSchemaAligner.StripLineComments( "a VARCHAR(10) COMMENT 'x -- y', -- 真注释\nb INT"); Assert.Contains("'x -- y'", stripped, StringComparison.Ordinal); Assert.DoesNotContain("真注释", stripped, StringComparison.Ordinal); Assert.Contains("b INT", stripped, StringComparison.Ordinal); } [Fact] public void NonNeutralTable_IsLeftUntouched() { const string sql = "CREATE TABLE IF NOT EXISTS qms_demo (id BIGINT)"; var planned = MdpSchemaAligner.Plan(sql, SchemaSnapshot.Missing); Assert.Equal([sql], planned); } }