Add protection domain foundation
This commit is contained in:
@@ -0,0 +1,213 @@
|
||||
import assert from "node:assert/strict";
|
||||
import { describe, it } from "node:test";
|
||||
import {
|
||||
allowedRatedCurrentsAByProtectionDeviceType,
|
||||
breakerTripCharacteristics,
|
||||
fuseUtilizationCategories,
|
||||
protectionDeviceTypes,
|
||||
ratedResidualCurrentsMa,
|
||||
rcdTypes,
|
||||
} from "../src/shared/constants/protection-device.js";
|
||||
import {
|
||||
createDefaultCircuitProtection,
|
||||
createDefaultGroupRcd,
|
||||
} from "../src/domain/services/protection-device-defaults.js";
|
||||
import { protectionDeviceConfigurationSchema } from "../src/shared/validation/protection-device.schemas.js";
|
||||
|
||||
describe("protection device catalog", () => {
|
||||
it("contains exactly the agreed protection device types", () => {
|
||||
assert.deepEqual(protectionDeviceTypes, [
|
||||
"D02",
|
||||
"NH000",
|
||||
"NH00",
|
||||
"NH0",
|
||||
"NH1",
|
||||
"NH2",
|
||||
"NH3",
|
||||
"LS",
|
||||
"FI",
|
||||
"FI_LS",
|
||||
"AFDD",
|
||||
]);
|
||||
assert.equal("D01" in allowedRatedCurrentsAByProtectionDeviceType, false);
|
||||
assert.equal("NH4" in allowedRatedCurrentsAByProtectionDeviceType, false);
|
||||
});
|
||||
|
||||
it("exposes the agreed device-specific rated currents", () => {
|
||||
assert.deepEqual(allowedRatedCurrentsAByProtectionDeviceType.D02, [
|
||||
20, 25, 32, 35, 40, 50, 63,
|
||||
]);
|
||||
assert.deepEqual(allowedRatedCurrentsAByProtectionDeviceType.NH000, [
|
||||
6, 10, 16, 20, 25, 32, 35, 40, 50, 63, 80, 100, 125, 160,
|
||||
]);
|
||||
assert.deepEqual(
|
||||
allowedRatedCurrentsAByProtectionDeviceType.NH00,
|
||||
allowedRatedCurrentsAByProtectionDeviceType.NH000
|
||||
);
|
||||
assert.deepEqual(allowedRatedCurrentsAByProtectionDeviceType.NH0, [
|
||||
6, 10, 16, 20, 25, 32, 35, 40, 50, 63, 80, 100, 125, 160, 200, 224,
|
||||
250,
|
||||
]);
|
||||
assert.deepEqual(allowedRatedCurrentsAByProtectionDeviceType.NH1, [
|
||||
16, 20, 25, 32, 35, 40, 50, 63, 80, 100, 125, 160, 200, 224, 250,
|
||||
]);
|
||||
assert.deepEqual(allowedRatedCurrentsAByProtectionDeviceType.NH2, [
|
||||
25, 32, 35, 40, 50, 63, 80, 100, 125, 160, 200, 224, 250, 300, 315,
|
||||
355, 400,
|
||||
]);
|
||||
assert.deepEqual(allowedRatedCurrentsAByProtectionDeviceType.NH3, [
|
||||
50, 63, 80, 100, 125, 160, 200, 224, 250, 315, 355, 400, 500, 630,
|
||||
800,
|
||||
]);
|
||||
assert.deepEqual(allowedRatedCurrentsAByProtectionDeviceType.FI, [
|
||||
16, 25, 40, 63, 80, 100, 125,
|
||||
]);
|
||||
assert.deepEqual(allowedRatedCurrentsAByProtectionDeviceType.LS, [
|
||||
6, 10, 13, 16, 20, 25, 32, 40, 50, 63, 80, 100, 125,
|
||||
]);
|
||||
assert.deepEqual(
|
||||
allowedRatedCurrentsAByProtectionDeviceType.FI_LS,
|
||||
allowedRatedCurrentsAByProtectionDeviceType.LS
|
||||
);
|
||||
assert.deepEqual(
|
||||
allowedRatedCurrentsAByProtectionDeviceType.AFDD,
|
||||
allowedRatedCurrentsAByProtectionDeviceType.LS
|
||||
);
|
||||
assert.deepEqual(fuseUtilizationCategories, ["gG", "gR"]);
|
||||
assert.deepEqual(breakerTripCharacteristics, ["B", "C", "D", "Z"]);
|
||||
assert.deepEqual(rcdTypes, ["A", "AC", "B", "B+"]);
|
||||
assert.deepEqual(ratedResidualCurrentsMa, [10, 30, 100, 300, 500]);
|
||||
});
|
||||
});
|
||||
|
||||
describe("protection device validation", () => {
|
||||
it("accepts valid fuse, breaker, RCD, combined and AFDD configurations", () => {
|
||||
const configurations = [
|
||||
{
|
||||
type: "D02",
|
||||
ratedCurrentA: 20,
|
||||
fuseUtilizationCategory: "gG",
|
||||
},
|
||||
{
|
||||
type: "NH3",
|
||||
ratedCurrentA: 800,
|
||||
fuseUtilizationCategory: "gR",
|
||||
},
|
||||
{
|
||||
type: "LS",
|
||||
ratedCurrentA: 13,
|
||||
tripCharacteristic: "Z",
|
||||
},
|
||||
{
|
||||
type: "FI",
|
||||
ratedCurrentA: 40,
|
||||
rcdType: "A",
|
||||
ratedResidualCurrentMa: 30,
|
||||
},
|
||||
{
|
||||
type: "FI_LS",
|
||||
ratedCurrentA: 16,
|
||||
tripCharacteristic: "B",
|
||||
rcdType: "A",
|
||||
ratedResidualCurrentMa: 30,
|
||||
},
|
||||
{
|
||||
type: "AFDD",
|
||||
ratedCurrentA: 16,
|
||||
tripCharacteristic: "B",
|
||||
},
|
||||
];
|
||||
|
||||
for (const configuration of configurations) {
|
||||
const result =
|
||||
protectionDeviceConfigurationSchema.safeParse(configuration);
|
||||
assert.equal(
|
||||
result.success,
|
||||
true,
|
||||
result.success
|
||||
? undefined
|
||||
: `${configuration.type}: ${result.error.message}`
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
it("rejects rated currents that do not belong to the selected type", () => {
|
||||
assert.equal(
|
||||
protectionDeviceConfigurationSchema.safeParse({
|
||||
type: "D02",
|
||||
ratedCurrentA: 16,
|
||||
fuseUtilizationCategory: "gG",
|
||||
}).success,
|
||||
false
|
||||
);
|
||||
});
|
||||
|
||||
it("requires and forbids fields according to device function", () => {
|
||||
assert.equal(
|
||||
protectionDeviceConfigurationSchema.safeParse({
|
||||
type: "LS",
|
||||
ratedCurrentA: 16,
|
||||
}).success,
|
||||
false
|
||||
);
|
||||
assert.equal(
|
||||
protectionDeviceConfigurationSchema.safeParse({
|
||||
type: "FI",
|
||||
ratedCurrentA: 40,
|
||||
tripCharacteristic: "B",
|
||||
rcdType: "A",
|
||||
ratedResidualCurrentMa: 30,
|
||||
}).success,
|
||||
false
|
||||
);
|
||||
assert.equal(
|
||||
protectionDeviceConfigurationSchema.safeParse({
|
||||
type: "AFDD",
|
||||
ratedCurrentA: 16,
|
||||
tripCharacteristic: "B",
|
||||
rcdType: "A",
|
||||
ratedResidualCurrentMa: 30,
|
||||
}).success,
|
||||
false
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe("protection device defaults", () => {
|
||||
it("creates the agreed circuit defaults", () => {
|
||||
assert.deepEqual(createDefaultCircuitProtection("lighting"), {
|
||||
type: "LS",
|
||||
ratedCurrentA: 10,
|
||||
tripCharacteristic: "B",
|
||||
});
|
||||
assert.deepEqual(createDefaultCircuitProtection("single_phase"), {
|
||||
type: "FI_LS",
|
||||
ratedCurrentA: 16,
|
||||
tripCharacteristic: "B",
|
||||
rcdType: "A",
|
||||
ratedResidualCurrentMa: 30,
|
||||
});
|
||||
assert.deepEqual(createDefaultCircuitProtection("three_phase"), {
|
||||
type: "FI_LS",
|
||||
ratedCurrentA: 16,
|
||||
tripCharacteristic: "B",
|
||||
rcdType: "A",
|
||||
ratedResidualCurrentMa: 30,
|
||||
});
|
||||
});
|
||||
|
||||
it("creates a 40 A, type A, 30 mA group RCD", () => {
|
||||
assert.deepEqual(createDefaultGroupRcd(), {
|
||||
type: "FI",
|
||||
ratedCurrentA: 40,
|
||||
rcdType: "A",
|
||||
ratedResidualCurrentMa: 30,
|
||||
});
|
||||
});
|
||||
|
||||
it("returns independent default objects", () => {
|
||||
const first = createDefaultCircuitProtection("lighting");
|
||||
const second = createDefaultCircuitProtection("lighting");
|
||||
assert.notEqual(first, second);
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user