fix(db): register legacy task migrations
CI - Build & Test / Backend (.NET) (push) Successful in 50s
CI - Build & Test / Backend integration (PostgreSQL/Toxiproxy) (push) Failing after 58s
CI - Build & Test / Frontend (Vue/TS) (push) Has been cancelled
CI - Build & Test / Security Check (push) Has been cancelled
CI - Build & Test / Deploy Nexus (push) Has been cancelled

This commit is contained in:
AzuTear
2026-08-01 01:44:52 +02:00
parent 3dac335767
commit bf32a7def2
5 changed files with 95 additions and 52 deletions
+19
View File
@@ -1,3 +1,7 @@
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Migrations;
using Nexus.Api.Data;
using Nexus.Api.Migrations; using Nexus.Api.Migrations;
using Xunit; using Xunit;
@@ -5,6 +9,21 @@ namespace Nexus.Api.Tests;
public sealed class MigrationSnapshotTests public sealed class MigrationSnapshotTests
{ {
[Fact]
public void Historical_task_migrations_are_discoverable()
{
var options = new DbContextOptionsBuilder<NexusDbContext>()
.UseNpgsql(
"Host=127.0.0.1;Database=nexus_migration_contract;Username=nexus;Password=unused")
.Options;
using var db = new NexusDbContext(options);
var migrations = db.GetService<IMigrationsAssembly>().Migrations.Keys;
Assert.Contains("20260618233000_AddTaskParentChild", migrations);
Assert.Contains("20260618233001_AddTaskDueDate", migrations);
Assert.Contains("20260618233002_AddActivityTaskReference", migrations);
}
[Fact] [Fact]
public void Snapshot_builds_agent_provisioning_relationships() public void Snapshot_builds_agent_provisioning_relationships()
{ {
@@ -140,7 +140,14 @@ public sealed class ToxiproxyAgentProvisioningIntegrationTests
var reconciled = await service.GetByIdAsync(created.Proposal.Id); var reconciled = await service.GetByIdAsync(created.Proposal.Id);
Assert.NotNull(reconciled); Assert.NotNull(reconciled);
Assert.Equal(AgentProposalStates.Ready, reconciled!.Status); Assert.True(
string.Equals(
AgentProposalStates.Ready,
reconciled!.Status,
StringComparison.Ordinal),
$"Expected ready after read-only reconciliation, got "
+ $"'{reconciled.Status}' ({reconciled.Error?.Code}: "
+ $"{reconciled.Error?.Message}).");
Assert.Equal("release-analyst", reconciled.OpenClawAgentId); Assert.Equal("release-analyst", reconciled.OpenClawAgentId);
Assert.Equal(1, gateway.CreateCalls); Assert.Equal(1, gateway.CreateCalls);
} }
@@ -1,49 +1,59 @@
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Migrations; using Microsoft.EntityFrameworkCore.Migrations;
using Nexus.Api.Data;
#nullable disable #nullable disable
namespace Nexus.Api.Migrations namespace Nexus.Api.Migrations
{ {
/// <inheritdoc /> /// <inheritdoc />
[DbContext(typeof(NexusDbContext))]
[Migration("20260618233000_AddTaskParentChild")]
public partial class AddTaskParentChild : Migration public partial class AddTaskParentChild : Migration
{ {
/// <inheritdoc /> /// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder) protected override void Up(MigrationBuilder migrationBuilder)
{ {
migrationBuilder.AddColumn<Guid>( // This historical migration originally lacked EF discovery
name: "ParentTaskId", // metadata. Keep the catch-up operations idempotent so existing
table: "Tasks", // installations and fresh PostgreSQL databases converge safely.
type: "uuid", migrationBuilder.Sql(
nullable: true); """
ALTER TABLE "Tasks"
ADD COLUMN IF NOT EXISTS "ParentTaskId" uuid;
migrationBuilder.CreateIndex( CREATE INDEX IF NOT EXISTS "IX_Tasks_ParentTaskId"
name: "IX_Tasks_ParentTaskId", ON "Tasks" ("ParentTaskId");
table: "Tasks",
column: "ParentTaskId");
migrationBuilder.AddForeignKey( DO $$
name: "FK_Tasks_Tasks_ParentTaskId", BEGIN
table: "Tasks", IF NOT EXISTS (
column: "ParentTaskId", SELECT 1
principalTable: "Tasks", FROM pg_constraint
principalColumn: "Id", WHERE conname = 'FK_Tasks_Tasks_ParentTaskId'
onDelete: ReferentialAction.SetNull); AND conrelid = '"Tasks"'::regclass
) THEN
ALTER TABLE "Tasks"
ADD CONSTRAINT "FK_Tasks_Tasks_ParentTaskId"
FOREIGN KEY ("ParentTaskId")
REFERENCES "Tasks" ("Id")
ON DELETE SET NULL;
END IF;
END $$;
""");
} }
/// <inheritdoc /> /// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder) protected override void Down(MigrationBuilder migrationBuilder)
{ {
migrationBuilder.DropForeignKey( migrationBuilder.Sql(
name: "FK_Tasks_Tasks_ParentTaskId", """
table: "Tasks"); ALTER TABLE "Tasks"
DROP CONSTRAINT IF EXISTS "FK_Tasks_Tasks_ParentTaskId";
migrationBuilder.DropIndex( DROP INDEX IF EXISTS "IX_Tasks_ParentTaskId";
name: "IX_Tasks_ParentTaskId", ALTER TABLE "Tasks"
table: "Tasks"); DROP COLUMN IF EXISTS "ParentTaskId";
""");
migrationBuilder.DropColumn(
name: "ParentTaskId",
table: "Tasks");
} }
} }
} }
@@ -1,28 +1,34 @@
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Migrations; using Microsoft.EntityFrameworkCore.Migrations;
using Nexus.Api.Data;
#nullable disable #nullable disable
namespace Nexus.Api.Migrations namespace Nexus.Api.Migrations
{ {
/// <inheritdoc /> /// <inheritdoc />
[DbContext(typeof(NexusDbContext))]
[Migration("20260618233001_AddTaskDueDate")]
public partial class AddTaskDueDate : Migration public partial class AddTaskDueDate : Migration
{ {
/// <inheritdoc /> /// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder) protected override void Up(MigrationBuilder migrationBuilder)
{ {
migrationBuilder.AddColumn<DateTimeOffset>( migrationBuilder.Sql(
name: "DueDate", """
table: "Tasks", ALTER TABLE "Tasks"
type: "timestamp with time zone", ADD COLUMN IF NOT EXISTS "DueDate" timestamp with time zone;
nullable: true); """);
} }
/// <inheritdoc /> /// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder) protected override void Down(MigrationBuilder migrationBuilder)
{ {
migrationBuilder.DropColumn( migrationBuilder.Sql(
name: "DueDate", """
table: "Tasks"); ALTER TABLE "Tasks"
DROP COLUMN IF EXISTS "DueDate";
""");
} }
} }
} }
@@ -1,37 +1,38 @@
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Migrations; using Microsoft.EntityFrameworkCore.Migrations;
using Nexus.Api.Data;
#nullable disable #nullable disable
namespace Nexus.Api.Migrations namespace Nexus.Api.Migrations
{ {
/// <inheritdoc /> /// <inheritdoc />
[DbContext(typeof(NexusDbContext))]
[Migration("20260618233002_AddActivityTaskReference")]
public partial class AddActivityTaskReference : Migration public partial class AddActivityTaskReference : Migration
{ {
/// <inheritdoc /> /// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder) protected override void Up(MigrationBuilder migrationBuilder)
{ {
migrationBuilder.AddColumn<Guid>( migrationBuilder.Sql(
name: "TaskId", """
table: "Activity", ALTER TABLE "Activity"
type: "uuid", ADD COLUMN IF NOT EXISTS "TaskId" uuid;
nullable: true);
migrationBuilder.CreateIndex( CREATE INDEX IF NOT EXISTS "IX_Activity_TaskId"
name: "IX_Activity_TaskId", ON "Activity" ("TaskId");
table: "Activity", """);
column: "TaskId");
} }
/// <inheritdoc /> /// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder) protected override void Down(MigrationBuilder migrationBuilder)
{ {
migrationBuilder.DropIndex( migrationBuilder.Sql(
name: "IX_Activity_TaskId", """
table: "Activity"); DROP INDEX IF EXISTS "IX_Activity_TaskId";
ALTER TABLE "Activity"
migrationBuilder.DropColumn( DROP COLUMN IF EXISTS "TaskId";
name: "TaskId", """);
table: "Activity");
} }
} }
} }