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"; import "./circuit-protection-project-command.repository.test.js"; import { getCircuitProtectionEditorInitialValue, toCircuitProtectionSnapshot, } from "../src/frontend/utils/circuit-protection-editing.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("maps editor values to complete circuit-owned snapshots", () => { const initial = getCircuitProtectionEditorInitialValue( "single_phase", undefined ); assert.deepEqual(initial, { type: "FI_LS", ratedCurrentA: 16, tripCharacteristic: "B", rcdType: "A", ratedResidualCurrentMa: 30, }); assert.deepEqual( toCircuitProtectionSnapshot("circuit-1", initial), { circuitId: "circuit-1", type: "FI_LS", ratedCurrentA: 16, fuseUtilizationCategory: null, tripCharacteristic: "B", rcdType: "A", ratedResidualCurrentMa: 30, } ); }); 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); }); });