1use mempill_types::{AgentId, AdjudicationRequest, ClaimRef};
12use crate::ports::{OraclePort, VectorPort};
13
14#[derive(Debug)]
18pub enum NoOpError {}
19
20impl std::fmt::Display for NoOpError {
21 fn fmt(&self, _f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
22 unreachable!()
24 }
25}
26
27impl std::error::Error for NoOpError {}
28
29#[derive(Debug, Clone)]
43pub struct NoOpOracle;
44
45impl OraclePort for NoOpOracle {
46 type Error = NoOpError;
47 type Handle = ();
48
49 fn request_adjudication(
50 &self,
51 _agent_id: &AgentId,
52 _request: AdjudicationRequest,
53 ) -> Result<Self::Handle, Self::Error> {
54 Ok(())
56 }
57
58 fn handle_to_uuid(_handle: &Self::Handle) -> uuid::Uuid {
61 uuid::Uuid::new_v4()
62 }
63}
64
65#[derive(Debug, Clone)]
76pub struct NoOpVector;
77
78impl VectorPort for NoOpVector {
79 type Error = NoOpError;
80
81 fn store_embedding(
82 &self,
83 _agent_id: &AgentId,
84 _claim_ref: &ClaimRef,
85 _vector: &[f32],
86 _embedding_model_id: &str,
87 ) -> Result<(), Self::Error> {
88 Ok(())
90 }
91
92 fn search(
93 &self,
94 _agent_id: &AgentId,
95 _query_vector: &[f32],
96 _k: usize,
97 _embedding_model_id: &str,
98 ) -> Result<Vec<ClaimRef>, Self::Error> {
99 Ok(vec![])
101 }
102}
103
104#[cfg(test)]
105mod tests {
106 use super::*;
107 use mempill_types::{
108 AgentId, AdjudicationRequest, Belief, Claim, ClaimRef, Cardinality, Criticality,
109 Confidence, CurrencySignal, CurrencyState, ExternalAnchor, ExternalKind,
110 Fact, OverturnReason, ProvenanceLabel, SubjectLineRef, TransactionTime, ValidTime,
111 };
112
113 fn make_agent() -> AgentId {
114 AgentId("test-agent".into())
115 }
116
117 fn make_belief() -> Belief {
118 Belief {
119 claim_ref: ClaimRef::new_random(),
120 fact: Fact {
121 subject: "alice".into(),
122 predicate: "age".into(),
123 value: serde_json::json!(30),
124 },
125 provenance: ProvenanceLabel::External(ExternalKind::UserAsserted),
126 valid_time: ValidTime { start: None, end: None, valid_time_confidence: 0.0 },
127 transaction_time: TransactionTime(chrono::Utc::now()),
128 confidence: Confidence { value_confidence: 0.9, valid_time_confidence: 0.0 },
129 currency_signal: CurrencySignal {
130 last_refreshed_at: TransactionTime(chrono::Utc::now()),
131 state: CurrencyState::Fresh,
132 corroboration_count: 0,
133 },
134 criticality: Criticality::Low,
135 }
136 }
137
138 fn make_challenger(agent: &AgentId) -> Claim {
139 Claim::new(
140 ClaimRef::new_random(),
141 agent.clone(),
142 Fact {
143 subject: "alice".into(),
144 predicate: "age".into(),
145 value: serde_json::json!(31),
146 },
147 Cardinality::Functional,
148 ProvenanceLabel::External(ExternalKind::UserAsserted),
149 ExternalAnchor { nearest_external_anchor: None, derivation_depth: 0 },
150 TransactionTime(chrono::Utc::now()),
151 ValidTime { start: None, end: None, valid_time_confidence: 0.0 },
152 Confidence { value_confidence: 0.9, valid_time_confidence: 0.0 },
153 Criticality::Low,
154 vec![],
155 None,
156 None,
157 )
158 }
159
160 #[test]
161 fn noop_oracle_implements_oracle_port_and_returns_ok() {
162 let oracle = NoOpOracle;
163 let agent = make_agent();
164 let request = AdjudicationRequest {
165 subject_line: SubjectLineRef {
166 agent_id: agent.clone(),
167 subject: "alice".into(),
168 predicate: "age".into(),
169 },
170 incumbent: make_belief(),
171 challenger: make_challenger(&agent),
172 criticality: Criticality::Low,
173 reason: OverturnReason::ExternalContradiction,
174 };
175
176 let result = oracle.request_adjudication(&agent, request);
177 assert!(result.is_ok());
178 let _handle: () = result.unwrap();
179 }
180
181 #[test]
182 fn noop_vector_store_embedding_is_noop() {
183 let vector = NoOpVector;
184 let agent = make_agent();
185 let claim_ref = ClaimRef::new_random();
186 let embedding = vec![0.1f32, 0.2, 0.3];
187 let result = vector.store_embedding(&agent, &claim_ref, &embedding, "text-embedding-3-small");
188 assert!(result.is_ok());
189 }
190
191 #[test]
192 fn noop_vector_search_returns_empty() {
193 let vector = NoOpVector;
194 let agent = make_agent();
195 let query = vec![0.1f32, 0.2, 0.3];
196 let result = vector.search(&agent, &query, 10, "text-embedding-3-small");
197 assert!(result.is_ok());
198 assert!(result.unwrap().is_empty());
199 }
200
201 #[test]
202 fn noop_oracle_is_clone_and_debug() {
203 let oracle = NoOpOracle;
204 let _cloned = oracle.clone();
205 let s = format!("{oracle:?}");
206 assert!(s.contains("NoOpOracle"));
207 }
208
209 #[test]
210 fn noop_vector_is_clone_and_debug() {
211 let vector = NoOpVector;
212 let _cloned = vector.clone();
213 let s = format!("{vector:?}");
214 assert!(s.contains("NoOpVector"));
215 }
216
217 #[test]
218 fn noop_oracle_satisfies_trait_bounds() {
219 fn assert_oracle_bounds<T: OraclePort + Send + Sync + 'static>() {}
220 assert_oracle_bounds::<NoOpOracle>();
221 }
222
223 #[test]
224 fn noop_vector_satisfies_trait_bounds() {
225 fn assert_vector_bounds<T: VectorPort + Send + Sync + 'static>() {}
226 assert_vector_bounds::<NoOpVector>();
227 }
228
229 #[test]
232 fn noop_oracle_handle_to_uuid_returns_non_nil_uuid() {
233 let uuid1 = NoOpOracle::handle_to_uuid(&());
234 let uuid2 = NoOpOracle::handle_to_uuid(&());
235 assert!(!uuid1.is_nil(), "handle_to_uuid must return a non-nil UUID");
236 assert_ne!(uuid1, uuid2, "successive handle_to_uuid calls must return distinct UUIDs");
237 }
238}