Skip to content

Commit ff02ea5

Browse files
authored
Merge pull request #2221 from input-output-hk/djo/2217/align_messages_golden_master_tests
Align messages golden master tests
2 parents 584e2c6 + 71b01e9 commit ff02ea5

24 files changed

+222
-201
lines changed

Cargo.lock

+1-1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
---
2+
slug: 8
3+
title: |
4+
8. Standardize JSON Message Testing
5+
authors:
6+
- name: Mithril Team
7+
tags: [Accepted]
8+
date: 2025-01-14
9+
---
10+
11+
## Status
12+
13+
Accepted
14+
15+
## Context
16+
17+
- To ensure backward compatibility and correctness of JSON messages exchanged between nodes, we need a standardized approach
18+
to test the deserialization of these messages.
19+
- Golden testing is a technique where the expected output (golden data) is stored and used to verify the correctness of
20+
the system's output. This approach helps in detecting unintended changes in the output and ensures that the system
21+
behaves as expected over time.
22+
- By using golden testing for JSON message deserialization, we can ensure that any changes to the message structures are
23+
backward compatible and that the deserialization process yields the expected results.
24+
- We have been using golden testing for JSON messages in the project, but the approach used ad-hoc versions that did not
25+
correspond to any OpenAPI versions, making it difficult to track the changes and maintain backward compatibility.
26+
27+
## Decision
28+
29+
We will standardize the testing of JSON messages by following the steps below:
30+
31+
When adding a new JSON message structure, the following steps should be taken:
32+
33+
- Introduce a constant `CURRENT_JSON` string containing an exhaustive example of the JSON currently exchanged between nodes.
34+
- Implement a `golden_message_current` method that returns the representation of the `CURRENT_JSON` using the current structure.
35+
- 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`.
36+
37+
When modifying an existing JSON message structure, if backward compatibility is maintained, the following steps should be taken:
38+
39+
- Given `X_Y_ZZ` is the version of the OpenAPI before the change:
40+
- 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`.
41+
- 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`.
42+
- 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`.
43+
- Modify the `CURRENT_JSON` string to reflect the new structure.
44+
- Modify the `golden_message_current` method to return the representation of the `CURRENT_JSON` using the new structure.
45+
46+
When modifying an existing JSON message structure, if backward compatibility is not maintained, the following steps should be taken:
47+
48+
- Modify the `CURRENT_JSON` string to reflect the new structure.
49+
- Modify the `golden_message_current` method to return the representation of the `CURRENT_JSON` using the new structure.
50+
- Remove all `golden_message_until_open_api_X_Y_ZZ` method and the corresponding structure and tests, as they are no longer relevant.
51+
52+
## Consequences
53+
54+
- Ensures that any changes to the JSON message structure are backward compatible.
55+
- Provides a clear and standardized approach to testing JSON message deserialization.
56+
- Helps maintain the integrity and reliability of the communication between nodes.
57+
- Requires maintaining multiple versions of message structures and corresponding tests, which may increase the maintenance overhead.

mithril-common/Cargo.toml

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "mithril-common"
3-
version = "0.4.103"
3+
version = "0.4.104"
44
description = "Common types, interfaces, and utilities for Mithril nodes."
55
authors = { workspace = true }
66
edition = { workspace = true }

mithril-common/src/messages/aggregator_features.rs

+12-13
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ mod tests {
6363
use super::*;
6464

6565
#[derive(Debug, Serialize, Deserialize, PartialEq)]
66-
struct AggregatorFeaturesMessagePrevious {
66+
struct AggregatorFeaturesMessageUntilV0_1_27 {
6767
pub open_api_version: String,
6868
pub documentation_url: String,
6969
pub capabilities: AggregatorCapabilitiesPrevious,
@@ -76,8 +76,8 @@ mod tests {
7676
pub cardano_transactions_prover: Option<CardanoTransactionsProverCapabilities>,
7777
}
7878

79-
fn golden_message_previous() -> AggregatorFeaturesMessagePrevious {
80-
AggregatorFeaturesMessagePrevious {
79+
fn golden_message_until_open_api_0_1_27() -> AggregatorFeaturesMessageUntilV0_1_27 {
80+
AggregatorFeaturesMessageUntilV0_1_27 {
8181
open_api_version: "0.0.1".to_string(),
8282
documentation_url: "https://example.com".to_string(),
8383
capabilities: AggregatorCapabilitiesPrevious {
@@ -91,7 +91,7 @@ mod tests {
9191
}
9292
}
9393

94-
fn golden_message_actual() -> AggregatorFeaturesMessage {
94+
fn golden_message_current() -> AggregatorFeaturesMessage {
9595
AggregatorFeaturesMessage {
9696
open_api_version: "0.0.1".to_string(),
9797
documentation_url: "https://example.com".to_string(),
@@ -110,7 +110,7 @@ mod tests {
110110
}
111111
}
112112

113-
const ACTUAL_JSON: &str = r#"{
113+
const CURRENT_JSON: &str = r#"{
114114
"open_api_version": "0.0.1",
115115
"documentation_url": "https://example.com",
116116
"capabilities": {
@@ -125,20 +125,19 @@ mod tests {
125125
}
126126
}"#;
127127

128-
// Test the backward compatibility with possible future upgrades.
129128
#[test]
130-
fn test_actual_json_deserialized_into_previous_message() {
131-
let json = ACTUAL_JSON;
132-
let message: AggregatorFeaturesMessagePrevious = serde_json::from_str(json).unwrap();
129+
fn test_current_json_deserialized_into_message_supported_until_open_api_0_1_27() {
130+
let json = CURRENT_JSON;
131+
let message: AggregatorFeaturesMessageUntilV0_1_27 = serde_json::from_str(json).unwrap();
133132

134-
assert_eq!(golden_message_previous(), message);
133+
assert_eq!(golden_message_until_open_api_0_1_27(), message);
135134
}
136135

137136
#[test]
138-
fn test_actual_json_deserialized_into_actual_message() {
139-
let json = ACTUAL_JSON;
137+
fn test_current_json_deserialized_into_current_message() {
138+
let json = CURRENT_JSON;
140139
let message: AggregatorFeaturesMessage = serde_json::from_str(json).unwrap();
141140

142-
assert_eq!(golden_message_actual(), message);
141+
assert_eq!(golden_message_current(), message);
143142
}
144143
}

mithril-common/src/messages/aggregator_status.rs

+5-6
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ pub struct AggregatorStatusMessage {
5757
mod tests {
5858
use super::*;
5959

60-
const ACTUAL_JSON: &str = r#"{
60+
const CURRENT_JSON: &str = r#"{
6161
"epoch": 48,
6262
"cardano_era": "conway",
6363
"cardano_network": "mainnet",
@@ -74,7 +74,7 @@ mod tests {
7474
"total_cardano_stake": 888888888
7575
}"#;
7676

77-
fn golden_actual_message() -> AggregatorStatusMessage {
77+
fn golden_current_message() -> AggregatorStatusMessage {
7878
AggregatorStatusMessage {
7979
epoch: Epoch(48),
8080
cardano_era: "conway".to_string(),
@@ -101,14 +101,13 @@ mod tests {
101101
}
102102
}
103103

104-
// Test the compatibility with current structure.
105104
#[test]
106-
fn test_actual_json_deserialized_into_actual_message() {
107-
let json = ACTUAL_JSON;
105+
fn test_current_json_deserialized_into_current_message() {
106+
let json = CURRENT_JSON;
108107
let message: AggregatorStatusMessage = serde_json::from_str(json).expect(
109108
"This JSON is expected to be successfully parsed into a AggregatorStatusMessage instance.",
110109
);
111110

112-
assert_eq!(golden_actual_message(), message);
111+
assert_eq!(golden_current_message(), message);
113112
}
114113
}

mithril-common/src/messages/cardano_database.rs

+5-6
Original file line numberDiff line numberDiff line change
@@ -101,7 +101,7 @@ impl CardanoDatabaseSnapshotMessage {
101101
mod tests {
102102
use super::*;
103103

104-
const ACTUAL_JSON: &str = r#"
104+
const CURRENT_JSON: &str = r#"
105105
{
106106
"hash": "d4071d518a3ace0f6c04a9c0745b9e9560e3e2af1b373bafc4e0398423e9abfb",
107107
"merkle_root": "c8224920b9f5ad7377594eb8a15f34f08eb3103cc5241d57cafc5638403ec7c6",
@@ -140,7 +140,7 @@ mod tests {
140140
"created_at": "2023-01-19T13:43:05.618857482Z"
141141
}"#;
142142

143-
fn golden_actual_message() -> CardanoDatabaseSnapshotMessage {
143+
fn golden_current_message() -> CardanoDatabaseSnapshotMessage {
144144
CardanoDatabaseSnapshotMessage {
145145
hash: "d4071d518a3ace0f6c04a9c0745b9e9560e3e2af1b373bafc4e0398423e9abfb".to_string(),
146146
merkle_root: "c8224920b9f5ad7377594eb8a15f34f08eb3103cc5241d57cafc5638403ec7c6"
@@ -176,14 +176,13 @@ mod tests {
176176
}
177177
}
178178

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

187-
assert_eq!(golden_actual_message(), message);
186+
assert_eq!(golden_current_message(), message);
188187
}
189188
}

mithril-common/src/messages/cardano_database_digest_list.rs

+5-6
Original file line numberDiff line numberDiff line change
@@ -29,29 +29,28 @@ impl CardanoDatabaseDigestListItemMessage {
2929
mod tests {
3030
use super::*;
3131

32-
const ACTUAL_JSON: &str = r#"
32+
const CURRENT_JSON: &str = r#"
3333
[
3434
{
3535
"immutable_file_name": "06685.chunk",
3636
"digest": "0af556ab2620dd9363bf76963a231abe8948a500ea6be31b131d87907ab09b1e"
3737
}
3838
]"#;
3939

40-
fn golden_actual_message() -> CardanoDatabaseDigestListMessage {
40+
fn golden_current_message() -> CardanoDatabaseDigestListMessage {
4141
vec![CardanoDatabaseDigestListItemMessage {
4242
immutable_file_name: "06685.chunk".to_string(),
4343
digest: "0af556ab2620dd9363bf76963a231abe8948a500ea6be31b131d87907ab09b1e".to_string(),
4444
}]
4545
}
4646

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

55-
assert_eq!(golden_actual_message(), message);
54+
assert_eq!(golden_current_message(), message);
5655
}
5756
}

mithril-common/src/messages/cardano_database_list.rs

+5-6
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ impl CardanoDatabaseSnapshotListItemMessage {
6161
mod tests {
6262
use super::*;
6363

64-
const ACTUAL_JSON: &str = r#"
64+
const CURRENT_JSON: &str = r#"
6565
[
6666
{
6767
"hash": "d4071d518a3ace0f6c04a9c0745b9e9560e3e2af1b373bafc4e0398423e9abfb",
@@ -78,7 +78,7 @@ mod tests {
7878
}
7979
]"#;
8080

81-
fn golden_actual_message() -> CardanoDatabaseSnapshotListMessage {
81+
fn golden_current_message() -> CardanoDatabaseSnapshotListMessage {
8282
vec![CardanoDatabaseSnapshotListItemMessage {
8383
hash: "d4071d518a3ace0f6c04a9c0745b9e9560e3e2af1b373bafc4e0398423e9abfb".to_string(),
8484
merkle_root: "c8224920b9f5ad7377594eb8a15f34f08eb3103cc5241d57cafc5638403ec7c6"
@@ -98,14 +98,13 @@ mod tests {
9898
}]
9999
}
100100

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

109-
assert_eq!(golden_actual_message(), message);
108+
assert_eq!(golden_current_message(), message);
110109
}
111110
}

mithril-common/src/messages/cardano_stake_distribution.rs

+12-11
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ impl CardanoStakeDistributionMessage {
4747
mod tests {
4848
use super::*;
4949

50-
fn golden_message() -> CardanoStakeDistributionMessage {
50+
fn golden_message_current() -> CardanoStakeDistributionMessage {
5151
CardanoStakeDistributionMessage {
5252
epoch: Epoch(1),
5353
hash: "hash-123".to_string(),
@@ -62,20 +62,21 @@ mod tests {
6262
}
6363
}
6464

65-
// Test the backward compatibility with possible future upgrades.
65+
const CURRENT_JSON: &str = r#"{
66+
"epoch": 1,
67+
"hash": "hash-123",
68+
"certificate_hash": "cert-hash-123",
69+
"stake_distribution": { "pool-123": 1000, "pool-456": 2000 },
70+
"created_at": "2024-07-29T16:15:05.618857482Z"
71+
}"#;
72+
6673
#[test]
67-
fn test_v1() {
68-
let json = r#"{
69-
"epoch": 1,
70-
"hash": "hash-123",
71-
"certificate_hash": "cert-hash-123",
72-
"stake_distribution": { "pool-123": 1000, "pool-456": 2000 },
73-
"created_at": "2024-07-29T16:15:05.618857482Z"
74-
}"#;
74+
fn test_current_json_deserialized_into_current_message() {
75+
let json = CURRENT_JSON;
7576
let message: CardanoStakeDistributionMessage = serde_json::from_str(json).expect(
7677
"This JSON is expected to be successfully parsed into a CardanoStakeDistributionMessage instance.",
7778
);
7879

79-
assert_eq!(golden_message(), message);
80+
assert_eq!(golden_message_current(), message);
8081
}
8182
}

mithril-common/src/messages/cardano_stake_distribution_list.rs

+11-10
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ impl CardanoStakeDistributionListItemMessage {
4040
mod tests {
4141
use super::*;
4242

43-
fn golden_message() -> CardanoStakeDistributionListMessage {
43+
fn golden_message_current() -> CardanoStakeDistributionListMessage {
4444
vec![CardanoStakeDistributionListItemMessage {
4545
epoch: Epoch(1),
4646
hash: "hash-123".to_string(),
@@ -51,19 +51,20 @@ mod tests {
5151
}]
5252
}
5353

54-
// Test the backward compatibility with possible future upgrades.
54+
const CURRENT_JSON: &str = r#"[{
55+
"epoch": 1,
56+
"hash": "hash-123",
57+
"certificate_hash": "cert-hash-123",
58+
"created_at": "2024-07-29T16:15:05.618857482Z"
59+
}]"#;
60+
5561
#[test]
56-
fn test_v1() {
57-
let json = r#"[{
58-
"epoch": 1,
59-
"hash": "hash-123",
60-
"certificate_hash": "cert-hash-123",
61-
"created_at": "2024-07-29T16:15:05.618857482Z"
62-
}]"#;
62+
fn test_current_json_deserialized_into_current_message() {
63+
let json = CURRENT_JSON;
6364
let message: CardanoStakeDistributionListMessage = serde_json::from_str(json).expect(
6465
"This JSON is expected to be successfully parsed into a CardanoStakeDistributionListMessage instance.",
6566
);
6667

67-
assert_eq!(golden_message(), message);
68+
assert_eq!(golden_message_current(), message);
6869
}
6970
}

0 commit comments

Comments
 (0)