ocppsim 0.1.0

A terminal OCPP-J charge point simulator.
Documentation
use serde_json::json;

use super::*;

#[test]
fn set_charging_profile_v1_6_updates_transaction_status() {
  let mut simulator = simulator_for_tests();
  simulator
    .start_transaction(1, "TOKEN".to_string(), false, None, false)
    .expect("start should succeed");
  let suspend_payload = json!({
    "connectorId": 1,
    "csChargingProfiles": {
      "chargingSchedule": {
        "chargingSchedulePeriod": [
          { "startPeriod": 0, "limit": 0 }
        ]
      }
    }
  });
  let resume_payload = json!({
    "connectorId": 1,
    "csChargingProfiles": {
      "chargingSchedule": {
        "chargingSchedulePeriod": [
          { "startPeriod": 0, "limit": 16 }
        ]
      }
    }
  });

  let suspend_status = simulator
    .set_charging_profile_v1_6(&suspend_payload)
    .expect("suspend profile should parse");
  assert_eq!(suspend_status, ResponseStatus::Accepted);
  assert_eq!(
    simulator
      .connectors
      .get(&1)
      .map(|item| item.status.display()),
    Some("SuspendedEVSE")
  );

  let resume_status = simulator
    .set_charging_profile_v1_6(&resume_payload)
    .expect("resume profile should parse");
  assert_eq!(resume_status, ResponseStatus::Accepted);
  assert_eq!(
    simulator
      .connectors
      .get(&1)
      .map(|item| item.status.display()),
    Some("Charging")
  );
}

#[test]
fn set_charging_profile_v2_x_updates_transaction_status() {
  let mut simulator = simulator_for_tests_v2_0_1();
  simulator
    .start_transaction(1, "TOKEN".to_string(), false, None, false)
    .expect("start should succeed");
  let suspend_payload = json!({
    "evseId": 1,
    "chargingProfile": {
      "chargingSchedule": [
        {
          "chargingSchedulePeriod": [
            { "startPeriod": 0, "limit": 0 }
          ]
        }
      ]
    }
  });
  let resume_payload = json!({
    "evseId": 1,
    "chargingProfile": {
      "chargingSchedule": [
        {
          "chargingSchedulePeriod": [
            { "startPeriod": 0, "limit": 16 }
          ]
        }
      ]
    }
  });

  let suspend_status = simulator
    .set_charging_profile_v2_x(&suspend_payload)
    .expect("suspend profile should parse");
  assert_eq!(suspend_status, ResponseStatus::Accepted);
  assert_eq!(
    simulator
      .connectors
      .get(&1)
      .map(|item| item.status.display()),
    Some("SuspendedEVSE")
  );

  let resume_status = simulator
    .set_charging_profile_v2_x(&resume_payload)
    .expect("resume profile should parse");
  assert_eq!(resume_status, ResponseStatus::Accepted);
  assert_eq!(
    simulator
      .connectors
      .get(&1)
      .map(|item| item.status.display()),
    Some("Occupied")
  );
}

#[test]
fn get_composite_schedule_v1_6_uses_profile_limit() {
  let mut simulator = simulator_for_tests();
  let schedule_payload = json!({ "connectorId": 1, "duration": 60 });
  let before = simulator
    .get_composite_schedule_v1_6(&schedule_payload)
    .expect("schedule request should parse");
  assert_eq!(before["status"], json!(ResponseStatus::Rejected.as_str()));

  let set_payload = json!({
    "connectorId": 1,
    "csChargingProfiles": {
      "chargingSchedule": {
        "chargingSchedulePeriod": [
          { "startPeriod": 0, "limit": 12.5 }
        ]
      }
    }
  });
  assert_eq!(
    simulator
      .set_charging_profile_v1_6(&set_payload)
      .expect("profile should parse"),
    ResponseStatus::Accepted
  );

  let after = simulator
    .get_composite_schedule_v1_6(&schedule_payload)
    .expect("schedule request should parse");
  assert_eq!(
    after["chargingSchedule"]["chargingSchedulePeriod"][0]["limit"],
    json!(12.5)
  );
}

#[test]
fn clear_charging_profile_v1_6_honors_connector_filter() {
  let mut simulator = simulator_for_tests();
  assert_eq!(
    simulator
      .set_charging_profile_v1_6(&json!({
        "connectorId": 1,
        "csChargingProfiles": {
          "chargingProfileId": 10,
          "chargingSchedule": {
            "chargingSchedulePeriod": [
              { "startPeriod": 0, "limit": 6.0 }
            ]
          }
        }
      }))
      .expect("profile should parse"),
    ResponseStatus::Accepted
  );
  assert_eq!(
    simulator
      .set_charging_profile_v1_6(&json!({
        "connectorId": 2,
        "csChargingProfiles": {
          "chargingProfileId": 20,
          "chargingSchedule": {
            "chargingSchedulePeriod": [
              { "startPeriod": 0, "limit": 10.0 }
            ]
          }
        }
      }))
      .expect("profile should parse"),
    ResponseStatus::Accepted
  );

  assert_eq!(
    simulator.clear_charging_profile_v1_6(&json!({ "connectorId": 1 })),
    ResponseStatus::Accepted
  );
  assert!(!simulator.charging_profiles.contains_key(&1));
  assert!(simulator.charging_profiles.contains_key(&2));
}

#[test]
fn clear_charging_profile_v1_6_honors_purpose_and_stack_filters() {
  let mut simulator = simulator_for_tests();
  assert_eq!(
    simulator
      .set_charging_profile_v1_6(&json!({
        "connectorId": 1,
        "csChargingProfiles": {
          "chargingProfileId": 10,
          "chargingProfilePurpose": "TxProfile",
          "stackLevel": 2,
          "chargingSchedule": {
            "chargingSchedulePeriod": [
              { "startPeriod": 0, "limit": 6.0 }
            ]
          }
        }
      }))
      .expect("profile should parse"),
    ResponseStatus::Accepted
  );

  assert_eq!(
    simulator.clear_charging_profile_v1_6(&json!({
      "chargingProfilePurpose": "ChargePointMaxProfile",
      "stackLevel": 2
    })),
    ResponseStatus::Unknown
  );
  assert!(simulator.charging_profiles.contains_key(&1));

  assert_eq!(
    simulator.clear_charging_profile_v1_6(&json!({
      "chargingProfilePurpose": "TxProfile",
      "stackLevel": 2
    })),
    ResponseStatus::Accepted
  );
  assert!(!simulator.charging_profiles.contains_key(&1));
}

#[test]
fn get_composite_schedule_v2_x_uses_profile_limit() {
  let mut simulator = simulator_for_tests_v2_0_1();
  let schedule_payload = json!({ "evseId": 1, "duration": 60 });
  let before = simulator
    .get_composite_schedule_v2_x(&schedule_payload)
    .expect("schedule request should parse");
  assert_eq!(before["status"], json!(ResponseStatus::Rejected.as_str()));

  let set_payload = json!({
    "evseId": 1,
    "chargingProfile": {
      "chargingSchedule": [
        {
          "chargingSchedulePeriod": [
            { "startPeriod": 0, "limit": 8.0 }
          ]
        }
      ]
    }
  });
  assert_eq!(
    simulator
      .set_charging_profile_v2_x(&set_payload)
      .expect("profile should parse"),
    ResponseStatus::Accepted
  );

  let after = simulator
    .get_composite_schedule_v2_x(&schedule_payload)
    .expect("schedule request should parse");
  assert_eq!(
    after["schedule"]["chargingSchedulePeriod"][0]["limit"],
    json!(8.0)
  );
}

#[test]
fn clear_charging_profile_v2_x_honors_profile_filter() {
  let mut simulator = simulator_for_tests_v2_0_1();
  assert_eq!(
    simulator
      .set_charging_profile_v2_x(&json!({
        "evseId": 1,
        "chargingProfile": {
          "id": 10,
          "chargingSchedule": [
            {
              "chargingSchedulePeriod": [
                { "startPeriod": 0, "limit": 6.0 }
              ]
            }
          ]
        }
      }))
      .expect("profile should parse"),
    ResponseStatus::Accepted
  );
  assert_eq!(
    simulator.clear_charging_profile_v2_x(&json!({
      "chargingProfileId": 99
    })),
    ResponseStatus::Unknown
  );
  assert!(simulator.charging_profiles.contains_key(&1));

  assert_eq!(
    simulator.clear_charging_profile_v2_x(&json!({
      "chargingProfileId": 10
    })),
    ResponseStatus::Accepted
  );
  assert!(!simulator.charging_profiles.contains_key(&1));
}

#[test]
fn clear_charging_profile_v2_x_honors_criteria_filters() {
  let mut simulator = simulator_for_tests_v2_0_1();
  assert_eq!(
    simulator
      .set_charging_profile_v2_x(&json!({
        "evseId": 2,
        "chargingProfile": {
          "id": 20,
          "chargingProfilePurpose": "TxProfile",
          "stackLevel": 3,
          "chargingSchedule": [
            {
              "chargingSchedulePeriod": [
                { "startPeriod": 0, "limit": 10.0 }
              ]
            }
          ]
        }
      }))
      .expect("profile should parse"),
    ResponseStatus::Accepted
  );

  assert_eq!(
    simulator.clear_charging_profile_v2_x(&json!({
      "chargingProfileCriteria": {
        "evseId": 1,
        "chargingProfilePurpose": "TxProfile",
        "stackLevel": 3
      }
    })),
    ResponseStatus::Unknown
  );
  assert!(simulator.charging_profiles.contains_key(&2));

  assert_eq!(
    simulator.clear_charging_profile_v2_x(&json!({
      "chargingProfileCriteria": {
        "evseId": 2,
        "chargingProfilePurpose": "TxProfile",
        "stackLevel": 3
      }
    })),
    ResponseStatus::Accepted
  );
  assert!(!simulator.charging_profiles.contains_key(&2));
}