refactored

This commit is contained in:
2026-05-27 13:33:52 +02:00
parent 4f98971ab3
commit ddf0cc54ce
31 changed files with 544 additions and 538 deletions
View File
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,293 @@
{
"meta": {
"vin": "WVWZZZE1ZMP010760",
"created_at": "2026-05-26T11:37:05.156831+00:00",
"interval_seconds": 30
},
"records": [
{
"ts": "2026-05-26T11:37:08.127749+00:00",
"charging": {
"chargingStatus": {
"chargingState": "ChargingState.CHARGE_PURPOSE_REACHED_CONSERVATION",
"chargePower": {
"value": 0.0,
"unit": "kW"
},
"remainingChargingTimeToComplete": {
"value": 0,
"unit": "min"
},
"chargeType": "ChargeType.AC"
},
"batteryStatus": {
"currentSOC": {
"value": 80,
"unit": "%"
},
"cruisingRangeElectric": {
"value": 280,
"unit": "km"
}
},
"chargingSettings": {
"targetSOC": {
"value": 80,
"unit": "%"
},
"maxChargeCurrentAC": "MaximumChargeCurrent.MAXIMUM",
"autoUnlockPlugWhenCharged": "UnlockPlugState.OFF"
},
"plugStatus": {
"plugConnectionState": "PlugConnectionState.CONNECTED",
"plugLockState": "PlugLockState.LOCKED",
"externalPower": "ExternalPower.READY"
}
},
"measurements": {
"odometerStatus": {
"odometer": {
"value": 67419,
"unit": "km"
}
},
"rangeStatus": {
"electricRange": {
"value": 280,
"unit": "km"
},
"totalRange": {
"value": 280,
"unit": "km"
}
},
"temperatureBatteryStatus": {
"temperatureHvBatteryMax": {
"value": 23.5,
"unit": "degC"
},
"temperatureHvBatteryMin": {
"value": 22.0,
"unit": "degC"
}
}
},
"climatisation": {
"climatisationStatus": {
"climatisationState": "ClimatizationState.OFF",
"remainingClimatisationTime": {
"value": 0,
"unit": "min"
}
},
"climatisationSettings": {
"targetTemperature": {
"value": 21.0,
"unit": "degC"
},
"unitInCar": "UnitInCar.CELSIUS"
},
"windowHeatingStatus": {
"front": "WindowHeatingState.OFF",
"rear": "WindowHeatingState.OFF"
}
},
"access": {
"accessStatus": {
"overallStatus": "OverallState.SAFE",
"doors": {
"bonnet": {
"lockState": "LockState.UNKNOWN",
"openState": "OpenState.CLOSED"
},
"trunk": {
"lockState": "LockState.LOCKED",
"openState": "OpenState.CLOSED"
},
"rearRight": {
"lockState": "LockState.LOCKED",
"openState": "OpenState.CLOSED"
},
"rearLeft": {
"lockState": "LockState.LOCKED",
"openState": "OpenState.CLOSED"
},
"frontRight": {
"lockState": "LockState.LOCKED",
"openState": "OpenState.CLOSED"
},
"frontLeft": {
"lockState": "LockState.LOCKED",
"openState": "OpenState.CLOSED"
}
},
"windows": {
"sunRoof": {
"openState": "OpenState.UNSUPPORTED"
},
"roofCover": {
"openState": "OpenState.UNSUPPORTED"
},
"sunRoofRear": {
"openState": "OpenState.UNSUPPORTED"
},
"frontLeft": {
"openState": "OpenState.CLOSED"
},
"frontRight": {
"openState": "OpenState.CLOSED"
},
"rearLeft": {
"openState": "OpenState.CLOSED"
},
"rearRight": {
"openState": "OpenState.CLOSED"
}
}
}
}
},
{
"ts": "2026-05-26T11:37:43.660250+00:00",
"charging": {
"chargingStatus": {
"chargingState": "ChargingState.CHARGE_PURPOSE_REACHED_CONSERVATION",
"chargePower": {
"value": 0.0,
"unit": "kW"
},
"remainingChargingTimeToComplete": {
"value": 0,
"unit": "min"
},
"chargeType": "ChargeType.AC"
},
"batteryStatus": {
"currentSOC": {
"value": 80,
"unit": "%"
},
"cruisingRangeElectric": {
"value": 280,
"unit": "km"
}
},
"chargingSettings": {
"targetSOC": {
"value": 80,
"unit": "%"
},
"maxChargeCurrentAC": "MaximumChargeCurrent.MAXIMUM",
"autoUnlockPlugWhenCharged": "UnlockPlugState.OFF"
},
"plugStatus": {
"plugConnectionState": "PlugConnectionState.CONNECTED",
"plugLockState": "PlugLockState.LOCKED",
"externalPower": "ExternalPower.READY"
}
},
"measurements": {
"odometerStatus": {
"odometer": {
"value": 67419,
"unit": "km"
}
},
"rangeStatus": {
"electricRange": {
"value": 280,
"unit": "km"
},
"totalRange": {
"value": 280,
"unit": "km"
}
},
"temperatureBatteryStatus": {
"temperatureHvBatteryMax": {
"value": 23.5,
"unit": "degC"
},
"temperatureHvBatteryMin": {
"value": 22.0,
"unit": "degC"
}
}
},
"climatisation": {
"climatisationStatus": {
"climatisationState": "ClimatizationState.OFF",
"remainingClimatisationTime": {
"value": 0,
"unit": "min"
}
},
"climatisationSettings": {
"targetTemperature": {
"value": 21.0,
"unit": "degC"
},
"unitInCar": "UnitInCar.CELSIUS"
},
"windowHeatingStatus": {
"front": "WindowHeatingState.OFF",
"rear": "WindowHeatingState.OFF"
}
},
"access": {
"accessStatus": {
"overallStatus": "OverallState.SAFE",
"doors": {
"bonnet": {
"lockState": "LockState.UNKNOWN",
"openState": "OpenState.CLOSED"
},
"trunk": {
"lockState": "LockState.LOCKED",
"openState": "OpenState.CLOSED"
},
"rearRight": {
"lockState": "LockState.LOCKED",
"openState": "OpenState.CLOSED"
},
"rearLeft": {
"lockState": "LockState.LOCKED",
"openState": "OpenState.CLOSED"
},
"frontRight": {
"lockState": "LockState.LOCKED",
"openState": "OpenState.CLOSED"
},
"frontLeft": {
"lockState": "LockState.LOCKED",
"openState": "OpenState.CLOSED"
}
},
"windows": {
"sunRoof": {
"openState": "OpenState.UNSUPPORTED"
},
"roofCover": {
"openState": "OpenState.UNSUPPORTED"
},
"sunRoofRear": {
"openState": "OpenState.UNSUPPORTED"
},
"frontLeft": {
"openState": "OpenState.CLOSED"
},
"frontRight": {
"openState": "OpenState.CLOSED"
},
"rearLeft": {
"openState": "OpenState.CLOSED"
},
"rearRight": {
"openState": "OpenState.CLOSED"
}
}
}
}
}
]
}
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,485 @@
{
"meta": {
"vin": "WVWZZZE1ZMP010760",
"created_at": "2026-05-27T05:53:07.953894+00:00",
"interval_seconds": 30
},
"records": [
{
"ts": "2026-05-27T05:53:08.103087+00:00",
"charging": {
"chargingStatus": {
"chargingState": "off",
"chargeType": "invalid",
"chargePower": {
"value": 0.0,
"unit": "kW"
},
"remainingChargingTimeToComplete": {
"value": 0,
"unit": "min"
}
},
"batteryStatus": {
"currentSOC": {
"value": 58.0,
"unit": "%"
},
"cruisingRangeElectric": {
"value": 209.0,
"unit": "km"
}
},
"chargingSettings": {
"targetSOC": {
"value": 80.0,
"unit": "%"
},
"maxChargeCurrentAC": {
"value": 16.0,
"unit": "A"
},
"autoUnlockPlugWhenCharged": "False"
},
"plugStatus": {
"plugConnectionState": "disconnected",
"plugLockState": "unlocked",
"externalPower": "unavailable"
}
},
"measurements": {
"odometerStatus": {
"odometer": {
"value": 67489.0,
"unit": "km"
}
},
"rangeStatus": {
"electricRange": {
"value": 209.0,
"unit": "km"
},
"totalRange": {
"value": 209.0,
"unit": "km"
}
},
"temperatureBatteryStatus": {
"temperatureHvBatteryMax": {
"value": 295.15,
"unit": "degC"
},
"temperatureHvBatteryMin": {
"value": 294.15,
"unit": "degC"
}
},
"temperatureOutsideStatus": {
"temperatureOutside": {
"value": null,
"unit": "degC"
}
}
},
"readiness": {
"readinessStatus": {
"connectionState": {
"isOnline": false,
"isActive": true
}
}
},
"climatisation": {
"climatisationStatus": {
"climatisationState": "off",
"remainingClimatisationTime": {
"value": 0,
"unit": "min"
}
},
"climatisationSettings": {
"targetTemperature": {
"value": 21.0,
"unit": "degC"
}
}
},
"parking": {
"parkingPosition": {
"lat": null,
"lon": null
}
},
"access": {
"accessStatus": {
"overallStatus": "None",
"doors": {},
"windows": {}
}
},
"procedural": {
"range_at_100": {
"value": 360.3,
"unit": "km"
}
}
},
{
"ts": "2026-05-27T05:53:38.257550+00:00",
"charging": {
"chargingStatus": {
"chargingState": "off",
"chargeType": "invalid",
"chargePower": {
"value": 0.0,
"unit": "kW"
},
"remainingChargingTimeToComplete": {
"value": 0,
"unit": "min"
}
},
"batteryStatus": {
"currentSOC": {
"value": 58.0,
"unit": "%"
},
"cruisingRangeElectric": {
"value": 209.0,
"unit": "km"
}
},
"chargingSettings": {
"targetSOC": {
"value": 80.0,
"unit": "%"
},
"maxChargeCurrentAC": {
"value": 16.0,
"unit": "A"
},
"autoUnlockPlugWhenCharged": "False"
},
"plugStatus": {
"plugConnectionState": "disconnected",
"plugLockState": "unlocked",
"externalPower": "unavailable"
}
},
"measurements": {
"odometerStatus": {
"odometer": {
"value": 67489.0,
"unit": "km"
}
},
"rangeStatus": {
"electricRange": {
"value": 209.0,
"unit": "km"
},
"totalRange": {
"value": 209.0,
"unit": "km"
}
},
"temperatureBatteryStatus": {
"temperatureHvBatteryMax": {
"value": 295.15,
"unit": "degC"
},
"temperatureHvBatteryMin": {
"value": 294.15,
"unit": "degC"
}
},
"temperatureOutsideStatus": {
"temperatureOutside": {
"value": null,
"unit": "degC"
}
}
},
"readiness": {
"readinessStatus": {
"connectionState": {
"isOnline": false,
"isActive": true
}
}
},
"climatisation": {
"climatisationStatus": {
"climatisationState": "off",
"remainingClimatisationTime": {
"value": 0,
"unit": "min"
}
},
"climatisationSettings": {
"targetTemperature": {
"value": 21.0,
"unit": "degC"
}
}
},
"parking": {
"parkingPosition": {
"lat": null,
"lon": null
}
},
"access": {
"accessStatus": {
"overallStatus": "None",
"doors": {},
"windows": {}
}
},
"procedural": {
"range_at_100": {
"value": 360.3,
"unit": "km"
}
}
},
{
"ts": "2026-05-27T05:54:08.438969+00:00",
"charging": {
"chargingStatus": {
"chargingState": "off",
"chargeType": "invalid",
"chargePower": {
"value": 0.0,
"unit": "kW"
},
"remainingChargingTimeToComplete": {
"value": 0,
"unit": "min"
}
},
"batteryStatus": {
"currentSOC": {
"value": 58.0,
"unit": "%"
},
"cruisingRangeElectric": {
"value": 209.0,
"unit": "km"
}
},
"chargingSettings": {
"targetSOC": {
"value": 80.0,
"unit": "%"
},
"maxChargeCurrentAC": {
"value": 16.0,
"unit": "A"
},
"autoUnlockPlugWhenCharged": "False"
},
"plugStatus": {
"plugConnectionState": "disconnected",
"plugLockState": "unlocked",
"externalPower": "unavailable"
}
},
"measurements": {
"odometerStatus": {
"odometer": {
"value": 67489.0,
"unit": "km"
}
},
"rangeStatus": {
"electricRange": {
"value": 209.0,
"unit": "km"
},
"totalRange": {
"value": 209.0,
"unit": "km"
}
},
"temperatureBatteryStatus": {
"temperatureHvBatteryMax": {
"value": 295.15,
"unit": "degC"
},
"temperatureHvBatteryMin": {
"value": 294.15,
"unit": "degC"
}
},
"temperatureOutsideStatus": {
"temperatureOutside": {
"value": null,
"unit": "degC"
}
}
},
"readiness": {
"readinessStatus": {
"connectionState": {
"isOnline": false,
"isActive": true
}
}
},
"climatisation": {
"climatisationStatus": {
"climatisationState": "off",
"remainingClimatisationTime": {
"value": 0,
"unit": "min"
}
},
"climatisationSettings": {
"targetTemperature": {
"value": 21.0,
"unit": "degC"
}
}
},
"parking": {
"parkingPosition": {
"lat": null,
"lon": null
}
},
"access": {
"accessStatus": {
"overallStatus": "None",
"doors": {},
"windows": {}
}
},
"procedural": {
"range_at_100": {
"value": 360.3,
"unit": "km"
}
}
},
{
"ts": "2026-05-27T05:54:38.603661+00:00",
"charging": {
"chargingStatus": {
"chargingState": "off",
"chargeType": "invalid",
"chargePower": {
"value": 0.0,
"unit": "kW"
},
"remainingChargingTimeToComplete": {
"value": 0,
"unit": "min"
}
},
"batteryStatus": {
"currentSOC": {
"value": 58.0,
"unit": "%"
},
"cruisingRangeElectric": {
"value": 209.0,
"unit": "km"
}
},
"chargingSettings": {
"targetSOC": {
"value": 80.0,
"unit": "%"
},
"maxChargeCurrentAC": {
"value": 16.0,
"unit": "A"
},
"autoUnlockPlugWhenCharged": "False"
},
"plugStatus": {
"plugConnectionState": "disconnected",
"plugLockState": "unlocked",
"externalPower": "unavailable"
}
},
"measurements": {
"odometerStatus": {
"odometer": {
"value": 67489.0,
"unit": "km"
}
},
"rangeStatus": {
"electricRange": {
"value": 209.0,
"unit": "km"
},
"totalRange": {
"value": 209.0,
"unit": "km"
}
},
"temperatureBatteryStatus": {
"temperatureHvBatteryMax": {
"value": 295.15,
"unit": "degC"
},
"temperatureHvBatteryMin": {
"value": 294.15,
"unit": "degC"
}
},
"temperatureOutsideStatus": {
"temperatureOutside": {
"value": null,
"unit": "degC"
}
}
},
"readiness": {
"readinessStatus": {
"connectionState": {
"isOnline": false,
"isActive": true
}
}
},
"climatisation": {
"climatisationStatus": {
"climatisationState": "off",
"remainingClimatisationTime": {
"value": 0,
"unit": "min"
}
},
"climatisationSettings": {
"targetTemperature": {
"value": 21.0,
"unit": "degC"
}
}
},
"parking": {
"parkingPosition": {
"lat": null,
"lon": null
}
},
"access": {
"accessStatus": {
"overallStatus": "None",
"doors": {},
"windows": {}
}
},
"procedural": {
"range_at_100": {
"value": 360.3,
"unit": "km"
}
}
}
]
}
@@ -0,0 +1,128 @@
{
"meta": {
"vin": "WVWZZZE1ZMP010760",
"created_at": "2026-05-27T05:55:03.027525+00:00",
"interval_seconds": 30
},
"records": [
{
"ts": "2026-05-27T05:55:03.219238+00:00",
"charging": {
"chargingStatus": {
"chargingState": "off",
"chargeType": "invalid",
"chargePower": {
"value": 0.0,
"unit": "kW"
},
"remainingChargingTimeToComplete": {
"value": 0,
"unit": "min"
}
},
"batteryStatus": {
"currentSOC": {
"value": 58.0,
"unit": "%"
},
"cruisingRangeElectric": {
"value": 209.0,
"unit": "km"
}
},
"chargingSettings": {
"targetSOC": {
"value": 80.0,
"unit": "%"
},
"maxChargeCurrentAC": {
"value": 16.0,
"unit": "A"
},
"autoUnlockPlugWhenCharged": "False"
},
"plugStatus": {
"plugConnectionState": "disconnected",
"plugLockState": "unlocked",
"externalPower": "unavailable"
}
},
"measurements": {
"odometerStatus": {
"odometer": {
"value": 67489.0,
"unit": "km"
}
},
"rangeStatus": {
"electricRange": {
"value": 209.0,
"unit": "km"
},
"totalRange": {
"value": 209.0,
"unit": "km"
}
},
"temperatureBatteryStatus": {
"temperatureHvBatteryMax": {
"value": 295.15,
"unit": "degC"
},
"temperatureHvBatteryMin": {
"value": 294.15,
"unit": "degC"
}
},
"temperatureOutsideStatus": {
"temperatureOutside": {
"value": null,
"unit": "degC"
}
}
},
"readiness": {
"readinessStatus": {
"connectionState": {
"isOnline": false,
"isActive": true
}
}
},
"climatisation": {
"climatisationStatus": {
"climatisationState": "off",
"remainingClimatisationTime": {
"value": 0,
"unit": "min"
}
},
"climatisationSettings": {
"targetTemperature": {
"value": 21.0,
"unit": "degC"
}
}
},
"parking": {
"parkingPosition": {
"lat": null,
"lon": null
}
},
"access": {
"accessStatus": {
"overallStatus": "None",
"doors": {},
"windows": {}
}
},
"procedural": {
"range_at_100": {
"value": 360.3,
"unit": "km"
}
}
}
]
}
@@ -0,0 +1,130 @@
{
"meta": {
"vin": "WVWZZZE1ZMP010760",
"created_at": "2026-05-27T06:17:18.385566+00:00",
"interval_seconds": 30
},
"records": [
{
"ts": "2026-05-27T06:17:18.554491+00:00",
"charging": {
"chargingStatus": {
"chargingState": "off",
"chargeType": "invalid",
"chargePower": {
"value": 0.0,
"unit": "kW"
},
"remainingChargingTimeToComplete": {
"value": 0,
"unit": "min"
}
},
"batteryStatus": {
"currentSOC": {
"value": 58.0,
"unit": "%"
},
"cruisingRangeElectric": {
"value": 209.0,
"unit": "km"
}
},
"chargingSettings": {
"targetSOC": {
"value": 80.0,
"unit": "%"
},
"maxChargeCurrentAC": {
"value": 16.0,
"unit": "A"
},
"autoUnlockPlugWhenCharged": "False"
},
"plugStatus": {
"plugConnectionState": "disconnected",
"plugLockState": "unlocked",
"externalPower": "unavailable"
}
},
"measurements": {
"odometerStatus": {
"odometer": {
"value": 67489.0,
"unit": "km"
}
},
"rangeStatus": {
"electricRange": {
"value": 209.0,
"unit": "km"
},
"totalRange": {
"value": 209.0,
"unit": "km"
}
},
"temperatureBatteryStatus": {
"temperatureHvBatteryMax": {
"value": 295.15,
"unit": "degC"
},
"temperatureHvBatteryMin": {
"value": 294.15,
"unit": "degC"
}
},
"temperatureOutsideStatus": {
"temperatureOutside": {
"value": null,
"unit": "degC"
}
}
},
"vehicle": {
"vehicle": {
"state": "unknown vehicle state"
}
},
"connectivity": {
"connection": {
"state": "reachable"
}
},
"climatisation": {
"climatisationStatus": {
"climatisationState": "off",
"remainingClimatisationTime": {
"value": 0,
"unit": "min"
}
},
"climatisationSettings": {
"targetTemperature": {
"value": 21.0,
"unit": "degC"
}
}
},
"position": {
"position": {
"lat": null,
"lon": null
}
},
"doors": {
"doors": {
"overallState": "None",
"doors": {},
"windows": {}
}
},
"procedural": {
"range_at_100": {
"value": 360.3,
"unit": "km"
}
}
}
]
}
@@ -0,0 +1,130 @@
{
"meta": {
"vin": "WVWZZZE1ZMP010760",
"created_at": "2026-05-27T06:18:28.783719+00:00",
"interval_seconds": 30
},
"records": [
{
"ts": "2026-05-27T06:18:28.981339+00:00",
"charging": {
"chargingStatus": {
"chargingState": "off",
"chargeType": "invalid",
"chargePower": {
"value": 0.0,
"unit": "kW"
},
"remainingChargingTimeToComplete": {
"value": 0,
"unit": "min"
}
},
"batteryStatus": {
"currentSOC": {
"value": 58.0,
"unit": "%"
},
"cruisingRangeElectric": {
"value": 209.0,
"unit": "km"
}
},
"chargingSettings": {
"targetSOC": {
"value": 80.0,
"unit": "%"
},
"maxChargeCurrentAC": {
"value": 16.0,
"unit": "A"
},
"autoUnlockPlugWhenCharged": "False"
},
"plugStatus": {
"plugConnectionState": "disconnected",
"plugLockState": "unlocked",
"externalPower": "unavailable"
}
},
"measurements": {
"odometerStatus": {
"odometer": {
"value": 67489.0,
"unit": "km"
}
},
"rangeStatus": {
"electricRange": {
"value": 209.0,
"unit": "km"
},
"totalRange": {
"value": 209.0,
"unit": "km"
}
},
"temperatureBatteryStatus": {
"temperatureHvBatteryMax": {
"value": 295.15,
"unit": "degC"
},
"temperatureHvBatteryMin": {
"value": 294.15,
"unit": "degC"
}
},
"temperatureOutsideStatus": {
"temperatureOutside": {
"value": null,
"unit": "degC"
}
}
},
"vehicle": {
"vehicle": {
"state": "unknown vehicle state"
}
},
"connectivity": {
"connection": {
"state": "reachable"
}
},
"climatisation": {
"climatisationStatus": {
"climatisationState": "off",
"remainingClimatisationTime": {
"value": 0,
"unit": "min"
}
},
"climatisationSettings": {
"targetTemperature": {
"value": 21.0,
"unit": "degC"
}
}
},
"position": {
"position": {
"lat": null,
"lon": null
}
},
"doors": {
"doors": {
"overallState": "None",
"doors": {},
"windows": {}
}
},
"procedural": {
"range_at_100": {
"value": 360.3,
"unit": "km"
}
}
}
]
}
@@ -0,0 +1,130 @@
{
"meta": {
"vin": "WVWZZZE1ZMP010760",
"created_at": "2026-05-27T06:21:01.933728+00:00",
"interval_seconds": 30
},
"records": [
{
"ts": "2026-05-27T06:21:02.150738+00:00",
"charging": {
"chargingStatus": {
"chargingState": "off",
"chargeType": "invalid",
"chargePower": {
"value": 0.0,
"unit": "kW"
},
"remainingChargingTimeToComplete": {
"value": 0,
"unit": "min"
}
},
"batteryStatus": {
"currentSOC": {
"value": 58.0,
"unit": "%"
},
"cruisingRangeElectric": {
"value": 209.0,
"unit": "km"
}
},
"chargingSettings": {
"targetSOC": {
"value": 80.0,
"unit": "%"
},
"maxChargeCurrentAC": {
"value": 16.0,
"unit": "A"
},
"autoUnlockPlugWhenCharged": "False"
},
"plugStatus": {
"plugConnectionState": "disconnected",
"plugLockState": "unlocked",
"externalPower": "unavailable"
}
},
"measurements": {
"odometerStatus": {
"odometer": {
"value": 67489.0,
"unit": "km"
}
},
"rangeStatus": {
"electricRange": {
"value": 209.0,
"unit": "km"
},
"totalRange": {
"value": 209.0,
"unit": "km"
}
},
"temperatureBatteryStatus": {
"temperatureHvBatteryMax": {
"value": 295.15,
"unit": "degC"
},
"temperatureHvBatteryMin": {
"value": 294.15,
"unit": "degC"
}
},
"temperatureOutsideStatus": {
"temperatureOutside": {
"value": null,
"unit": "degC"
}
}
},
"vehicle": {
"vehicle": {
"state": "unknown vehicle state"
}
},
"connectivity": {
"connection": {
"state": "reachable"
}
},
"climatisation": {
"climatisationStatus": {
"climatisationState": "off",
"remainingClimatisationTime": {
"value": 0,
"unit": "min"
}
},
"climatisationSettings": {
"targetTemperature": {
"value": 21.0,
"unit": "degC"
}
}
},
"position": {
"position": {
"lat": null,
"lon": null
}
},
"doors": {
"doors": {
"overallState": "None",
"doors": {},
"windows": {}
}
},
"procedural": {
"range_at_100": {
"value": 360.3,
"unit": "km"
}
}
}
]
}
@@ -0,0 +1,130 @@
{
"meta": {
"vin": "WVWZZZE1ZMP010760",
"created_at": "2026-05-27T06:33:08.561803+00:00",
"interval_seconds": 30
},
"records": [
{
"ts": "2026-05-27T06:33:08.745827+00:00",
"charging": {
"chargingStatus": {
"chargingState": "off",
"chargeType": "invalid",
"chargePower": {
"value": 0.0,
"unit": "kW"
},
"remainingChargingTimeToComplete": {
"value": 0,
"unit": "min"
}
},
"batteryStatus": {
"currentSOC": {
"value": 58.0,
"unit": "%"
},
"cruisingRangeElectric": {
"value": 209.0,
"unit": "km"
}
},
"chargingSettings": {
"targetSOC": {
"value": 80.0,
"unit": "%"
},
"maxChargeCurrentAC": {
"value": 16.0,
"unit": "A"
},
"autoUnlockPlugWhenCharged": "False"
},
"plugStatus": {
"plugConnectionState": "disconnected",
"plugLockState": "unlocked",
"externalPower": "unavailable"
}
},
"electric_system": {
"electric_system": {
"range": {
"value": 209.0,
"unit": "km"
},
"range_full": {
"value": 360.3448275862069,
"unit": "km"
},
"battery": {
"temperature_max": {
"value": 295.15,
"unit": "degC"
},
"temperature_min": {
"value": 294.15,
"unit": "degC"
}
}
},
"odometer": {
"odometer": {
"value": 67489.0,
"unit": "km"
}
},
"environment": {
"temperature_outside": {
"value": null,
"unit": "degC"
}
}
},
"vehicle": {
"vehicle": {
"state": "unknown vehicle state"
}
},
"connectivity": {
"connectivity": {
"state": "reachable"
}
},
"climatisation": {
"climatisationStatus": {
"climatisationState": "off",
"remainingClimatisationTime": {
"value": 0,
"unit": "min"
}
},
"climatisationSettings": {
"targetTemperature": {
"value": 21.0,
"unit": "degC"
}
}
},
"position": {
"position": {
"lat": null,
"lon": null
}
},
"doors": {
"doors": {
"overallState": "None",
"doors": {},
"windows": {}
}
},
"procedural": {
"range_at_100": {
"value": 360.3,
"unit": "km"
}
}
}
]
}
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,627 @@
{
"meta": {
"vin": "WVWZZZE1ZMP010760",
"created_at": "2026-05-27T09:31:50.175248+00:00",
"interval_seconds": 30
},
"records": [
{
"ts": "2026-05-27T09:31:50.333801+00:00",
"charging": {
"state": "off",
"type": "invalid",
"power": {
"value": 0.0,
"unit": "kW"
},
"remain": {
"value": 0,
"unit": "min"
},
"settings": {
"target_level": {
"value": 80.0,
"unit": "%"
},
"maximum_current": {
"value": 16.0,
"unit": "A"
},
"auto_unlock": "False"
},
"plugStatus": {
"connection_state": "disconnected",
"lock_state": "unlocked",
"external_power": "unavailable"
}
},
"electric_drive": {
"range": {
"value": 209.0,
"unit": "km"
},
"range_full": {
"value": 360.3,
"unit": "km"
},
"battery": {
"soc": {
"value": 58.0,
"unit": "%"
},
"temperature_max": {
"value": 295.15,
"unit": "degC"
},
"temperature_min": {
"value": 294.15,
"unit": "degC"
}
},
"odometer": {
"odometer": {
"value": 67489.0,
"unit": "km"
}
}
},
"vehicle": {
"state": "unknown vehicle state"
},
"connectivity": {
"state": "reachable"
},
"climatisation": {
"state": "off",
"remain": {
"value": 0,
"unit": "min"
},
"settings": {
"target_temperature": {
"value": 21.0,
"unit": "degC"
}
},
"environment": {
"temperature_outside": {
"value": null,
"unit": "degC"
}
}
},
"position": {
"lat": null,
"lon": null
},
"doors": {
"doors": {
"overallState": "None",
"doors": {},
"windows": {}
}
},
"procedural": {
"range_at_100": {
"value": 360.3,
"unit": "km"
}
}
},
{
"ts": "2026-05-27T09:32:20.484182+00:00",
"charging": {
"state": "off",
"type": "invalid",
"power": {
"value": 0.0,
"unit": "kW"
},
"remain": {
"value": 0,
"unit": "min"
},
"settings": {
"target_level": {
"value": 80.0,
"unit": "%"
},
"maximum_current": {
"value": 16.0,
"unit": "A"
},
"auto_unlock": "False"
},
"plugStatus": {
"connection_state": "disconnected",
"lock_state": "unlocked",
"external_power": "unavailable"
}
},
"electric_drive": {
"range": {
"value": 209.0,
"unit": "km"
},
"range_full": {
"value": 360.3,
"unit": "km"
},
"battery": {
"soc": {
"value": 58.0,
"unit": "%"
},
"temperature_max": {
"value": 295.15,
"unit": "degC"
},
"temperature_min": {
"value": 294.15,
"unit": "degC"
}
},
"odometer": {
"odometer": {
"value": 67489.0,
"unit": "km"
}
}
},
"vehicle": {
"state": "unknown vehicle state"
},
"connectivity": {
"state": "reachable"
},
"climatisation": {
"state": "off",
"remain": {
"value": 0,
"unit": "min"
},
"settings": {
"target_temperature": {
"value": 21.0,
"unit": "degC"
}
},
"environment": {
"temperature_outside": {
"value": null,
"unit": "degC"
}
}
},
"position": {
"lat": null,
"lon": null
},
"doors": {
"doors": {
"overallState": "None",
"doors": {},
"windows": {}
}
},
"procedural": {
"range_at_100": {
"value": 360.3,
"unit": "km"
}
}
},
{
"ts": "2026-05-27T09:32:50.642394+00:00",
"charging": {
"state": "off",
"type": "invalid",
"power": {
"value": 0.0,
"unit": "kW"
},
"remain": {
"value": 0,
"unit": "min"
},
"settings": {
"target_level": {
"value": 80.0,
"unit": "%"
},
"maximum_current": {
"value": 16.0,
"unit": "A"
},
"auto_unlock": "False"
},
"plugStatus": {
"connection_state": "disconnected",
"lock_state": "unlocked",
"external_power": "unavailable"
}
},
"electric_drive": {
"range": {
"value": 209.0,
"unit": "km"
},
"range_full": {
"value": 360.3,
"unit": "km"
},
"battery": {
"soc": {
"value": 58.0,
"unit": "%"
},
"temperature_max": {
"value": 295.15,
"unit": "degC"
},
"temperature_min": {
"value": 294.15,
"unit": "degC"
}
},
"odometer": {
"odometer": {
"value": 67489.0,
"unit": "km"
}
}
},
"vehicle": {
"state": "unknown vehicle state"
},
"connectivity": {
"state": "reachable"
},
"climatisation": {
"state": "off",
"remain": {
"value": 0,
"unit": "min"
},
"settings": {
"target_temperature": {
"value": 21.0,
"unit": "degC"
}
},
"environment": {
"temperature_outside": {
"value": null,
"unit": "degC"
}
}
},
"position": {
"lat": null,
"lon": null
},
"doors": {
"doors": {
"overallState": "None",
"doors": {},
"windows": {}
}
},
"procedural": {
"range_at_100": {
"value": 360.3,
"unit": "km"
}
}
},
{
"ts": "2026-05-27T09:33:20.808989+00:00",
"charging": {
"state": "off",
"type": "invalid",
"power": {
"value": 0.0,
"unit": "kW"
},
"remain": {
"value": 0,
"unit": "min"
},
"settings": {
"target_level": {
"value": 80.0,
"unit": "%"
},
"maximum_current": {
"value": 16.0,
"unit": "A"
},
"auto_unlock": "False"
},
"plugStatus": {
"connection_state": "disconnected",
"lock_state": "unlocked",
"external_power": "unavailable"
}
},
"electric_drive": {
"range": {
"value": 209.0,
"unit": "km"
},
"range_full": {
"value": 360.3,
"unit": "km"
},
"battery": {
"soc": {
"value": 58.0,
"unit": "%"
},
"temperature_max": {
"value": 295.15,
"unit": "degC"
},
"temperature_min": {
"value": 294.15,
"unit": "degC"
}
},
"odometer": {
"odometer": {
"value": 67489.0,
"unit": "km"
}
}
},
"vehicle": {
"state": "unknown vehicle state"
},
"connectivity": {
"state": "reachable"
},
"climatisation": {
"state": "off",
"remain": {
"value": 0,
"unit": "min"
},
"settings": {
"target_temperature": {
"value": 21.0,
"unit": "degC"
}
},
"environment": {
"temperature_outside": {
"value": null,
"unit": "degC"
}
}
},
"position": {
"lat": null,
"lon": null
},
"doors": {
"doors": {
"overallState": "None",
"doors": {},
"windows": {}
}
},
"procedural": {
"range_at_100": {
"value": 360.3,
"unit": "km"
}
}
},
{
"ts": "2026-05-27T09:33:50.960434+00:00",
"charging": {
"state": "off",
"type": "invalid",
"power": {
"value": 0.0,
"unit": "kW"
},
"remain": {
"value": 0,
"unit": "min"
},
"settings": {
"target_level": {
"value": 80.0,
"unit": "%"
},
"maximum_current": {
"value": 16.0,
"unit": "A"
},
"auto_unlock": "False"
},
"plugStatus": {
"connection_state": "disconnected",
"lock_state": "unlocked",
"external_power": "unavailable"
}
},
"electric_drive": {
"range": {
"value": 209.0,
"unit": "km"
},
"range_full": {
"value": 360.3,
"unit": "km"
},
"battery": {
"soc": {
"value": 58.0,
"unit": "%"
},
"temperature_max": {
"value": 295.15,
"unit": "degC"
},
"temperature_min": {
"value": 294.15,
"unit": "degC"
}
},
"odometer": {
"odometer": {
"value": 67489.0,
"unit": "km"
}
}
},
"vehicle": {
"state": "unknown vehicle state"
},
"connectivity": {
"state": "reachable"
},
"climatisation": {
"state": "off",
"remain": {
"value": 0,
"unit": "min"
},
"settings": {
"target_temperature": {
"value": 21.0,
"unit": "degC"
}
},
"environment": {
"temperature_outside": {
"value": null,
"unit": "degC"
}
}
},
"position": {
"lat": null,
"lon": null
},
"doors": {
"doors": {
"overallState": "None",
"doors": {},
"windows": {}
}
},
"procedural": {
"range_at_100": {
"value": 360.3,
"unit": "km"
}
}
},
{
"ts": "2026-05-27T09:34:21.107584+00:00",
"charging": {
"state": "off",
"type": "invalid",
"power": {
"value": 0.0,
"unit": "kW"
},
"remain": {
"value": 0,
"unit": "min"
},
"settings": {
"target_level": {
"value": 80.0,
"unit": "%"
},
"maximum_current": {
"value": 16.0,
"unit": "A"
},
"auto_unlock": "False"
},
"plugStatus": {
"connection_state": "disconnected",
"lock_state": "unlocked",
"external_power": "unavailable"
}
},
"electric_drive": {
"range": {
"value": 209.0,
"unit": "km"
},
"range_full": {
"value": 360.3,
"unit": "km"
},
"battery": {
"soc": {
"value": 58.0,
"unit": "%"
},
"temperature_max": {
"value": 295.15,
"unit": "degC"
},
"temperature_min": {
"value": 294.15,
"unit": "degC"
}
},
"odometer": {
"odometer": {
"value": 67489.0,
"unit": "km"
}
}
},
"vehicle": {
"state": "unknown vehicle state"
},
"connectivity": {
"state": "reachable"
},
"climatisation": {
"state": "off",
"remain": {
"value": 0,
"unit": "min"
},
"settings": {
"target_temperature": {
"value": 21.0,
"unit": "degC"
}
},
"environment": {
"temperature_outside": {
"value": null,
"unit": "degC"
}
}
},
"position": {
"lat": null,
"lon": null
},
"doors": {
"doors": {
"overallState": "None",
"doors": {},
"windows": {}
}
},
"procedural": {
"range_at_100": {
"value": 360.3,
"unit": "km"
}
}
}
]
}
File diff suppressed because it is too large Load Diff
+211
View File
@@ -0,0 +1,211 @@
# ── Corner cases: data types ───────────────────────────────────────────────────
import pytest
from copy import deepcopy
from jaydiff.diff import diff_full, diff_add, diff_del, diff_upd, diff_upd_add
from jaydiff.merge import merge_add, merge_delete, merge_update, merge_full
class TestNumericAndScalarTypes:
def test_diff_int_value_changed(self):
assert diff_upd({'a': 1, 'b': 2}, {'a': 1, 'b': 3}) == {'b': 3}
def test_diff_float_value_changed(self):
assert diff_upd({'a': 1.0}, {'a': 1.1}) == {'a': 1.1}
def test_diff_int_unchanged(self):
assert diff_upd({'a': 42}, {'a': 42}) == {}
def test_diff_bool_changed(self):
assert diff_upd({'a': True}, {'a': False}) == {'a': False}
def test_diff_bool_false_to_true(self):
assert diff_upd({'online': False}, {'online': True}) == {'online': True}
def test_diff_none_value_changed(self):
assert diff_upd({'a': None}, {'a': 'something'}) == {'a': 'something'}
def test_diff_value_to_none(self):
assert diff_upd({'a': 'something'}, {'a': None}) == {'a': None}
def test_diff_list_unchanged(self):
assert diff_upd({'a': [1, 2, 3]}, {'a': [1, 2, 3]}) == {}
def test_diff_list_changed(self):
assert diff_upd({'a': [1, 2]}, {'a': [1, 2, 3]}) == {'a': [1, 2, 3]}
def test_add_numeric_key(self):
assert diff_add({'a': 1}, {'a': 1, 'b': 2}) == {'b': 2}
def test_full_combined_numeric(self):
old = {'soc': 80, 'range': 300, 'temp': 22.5}
new = {'soc': 75, 'range': 280, 'temp': 22.5}
diff = diff_full(old, new)
assert diff == {'update_add': {'soc': 75, 'range': 280}}
def test_merge_numeric_update(self):
assert merge_full({'soc': 80, 'range': 300}, {'update_add': {'soc': 75}}) \
== {'soc': 75, 'range': 300}
# ── Corner cases: type changes (dict ↔ scalar) ────────────────────────────────
class TestTypeChanges:
def test_dict_replaced_by_scalar_in_diff(self):
# A key that was a dict becomes a scalar — upd treats the whole value as changed
old = {'a': {'b': 'c'}}
new = {'a': 'flat'}
diff = diff_upd(old, new)
assert diff == {'a': 'flat'}
def test_merge_dict_to_scalar(self):
old = {'a': {'b': 'c'}, 'x': 1}
diff = {'update_add': {'a': 'flat'}}
result = merge_full(old, diff)
assert result == {'a': 'flat', 'x': 1}
def test_diff_full_dict_to_scalar_combined(self):
old = {'status': {'value': 42}}
new = {'status': 'unknown'}
diff = diff_full(old, new)
result = merge_full(deepcopy(old), diff)
assert result == new
def test_diff_add_new_nested_structure(self):
# Key absent in old, present as dict in new — treated as addition
old = {'a': 1}
new = {'a': 1, 'b': {'c': 2}}
diff = diff_add(old, new)
assert diff == {'b': {'c': 2}}
# ── Corner cases: completely different structures ─────────────────────────────
class TestCompletelyDifferentStructure:
def test_all_keys_replaced(self):
old = {'a': 1, 'b': 2}
new = {'x': 10, 'y': 20}
diff = diff_full(old, new)
assert diff == {'update_add': {'x': 10, 'y': 20}, 'delete': {'a': 1, 'b': 2}}
def test_all_keys_replaced_deep(self):
old = {'charging': {'state': 'charging', 'power': 11}}
new = {'electric_drive': {'range': 300, 'soc': 80}}
diff = diff_full(old, new)
assert diff == {
'update_add': {'electric_drive': {'range': 300, 'soc': 80}},
'delete': {'charging': None},
}
def test_merge_round_trip_all_keys_replaced(self):
old = {'a': 1, 'b': 2}
new = {'x': 10, 'y': 20}
diff = diff_full(old, new)
result = merge_full(deepcopy(old), diff)
assert result == new
def test_diff_disjoint_no_update(self):
# No shared keys → no updates, only add/delete
old = {'a': 1}
new = {'b': 2}
diff = diff_upd(old, new)
assert diff == {}
def test_diff_disjoint_add(self):
assert diff_add({'a': 1}, {'b': 2}) == {'b': 2}
def test_diff_disjoint_del(self):
assert diff_del({'a': 1}, {'b': 2}) == {'a': 1}
# ── Corner cases: empty containers ────────────────────────────────────────────
class TestEmptyContainers:
def test_diff_both_empty(self):
assert diff_full({}, {}) == {}
def test_diff_old_empty_new_has_data(self):
diff = diff_full({}, {'a': 1})
assert diff == {'update_add': {'a': 1}}
def test_diff_new_empty_old_has_data(self):
diff = diff_full({'a': 1}, {})
assert diff == {'delete': {'a': 1}}
def test_merge_delete_all_keys(self):
result = merge_full({'a': 1, 'b': 2}, {'delete': {'a': 1, 'b': 2}})
assert result == {}
def test_delete_empty_dict_value(self):
# Deleting a key whose value is {} — should not recurse (fixed bug)
result = merge_delete({'a': 'keep', 'b': {}}, {'b': {}})
assert result == {'a': 'keep'}
def test_diff_nested_empty_dict(self):
old = {'a': {'b': 'c'}}
new = {'a': {}}
diff = diff_del(old, new)
assert diff == {'a': {'b': 'c'}}
def test_empty_dict_preserved_after_delete_all_subkeys(self):
# When new has an empty dict at a location, the key must survive with {}
# (not be pruned away). This would break if merge incorrectly prunes empty dicts.
old = {'x': {'p': 1, 'q': 2}}
new = {'x': {}}
diff = diff_full(old, new)
result = merge_full(deepcopy(old), diff)
assert result == new # 'x' must remain as {}
# ── Corner cases: deep nesting ────────────────────────────────────────────────
class TestDeepNesting:
def test_diff_three_levels(self):
old = {'l1': {'l2': {'l3': 'old'}}}
new = {'l1': {'l2': {'l3': 'new'}}}
assert diff_upd(old, new) == {'l1': {'l2': {'l3': 'new'}}}
def test_merge_three_levels(self):
old = {'l1': {'l2': {'l3': 'old', 'keep': 'yes'}}}
diff = {'update_add': {'l1': {'l2': {'l3': 'new'}}}}
result = merge_full(deepcopy(old), diff)
assert result == {'l1': {'l2': {'l3': 'new', 'keep': 'yes'}}}
def test_diff_full_three_levels(self):
old = {'a': {'b': {'c': {'d': 'deep'}}}}
new = {'a': {'b': {'c': {'d': 'deeper'}}}}
diff = diff_full(old, new)
result = merge_full(deepcopy(old), diff)
assert result == new
# ── Round-trip: merge(old, diff(old, new)) == new ────────────────────────────
class TestRoundTrip:
"""Verify that applying a diff to old always produces new."""
@pytest.mark.parametrize("old,new", [
({'a': 1}, {'a': 2}),
({'a': 1, 'b': 2}, {'b': 3, 'c': 4}),
({'x': {'y': 'old'}}, {'x': {'y': 'new', 'z': 'added'}}),
({'a': {'b': 1}, 'c': 2}, {'a': {'b': 1}, 'd': 3}),
({'soc': 80, 'range': 300}, {'soc': 60, 'range': 240, 'temp': 25}),
({'a': True, 'b': None}, {'a': False, 'c': 42}),
({'l1': {'l2': {'v': 1}}}, {'l1': {'l2': {'v': 2}}}),
({}, {'a': 1}),
({'a': 1}, {}),
])
def test_round_trip_combined(self, old, new):
diff = diff_full(deepcopy(old), deepcopy(new), combine_upd_add=True)
result = merge_full(deepcopy(old), diff)
assert result == new
@pytest.mark.parametrize("old,new", [
({'a': 1}, {'a': 2}),
({'a': 1, 'b': 2}, {'b': 3, 'c': 4}),
({'x': {'y': 'old'}}, {'x': {'y': 'new', 'z': 'added'}}),
({}, {'a': 1}),
({'a': 1}, {}),
])
def test_round_trip_separate(self, old, new):
diff = diff_full(deepcopy(old), deepcopy(new), combine_upd_add=False)
result = merge_full(deepcopy(old), diff)
assert result == new
+232
View File
@@ -0,0 +1,232 @@
"""Tests for diff."""
import pytest
from copy import deepcopy
from jaydiff.diff import diff_full, diff_add, diff_del, diff_upd, diff_upd_add
# ── diff_add ──────────────────────────────────────────────────────────────
class TestJayDiffAdd:
def test_add_to_empty(self):
assert diff_add({}, {'a': 'hamburger', 'b': 'fries', 'c': {'e': 'coke', 'f': 'coffee'}}) \
== {'a': 'hamburger', 'b': 'fries', 'c': {'e': 'coke', 'f': 'coffee'}}
def test_no_new_keys(self):
assert diff_add({'a': 'hamburger', 'b': 'fries', 'c': 'pepsi'},
{'a': 'hamburger', 'b': 'fries', 'c': 'coke'}) == {}
def test_old_has_extra_keys(self):
assert diff_add({'a': 'hamburger', 'b': 'fries', 'c': 'pepsi', 'd': 'icecream'},
{'a': 'hamburger', 'b': 'fries', 'c': 'pepsi'}) == {}
def test_one_new_key(self):
assert diff_add({'a': 'hamburger', 'b': 'fries', 'c': 'pepsi'},
{'a': 'hamburger', 'd': 'icecream', 'c': 'coke'}) == {'d': 'icecream'}
def test_new_key_among_overlap(self):
assert diff_add({'a': 'hamburger', 'c': 'pepsi', 'd': 'icecream'},
{'a': 'hamburger', 'b': 'fries', 'c': 'coke'}) == {'b': 'fries'}
def test_nested_new_key(self):
assert diff_add({'a': 'hamburger', 'b': 'fries', 'c': {'e': 'coke'}},
{'c': {'f': 'coffee'}}) == {'c': {'f': 'coffee'}}
def test_nested_no_new_keys(self):
assert diff_add({'a': 'hamburger', 'b': 'fries', 'c': {'e': 'coke', 'f': 'coffee'}},
{'a': 'hamburger', 'b': 'fries', 'c': {'f': 'coffee'}}) == {}
# ── diff_del ──────────────────────────────────────────────────────────────
class TestJayDiffDel:
def test_one_deleted_key(self):
assert diff_del({'a': 'hamburger', 'c': 'pepsi', 'd': 'icecream'},
{'a': 'hamburger', 'b': 'fries', 'c': 'coke'}) == {'d': 'icecream'}
def test_no_deleted_keys_same_values(self):
assert diff_del({'a': 'hamburger', 'b': 'fries', 'c': 'coke'},
{'a': 'hamburger', 'b': 'fries', 'c': 'coke'}) == {}
def test_no_deleted_keys_different_values(self):
assert diff_del({'a': 'hamburger', 'b': 'fries', 'c': 'coke'},
{'a': 'hamburger', 'b': 'fries', 'c': 'pepsi'}) == {}
def test_key_absent_from_new(self):
assert diff_del({'a': 'hamburger', 'b': 'fries', 'c': 'pepsi'},
{'a': 'hamburger', 'b': 'fries'}) == {'c': 'pepsi'}
def test_nested_deleted_key(self):
# 'c' is absent from _new → None signals "delete entire key"
assert diff_del({'a': 'hamburger', 'b': 'fries', 'c': {'e': 'coke', 'f': 'coffee'}},
{'a': 'hamburger', 'b': 'fries'}) == {'c': None}
def test_nested_partial_delete(self):
assert diff_del({'a': 'hamburger', 'b': 'fries', 'c': {'e': 'coke', 'f': 'coffee'}},
{'c': {'e': 'coke'}}) == {'a': 'hamburger', 'b': 'fries', 'c': {'f': 'coffee'}}
def test_nested_no_delete_different_value(self):
assert diff_del({'a': 'hamburger', 'b': 'fries', 'c': {'e': 'coke', 'f': 'coffee'}},
{'a': 'hamburger', 'b': 'fries', 'c': {'e': 'pepsi', 'f': 'coffee'}}) == {}
# ── diff_upd ──────────────────────────────────────────────────────────────
class TestJayDiffUpd:
def test_multiple_updates(self):
assert diff_upd({'a': 'hamburger', 'b': 'fries', 'c': {'e': 'coke', 'f': 'coffee'}},
{'a': 'hotdog', 'b': 'potatoes'}) == {'a': 'hotdog', 'b': 'potatoes'}
def test_nested_update(self):
assert diff_upd({'a': 'hamburger', 'b': 'fries', 'c': {'e': 'coke', 'f': 'coffee'}},
{'c': {'e': 'pepsi'}}) == {'c': {'e': 'pepsi'}}
def test_one_update(self):
assert diff_upd({'a': 'hamburger', 'b': 'fries', 'c': {'e': 'coke', 'f': 'coffee'}},
{'a': 'hotdog', 'b': 'fries'}) == {'a': 'hotdog'}
def test_no_update_new_key_ignored(self):
assert diff_upd({'a': 'hamburger', 'b': 'fries', 'c': 'pepsi'},
{'a': 'hamburger', 'd': 'icecream', 'c': 'coke'}) == {'c': 'coke'}
def test_nested_no_update_new_subkey_ignored(self):
assert diff_upd({'a': 'hamburger', 'b': 'fries', 'c': {'e': 'coke'}},
{'c': {'f': 'coffee'}}) == {}
def test_nested_update_one_of_two(self):
assert diff_upd({'a': 'hamburger', 'b': 'fries', 'c': {'e': 'coke', 'f': 'coffee'}},
{'a': 'hamburger', 'b': 'fries', 'c': {'e': 'pepsi', 'f': 'coffee'}}) \
== {'c': {'e': 'pepsi'}}
# ── diff_upd_add ──────────────────────────────────────────────────────────
class TestJayDiffUpdAdd:
def test_update_and_add(self):
assert diff_upd_add({'a': 'hamburger', 'b': 'fries', 'c': {'e': 'coke', 'f': 'coffee'}},
{'a': 'hotdog', 'b': 'potatoes', 'd': 'icecream'}) \
== {'a': 'hotdog', 'b': 'potatoes', 'd': 'icecream'}
def test_nested_update_and_add(self):
assert diff_upd_add({'a': 'hamburger', 'b': 'fries', 'c': {'e': 'coke', 'f': 'coffee'}},
{'c': {'e': 'pepsi', 'g': 'tea', 'h': 'cake'}, 'b': 'potatoes'}) \
== {'b': 'potatoes', 'c': {'e': 'pepsi', 'g': 'tea', 'h': 'cake'}}
def test_update_only(self):
assert diff_upd_add({'a': 'hamburger', 'b': 'fries', 'c': {'e': 'coke', 'f': 'coffee'}},
{'a': 'hotdog', 'b': 'fries'}) == {'a': 'hotdog'}
def test_nested_update_and_new_subkey(self):
assert diff_upd_add({'a': 'hamburger', 'b': 'fries', 'c': {'e': 'coke', 'f': 'coffee'}},
{'a': 'hamburger', 'b': 'fries', 'c': {'e': 'pepsi', 'f': 'coffee', 'h': 'cake'}}) \
== {'c': {'e': 'pepsi', 'h': 'cake'}}
def test_add_new_top_and_update(self):
assert diff_upd_add({'a': 'hamburger', 'b': 'fries', 'c': 'pepsi'},
{'a': 'hamburger', 'd': 'icecream', 'c': 'coke'}) \
== {'c': 'coke', 'd': 'icecream'}
def test_identical(self):
assert diff_upd_add({'a': 'hamburger', 'b': 'fries'},
{'a': 'hamburger', 'b': 'fries'}) == {}
# ── diff_full (combine_upd_add=False) ─────────────────────────────────────
class TestJayDiffFullSeparate:
F = False
def test_add_all(self):
assert diff_full({}, {'a': 'hamburger', 'b': 'fries', 'c': {'e': 'coke', 'f': 'coffee'}}, self.F) \
== {'add': {'a': 'hamburger', 'b': 'fries', 'c': {'e': 'coke', 'f': 'coffee'}}}
def test_update_one(self):
assert diff_full({'a': 'hamburger', 'b': 'fries', 'c': 'pepsi'},
{'a': 'hamburger', 'b': 'fries', 'c': 'coke'}, self.F) \
== {'update': {'c': 'coke'}}
def test_delete_one(self):
assert diff_full({'a': 'hamburger', 'b': 'fries', 'c': 'pepsi', 'd': 'icecream'},
{'a': 'hamburger', 'b': 'fries', 'c': 'pepsi'}, self.F) \
== {'delete': {'d': 'icecream'}}
def test_add_delete_update(self):
assert diff_full({'a': 'hamburger', 'b': 'fries', 'c': 'pepsi'},
{'a': 'hamburger', 'd': 'icecream', 'c': 'coke'}, self.F) \
== {'add': {'d': 'icecream'}, 'delete': {'b': 'fries'}, 'update': {'c': 'coke'}}
def test_identical(self):
assert diff_full({'a': 'hamburger', 'b': 'fries', 'c': 'coke'},
{'a': 'hamburger', 'b': 'fries', 'c': 'coke'}, self.F) == {}
def test_delete_nested(self):
assert diff_full({'a': 'hamburger', 'b': 'fries', 'c': {'e': 'coke', 'f': 'coffee'}},
{'a': 'hamburger', 'b': 'fries'}, self.F) \
== {'delete': {'c': None}}
def test_nested_partial_delete(self):
assert diff_full({'a': 'hamburger', 'b': 'fries', 'c': {'e': 'coke', 'f': 'coffee'}},
{'a': 'hamburger', 'b': 'fries', 'c': {'f': 'coffee'}}, self.F) \
== {'delete': {'c': {'e': 'coke'}}}
def test_update_and_delete(self):
assert diff_full({'a': 'hamburger', 'b': 'fries', 'c': {'e': 'coke', 'f': 'coffee'}},
{'a': 'hotdog', 'b': 'potatoes'}, self.F) \
== {'delete': {'c': None}, 'update': {'a': 'hotdog', 'b': 'potatoes'}}
def test_nested_add_delete_update(self):
assert diff_full({'a': 'hamburger', 'b': 'fries', 'c': {'e': 'coke', 'f': 'coffee'}},
{'a': 'hotdog', 'b': 'potatoes', 'd': 'icecream'}, self.F) \
== {'add': {'d': 'icecream'}, 'delete': {'c': None}, 'update': {'a': 'hotdog', 'b': 'potatoes'}}
# ── diff_full (combine_upd_add=True) ──────────────────────────────────────
class TestJayDiffFullCombined:
T = True
def test_add_all(self):
assert diff_full({}, {'a': 'hamburger', 'b': 'fries', 'c': {'e': 'coke', 'f': 'coffee'}}, self.T) \
== {'update_add': {'a': 'hamburger', 'b': 'fries', 'c': {'e': 'coke', 'f': 'coffee'}}}
def test_update_one(self):
assert diff_full({'a': 'hamburger', 'b': 'fries', 'c': 'pepsi'},
{'a': 'hamburger', 'b': 'fries', 'c': 'coke'}, self.T) \
== {'update_add': {'c': 'coke'}}
def test_delete_one(self):
assert diff_full({'a': 'hamburger', 'b': 'fries', 'c': 'pepsi', 'd': 'icecream'},
{'a': 'hamburger', 'b': 'fries', 'c': 'pepsi'}, self.T) \
== {'delete': {'d': 'icecream'}}
def test_delete_and_update_add(self):
assert diff_full({'a': 'hamburger', 'b': 'fries', 'c': 'pepsi'},
{'a': 'hamburger', 'd': 'icecream', 'c': 'coke'}, self.T) \
== {'delete': {'b': 'fries'}, 'update_add': {'c': 'coke', 'd': 'icecream'}}
def test_identical(self):
assert diff_full({'a': 'hamburger', 'b': 'fries', 'c': 'coke'},
{'a': 'hamburger', 'b': 'fries', 'c': 'coke'}, self.T) == {}
def test_delete_nested(self):
assert diff_full({'a': 'hamburger', 'b': 'fries', 'c': {'e': 'coke', 'f': 'coffee'}},
{'a': 'hamburger', 'b': 'fries'}, self.T) \
== {'delete': {'c': None}}
def test_update_and_delete_nested(self):
assert diff_full({'a': 'hamburger', 'b': 'fries', 'c': {'e': 'coke', 'f': 'coffee'}},
{'a': 'hotdog', 'b': 'potatoes'}, self.T) \
== {'delete': {'c': None}, 'update_add': {'a': 'hotdog', 'b': 'potatoes'}}
def test_nested_update_add_and_delete(self):
assert diff_full({'a': 'hamburger', 'b': 'fries', 'c': {'e': 'coke', 'f': 'coffee'}},
{'c': {'e': 'pepsi', 'g': 'tea', 'h': 'cake'}, 'b': 'potatoes'}, self.T) \
== {'delete': {'a': 'hamburger', 'c': {'f': 'coffee'}},
'update_add': {'b': 'potatoes', 'c': {'e': 'pepsi', 'g': 'tea', 'h': 'cake'}}}
def test_nested_add_subkey(self):
assert diff_full({'a': 'hamburger', 'b': 'fries', 'c': {'e': 'coke', 'f': 'coffee'}},
{'a': 'hamburger', 'b': 'fries', 'c': {'e': 'pepsi', 'f': 'coffee', 'h': 'cake'}}, self.T) \
== {'update_add': {'c': {'e': 'pepsi', 'h': 'cake'}}}
+93
View File
@@ -0,0 +1,93 @@
import pytest
from jaydiff.merge import merge_add, merge_delete, merge_update, merge_full
# ── merge_* ───────────────────────────────────────────────────────────────
class TestJayMerge:
def test_merge_add_to_empty(self):
assert merge_add({}, {'a': 'hamburger', 'b': 'fries', 'c': {'e': 'coke', 'f': 'coffee'}}) \
== {'a': 'hamburger', 'b': 'fries', 'c': {'e': 'coke', 'f': 'coffee'}}
def test_merge_add_new_key(self):
assert merge_add({'a': 'hamburger', 'b': 'fries', 'c': {'e': 'coke', 'f': 'coffee'}},
{'d': 'icecream'}) \
== {'a': 'hamburger', 'b': 'fries', 'c': {'e': 'coke', 'f': 'coffee'}, 'd': 'icecream'}
def test_merge_add_existing_unchanged(self):
assert merge_add({'a': 'hamburger', 'b': 'fries', 'c': {'f': 'coffee'}, 'd': 'icecream'},
{'d': 'icecream'}) \
== {'a': 'hamburger', 'b': 'fries', 'c': {'f': 'coffee'}, 'd': 'icecream'}
def test_merge_delete_top_key(self):
assert merge_delete({'a': 'hamburger', 'b': 'fries', 'c': {'e': 'coke', 'f': 'coffee'}, 'd': 'icecream'},
{'d': 'icecream'}) \
== {'a': 'hamburger', 'b': 'fries', 'c': {'e': 'coke', 'f': 'coffee'}}
def test_merge_delete_nested_key(self):
assert merge_delete({'a': 'hamburger', 'b': 'fries', 'c': {'e': 'coke', 'f': 'coffee'}},
{'c': {'f': 'coffee'}}) \
== {'a': 'hamburger', 'b': 'fries', 'c': {'e': 'coke'}}
def test_merge_delete_nested_leaves_sibling(self):
assert merge_delete({'a': 'hamburger', 'b': 'fries', 'c': {'e': 'coke', 'f': 'coffee'}},
{'c': {'e': 'coke'}}) \
== {'a': 'hamburger', 'b': 'fries', 'c': {'f': 'coffee'}}
class TestJayMergeFull:
def test_passthrough_when_old_empty(self):
assert merge_full({}, {'a': 'hamburger', 'b': 'fries', 'c': {'e': 'coke', 'f': 'coffee'}}) \
== {'a': 'hamburger', 'b': 'fries', 'c': {'e': 'coke', 'f': 'coffee'}}
def test_empty_diff_unchanged(self):
assert merge_full({'a': 'hamburger', 'b': 'fries', 'c': 'coke'}, {}) \
== {'a': 'hamburger', 'b': 'fries', 'c': 'coke'}
def test_update(self):
assert merge_full({'a': 'hamburger', 'b': 'fries', 'c': 'coke'}, {'update': {'c': 'pepsi'}}) \
== {'a': 'hamburger', 'b': 'fries', 'c': 'pepsi'}
def test_update_add(self):
assert merge_full({'a': 'hamburger', 'b': 'fries', 'c': 'pepsi'}, {'update_add': {'c': 'coke'}}) \
== {'a': 'hamburger', 'b': 'fries', 'c': 'coke'}
def test_delete(self):
assert merge_full({'a': 'hamburger', 'b': 'fries', 'c': 'pepsi', 'd': 'icecream'},
{'delete': {'d': 'icecream'}}) \
== {'a': 'hamburger', 'b': 'fries', 'c': 'pepsi'}
def test_add_delete_update_combined(self):
assert merge_full({'a': 'hamburger', 'b': 'fries', 'c': 'pepsi'},
{'add': {'d': 'icecream'}, 'delete': {'b': 'fries'}, 'update': {'c': 'coke'}}) \
== {'a': 'hamburger', 'd': 'icecream', 'c': 'coke'}
def test_nested_update_and_delete(self):
assert merge_full({'a': 'hamburger', 'b': 'fries', 'c': {'e': 'coke', 'f': 'coffee'}},
{'delete': {'a': 'hamburger', 'b': 'fries', 'c': {'f': 'coffee'}},
'update': {'c': {'e': 'pepsi'}}}) \
== {'c': {'e': 'pepsi'}}
def test_update_add_new_subkey(self):
assert merge_full({'a': 'hamburger', 'b': 'fries', 'c': {'e': 'coke', 'f': 'coffee'}},
{'update_add': {'c': {'e': 'pepsi', 'h': 'cake'}}}) \
== {'a': 'hamburger', 'b': 'fries', 'c': {'e': 'pepsi', 'f': 'coffee', 'h': 'cake'}}
def test_real_world_snapshot_update(self):
old = {'charging': {
'batteryStatus': {'value': {'carCapturedTimestamp': '2024-10-19T05:34:34Z', 'cruisingRangeElectric_km': 192}},
'chargingStatus': {'value': {'carCapturedTimestamp': '2024-10-19T05:34:34Z'}},
'chargingSettings': {'value': {'carCapturedTimestamp': '2024-10-19T05:34:30Z'}},
'plugStatus': {'value': {'carCapturedTimestamp': '2024-10-19T05:34:29Z'}}}}
update = {'charging': {
'batteryStatus': {'value': {'carCapturedTimestamp': '2024-10-19T05:35:46Z', 'cruisingRangeElectric_km': 191}},
'chargingStatus': {'value': {'carCapturedTimestamp': '2024-10-19T05:35:46Z'}}}}
add = {'charging': {'batteryStatus': {'value': {'currentSOC_pct': 55}}},
'fuelStatus': {'rangeStatus': {'value': {'primaryEngine': {'currentSOC_pct': 55}}}},
'measurements': {'fuelLevelStatus': {'value': {'currentSOC_pct': 55}}}}
delete = {'charging': {
'chargingSettings': {'value': {'carCapturedTimestamp': '2024-10-19T05:34:30Z'}},
'plugStatus': {'value': {'carCapturedTimestamp': '2024-10-19T05:34:29Z'}}}}
result = merge_full(old, {'update': update, 'add': add, 'delete': delete})
assert result == old
+94
View File
@@ -0,0 +1,94 @@
"""
Round-trip tests for diff using real snapshot records from ./tests/data/.
For every pair of consecutive records (within a file and across adjacent files)
we verify:
merge_full(a, diff_full(a, b)) == b
We also verify that diffing identical records produces an empty diff, and that
applying an empty diff is a no-op.
"""
import json
import pathlib
import pytest
from jaydiff.diff import diff_full
from jaydiff.merge import merge_full
DATA_DIR = pathlib.Path(__file__).parent / "data"
def _load_all_records() -> list[dict]:
"""Return all records from all data files, sorted by timestamp."""
records: list[dict] = []
for path in sorted(DATA_DIR.glob("*.json")):
store = json.loads(path.read_text())
records.extend(store.get("records", []))
records.sort(key=lambda r: r.get("ts", ""))
return records
ALL_RECORDS = _load_all_records()
CONSECUTIVE_PAIRS = list(zip(ALL_RECORDS, ALL_RECORDS[1:]))
class TestRealDataRoundTrip:
"""Diff+merge of every consecutive snapshot pair must reconstruct the target."""
@pytest.mark.parametrize("pair_index", range(len(CONSECUTIVE_PAIRS)))
def test_consecutive_round_trip(self, pair_index: int) -> None:
a, b = CONSECUTIVE_PAIRS[pair_index]
diff = diff_full(a, b)
result = merge_full(a, diff)
assert result == b, (
f"Round-trip failed for pair #{pair_index} "
f"(ts: {a.get('ts')}{b.get('ts')})"
)
@pytest.mark.parametrize("pair_index", range(len(CONSECUTIVE_PAIRS)))
def test_diff_of_identical_is_empty(self, pair_index: int) -> None:
a, _ = CONSECUTIVE_PAIRS[pair_index]
assert diff_full(a, a) == {}
@pytest.mark.parametrize("pair_index", range(len(CONSECUTIVE_PAIRS)))
def test_empty_diff_is_noop(self, pair_index: int) -> None:
a, _ = CONSECUTIVE_PAIRS[pair_index]
assert merge_full(a, {}) == a
class TestRealDataProperties:
"""Structural invariants on the real snapshot data."""
def test_data_dir_has_files(self) -> None:
assert len(list(DATA_DIR.glob("*.json"))) > 0, "No JSON files in tests/data/"
def test_all_records_have_timestamp(self) -> None:
for rec in ALL_RECORDS:
assert "ts" in rec, f"Record missing 'ts': {list(rec.keys())}"
def test_records_are_sorted(self) -> None:
ts_list = [r["ts"] for r in ALL_RECORDS]
assert ts_list == sorted(ts_list), "Records are not in ascending timestamp order"
def test_total_record_count(self) -> None:
assert len(ALL_RECORDS) > 100, (
f"Expected >100 records across all files, got {len(ALL_RECORDS)}"
)
def test_diff_size_smaller_than_full_snapshot(self) -> None:
"""Diffs between similar consecutive records should usually be smaller."""
savings = 0
for a, b in CONSECUTIVE_PAIRS:
diff = diff_full(a, b)
if diff: # skip identical pairs
diff_len = len(json.dumps(diff))
full_len = len(json.dumps(b))
if diff_len < full_len:
savings += 1
ratio = savings / len(CONSECUTIVE_PAIRS) if CONSECUTIVE_PAIRS else 1.0
assert ratio > 0.5, (
f"Expected diffs to be smaller than full snapshots for >50% of pairs, "
f"got {ratio:.0%}"
)