Add external object new row command
This commit is contained in:
@@ -5,6 +5,7 @@ import { eq } from "drizzle-orm";
|
||||
import { migrate } from "drizzle-orm/better-sqlite3/migrator";
|
||||
import { createDatabaseContext, type AppDatabase } from "../src/db/database-context.js";
|
||||
import { ExternalObjectRowAssignmentProjectCommandRepository } from "../src/db/repositories/external-object-row-assignment-project-command.repository.js";
|
||||
import { ExternalObjectNewRowProjectCommandRepository } from "../src/db/repositories/external-object-new-row-project-command.repository.js";
|
||||
import { toCircuitDeviceRowSnapshot } from "../src/db/repositories/circuit-device-row-structure.persistence.js";
|
||||
import { circuitDeviceRows } from "../src/db/schema/circuit-device-rows.js";
|
||||
import { circuitLists } from "../src/db/schema/circuit-lists.js";
|
||||
@@ -19,10 +20,14 @@ import { floors } from "../src/db/schema/floors.js";
|
||||
import { projects } from "../src/db/schema/projects.js";
|
||||
import { rooms } from "../src/db/schema/rooms.js";
|
||||
import { createExternalObjectRowAssignmentProjectCommand } from "../src/domain/models/external-object-row-assignment-project-command.model.js";
|
||||
import {
|
||||
createExternalObjectNewRowProjectCommand,
|
||||
externalObjectAssignToNewRowCommandType,
|
||||
} from "../src/domain/models/external-object-new-row-project-command.model.js";
|
||||
import type { ExternalModelObjectSnapshot } from "../src/external-model/domain/external-model-contracts.js";
|
||||
import { externalCsvTestConfiguration } from "./fixtures/revit-csv-fixtures.js";
|
||||
|
||||
function createFixture() {
|
||||
function createFixture(initialObjectRowId: string | null = "row-1") {
|
||||
const context = createDatabaseContext(":memory:");
|
||||
migrate(context.db, { migrationsFolder: path.resolve("src", "db", "migrations") });
|
||||
const database = context.db;
|
||||
@@ -46,7 +51,7 @@ function createFixture() {
|
||||
roomId: "room-1",
|
||||
roomNumberSnapshot: "101",
|
||||
roomNameSnapshot: "Büro",
|
||||
quantity: index === 1 ? 3 : 0,
|
||||
quantity: index === 1 ? (initialObjectRowId === "row-1" ? 3 : 1) : 0,
|
||||
manualQuantity: index === 1 ? 1 : 0,
|
||||
powerPerUnit: 0.12,
|
||||
simultaneityFactor: 1,
|
||||
@@ -78,7 +83,7 @@ function createFixture() {
|
||||
roomId: "room-1",
|
||||
defaultDistributionBoardId: "board-1",
|
||||
}).run();
|
||||
database.insert(externalModelObjects).values(objectSnapshot("row-1")).run();
|
||||
database.insert(externalModelObjects).values(objectSnapshot(initialObjectRowId)).run();
|
||||
return context;
|
||||
}
|
||||
|
||||
@@ -225,3 +230,149 @@ describe("external object row assignment project command", () => {
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
describe("external object new-row project command", () => {
|
||||
it("creates an external-only row and supports exact persisted undo and redo", () => {
|
||||
const context = createFixture(null);
|
||||
try {
|
||||
const base = row(context.db, "row-2");
|
||||
const newRow = {
|
||||
...base,
|
||||
id: "row-new",
|
||||
quantity: 2,
|
||||
manualQuantity: 0,
|
||||
sortOrder: 20,
|
||||
};
|
||||
const object = objectSnapshot(null);
|
||||
const command = createExternalObjectNewRowProjectCommand(
|
||||
externalObjectAssignToNewRowCommandType,
|
||||
{
|
||||
row: newRow,
|
||||
objects: [{
|
||||
expected: object,
|
||||
target: { ...object, circuitDeviceRowId: "row-new" },
|
||||
}],
|
||||
confirmedConflictObjectIds: [],
|
||||
}
|
||||
);
|
||||
const repository = new ExternalObjectNewRowProjectCommandRepository(context.db);
|
||||
const inserted = repository.execute({
|
||||
projectId: "project-1",
|
||||
expectedRevision: 0,
|
||||
source: "user",
|
||||
command,
|
||||
});
|
||||
assert.equal(row(context.db, "row-new").manualQuantity, 0);
|
||||
assert.equal(row(context.db, "row-new").quantity, 2);
|
||||
assert.equal(context.db.select().from(externalModelObjects).get()!.circuitDeviceRowId, "row-new");
|
||||
assert.throws(
|
||||
() => repository.execute({
|
||||
projectId: "project-1",
|
||||
expectedRevision: 1,
|
||||
source: "user",
|
||||
command: inserted.inverse,
|
||||
}),
|
||||
/only be removed through project history/
|
||||
);
|
||||
|
||||
const undone = repository.execute({
|
||||
projectId: "project-1",
|
||||
expectedRevision: 1,
|
||||
source: "undo",
|
||||
historyTargetChangeSetId: inserted.revision.changeSetId,
|
||||
command: inserted.inverse,
|
||||
});
|
||||
assert.equal(
|
||||
context.db.select().from(circuitDeviceRows).where(eq(circuitDeviceRows.id, "row-new")).get(),
|
||||
undefined
|
||||
);
|
||||
assert.equal(context.db.select().from(externalModelObjects).get()!.circuitDeviceRowId, null);
|
||||
|
||||
repository.execute({
|
||||
projectId: "project-1",
|
||||
expectedRevision: 2,
|
||||
source: "redo",
|
||||
historyTargetChangeSetId: inserted.revision.changeSetId,
|
||||
command: undone.inverse,
|
||||
});
|
||||
assert.equal(row(context.db, "row-new").quantity, 2);
|
||||
} finally {
|
||||
context.close();
|
||||
}
|
||||
});
|
||||
|
||||
it("rolls back the row and object link when history persistence fails", () => {
|
||||
const context = createFixture(null);
|
||||
try {
|
||||
context.sqlite.exec(`
|
||||
CREATE TRIGGER fail_new_row_history
|
||||
BEFORE INSERT ON project_change_sets
|
||||
BEGIN SELECT RAISE(ABORT, 'late history failure'); END;
|
||||
`);
|
||||
const base = row(context.db, "row-2");
|
||||
const object = objectSnapshot(null);
|
||||
const command = createExternalObjectNewRowProjectCommand(
|
||||
externalObjectAssignToNewRowCommandType,
|
||||
{
|
||||
row: { ...base, id: "row-new", quantity: 2, manualQuantity: 0, sortOrder: 20 },
|
||||
objects: [{ expected: object, target: { ...object, circuitDeviceRowId: "row-new" } }],
|
||||
confirmedConflictObjectIds: [],
|
||||
}
|
||||
);
|
||||
assert.throws(
|
||||
() => new ExternalObjectNewRowProjectCommandRepository(context.db).execute({
|
||||
projectId: "project-1",
|
||||
expectedRevision: 0,
|
||||
source: "user",
|
||||
command,
|
||||
}),
|
||||
/late history failure/
|
||||
);
|
||||
assert.equal(
|
||||
context.db.select().from(circuitDeviceRows).where(eq(circuitDeviceRows.id, "row-new")).get(),
|
||||
undefined
|
||||
);
|
||||
assert.equal(context.db.select().from(externalModelObjects).get()!.circuitDeviceRowId, null);
|
||||
} finally {
|
||||
context.close();
|
||||
}
|
||||
});
|
||||
|
||||
it("keeps the created row when it changed after assignment", () => {
|
||||
const context = createFixture(null);
|
||||
try {
|
||||
const base = row(context.db, "row-2");
|
||||
const object = objectSnapshot(null);
|
||||
const repository = new ExternalObjectNewRowProjectCommandRepository(context.db);
|
||||
const inserted = repository.execute({
|
||||
projectId: "project-1",
|
||||
expectedRevision: 0,
|
||||
source: "user",
|
||||
command: createExternalObjectNewRowProjectCommand(
|
||||
externalObjectAssignToNewRowCommandType,
|
||||
{
|
||||
row: { ...base, id: "row-new", quantity: 2, manualQuantity: 0, sortOrder: 20 },
|
||||
objects: [{ expected: object, target: { ...object, circuitDeviceRowId: "row-new" } }],
|
||||
confirmedConflictObjectIds: [],
|
||||
}
|
||||
),
|
||||
});
|
||||
context.db.update(circuitDeviceRows).set({ displayName: "Geändert" })
|
||||
.where(eq(circuitDeviceRows.id, "row-new")).run();
|
||||
assert.throws(
|
||||
() => repository.execute({
|
||||
projectId: "project-1",
|
||||
expectedRevision: 1,
|
||||
source: "undo",
|
||||
historyTargetChangeSetId: inserted.revision.changeSetId,
|
||||
command: inserted.inverse,
|
||||
}),
|
||||
/changed before history removal/
|
||||
);
|
||||
assert.equal(row(context.db, "row-new").displayName, "Geändert");
|
||||
assert.equal(context.db.select().from(externalModelObjects).get()!.circuitDeviceRowId, "row-new");
|
||||
} finally {
|
||||
context.close();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user