Skip to main content

reifydb_core/testing/
mod.rs

1// SPDX-License-Identifier: Apache-2.0
2// Copyright (c) 2025 ReifyDB
3
4use std::collections::HashMap;
5
6use crate::{
7	encoded::{encoded::EncodedValues, schema::Schema},
8	interface::catalog::column::ColumnDef,
9	value::column::columns::Columns,
10};
11
12/// A captured event dispatch during test execution.
13#[derive(Clone, Debug)]
14pub struct CapturedEvent {
15	pub sequence: u64,
16	pub namespace: String,
17	pub event: String,
18	pub variant: String,
19	pub depth: u8,
20	pub columns: Columns,
21}
22
23/// A captured handler invocation during test execution.
24#[derive(Clone, Debug)]
25pub struct HandlerInvocation {
26	pub sequence: u64,
27	pub namespace: String,
28	pub handler: String,
29	pub event: String,
30	pub variant: String,
31	pub duration_ns: u64,
32	pub outcome: String,
33	pub message: String,
34}
35
36/// A captured mutation (insert/update/delete) during test execution.
37#[derive(Clone, Debug)]
38pub struct MutationRecord {
39	pub sequence: u64,
40	pub op: String,
41	pub old: Columns,
42	pub new: Columns,
43}
44
45/// Audit log that captures events, handler invocations, and mutations during test execution.
46///
47/// Only allocated when `vm.in_test_context` is true. Zero cost in production.
48#[derive(Clone, Debug)]
49pub struct TestingContext {
50	pub events: Vec<CapturedEvent>,
51	pub handler_invocations: Vec<HandlerInvocation>,
52	/// Keyed by "namespace::primitive_name"
53	pub mutations: HashMap<String, Vec<MutationRecord>>,
54	event_seq: u64,
55	handler_seq: u64,
56	mutation_seq: u64,
57}
58
59impl TestingContext {
60	pub fn new() -> Self {
61		Self {
62			events: Vec::new(),
63			handler_invocations: Vec::new(),
64			mutations: HashMap::new(),
65			event_seq: 0,
66			handler_seq: 0,
67			mutation_seq: 0,
68		}
69	}
70
71	pub fn clear(&mut self) {
72		self.events.clear();
73		self.handler_invocations.clear();
74		self.mutations.clear();
75		self.event_seq = 0;
76		self.handler_seq = 0;
77		self.mutation_seq = 0;
78	}
79
80	pub fn record_event(&mut self, namespace: String, event: String, variant: String, depth: u8, columns: Columns) {
81		self.event_seq += 1;
82		self.events.push(CapturedEvent {
83			sequence: self.event_seq,
84			namespace,
85			event,
86			variant,
87			depth,
88			columns,
89		});
90	}
91
92	pub fn record_handler_invocation(
93		&mut self,
94		namespace: String,
95		handler: String,
96		event: String,
97		variant: String,
98		duration_ns: u64,
99		outcome: String,
100		message: String,
101	) {
102		self.handler_seq += 1;
103		self.handler_invocations.push(HandlerInvocation {
104			sequence: self.handler_seq,
105			namespace,
106			handler,
107			event,
108			variant,
109			duration_ns,
110			outcome,
111			message,
112		});
113	}
114
115	pub fn record_mutation(&mut self, primitive_key: String, op: String, old: Columns, new: Columns) {
116		self.mutation_seq += 1;
117		self.mutations.entry(primitive_key).or_default().push(MutationRecord {
118			sequence: self.mutation_seq,
119			op,
120			old,
121			new,
122		});
123	}
124
125	pub fn record_insert(&mut self, key: String, new: Columns) {
126		self.record_mutation(key, "insert".to_string(), Columns::empty(), new);
127	}
128
129	pub fn record_delete(&mut self, key: String, old: Columns) {
130		self.record_mutation(key, "delete".to_string(), old, Columns::empty());
131	}
132
133	pub fn record_update(&mut self, key: String, old: Columns, new: Columns) {
134		self.record_mutation(key, "update".to_string(), old, new);
135	}
136}
137
138pub fn columns_from_encoded(columns: &[ColumnDef], schema: &Schema, encoded: &EncodedValues) -> Columns {
139	Columns::single_row(
140		columns.iter().enumerate().map(|(i, col)| (col.name.as_str(), schema.get_value(encoded, i))),
141	)
142}