Skip to content

Commit 065ca55

Browse files
committed
Add tests for subgraph data source trigger scanning
1 parent 14853a5 commit 065ca55

File tree

1 file changed

+178
-0
lines changed

1 file changed

+178
-0
lines changed
Lines changed: 178 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,178 @@
1+
use std::{collections::BTreeMap, ops::Range, sync::Arc};
2+
3+
use graph::{
4+
blockchain::{
5+
block_stream::{
6+
EntitySubgraphOperation, EntityWithType, SubgraphTriggerScanRange,
7+
TriggersAdapterWrapper,
8+
},
9+
mock::MockTriggersAdapter,
10+
SubgraphFilter,
11+
},
12+
components::store::SourceableStore,
13+
data_source::CausalityRegion,
14+
prelude::{BlockHash, BlockNumber, BlockPtr, DeploymentHash, StoreError, Value},
15+
schema::{EntityType, InputSchema},
16+
};
17+
use slog::Logger;
18+
use tonic::async_trait;
19+
20+
pub struct MockSourcableStore {
21+
entities: BTreeMap<BlockNumber, Vec<EntityWithType>>,
22+
schema: InputSchema,
23+
block_ptr: Option<BlockPtr>,
24+
}
25+
26+
impl MockSourcableStore {
27+
pub fn new(
28+
entities: BTreeMap<BlockNumber, Vec<EntityWithType>>,
29+
schema: InputSchema,
30+
block_ptr: Option<BlockPtr>,
31+
) -> Self {
32+
Self {
33+
entities,
34+
schema,
35+
block_ptr,
36+
}
37+
}
38+
39+
pub fn set_block_ptr(&mut self, ptr: BlockPtr) {
40+
self.block_ptr = Some(ptr);
41+
}
42+
43+
pub fn clear_block_ptr(&mut self) {
44+
self.block_ptr = None;
45+
}
46+
47+
pub fn increment_block(&mut self) -> Result<(), &'static str> {
48+
if let Some(ptr) = &self.block_ptr {
49+
let new_number = ptr.number + 1;
50+
self.block_ptr = Some(BlockPtr::new(ptr.hash.clone(), new_number));
51+
Ok(())
52+
} else {
53+
Err("No block pointer set")
54+
}
55+
}
56+
57+
pub fn decrement_block(&mut self) -> Result<(), &'static str> {
58+
if let Some(ptr) = &self.block_ptr {
59+
if ptr.number == 0 {
60+
return Err("Block number already at 0");
61+
}
62+
let new_number = ptr.number - 1;
63+
self.block_ptr = Some(BlockPtr::new(ptr.hash.clone(), new_number));
64+
Ok(())
65+
} else {
66+
Err("No block pointer set")
67+
}
68+
}
69+
}
70+
71+
#[async_trait]
72+
impl SourceableStore for MockSourcableStore {
73+
fn get_range(
74+
&self,
75+
_entity_types: Vec<EntityType>,
76+
_causality_region: CausalityRegion,
77+
block_range: Range<BlockNumber>,
78+
) -> Result<BTreeMap<BlockNumber, Vec<EntityWithType>>, StoreError> {
79+
Ok(self
80+
.entities
81+
.range(block_range)
82+
.map(|(k, v)| (*k, v.clone()))
83+
.collect())
84+
}
85+
86+
fn input_schema(&self) -> InputSchema {
87+
self.schema.clone()
88+
}
89+
90+
async fn block_ptr(&self) -> Result<Option<BlockPtr>, StoreError> {
91+
Ok(self.block_ptr.clone())
92+
}
93+
}
94+
95+
#[tokio::test]
96+
async fn test_triggers_adapter_with_entities() {
97+
// Create a schema for a User entity
98+
let id = DeploymentHash::new("test_deployment").unwrap();
99+
let schema = InputSchema::parse_latest(
100+
"type User @entity { id: String!, name: String!, age: Int }",
101+
id.clone(),
102+
)
103+
.unwrap();
104+
105+
// Create some test entities
106+
let user1 = schema
107+
.make_entity(vec![
108+
("id".into(), Value::String("user1".to_owned())),
109+
("name".into(), Value::String("Alice".to_owned())),
110+
("age".into(), Value::Int(30)),
111+
])
112+
.unwrap();
113+
114+
let user2 = schema
115+
.make_entity(vec![
116+
("id".into(), Value::String("user2".to_owned())),
117+
("name".into(), Value::String("Bob".to_owned())),
118+
("age".into(), Value::Int(25)),
119+
])
120+
.unwrap();
121+
122+
// Create EntityWithType instances
123+
let user_type = schema.entity_type("User").unwrap();
124+
let entity1 = EntityWithType {
125+
entity_type: user_type.clone(),
126+
entity: user1,
127+
entity_op: EntitySubgraphOperation::Create,
128+
vid: 1,
129+
};
130+
131+
let entity2 = EntityWithType {
132+
entity_type: user_type,
133+
entity: user2,
134+
entity_op: EntitySubgraphOperation::Create,
135+
vid: 2,
136+
};
137+
138+
// Create a BTreeMap with entities at different blocks
139+
let mut entities = BTreeMap::new();
140+
entities.insert(1, vec![entity1]);
141+
entities.insert(2, vec![entity2]);
142+
143+
// Create block hash properly
144+
let hash_bytes: [u8; 32] = [0u8; 32];
145+
let block_hash = BlockHash(hash_bytes.to_vec().into_boxed_slice());
146+
147+
// Create the mock store with proper BlockPtr
148+
let initial_block = BlockPtr::new(block_hash, 0);
149+
let store = Arc::new(MockSourcableStore::new(
150+
entities,
151+
schema.clone(),
152+
Some(initial_block),
153+
));
154+
155+
// Create the adapter wrapper
156+
let adapter = Arc::new(MockTriggersAdapter {});
157+
let wrapper = TriggersAdapterWrapper::new(adapter, vec![store]);
158+
159+
// Create a test filter with correct structure
160+
let filter = SubgraphFilter {
161+
subgraph: id,
162+
start_block: 0, // Start from block 0
163+
entities: vec!["User".to_string()], // Monitor the User entity
164+
};
165+
166+
// Test scanning a range of blocks
167+
let logger = Logger::root(slog::Discard, slog::o!());
168+
let result = wrapper
169+
.blocks_with_subgraph_triggers(&logger, &[filter], SubgraphTriggerScanRange::Range(1, 2))
170+
.await;
171+
172+
assert!(result.is_ok(), "Failed to get triggers: {:?}", result.err());
173+
174+
let blocks = result.unwrap();
175+
assert!(!blocks.is_empty(), "Should have found blocks with triggers");
176+
177+
// Additional assertions could be made here about the specific blocks and triggers found
178+
}

0 commit comments

Comments
 (0)