Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions .changeset/v03-scenarios-assertions.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
---
'@ocpp-debugkit/toolkit': minor
---

Add 5 new scenarios using rich assertions (15 total):
- `slow-csms-response` — SLOW_RESPONSE + timing assertion
- `meter-anomaly` — METER_VALUE_ANOMALY + payload_field/event_count assertions
- `short-session` — SUSPICIOUS_SESSION_DURATION + session_state/event_count assertions
- `heartbeat-irregular` — HEARTBEAT_INTERVAL_VIOLATION + event_count assertion
- `unresponsive-csms` — UNRESPONSIVE_CSMS + failure_severity/failure_count assertions

Add `compareScenarioReports()` — compare two scenario evaluation results for regression testing.
1 change: 1 addition & 0 deletions CURRENT_STATE.md
Original file line number Diff line number Diff line change
Expand Up @@ -202,6 +202,7 @@ integration examples, and contributor onboarding.
`METER_VALUE_ANOMALY`, `UNRESPONSIVE_CSMS` (15 total rules)
- ✅ Trace diffing — `diffTraces()` API (Issue #85)
- ✅ Rich scenario assertions — 8 assertion types, `evaluateScenario()` (Issue #86)
- ✅ Assert-based scenarios — 5 new scenarios (15 total) + `compareScenarioReports()` (Issue #87)

## What's Next

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
export default {
name: 'heartbeat-irregular',
description:
'Heartbeats sent at irregular intervals — 300s expected, but 600s gap observed. Expects HEARTBEAT_INTERVAL_VIOLATION failure. Uses event_count assertion for heartbeats.',
trace: {
traceId: 'scenario-heartbeat-irregular',
metadata: {
stationId: 'CS-SYNTHETIC-014',
ocppVersion: '1.6',
source: 'synthetic-scenario',
description: 'Heartbeats sent at irregular intervals — 300s expected, 600s gap observed.',
},
events: [
{
timestamp: '2024-01-15T12:00:00.000Z',
direction: 'CS_TO_CSMS',
message: [
2,
'msg-001',
'BootNotification',
{
chargePointVendor: 'SyntheticVendor',
chargePointModel: 'SM-100',
chargePointSerialNumber: 'CS-SYNTHETIC-014',
firmwareVersion: '1.0.0',
},
],
},
{
timestamp: '2024-01-15T12:00:00.500Z',
direction: 'CSMS_TO_CS',
message: [
3,
'msg-001',
{
currentTime: '2024-01-15T12:00:00.500Z',
interval: 300,
status: 'Accepted',
},
],
},
{
timestamp: '2024-01-15T12:05:00.000Z',
direction: 'CS_TO_CSMS',
message: [2, 'msg-hb-1', 'Heartbeat', {}],
},
{
timestamp: '2024-01-15T12:05:00.500Z',
direction: 'CSMS_TO_CS',
message: [3, 'msg-hb-1', { currentTime: '2024-01-15T12:05:00.500Z' }],
},
{
timestamp: '2024-01-15T12:15:00.000Z',
direction: 'CS_TO_CSMS',
message: [2, 'msg-hb-2', 'Heartbeat', {}],
},
{
timestamp: '2024-01-15T12:15:00.500Z',
direction: 'CSMS_TO_CS',
message: [3, 'msg-hb-2', { currentTime: '2024-01-15T12:15:00.500Z' }],
},
{
timestamp: '2024-01-15T12:25:00.000Z',
direction: 'CS_TO_CSMS',
message: [2, 'msg-hb-3', 'Heartbeat', {}],
},
{
timestamp: '2024-01-15T12:25:00.500Z',
direction: 'CSMS_TO_CS',
message: [3, 'msg-hb-3', { currentTime: '2024-01-15T12:25:00.500Z' }],
},
],
},
expectedFailures: ['HEARTBEAT_INTERVAL_VIOLATION'],
assertions: [
{
type: 'event_count',
params: { min: 3, action: 'Heartbeat' },
},
],
};
153 changes: 153 additions & 0 deletions packages/toolkit/src/scenarios/__scenarios__/meter-anomaly.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,153 @@
export default {
name: 'meter-anomaly',
description:
'Meter values decrease during an active transaction (non-monotonic). Expects METER_VALUE_ANOMALY failure. Uses payload_field assertion to verify meter values.',
trace: {
traceId: 'scenario-meter-anomaly',
metadata: {
stationId: 'CS-SYNTHETIC-012',
ocppVersion: '1.6',
source: 'synthetic-scenario',
description:
'Meter reading decreases from 5000 to 3000 during charging — non-monotonic anomaly.',
},
events: [
{
timestamp: '2024-01-15T14: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-15T14:00:00.500Z',
direction: 'CSMS_TO_CS',
message: [
3,
'msg-001',
{
currentTime: '2024-01-15T14:00:00.500Z',
interval: 300,
status: 'Accepted',
},
],
},
{
timestamp: '2024-01-15T14:01:00.000Z',
direction: 'CS_TO_CSMS',
message: [2, 'msg-002', 'Authorize', { idTag: 'SYNTHETIC-TAG-012' }],
},
{
timestamp: '2024-01-15T14:01:00.500Z',
direction: 'CSMS_TO_CS',
message: [3, 'msg-002', { idTagInfo: { status: 'Accepted' } }],
},
{
timestamp: '2024-01-15T14:02:00.000Z',
direction: 'CS_TO_CSMS',
message: [
2,
'msg-003',
'StartTransaction',
{ connectorId: 1, idTag: 'SYNTHETIC-TAG-012', meterStart: 0 },
],
},
{
timestamp: '2024-01-15T14:02:00.500Z',
direction: 'CSMS_TO_CS',
message: [3, 'msg-003', { idTagInfo: { status: 'Accepted' }, transactionId: 100012 }],
},
{
timestamp: '2024-01-15T14:05:00.000Z',
direction: 'CS_TO_CSMS',
message: [
2,
'msg-004',
'MeterValues',
{
connectorId: 1,
transactionId: 100012,
meterValue: [{ sampledValue: [{ value: '5000' }] }],
},
],
},
{
timestamp: '2024-01-15T14:05:00.500Z',
direction: 'CSMS_TO_CS',
message: [3, 'msg-004', {}],
},
{
timestamp: '2024-01-15T14:06:00.000Z',
direction: 'CS_TO_CSMS',
message: [
2,
'msg-005',
'MeterValues',
{
connectorId: 1,
transactionId: 100012,
meterValue: [{ sampledValue: [{ value: '3000' }] }],
},
],
},
{
timestamp: '2024-01-15T14:06:00.500Z',
direction: 'CSMS_TO_CS',
message: [3, 'msg-005', {}],
},
{
timestamp: '2024-01-15T14:10:00.000Z',
direction: 'CS_TO_CSMS',
message: [2, 'msg-006', 'Heartbeat', {}],
},
{
timestamp: '2024-01-15T14:10:00.500Z',
direction: 'CSMS_TO_CS',
message: [3, 'msg-006', { currentTime: '2024-01-15T14:10:00.500Z' }],
},
{
timestamp: '2024-01-15T14:30:00.000Z',
direction: 'CS_TO_CSMS',
message: [
2,
'msg-007',
'StopTransaction',
{
transactionId: 100012,
idTag: 'SYNTHETIC-TAG-012',
meterStop: 3000,
reason: 'EVDisconnected',
},
],
},
{
timestamp: '2024-01-15T14:30:00.500Z',
direction: 'CSMS_TO_CS',
message: [3, 'msg-007', { idTagInfo: { status: 'Accepted' } }],
},
],
},
expectedFailures: ['METER_VALUE_ANOMALY'],
assertions: [
{
type: 'payload_field',
params: {
action: 'StartTransaction',
field: 'connectorId',
equals: 1,
},
},
{
type: 'event_count',
params: { min: 2, max: 2, action: 'MeterValues' },
},
],
};
129 changes: 129 additions & 0 deletions packages/toolkit/src/scenarios/__scenarios__/short-session.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
export default {
name: 'short-session',
description:
'Session lasts only 10 seconds — suspiciously short. Expects SUSPICIOUS_SESSION_DURATION failure. Uses session_state and event_count assertions.',
trace: {
traceId: 'scenario-short-session',
metadata: {
stationId: 'CS-SYNTHETIC-013',
ocppVersion: '1.6',
source: 'synthetic-scenario',
description: 'Transaction starts and stops within 10 seconds — suspiciously short.',
},
events: [
{
timestamp: '2024-01-15T09:00:00.000Z',
direction: 'CS_TO_CSMS',
message: [
2,
'msg-001',
'BootNotification',
{
chargePointVendor: 'SyntheticVendor',
chargePointModel: 'SM-100',
chargePointSerialNumber: 'CS-SYNTHETIC-013',
firmwareVersion: '1.0.0',
},
],
},
{
timestamp: '2024-01-15T09:00:00.500Z',
direction: 'CSMS_TO_CS',
message: [
3,
'msg-001',
{
currentTime: '2024-01-15T09:00:00.500Z',
interval: 300,
status: 'Accepted',
},
],
},
{
timestamp: '2024-01-15T09:00:01.000Z',
direction: 'CS_TO_CSMS',
message: [2, 'msg-002', 'Authorize', { idTag: 'SYNTHETIC-TAG-013' }],
},
{
timestamp: '2024-01-15T09:00:01.500Z',
direction: 'CSMS_TO_CS',
message: [3, 'msg-002', { idTagInfo: { status: 'Accepted' } }],
},
{
timestamp: '2024-01-15T09:00:02.000Z',
direction: 'CS_TO_CSMS',
message: [
2,
'msg-003',
'StartTransaction',
{ connectorId: 1, idTag: 'SYNTHETIC-TAG-013', meterStart: 0 },
],
},
{
timestamp: '2024-01-15T09:00:02.500Z',
direction: 'CSMS_TO_CS',
message: [3, 'msg-003', { idTagInfo: { status: 'Accepted' }, transactionId: 100013 }],
},
{
timestamp: '2024-01-15T09:00:03.000Z',
direction: 'CS_TO_CSMS',
message: [
2,
'msg-004',
'MeterValues',
{
connectorId: 1,
transactionId: 100013,
meterValue: [{ sampledValue: [{ value: '100' }] }],
},
],
},
{
timestamp: '2024-01-15T09:00:03.500Z',
direction: 'CSMS_TO_CS',
message: [3, 'msg-004', {}],
},
{
timestamp: '2024-01-15T09:00:04.000Z',
direction: 'CS_TO_CSMS',
message: [2, 'msg-005', 'Heartbeat', {}],
},
{
timestamp: '2024-01-15T09:00:04.500Z',
direction: 'CSMS_TO_CS',
message: [3, 'msg-005', { currentTime: '2024-01-15T09:00:04.500Z' }],
},
{
timestamp: '2024-01-15T09:00:10.000Z',
direction: 'CS_TO_CSMS',
message: [
2,
'msg-006',
'StopTransaction',
{
transactionId: 100013,
idTag: 'SYNTHETIC-TAG-013',
meterStop: 100,
reason: 'EVDisconnected',
},
],
},
{
timestamp: '2024-01-15T09:00:10.500Z',
direction: 'CSMS_TO_CS',
message: [3, 'msg-006', { idTagInfo: { status: 'Accepted' } }],
},
],
},
expectedFailures: ['SUSPICIOUS_SESSION_DURATION'],
assertions: [
{
type: 'session_state',
params: { expected: 'completed' },
},
{
type: 'event_count',
params: { min: 1, action: 'StartTransaction' },
},
],
};
Loading
Loading