Add versioned project settings modal

This commit is contained in:
2026-07-28 18:16:22 +02:00
parent e1fc81a083
commit d77cfbeb49
22 changed files with 2563 additions and 111 deletions
@@ -25,6 +25,7 @@ function createTestDatabase(): DatabaseContext {
.values({
id: "project-1",
name: "Testprojekt",
internalProjectNumber: "INT-1",
singlePhaseVoltageV: 230,
threePhaseVoltageV: 400,
})
@@ -35,6 +36,11 @@ function createTestDatabase(): DatabaseContext {
function getSettings(context: DatabaseContext) {
return context.db
.select({
name: projects.name,
internalProjectNumber: projects.internalProjectNumber,
externalProjectNumber: projects.externalProjectNumber,
buildingOwner: projects.buildingOwner,
description: projects.description,
singlePhaseVoltageV: projects.singlePhaseVoltageV,
threePhaseVoltageV: projects.threePhaseVoltageV,
currentRevision: projects.currentRevision,
@@ -44,15 +50,32 @@ function getSettings(context: DatabaseContext) {
.get();
}
function settings(
overrides: Partial<Parameters<
typeof createProjectSettingsUpdateProjectCommand
>[0]> = {}
) {
return {
name: "Testprojekt",
internalProjectNumber: "INT-1",
externalProjectNumber: null,
buildingOwner: null,
description: null,
singlePhaseVoltageV: 230,
threePhaseVoltageV: 400,
...overrides,
};
}
describe("project settings project-command repository", () => {
it("validates settings commands and sends the expected revision from the frontend", async () => {
assert.throws(
() =>
createProjectSettingsUpdateProjectCommand({
...settings(),
singlePhaseVoltageV: 0,
threePhaseVoltageV: 400,
}),
/invalid voltages/
/invalid values/
);
const requests: Array<{ url: string; body: unknown }> = [];
const originalFetch = globalThis.fetch;
@@ -68,6 +91,7 @@ describe("project settings project-command repository", () => {
};
try {
await updateProjectSettings("project-1", 7, {
...settings(),
singlePhaseVoltageV: 240,
threePhaseVoltageV: 415,
});
@@ -79,6 +103,11 @@ describe("project settings project-command repository", () => {
url: "/api/projects/project-1",
body: {
expectedRevision: 7,
name: "Testprojekt",
internalProjectNumber: "INT-1",
externalProjectNumber: null,
buildingOwner: null,
description: null,
singlePhaseVoltageV: 240,
threePhaseVoltageV: 415,
},
@@ -94,6 +123,9 @@ describe("project settings project-command repository", () => {
);
const history = new ProjectHistoryRepository(context.db);
const command = createProjectSettingsUpdateProjectCommand({
...settings(),
name: "Neuer Projektname",
buildingOwner: "Beispiel Bau GmbH",
singlePhaseVoltageV: 240,
threePhaseVoltageV: 415,
});
@@ -104,11 +136,21 @@ describe("project settings project-command repository", () => {
command,
});
assert.deepEqual(getSettings(context), {
name: "Neuer Projektname",
internalProjectNumber: "INT-1",
externalProjectNumber: null,
buildingOwner: "Beispiel Bau GmbH",
description: null,
singlePhaseVoltageV: 240,
threePhaseVoltageV: 415,
currentRevision: 1,
});
assert.deepEqual(forward.inverse.payload, {
name: "Testprojekt",
internalProjectNumber: "INT-1",
externalProjectNumber: null,
buildingOwner: null,
description: null,
singlePhaseVoltageV: 230,
threePhaseVoltageV: 400,
});
@@ -123,6 +165,11 @@ describe("project settings project-command repository", () => {
command: forward.inverse,
});
assert.deepEqual(getSettings(context), {
name: "Testprojekt",
internalProjectNumber: "INT-1",
externalProjectNumber: null,
buildingOwner: null,
description: null,
singlePhaseVoltageV: 230,
threePhaseVoltageV: 400,
currentRevision: 2,
@@ -138,6 +185,11 @@ describe("project settings project-command repository", () => {
command,
});
assert.deepEqual(getSettings(context), {
name: "Neuer Projektname",
internalProjectNumber: "INT-1",
externalProjectNumber: null,
buildingOwner: "Beispiel Bau GmbH",
description: null,
singlePhaseVoltageV: 240,
threePhaseVoltageV: 415,
currentRevision: 3,
@@ -147,6 +199,37 @@ describe("project settings project-command repository", () => {
}
});
it("applies persisted version-one voltage commands without clearing metadata", () => {
const context = createTestDatabase();
try {
const repository = new ProjectSettingsProjectCommandRepository(
context.db
);
repository.executeUpdate({
projectId: "project-1",
expectedRevision: 0,
source: "user",
command: {
schemaVersion: 1,
type: "project.update-settings",
payload: {
singlePhaseVoltageV: 240,
threePhaseVoltageV: 415,
},
},
});
assert.deepEqual(getSettings(context), {
...settings({
singlePhaseVoltageV: 240,
threePhaseVoltageV: 415,
}),
currentRevision: 1,
});
} finally {
context.close();
}
});
it("rejects no-op, invalid project and stale revision without partial writes", () => {
const context = createTestDatabase();
try {
@@ -160,8 +243,7 @@ describe("project settings project-command repository", () => {
expectedRevision: 0,
source: "user",
command: createProjectSettingsUpdateProjectCommand({
singlePhaseVoltageV: 230,
threePhaseVoltageV: 400,
...settings(),
}),
}),
/did not change/
@@ -173,6 +255,7 @@ describe("project settings project-command repository", () => {
expectedRevision: 0,
source: "user",
command: createProjectSettingsUpdateProjectCommand({
...settings(),
singlePhaseVoltageV: 240,
threePhaseVoltageV: 415,
}),
@@ -186,6 +269,7 @@ describe("project settings project-command repository", () => {
expectedRevision: 1,
source: "user",
command: createProjectSettingsUpdateProjectCommand({
...settings(),
singlePhaseVoltageV: 240,
threePhaseVoltageV: 415,
}),
@@ -193,6 +277,11 @@ describe("project settings project-command repository", () => {
ProjectRevisionConflictError
);
assert.deepEqual(getSettings(context), {
name: "Testprojekt",
internalProjectNumber: "INT-1",
externalProjectNumber: null,
buildingOwner: null,
description: null,
singlePhaseVoltageV: 230,
threePhaseVoltageV: 400,
currentRevision: 0,
@@ -226,6 +315,7 @@ describe("project settings project-command repository", () => {
expectedRevision: 0,
source: "user",
command: createProjectSettingsUpdateProjectCommand({
...settings(),
singlePhaseVoltageV: 240,
threePhaseVoltageV: 415,
}),
@@ -233,6 +323,11 @@ describe("project settings project-command repository", () => {
/forced project settings history failure/
);
assert.deepEqual(getSettings(context), {
name: "Testprojekt",
internalProjectNumber: "INT-1",
externalProjectNumber: null,
buildingOwner: null,
description: null,
singlePhaseVoltageV: 230,
threePhaseVoltageV: 400,
currentRevision: 0,