Skip to content

Commit 54969a0

Browse files
committed
testing processor
1 parent 1f3dfde commit 54969a0

File tree

3 files changed

+294
-0
lines changed

3 files changed

+294
-0
lines changed

apps/nextra/next.config.mjs

+7
Original file line numberDiff line numberDiff line change
@@ -473,6 +473,13 @@ export default withBundleAnalyzer(
473473
"/en/build/indexer/indexer-sdk/documentation/advanced-tutorials/txn-script",
474474
permanent: true,
475475
},
476+
{
477+
source:
478+
"/indexer/indexer-sdk/documentation/advanced-tutorials/processor-test",
479+
destination:
480+
"/en/build/indexer/indexer-sdk/documentation/advanced-tutorials/processor-test",
481+
permanent: true,
482+
},
476483
{
477484
source: "/indexer/txn-stream/labs-hosted",
478485
destination: "/en/build/indexer/api/labs-hosted",

apps/nextra/pages/en/build/indexer/indexer-sdk/advanced-tutorials/_meta.tsx

+3
Original file line numberDiff line numberDiff line change
@@ -8,4 +8,7 @@ export default {
88
"txn-script": {
99
title: "Generating Transactions with Move Scripts",
1010
},
11+
"processor-test": {
12+
title: "Testing Your Processor",
13+
},
1114
};
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,284 @@
1+
---
2+
title: "Testing Processor"
3+
---
4+
5+
import { Callout } from "nextra/components"
6+
7+
8+
# Overview
9+
### What Is a Processor?
10+
A processor is a core component of the Aptos Indexer that handles blockchain transaction processing. It validates, transforms, and stores transactions into a database, enabling downstream applications like analytics, indexing, and querying. Testing the processor ensures that all transactions are correctly handled, maintaining data accuracy and consistency.
11+
12+
13+
### What Are We Testing With This?
14+
15+
- **Transaction correctness**: Ensure that each transaction is processed and stored accurately.
16+
- **Schema consistency**: Verify that the database schema is correctly set up and maintained throughout the tests.
17+
18+
19+
### General Flow of how Processor Testing Works
20+
21+
1. You specify the transactions to test
22+
2. Testing framework SDK spins up a mock gRPC Service with the transactions you specified to return when the processor requests transactions.
23+
3. Processor processes the transactions and writes the output to a database.
24+
4. Optionally, you can generate expected database output for validation.
25+
26+
Type of Scenarios it Supports:
27+
1. A single transaction
28+
2. A single batch of multiple transactions
29+
Input [A, B, C]
30+
1. Processor processes A, B, and C
31+
3. Sequential multiple transaction batches:
32+
Input [A, B, C]
33+
1. Processor processes A and B
34+
2. Processor processes C
35+
36+
## Prerequisites
37+
1. Ensure Docker Desktop is running for PostgreSQL container support.
38+
- **Docker Desktop Installation**: Install Docker Desktop following [this guide](https://docs.docker.com/desktop/) on your machine.
39+
- Start Docker Desktop if it's not running
40+
2. Identify the transactions to test.
41+
- Use imported transactions or write your own custom Move scripts to generate test transactions. Refer to [Importing Transaction Guide](./txn-importer.mdx) and [Generating Transaction using Move Script Guide](./txn-script.mdx) for detailed instructions.
42+
3. Import aptos-indexer-testing-framework to your Cargo.toml
43+
44+
<Callout> - This tutorial assumes you are using Postgres as the database. </Callout>
45+
- **Adapting to Other Databases**:
46+
- Replace PostgreSQL-specific code with relevant database code you intend to use (e.g., MySQL).
47+
- Update schema initialization and query methods.
48+
- **References to Processor Tests**:
49+
- Example: [Event Processor Tests](https://github.com/aptos-labs/aptos-indexer-processors/blob/main/rust/integration-tests/src/sdk_tests/events_processor_tests.rs#L139).
50+
51+
52+
## Steps to Write a Test
53+
54+
### 1. Set Up the Test Environment
55+
Before setting up the test environment, it’s important to understand the configurations being used in this step:
56+
57+
58+
**What Are These Configurations?**
59+
60+
`generate_file_flag`
61+
- If `generate_file_flag` is true, the test will overwrite any saved database outputs from previous test runs. If `generate_file_flag` is false, the test will only compare the the actual database output with the expected database output and log differences.
62+
63+
64+
`custom_output_path`
65+
- An optional configuration to specify a custom path where the expected database output will be stored.
66+
If not provided, the test will use the default path defined by DEFAULT_OUTPUT_FOLDER.
67+
68+
`DEFAULT_OUTPUT_FOLDER`
69+
- This constant defines the default folder where the system stores output files for the tests.
70+
Example: "sdk_expected_db_output_files".
71+
Modify this value in your configuration if you prefer a different default directory.
72+
73+
74+
```rust
75+
let (generate_file_flag, custom_output_path) = get_test_config();
76+
let output_path = custom_output_path.unwrap_or_else(|| format!("{}/imported_mainnet_txns", DEFAULT_OUTPUT_FOLDER));
77+
78+
// Setup DB and replace as needed
79+
let mut db = PostgresTestDatabase::new();
80+
db.setup().await.unwrap();
81+
82+
let mut test_context = SdkTestContext::new(&[CONST_VARIABLE_OF_YOUR_TEST_TRANSACTION]); // Replace with your test transaction
83+
if test_context.init_mock_grpc().await.is_err() {
84+
panic!("Failed to initialize mock grpc");
85+
};
86+
```
87+
88+
**Explanation of Each Component:**
89+
90+
`get_test_config():`
91+
92+
This function fetches the configurations (diff_flag and custom_output_path) for the test.
93+
Modify or extend this function if you want to support additional custom flags or configurations.
94+
output_path:
95+
96+
Combines DEFAULT_OUTPUT_FOLDER with the subfolder imported_mainnet_txns if no custom_output_path is specified.
97+
This ensures all output files are stored in a predictable location.
98+
99+
`PostgresTestDatabase::new():`
100+
101+
Creates a new PostgreSQL database instance for testing.
102+
This database is isolated, ensuring no interference with production or other test environments.
103+
104+
`SdkTestContext::new():`
105+
106+
Initializes the test context with the transaction(s) you want to test.
107+
Replace CONST_VARIABLE_OF_YOUR_TEST_TRANSACTION with the appropriate variable or constant representing the transaction(s) to be tested.
108+
109+
`init_mock_grpc():`
110+
111+
Initializes a mock gRPC service for the test.
112+
This allows the processor to simulate transactions without interacting with live blockchain data.
113+
114+
115+
### 2. Configure the Processor
116+
<Callout>
117+
- Each test runs in an isolated environment using a PostgreSQL container to prevent interference.
118+
</Callout>
119+
120+
```rust
121+
let db_url = db.get_db_url();
122+
let transaction_stream_config = test_context.create_transaction_stream_config();
123+
let postgres_config = PostgresConfig {
124+
connection_string: db_url.to_string(),
125+
db_pool_size: 100,
126+
};
127+
128+
let db_config = DbConfig::PostgresConfig(postgres_config);
129+
let default_processor_config = DefaultProcessorConfig {
130+
per_table_chunk_sizes: AHashMap::new(),
131+
channel_size: 100,
132+
deprecated_tables: HashSet::new(),
133+
};
134+
135+
let processor_config = ProcessorConfig::DefaultProcessor(default_processor_config);
136+
let processor_name = processor_config.name();
137+
```
138+
139+
### 3. Create the Processor
140+
141+
```rust
142+
let processor = DefaultProcessor::new(indexer_processor_config)
143+
.await
144+
.expect("Failed to create processor");
145+
```
146+
Note: Replace `DefaultProcessor` with the processor you are testing.
147+
148+
### 4. Setup a Query
149+
150+
Set up a query to load data from the local database and compare it with expected results, see [example loading function](https://github.com/aptos-labs/aptos-indexer-processors/blob/a8f9c5915f4e3f1f596ed3412b8eb01feca1aa7b/rust/integration-tests/src/diff_test_helper/default_processor.rs#L45)
151+
152+
153+
### 5. Setup a Test Context run function
154+
Use the test_context.run() function to execute the processor, validate outputs using your query, and optionally generate database output files:
155+
156+
<Callout>
157+
Key Considerations:
158+
- Each test runs in an isolated environment using a PostgreSQL container to prevent interference.
159+
- Proper handling of versions ensures transactions are processed and validated in the correct order.
160+
- Validation logic must detect changes or issues by comparing processor output with the expected baseline.
161+
</Callout>
162+
163+
```rust
164+
let txn_versions: Vec<i64> = test_context
165+
.get_test_transaction_versions()
166+
.into_iter()
167+
.map(|v| v as i64)
168+
.collect();
169+
170+
let db_values = test_context
171+
.run(
172+
&processor,
173+
generate_file_flag,
174+
output_path.clone(),
175+
custom_file_name,
176+
move || {
177+
let mut conn = PgConnection::establish(&db_url).unwrap_or_else(|e| {
178+
eprintln!("[ERROR] Failed to establish DB connection: {:?}", e);
179+
panic!("Failed to establish DB connection: {:?}", e);
180+
});
181+
182+
let db_values = match load_data(&mut conn, txn_versions.clone()) {
183+
Ok(db_data) => db_data,
184+
Err(e) => {
185+
eprintln!("[ERROR] Failed to load data {}", e);
186+
return Err(e);
187+
},
188+
};
189+
190+
if db_values.is_empty() {
191+
eprintln!("[WARNING] No data found for versions: {:?}", txn_versions);
192+
}
193+
194+
Ok(db_values)
195+
},
196+
)
197+
```
198+
199+
200+
### 6. Run the Processor Test
201+
202+
Once you have your test ready, run the following command to generate the expected output for validation:
203+
204+
```bash
205+
cargo test sdk_tests -- generate-output
206+
```
207+
208+
Arguments:
209+
generate-output: Set this true if you want to generate or overwrite saved database output, or false if you want to compare database outputs in diff mode.
210+
output-path: it's an optional argument to specify the output path for the db output.
211+
212+
The expected database output will be saved in the specified output_path or `sdk_expected_db_output_files` by default.
213+
214+
215+
---
216+
217+
## FAQ
218+
219+
### What Types of Tests Does It Support?
220+
221+
- The testing framework allows you to write tests that compare the database outputs of processors. It helps you catch changes in database output when you're updating or developing your processor.
222+
223+
### What Is `TestContext`?
224+
225+
`TestContext` is a struct that manages:
226+
227+
- `transaction_batches`: A collection of transaction batches.
228+
- `postgres_container`: A PostgreSQL container for test isolation.
229+
230+
It initializes and manages the database and transaction context for tests.
231+
232+
#### What Does `TestContext.run` Do?
233+
234+
This function executes the processor, applies validation logic, and optionally generates output files.
235+
236+
#### Key Features:
237+
238+
- Flexible Validation: Accepts a user-provided verification function.
239+
- Multi-Table Support: Handles data across multiple tables.
240+
- Retries: Uses exponential backoff and timeout for retries.
241+
- Optional File Generation: Controlled by a flag.
242+
243+
#### Example Usage:
244+
245+
```rust
246+
pub async fn run<F>(
247+
&mut self,
248+
processor: &impl ProcessorTrait,
249+
txn_version: u64,
250+
generate_files: bool, // Flag to control file generation
251+
output_path: String, // Output path
252+
custom_file_name: Option<String>, // Custom file name
253+
verification_f: F, // Verification function
254+
) -> anyhow::Result<HashMap<String, Value>>
255+
where
256+
```
257+
258+
### How to Generate Expected DB Output?
259+
260+
Run the following command:
261+
262+
```bash
263+
cargo test sdk_tests -- --nocapture generate-output
264+
```
265+
266+
Supported Test Args:
267+
268+
1. `generate-output`
269+
2. `output_path`
270+
271+
---
272+
273+
## Troubleshooting and Tips
274+
275+
1. **Isolate Tests**: Use Docker containers for database isolation.
276+
2. **Handle Non-Deterministic Fields**: Use helpers like `remove_inserted_at` to clean up timestamps before validation.
277+
3. **Enable Debugging**: Use `eprintln!` for detailed error logging.
278+
279+
#### How to Debug Test Failures?
280+
run following command to get detailed logs:
281+
282+
```bash
283+
cargo test sdk_tests -- --nocapture
284+
```

0 commit comments

Comments
 (0)