Skip to content

Commit

Permalink
Merge pull request #2221 from input-output-hk/djo/2217/align_messages…
Browse files Browse the repository at this point in the history
…_golden_master_tests

Align messages golden master tests
  • Loading branch information
Alenar authored Jan 14, 2025
2 parents 584e2c6 + 71b01e9 commit ff02ea5
Show file tree
Hide file tree
Showing 24 changed files with 222 additions and 201 deletions.
2 changes: 1 addition & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

57 changes: 57 additions & 0 deletions docs/website/adr/008-standardize-json-messages-testing.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
---
slug: 8
title: |
8. Standardize JSON Message Testing
authors:
- name: Mithril Team
tags: [Accepted]
date: 2025-01-14
---

## Status

Accepted

## Context

- To ensure backward compatibility and correctness of JSON messages exchanged between nodes, we need a standardized approach
to test the deserialization of these messages.
- Golden testing is a technique where the expected output (golden data) is stored and used to verify the correctness of
the system's output. This approach helps in detecting unintended changes in the output and ensures that the system
behaves as expected over time.
- By using golden testing for JSON message deserialization, we can ensure that any changes to the message structures are
backward compatible and that the deserialization process yields the expected results.
- We have been using golden testing for JSON messages in the project, but the approach used ad-hoc versions that did not
correspond to any OpenAPI versions, making it difficult to track the changes and maintain backward compatibility.

## Decision

We will standardize the testing of JSON messages by following the steps below:

When adding a new JSON message structure, the following steps should be taken:

- Introduce a constant `CURRENT_JSON` string containing an exhaustive example of the JSON currently exchanged between nodes.
- Implement a `golden_message_current` method that returns the representation of the `CURRENT_JSON` using the current structure.
- Implement a `test_current_json_deserialized_into_current_message` test that checks that deserializing the `CURRENT_JSON` into the current structure yields the output stored in `golden_message_current`.

When modifying an existing JSON message structure, if backward compatibility is maintained, the following steps should be taken:

- Given `X_Y_ZZ` is the version of the OpenAPI before the change:
- Create a copy of the previous version structure as it was before the backward-compatible change, suffixed with `UntilVX_Y_ZZ`, e.g., `CertificateMessageUntilV0_1_32`.
- Create a copy the `golden_message_current` method named `golden_message_until_open_api_X_Y_ZZ`, and update its return type to the version structure suffixed with `UntilVX_Y_ZZ`.
- Implement a `test_current_json_deserialized_into_message_supported_until_open_api_X_Y_ZZ` test that checks that deserializing the `CURRENT_JSON` into the previous structure yields the output stored in `golden_message_until_open_api_X_Y_ZZ`.
- Modify the `CURRENT_JSON` string to reflect the new structure.
- Modify the `golden_message_current` method to return the representation of the `CURRENT_JSON` using the new structure.

When modifying an existing JSON message structure, if backward compatibility is not maintained, the following steps should be taken:

- Modify the `CURRENT_JSON` string to reflect the new structure.
- Modify the `golden_message_current` method to return the representation of the `CURRENT_JSON` using the new structure.
- Remove all `golden_message_until_open_api_X_Y_ZZ` method and the corresponding structure and tests, as they are no longer relevant.

## Consequences

- Ensures that any changes to the JSON message structure are backward compatible.
- Provides a clear and standardized approach to testing JSON message deserialization.
- Helps maintain the integrity and reliability of the communication between nodes.
- Requires maintaining multiple versions of message structures and corresponding tests, which may increase the maintenance overhead.
2 changes: 1 addition & 1 deletion mithril-common/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "mithril-common"
version = "0.4.103"
version = "0.4.104"
description = "Common types, interfaces, and utilities for Mithril nodes."
authors = { workspace = true }
edition = { workspace = true }
Expand Down
25 changes: 12 additions & 13 deletions mithril-common/src/messages/aggregator_features.rs
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ mod tests {
use super::*;

#[derive(Debug, Serialize, Deserialize, PartialEq)]
struct AggregatorFeaturesMessagePrevious {
struct AggregatorFeaturesMessageUntilV0_1_27 {
pub open_api_version: String,
pub documentation_url: String,
pub capabilities: AggregatorCapabilitiesPrevious,
Expand All @@ -76,8 +76,8 @@ mod tests {
pub cardano_transactions_prover: Option<CardanoTransactionsProverCapabilities>,
}

fn golden_message_previous() -> AggregatorFeaturesMessagePrevious {
AggregatorFeaturesMessagePrevious {
fn golden_message_until_open_api_0_1_27() -> AggregatorFeaturesMessageUntilV0_1_27 {
AggregatorFeaturesMessageUntilV0_1_27 {
open_api_version: "0.0.1".to_string(),
documentation_url: "https://example.com".to_string(),
capabilities: AggregatorCapabilitiesPrevious {
Expand All @@ -91,7 +91,7 @@ mod tests {
}
}

fn golden_message_actual() -> AggregatorFeaturesMessage {
fn golden_message_current() -> AggregatorFeaturesMessage {
AggregatorFeaturesMessage {
open_api_version: "0.0.1".to_string(),
documentation_url: "https://example.com".to_string(),
Expand All @@ -110,7 +110,7 @@ mod tests {
}
}

const ACTUAL_JSON: &str = r#"{
const CURRENT_JSON: &str = r#"{
"open_api_version": "0.0.1",
"documentation_url": "https://example.com",
"capabilities": {
Expand All @@ -125,20 +125,19 @@ mod tests {
}
}"#;

// Test the backward compatibility with possible future upgrades.
#[test]
fn test_actual_json_deserialized_into_previous_message() {
let json = ACTUAL_JSON;
let message: AggregatorFeaturesMessagePrevious = serde_json::from_str(json).unwrap();
fn test_current_json_deserialized_into_message_supported_until_open_api_0_1_27() {
let json = CURRENT_JSON;
let message: AggregatorFeaturesMessageUntilV0_1_27 = serde_json::from_str(json).unwrap();

assert_eq!(golden_message_previous(), message);
assert_eq!(golden_message_until_open_api_0_1_27(), message);
}

#[test]
fn test_actual_json_deserialized_into_actual_message() {
let json = ACTUAL_JSON;
fn test_current_json_deserialized_into_current_message() {
let json = CURRENT_JSON;
let message: AggregatorFeaturesMessage = serde_json::from_str(json).unwrap();

assert_eq!(golden_message_actual(), message);
assert_eq!(golden_message_current(), message);
}
}
11 changes: 5 additions & 6 deletions mithril-common/src/messages/aggregator_status.rs
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ pub struct AggregatorStatusMessage {
mod tests {
use super::*;

const ACTUAL_JSON: &str = r#"{
const CURRENT_JSON: &str = r#"{
"epoch": 48,
"cardano_era": "conway",
"cardano_network": "mainnet",
Expand All @@ -74,7 +74,7 @@ mod tests {
"total_cardano_stake": 888888888
}"#;

fn golden_actual_message() -> AggregatorStatusMessage {
fn golden_current_message() -> AggregatorStatusMessage {
AggregatorStatusMessage {
epoch: Epoch(48),
cardano_era: "conway".to_string(),
Expand All @@ -101,14 +101,13 @@ mod tests {
}
}

// Test the compatibility with current structure.
#[test]
fn test_actual_json_deserialized_into_actual_message() {
let json = ACTUAL_JSON;
fn test_current_json_deserialized_into_current_message() {
let json = CURRENT_JSON;
let message: AggregatorStatusMessage = serde_json::from_str(json).expect(
"This JSON is expected to be successfully parsed into a AggregatorStatusMessage instance.",
);

assert_eq!(golden_actual_message(), message);
assert_eq!(golden_current_message(), message);
}
}
11 changes: 5 additions & 6 deletions mithril-common/src/messages/cardano_database.rs
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,7 @@ impl CardanoDatabaseSnapshotMessage {
mod tests {
use super::*;

const ACTUAL_JSON: &str = r#"
const CURRENT_JSON: &str = r#"
{
"hash": "d4071d518a3ace0f6c04a9c0745b9e9560e3e2af1b373bafc4e0398423e9abfb",
"merkle_root": "c8224920b9f5ad7377594eb8a15f34f08eb3103cc5241d57cafc5638403ec7c6",
Expand Down Expand Up @@ -140,7 +140,7 @@ mod tests {
"created_at": "2023-01-19T13:43:05.618857482Z"
}"#;

fn golden_actual_message() -> CardanoDatabaseSnapshotMessage {
fn golden_current_message() -> CardanoDatabaseSnapshotMessage {
CardanoDatabaseSnapshotMessage {
hash: "d4071d518a3ace0f6c04a9c0745b9e9560e3e2af1b373bafc4e0398423e9abfb".to_string(),
merkle_root: "c8224920b9f5ad7377594eb8a15f34f08eb3103cc5241d57cafc5638403ec7c6"
Expand Down Expand Up @@ -176,14 +176,13 @@ mod tests {
}
}

// Test the backward compatibility with possible future upgrades.
#[test]
fn test_actual_json_deserialized_into_actual_message() {
let json = ACTUAL_JSON;
fn test_current_json_deserialized_into_current_message() {
let json = CURRENT_JSON;
let message: CardanoDatabaseSnapshotMessage = serde_json::from_str(json).expect(
"This JSON is expected to be successfully parsed into a CardanoDatabaseSnapshotMessage instance.",
);

assert_eq!(golden_actual_message(), message);
assert_eq!(golden_current_message(), message);
}
}
11 changes: 5 additions & 6 deletions mithril-common/src/messages/cardano_database_digest_list.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,29 +29,28 @@ impl CardanoDatabaseDigestListItemMessage {
mod tests {
use super::*;

const ACTUAL_JSON: &str = r#"
const CURRENT_JSON: &str = r#"
[
{
"immutable_file_name": "06685.chunk",
"digest": "0af556ab2620dd9363bf76963a231abe8948a500ea6be31b131d87907ab09b1e"
}
]"#;

fn golden_actual_message() -> CardanoDatabaseDigestListMessage {
fn golden_current_message() -> CardanoDatabaseDigestListMessage {
vec![CardanoDatabaseDigestListItemMessage {
immutable_file_name: "06685.chunk".to_string(),
digest: "0af556ab2620dd9363bf76963a231abe8948a500ea6be31b131d87907ab09b1e".to_string(),
}]
}

// Test the backward compatibility with possible future upgrades.
#[test]
fn test_actual_json_deserialized_into_actual_message() {
let json = ACTUAL_JSON;
fn test_current_json_deserialized_into_current_message() {
let json = CURRENT_JSON;
let message: CardanoDatabaseDigestListMessage = serde_json::from_str(json).expect(
"This JSON is expected to be successfully parsed into a CardanoDatabaseDigestListMessage instance.",
);

assert_eq!(golden_actual_message(), message);
assert_eq!(golden_current_message(), message);
}
}
11 changes: 5 additions & 6 deletions mithril-common/src/messages/cardano_database_list.rs
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ impl CardanoDatabaseSnapshotListItemMessage {
mod tests {
use super::*;

const ACTUAL_JSON: &str = r#"
const CURRENT_JSON: &str = r#"
[
{
"hash": "d4071d518a3ace0f6c04a9c0745b9e9560e3e2af1b373bafc4e0398423e9abfb",
Expand All @@ -78,7 +78,7 @@ mod tests {
}
]"#;

fn golden_actual_message() -> CardanoDatabaseSnapshotListMessage {
fn golden_current_message() -> CardanoDatabaseSnapshotListMessage {
vec![CardanoDatabaseSnapshotListItemMessage {
hash: "d4071d518a3ace0f6c04a9c0745b9e9560e3e2af1b373bafc4e0398423e9abfb".to_string(),
merkle_root: "c8224920b9f5ad7377594eb8a15f34f08eb3103cc5241d57cafc5638403ec7c6"
Expand All @@ -98,14 +98,13 @@ mod tests {
}]
}

// Test the backward compatibility with possible future upgrades.
#[test]
fn test_actual_json_deserialized_into_actual_message() {
let json = ACTUAL_JSON;
fn test_current_json_deserialized_into_current_message() {
let json = CURRENT_JSON;
let message: CardanoDatabaseSnapshotListMessage = serde_json::from_str(json).expect(
"This JSON is expected to be successfully parsed into a CardanoDatabaseSnapshotListMessage instance.",
);

assert_eq!(golden_actual_message(), message);
assert_eq!(golden_current_message(), message);
}
}
23 changes: 12 additions & 11 deletions mithril-common/src/messages/cardano_stake_distribution.rs
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ impl CardanoStakeDistributionMessage {
mod tests {
use super::*;

fn golden_message() -> CardanoStakeDistributionMessage {
fn golden_message_current() -> CardanoStakeDistributionMessage {
CardanoStakeDistributionMessage {
epoch: Epoch(1),
hash: "hash-123".to_string(),
Expand All @@ -62,20 +62,21 @@ mod tests {
}
}

// Test the backward compatibility with possible future upgrades.
const CURRENT_JSON: &str = r#"{
"epoch": 1,
"hash": "hash-123",
"certificate_hash": "cert-hash-123",
"stake_distribution": { "pool-123": 1000, "pool-456": 2000 },
"created_at": "2024-07-29T16:15:05.618857482Z"
}"#;

#[test]
fn test_v1() {
let json = r#"{
"epoch": 1,
"hash": "hash-123",
"certificate_hash": "cert-hash-123",
"stake_distribution": { "pool-123": 1000, "pool-456": 2000 },
"created_at": "2024-07-29T16:15:05.618857482Z"
}"#;
fn test_current_json_deserialized_into_current_message() {
let json = CURRENT_JSON;
let message: CardanoStakeDistributionMessage = serde_json::from_str(json).expect(
"This JSON is expected to be successfully parsed into a CardanoStakeDistributionMessage instance.",
);

assert_eq!(golden_message(), message);
assert_eq!(golden_message_current(), message);
}
}
21 changes: 11 additions & 10 deletions mithril-common/src/messages/cardano_stake_distribution_list.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ impl CardanoStakeDistributionListItemMessage {
mod tests {
use super::*;

fn golden_message() -> CardanoStakeDistributionListMessage {
fn golden_message_current() -> CardanoStakeDistributionListMessage {
vec![CardanoStakeDistributionListItemMessage {
epoch: Epoch(1),
hash: "hash-123".to_string(),
Expand All @@ -51,19 +51,20 @@ mod tests {
}]
}

// Test the backward compatibility with possible future upgrades.
const CURRENT_JSON: &str = r#"[{
"epoch": 1,
"hash": "hash-123",
"certificate_hash": "cert-hash-123",
"created_at": "2024-07-29T16:15:05.618857482Z"
}]"#;

#[test]
fn test_v1() {
let json = r#"[{
"epoch": 1,
"hash": "hash-123",
"certificate_hash": "cert-hash-123",
"created_at": "2024-07-29T16:15:05.618857482Z"
}]"#;
fn test_current_json_deserialized_into_current_message() {
let json = CURRENT_JSON;
let message: CardanoStakeDistributionListMessage = serde_json::from_str(json).expect(
"This JSON is expected to be successfully parsed into a CardanoStakeDistributionListMessage instance.",
);

assert_eq!(golden_message(), message);
assert_eq!(golden_message_current(), message);
}
}
Loading

0 comments on commit ff02ea5

Please sign in to comment.