using DynamicCrud.Tests.Fixtures; using ServicesDAO.Repositories; using Xunit; namespace DynamicCrud.Tests; // backup_restore tiene FKs, pero todas nullable — seguro para Create/Update/Delete dejándolas en null. public class BackupRestoreTests : IClassFixture { private readonly DbContextFixture _fixture; public BackupRestoreTests(DbContextFixture fixture) { _fixture = fixture; } [Fact] public async Task Create_Search_Update_Delete_FullCycle() { var entityType = ModelRegistry.Resolve("backup_restore")!; var veleroBackupName = $"test-backup-{Guid.NewGuid():N}"; var createData = new Dictionary { ["veleroBackupName"] = veleroBackupName, ["restoreNameRequested"] = "test-restore-crud", ["k8sContext"] = "test-context-crud", ["veleroNamespace"] = "velero", ["phase"] = "New", // request_datetime es NOT NULL sin default manejado por ApplyAuditCreate // (solo CreatedAt/UpdatedAt se autocompletan) — hay que darlo explícito. ["requestDatetime"] = DateTime.UtcNow, }; using var ctx1 = _fixture.CreateContext(); var (createOk, _, insertedId, createDetail) = await RepoHelper.CreateRecord(RepoHelper.Create(ctx1, entityType), createData); Assert.True(createOk, $"Create falló: {createDetail}"); Assert.NotNull(insertedId); using var ctx2 = _fixture.CreateContext(); var found = await RepoHelper.Search( RepoHelper.Create(ctx2, entityType), and: new[] { new FilterCondition("veleroBackupName", "eq", veleroBackupName) }); Assert.Single(found); using var ctx3 = _fixture.CreateContext(); var (updateOk, updateMsg, updateStatus) = await RepoHelper.Update( RepoHelper.Create(ctx3, entityType), insertedId!, new Dictionary { ["phase"] = "InProgress" }); Assert.True(updateOk, $"Update falló: {updateMsg}"); using var ctx4 = _fixture.CreateContext(); var (deleteOk, deleteMsg) = await RepoHelper.Delete(RepoHelper.Create(ctx4, entityType), insertedId!); Assert.True(deleteOk, $"Delete falló: {deleteMsg}"); } [Fact] public async Task Search_IsNull_And_BulkUpdate_In_SetSupply_LikeThePendingJobQuery() { // Replica el patrón que usa client_bkps: encontrar pendientes con // Search(supply isnull) y reclamarlos con BulkUpdate(in sobre la PK). var entityType = ModelRegistry.Resolve("backup_restore")!; var veleroBackupName = $"test-supply-{Guid.NewGuid():N}"; var createData = new Dictionary { ["veleroBackupName"] = veleroBackupName, ["k8sContext"] = "test-context-supply", ["veleroNamespace"] = "velero", ["requestDatetime"] = DateTime.UtcNow, // supply queda sin especificar -> NULL (pendiente) }; using var ctx1 = _fixture.CreateContext(); var (createOk, _, insertedId, createDetail) = await RepoHelper.CreateRecord(RepoHelper.Create(ctx1, entityType), createData); Assert.True(createOk, $"Create falló: {createDetail}"); using var ctx2 = _fixture.CreateContext(); var pending = await RepoHelper.Search( RepoHelper.Create(ctx2, entityType), and: new[] { new FilterCondition("veleroBackupName", "eq", veleroBackupName), new FilterCondition("supply", "isnull", ""), }); Assert.Single(pending); using var ctx3 = _fixture.CreateContext(); var (bulkOk, bulkMsg, affected, bulkDetail) = await RepoHelper.BulkUpdate( RepoHelper.Create(ctx3, entityType), and: new[] { new FilterCondition("backupRestoreID", "in", insertedId!.ToString()!) }, or: null, data: new Dictionary { ["supply"] = true }); Assert.True(bulkOk, $"BulkUpdate falló: {bulkDetail}"); Assert.Equal(1, affected); using var ctx4 = _fixture.CreateContext(); await RepoHelper.Delete(RepoHelper.Create(ctx4, entityType), insertedId); } }