< Summary

Line coverage
91%
Covered lines: 137
Uncovered lines: 12
Coverable lines: 149
Total lines: 205
Line coverage: 91.9%
Branch coverage
N/A
Covered branches: 0
Total branches: 0
Branch coverage: N/A
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
File 1: .ctor(...)100%11100%
File 1: Up(...)100%11100%
File 1: Down(...)100%210%
File 2: BuildTargetModel(...)100%11100%

File(s)

/home/runner/work/elsa-core/elsa-core/src/modules/Elsa.Secrets.Persistence.EFCore.SqlServer/Migrations/Secrets/20260914120000_SecretDefaultTenantUniqueness.cs

#LineLine coverage
 1using Microsoft.EntityFrameworkCore.Migrations;
 2
 3#nullable disable
 4
 5namespace Elsa.Secrets.Persistence.EFCore.SqlServer.Migrations.Secrets
 6{
 7    /// <inheritdoc />
 8    public partial class SecretDefaultTenantUniqueness : Migration
 9    {
 10        private readonly Elsa.Persistence.EFCore.IElsaDbContextSchema _schema;
 11
 12        /// <inheritdoc />
 213        public SecretDefaultTenantUniqueness(Elsa.Persistence.EFCore.IElsaDbContextSchema schema)
 14        {
 215            _schema = schema;
 216        }
 17
 18        /// <inheritdoc />
 19        protected override void Up(MigrationBuilder migrationBuilder)
 20        {
 221            migrationBuilder.DropIndex(
 222                name: "IX_Secret_TenantId_NormalizedName",
 223                schema: _schema.Schema,
 224                table: "Secrets");
 25
 26            // Default tenant is "" (not null). Stamp leftover nulls so the filtered unique
 27            // index (TenantId IS NOT NULL) covers them. Duplicates are not deleted.
 228            migrationBuilder.Sql($"""
 229                UPDATE [{_schema.Schema}].[Secrets]
 230                SET [TenantId] = N''
 231                WHERE [TenantId] IS NULL;
 232                """);
 33
 34            // No silent dedupe. List leftover keys and abort; operators must resolve them before upgrading.
 235            migrationBuilder.Sql($"""
 236                IF EXISTS (
 237                    SELECT 1
 238                    FROM [{_schema.Schema}].[Secrets]
 239                    GROUP BY [TenantId], [NormalizedName]
 240                    HAVING COUNT(*) > 1
 241                )
 242                BEGIN
 243                    DECLARE @DuplicateKeys nvarchar(max);
 244
 245                    SELECT @DuplicateKeys = STRING_AGG(
 246                        CAST(CONCAT(N'(', ISNULL([TenantId], N'<null>'), N', ', [NormalizedName], N')') AS nvarchar(max)
 247                        N'; '
 248                    )
 249                    FROM (
 250                        SELECT [TenantId], [NormalizedName]
 251                        FROM [{_schema.Schema}].[Secrets]
 252                        GROUP BY [TenantId], [NormalizedName]
 253                        HAVING COUNT(*) > 1
 254                    ) AS Duplicates;
 255
 256                    DECLARE @ErrorMessage nvarchar(2048);
 257                    SET @ErrorMessage = CONCAT(
 258                        N'Cannot create unique index IX_Secret_TenantId_NormalizedName because leftover duplicate (Tenan
 259                        LEFT(@DuplicateKeys, 1500)
 260                    );
 261                    THROW 50001, @ErrorMessage, 1;
 262                END
 263                """);
 64
 265            migrationBuilder.CreateIndex(
 266                name: "IX_Secret_TenantId_NormalizedName",
 267                schema: _schema.Schema,
 268                table: "Secrets",
 269                columns: new[] { "TenantId", "NormalizedName" },
 270                unique: true,
 271                filter: "[TenantId] IS NOT NULL");
 272        }
 73
 74        /// <inheritdoc />
 75        protected override void Down(MigrationBuilder migrationBuilder)
 76        {
 077            migrationBuilder.DropIndex(
 078                name: "IX_Secret_TenantId_NormalizedName",
 079                schema: _schema.Schema,
 080                table: "Secrets");
 81
 082            migrationBuilder.CreateIndex(
 083                name: "IX_Secret_TenantId_NormalizedName",
 084                schema: _schema.Schema,
 085                table: "Secrets",
 086                columns: new[] { "TenantId", "NormalizedName" },
 087                unique: true,
 088                filter: "[TenantId] IS NOT NULL");
 089        }
 90    }
 91}

/home/runner/work/elsa-core/elsa-core/src/modules/Elsa.Secrets.Persistence.EFCore.SqlServer/Migrations/Secrets/20260914120000_SecretDefaultTenantUniqueness.Designer.cs

#LineLine coverage
 1// <auto-generated />
 2using System;
 3using Elsa.Secrets.Persistence.EFCore;
 4using Microsoft.EntityFrameworkCore;
 5using Microsoft.EntityFrameworkCore.Infrastructure;
 6using Microsoft.EntityFrameworkCore.Metadata;
 7using Microsoft.EntityFrameworkCore.Migrations;
 8using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
 9
 10#nullable disable
 11
 12namespace Elsa.Secrets.Persistence.EFCore.SqlServer.Migrations.Secrets
 13{
 14    [DbContext(typeof(SecretsElsaDbContext))]
 15    [Migration("20260914120000_SecretDefaultTenantUniqueness")]
 16    partial class SecretDefaultTenantUniqueness
 17    {
 18        /// <inheritdoc />
 19        protected override void BuildTargetModel(ModelBuilder modelBuilder)
 20        {
 21#pragma warning disable 612, 618
 222            modelBuilder
 223                .HasDefaultSchema("Elsa")
 224                .HasAnnotation("ProductVersion", "10.0.9")
 225                .HasAnnotation("Relational:MaxIdentifierLength", 128);
 26
 227            SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder);
 28
 229            modelBuilder.Entity("Elsa.Secrets.Models.Secret", b =>
 230                {
 231                    b.Property<string>("Id")
 232                        .HasColumnType("nvarchar(450)");
 233
 234                    b.Property<DateTimeOffset>("CreatedAt")
 235                        .HasColumnType("datetimeoffset");
 236
 237                    b.Property<string>("Description")
 238                        .HasColumnType("nvarchar(max)");
 239
 240                    b.Property<string>("DisplayName")
 241                        .IsRequired()
 242                        .HasMaxLength(200)
 243                        .HasColumnType("nvarchar(200)");
 244
 245                    b.Property<string>("Name")
 246                        .IsRequired()
 247                        .HasMaxLength(200)
 248                        .HasColumnType("nvarchar(200)");
 249
 250                    b.Property<string>("NormalizedName")
 251                        .IsRequired()
 252                        .HasMaxLength(200)
 253                        .HasColumnType("nvarchar(200)");
 254
 255                    b.Property<string>("Scope")
 256                        .HasMaxLength(200)
 257                        .HasColumnType("nvarchar(200)");
 258
 259                    b.Property<string>("SerializedTags")
 260                        .IsRequired()
 261                        .HasColumnType("nvarchar(max)")
 262                        .HasColumnName("Tags");
 263
 264                    b.Property<string>("SerializedVersions")
 265                        .IsRequired()
 266                        .HasColumnType("nvarchar(max)")
 267                        .HasColumnName("Versions");
 268
 269                    b.Property<string>("Status")
 270                        .IsRequired()
 271                        .HasMaxLength(32)
 272                        .HasColumnType("nvarchar(32)");
 273
 274                    b.Property<string>("StoreName")
 275                        .IsRequired()
 276                        .HasMaxLength(100)
 277                        .HasColumnType("nvarchar(100)");
 278
 279                    b.Property<string>("TenantId")
 280                        .HasColumnType("nvarchar(450)");
 281
 282                    b.Property<string>("TypeName")
 283                        .IsRequired()
 284                        .HasMaxLength(100)
 285                        .HasColumnType("nvarchar(100)");
 286
 287                    b.Property<DateTimeOffset?>("UpdatedAt")
 288                        .HasColumnType("datetimeoffset");
 289
 290                    b.HasKey("Id");
 291
 292                    b.HasIndex("Scope")
 293                        .HasDatabaseName("IX_Secret_Scope");
 294
 295                    b.HasIndex("Status")
 296                        .HasDatabaseName("IX_Secret_Status");
 297
 298                    b.HasIndex("StoreName")
 299                        .HasDatabaseName("IX_Secret_StoreName");
 2100
 2101                    b.HasIndex("TypeName")
 2102                        .HasDatabaseName("IX_Secret_TypeName");
 2103
 2104                    b.HasIndex("TenantId", "NormalizedName")
 2105                        .IsUnique()
 2106                        .HasDatabaseName("IX_Secret_TenantId_NormalizedName")
 2107                        .HasFilter("[TenantId] IS NOT NULL");
 2108
 2109                    b.ToTable("Secrets", "Elsa");
 4110                });
 111#pragma warning restore 612, 618
 2112        }
 113    }
 114}