From 0e1cda099fb2f33c75046b88d19da3335bcd90ca Mon Sep 17 00:00:00 2001 From: YANGCHUNHONG3000 Date: Sun, 26 Jul 2026 09:23:12 +0800 Subject: [PATCH 1/2] feat(scenarios): add firmware update success scenario --- .../__scenarios__/firmware-update-success.ts | 91 +++++++++++++++++++ packages/toolkit/src/scenarios/index.test.ts | 7 +- packages/toolkit/src/scenarios/index.ts | 5 + tests/external-fixture/test.mjs | 4 +- 4 files changed, 103 insertions(+), 4 deletions(-) create mode 100644 packages/toolkit/src/scenarios/__scenarios__/firmware-update-success.ts diff --git a/packages/toolkit/src/scenarios/__scenarios__/firmware-update-success.ts b/packages/toolkit/src/scenarios/__scenarios__/firmware-update-success.ts new file mode 100644 index 0000000..aad045d --- /dev/null +++ b/packages/toolkit/src/scenarios/__scenarios__/firmware-update-success.ts @@ -0,0 +1,91 @@ +export default { + name: 'firmware-update-success', + description: + 'Successful firmware update: station boots, downloads firmware, installs it successfully, and continues operating normally. No failures expected.', + trace: { + traceId: 'scenario-firmware-update-success', + metadata: { + stationId: 'CS-SYNTHETIC-012', + ocppVersion: '1.6', + source: 'synthetic-scenario', + description: + 'Station boots, reports the firmware as downloaded and installed within seconds, then sends a heartbeat.', + }, + events: [ + { + timestamp: '2024-01-15T06:00:00.000Z', + direction: 'CS_TO_CSMS', + message: [ + 2, + 'msg-001', + 'BootNotification', + { + chargePointVendor: 'SyntheticVendor', + chargePointModel: 'SM-100', + chargePointSerialNumber: 'CS-SYNTHETIC-012', + firmwareVersion: '1.0.0', + }, + ], + }, + { + timestamp: '2024-01-15T06:00:00.500Z', + direction: 'CSMS_TO_CS', + message: [ + 3, + 'msg-001', + { + currentTime: '2024-01-15T06:00:00.500Z', + interval: 300, + status: 'Accepted', + }, + ], + }, + { + timestamp: '2024-01-15T06:00:30.000Z', + direction: 'CS_TO_CSMS', + message: [ + 2, + 'msg-002', + 'FirmwareStatusNotification', + { status: 'Downloaded' }, + ], + }, + { + timestamp: '2024-01-15T06:00:30.500Z', + direction: 'CSMS_TO_CS', + message: [3, 'msg-002', {}], + }, + { + timestamp: '2024-01-15T06:01:00.000Z', + direction: 'CS_TO_CSMS', + message: [ + 2, + 'msg-003', + 'FirmwareStatusNotification', + { status: 'Installed' }, + ], + }, + { + timestamp: '2024-01-15T06:01:00.500Z', + direction: 'CSMS_TO_CS', + message: [3, 'msg-003', {}], + }, + { + timestamp: '2024-01-15T06:03:00.000Z', + direction: 'CS_TO_CSMS', + message: [2, 'msg-hb-1', 'Heartbeat', {}], + }, + { + timestamp: '2024-01-15T06:03:00.500Z', + direction: 'CSMS_TO_CS', + message: [ + 3, + 'msg-hb-1', + { currentTime: '2024-01-15T06:03:00.500Z' }, + ], + }, + ], + }, + expectedFailures: [], + assertions: [{ type: 'no_failures', params: {} }], +}; diff --git a/packages/toolkit/src/scenarios/index.test.ts b/packages/toolkit/src/scenarios/index.test.ts index 92ffafd..dcf3324 100644 --- a/packages/toolkit/src/scenarios/index.test.ts +++ b/packages/toolkit/src/scenarios/index.test.ts @@ -18,6 +18,7 @@ import { shortSessionScenario, heartbeatIrregularScenario, unresponsiveCsmsScenario, + firmwareUpdateSuccessScenario, } from './index.js'; import { parseTrace, buildSessionTimeline, detectFailures } from '../core/index.js'; @@ -26,8 +27,8 @@ import { parseTrace, buildSessionTimeline, detectFailures } from '../core/index. // --------------------------------------------------------------------------- describe('scenario registry', () => { - it('exports exactly 15 scenarios', () => { - expect(scenarios).toHaveLength(15); + it('exports exactly 16 scenarios', () => { + expect(scenarios).toHaveLength(16); }); it('exports scenario names in order', () => { @@ -47,6 +48,7 @@ describe('scenario registry', () => { 'short-session', 'heartbeat-irregular', 'unresponsive-csms', + 'firmware-update-success', ]); }); @@ -75,6 +77,7 @@ describe('scenario registry', () => { expect(getScenario('short-session')).toBe(shortSessionScenario); expect(getScenario('heartbeat-irregular')).toBe(heartbeatIrregularScenario); expect(getScenario('unresponsive-csms')).toBe(unresponsiveCsmsScenario); + expect(getScenario('firmware-update-success')).toBe(firmwareUpdateSuccessScenario); }); it('getScenario returns undefined for unknown name', () => { diff --git a/packages/toolkit/src/scenarios/index.ts b/packages/toolkit/src/scenarios/index.ts index 38aad78..7fd231b 100644 --- a/packages/toolkit/src/scenarios/index.ts +++ b/packages/toolkit/src/scenarios/index.ts @@ -20,6 +20,7 @@ import meterAnomaly from './__scenarios__/meter-anomaly.js'; import shortSession from './__scenarios__/short-session.js'; import heartbeatIrregular from './__scenarios__/heartbeat-irregular.js'; import unresponsiveCsms from './__scenarios__/unresponsive-csms.js'; +import firmwareUpdateSuccess from './__scenarios__/firmware-update-success.js'; // --------------------------------------------------------------------------- // Scenarios derived from core fixtures @@ -66,6 +67,7 @@ const meterAnomalyScenario: Scenario = meterAnomaly as unknown as Scenario; const shortSessionScenario: Scenario = shortSession as unknown as Scenario; const heartbeatIrregularScenario: Scenario = heartbeatIrregular as unknown as Scenario; const unresponsiveCsmsScenario: Scenario = unresponsiveCsms as unknown as Scenario; +const firmwareUpdateSuccessScenario: Scenario = firmwareUpdateSuccess as unknown as Scenario; // --------------------------------------------------------------------------- // Registry @@ -87,6 +89,7 @@ export const scenarios = [ shortSessionScenario, heartbeatIrregularScenario, unresponsiveCsmsScenario, + firmwareUpdateSuccessScenario, ] as const; export const scenarioNames = [ @@ -105,6 +108,7 @@ export const scenarioNames = [ 'short-session', 'heartbeat-irregular', 'unresponsive-csms', + 'firmware-update-success', ] as const; export { @@ -123,6 +127,7 @@ export { shortSessionScenario, heartbeatIrregularScenario, unresponsiveCsmsScenario, + firmwareUpdateSuccessScenario, }; export { compareScenarioReports } from './compare.js'; diff --git a/tests/external-fixture/test.mjs b/tests/external-fixture/test.mjs index 337b6e9..3b64809 100644 --- a/tests/external-fixture/test.mjs +++ b/tests/external-fixture/test.mjs @@ -158,8 +158,8 @@ const scenarios = await import('@ocpp-debugkit/toolkit/scenarios'); assert(Array.isArray(scenarios.scenarios), 'scenarios is an array'); assert( - scenarios.scenarios.length === 15, - `15 scenarios exported (got ${scenarios.scenarios.length})`, + scenarios.scenarios.length === 16, + `16 scenarios exported (got ${scenarios.scenarios.length})`, ); assert(typeof scenarios.getScenario === 'function', 'getScenario is a function'); assert(scenarios.getScenario('normal-session') !== undefined, 'normal-session scenario exists'); From 61e626fe530e4f42315696bcf44dad2c7846d26b Mon Sep 17 00:00:00 2001 From: Mike Yang <250981657@qq.com> Date: Mon, 27 Jul 2026 00:40:17 +0800 Subject: [PATCH 2/2] fix: address review feedback (station ID, doc counts, changeset) --- .changeset/warm-dragons-smile.md | 5 +++++ README.md | 2 +- packages/toolkit/README.md | 2 +- .../src/scenarios/__scenarios__/firmware-update-success.ts | 4 ++-- 4 files changed, 9 insertions(+), 4 deletions(-) create mode 100644 .changeset/warm-dragons-smile.md diff --git a/.changeset/warm-dragons-smile.md b/.changeset/warm-dragons-smile.md new file mode 100644 index 0000000..d39165b --- /dev/null +++ b/.changeset/warm-dragons-smile.md @@ -0,0 +1,5 @@ +--- +"@ocpp-debugkit/toolkit": minor +--- + +feat(scenarios): add firmware update success scenario diff --git a/README.md b/README.md index 170cce1..46b71d3 100644 --- a/README.md +++ b/README.md @@ -22,7 +22,7 @@ validate behavior against known scenarios. firmware update failures, suspicious session duration, slow CSMS responses, heartbeat interval violations, meter value anomalies, unresponsive CSMS, and repeated boot notifications. -- **Scenario Evaluator** — 15 predefined scenarios with expected failure +- **Scenario Evaluator** — 16 predefined scenarios with expected failure outcomes for testing the analysis engine. Supports external scenario files. - **Replay Engine** — Deterministic, pure replay engine with step forward/back, jump-to-event, and configurable playback speed. diff --git a/packages/toolkit/README.md b/packages/toolkit/README.md index c68dcf8..5c3c3e3 100644 --- a/packages/toolkit/README.md +++ b/packages/toolkit/README.md @@ -20,7 +20,7 @@ report generation, React components, and CLI. firmware update failures, suspicious session duration, slow CSMS responses, heartbeat interval violations, meter value anomalies, unresponsive CSMS, and repeated boot notifications. -- **Scenario Evaluator** — 15 predefined scenarios with expected failure +- **Scenario Evaluator** — 16 predefined scenarios with expected failure outcomes for testing the analysis engine. Supports external scenario files. - **Replay Engine** — Deterministic, pure replay engine with step forward/back, jump-to-event, and configurable playback speed. No timers or I/O. diff --git a/packages/toolkit/src/scenarios/__scenarios__/firmware-update-success.ts b/packages/toolkit/src/scenarios/__scenarios__/firmware-update-success.ts index aad045d..4f13e64 100644 --- a/packages/toolkit/src/scenarios/__scenarios__/firmware-update-success.ts +++ b/packages/toolkit/src/scenarios/__scenarios__/firmware-update-success.ts @@ -5,7 +5,7 @@ export default { trace: { traceId: 'scenario-firmware-update-success', metadata: { - stationId: 'CS-SYNTHETIC-012', + stationId: 'CS-SYNTHETIC-016', ocppVersion: '1.6', source: 'synthetic-scenario', description: @@ -22,7 +22,7 @@ export default { { chargePointVendor: 'SyntheticVendor', chargePointModel: 'SM-100', - chargePointSerialNumber: 'CS-SYNTHETIC-012', + chargePointSerialNumber: 'CS-SYNTHETIC-016', firmwareVersion: '1.0.0', }, ],