@@ -116,27 +116,199 @@ impl ProverBackend for MockProver {
116116}
117117
118118#[ tokio:: test]
119- #[ ignore = "Requires API updates to SqliteMemory" ]
120119async fn test_proof_memory_storage_and_retrieval ( ) {
121- todo ! ( "API updates needed for SqliteMemory" )
120+ let tmp = TempDir :: new ( ) . unwrap ( ) ;
121+ let memory = SqliteMemory :: new ( tmp. path ( ) . join ( "test.db" ) ) ;
122+ memory. init ( ) . await . unwrap ( ) ;
123+
124+ let goal = AgenticGoal {
125+ goal : Goal {
126+ id : "mem_test" . to_string ( ) ,
127+ target : Term :: Var ( "P" . to_string ( ) ) ,
128+ hypotheses : vec ! [ ] ,
129+ } ,
130+ priority : Priority :: Medium ,
131+ attempts : 0 ,
132+ max_attempts : 3 ,
133+ preferred_prover : None ,
134+ aspects : vec ! [ "logic" . to_string( ) ] ,
135+ parent : None ,
136+ } ;
137+
138+ let proof = echidna:: core:: ProofState {
139+ goals : vec ! [ ] ,
140+ context : Default :: default ( ) ,
141+ proof_script : vec ! [ ] ,
142+ metadata : Default :: default ( ) ,
143+ } ;
144+
145+ // Store a success
146+ memory
147+ . store_success ( & goal, & proof, ProverKind :: Coq , 250 )
148+ . await
149+ . unwrap ( ) ;
150+
151+ // Retrieve successes
152+ let successes = memory. get_successes ( ) . await . unwrap ( ) ;
153+ assert_eq ! ( successes. len( ) , 1 ) ;
154+ assert_eq ! ( successes[ 0 ] . goal_id, "mem_test" ) ;
155+ assert_eq ! ( successes[ 0 ] . prover, ProverKind :: Coq ) ;
156+ assert_eq ! ( successes[ 0 ] . time_ms, 250 ) ;
157+ assert_eq ! ( successes[ 0 ] . aspects, vec![ "logic" . to_string( ) ] ) ;
158+
159+ // Check stats
160+ let stats = memory. stats ( ) . await . unwrap ( ) ;
161+ assert_eq ! ( stats. total_proofs, 1 ) ;
162+ assert_eq ! ( stats. total_failures, 0 ) ;
163+ assert ! ( ( stats. success_rate - 1.0 ) . abs( ) < f64 :: EPSILON ) ;
164+ assert ! ( ( stats. avg_proof_time_ms - 250.0 ) . abs( ) < f64 :: EPSILON ) ;
122165}
123166
124167#[ tokio:: test]
125- #[ ignore = "Requires API updates to ProverRouter" ]
126168async fn test_prover_router_learning ( ) {
127- todo ! ( "API updates needed for ProverRouter" )
169+ let router = ProverRouter :: new ( ) ;
170+
171+ let goal = AgenticGoal {
172+ goal : Goal {
173+ id : "router_test" . to_string ( ) ,
174+ target : Term :: Var ( "X" . to_string ( ) ) ,
175+ hypotheses : vec ! [ ] ,
176+ } ,
177+ priority : Priority :: Medium ,
178+ attempts : 0 ,
179+ max_attempts : 3 ,
180+ preferred_prover : None ,
181+ aspects : vec ! [ "arithmetic" . to_string( ) ] ,
182+ parent : None ,
183+ } ;
184+
185+ // Record several Z3 successes for arithmetic
186+ for _ in 0 ..5 {
187+ router. record_success ( & goal, ProverKind :: Z3 ) . await ;
188+ }
189+
190+ // Record Coq failures for arithmetic
191+ for _ in 0 ..5 {
192+ router. record_failure ( & goal, ProverKind :: Coq ) . await ;
193+ }
194+
195+ // Verify global stats reflect the history
196+ let stats = router. get_all_stats ( ) . await ;
197+ let z3_stats = stats. get ( & ProverKind :: Z3 ) . unwrap ( ) ;
198+ assert_eq ! ( z3_stats. successes, 5 ) ;
199+ assert_eq ! ( z3_stats. failures, 0 ) ;
200+ assert ! ( ( z3_stats. success_rate( ) - 1.0 ) . abs( ) < f64 :: EPSILON ) ;
201+
202+ let coq_stats = stats. get ( & ProverKind :: Coq ) . unwrap ( ) ;
203+ assert_eq ! ( coq_stats. successes, 0 ) ;
204+ assert_eq ! ( coq_stats. failures, 5 ) ;
205+ assert ! ( ( coq_stats. success_rate( ) - 0.0 ) . abs( ) < f64 :: EPSILON ) ;
128206}
129207
130208#[ tokio:: test]
131- #[ ignore = "Requires API updates to RulePlanner" ]
132209async fn test_goal_decomposition ( ) {
133- todo ! ( "API updates needed for RulePlanner" )
210+ let planner = RulePlanner :: new ( ) ;
211+
212+ // Create an implication goal: A → B
213+ let impl_goal = AgenticGoal {
214+ goal : Goal {
215+ id : "impl_test" . to_string ( ) ,
216+ target : Term :: Pi {
217+ param : "x" . to_string ( ) ,
218+ param_type : Box :: new ( Term :: Var ( "A" . to_string ( ) ) ) ,
219+ body : Box :: new ( Term :: Var ( "B" . to_string ( ) ) ) ,
220+ } ,
221+ hypotheses : vec ! [ ] ,
222+ } ,
223+ priority : Priority :: High ,
224+ attempts : 0 ,
225+ max_attempts : 3 ,
226+ preferred_prover : None ,
227+ aspects : vec ! [ "logic" . to_string( ) ] ,
228+ parent : None ,
229+ } ;
230+
231+ let sub_goals = planner. decompose ( & impl_goal) . await . unwrap ( ) ;
232+ assert ! ( !sub_goals. is_empty( ) , "Implication should decompose into sub-goals" ) ;
233+
234+ // The conclusion sub-goal should target B
235+ let conclusion = & sub_goals[ 0 ] ;
236+ assert_eq ! ( conclusion. goal. id, "impl_test_conclusion" ) ;
237+ assert_eq ! ( conclusion. parent, Some ( "impl_test" . to_string( ) ) ) ;
238+
239+ // A non-decomposable goal should return empty
240+ let atomic_goal = AgenticGoal {
241+ goal : Goal {
242+ id : "atomic_test" . to_string ( ) ,
243+ target : Term :: Var ( "X" . to_string ( ) ) ,
244+ hypotheses : vec ! [ ] ,
245+ } ,
246+ priority : Priority :: Low ,
247+ attempts : 0 ,
248+ max_attempts : 3 ,
249+ preferred_prover : None ,
250+ aspects : vec ! [ ] ,
251+ parent : None ,
252+ } ;
253+
254+ // Non-decomposable goals return themselves as-is (vec![goal.clone()])
255+ let no_sub = planner. decompose ( & atomic_goal) . await . unwrap ( ) ;
256+ assert_eq ! ( no_sub. len( ) , 1 , "Atomic goal returns itself unchanged" ) ;
257+ assert_eq ! ( no_sub[ 0 ] . goal. id, "atomic_test" ) ;
134258}
135259
136260#[ tokio:: test]
137- #[ ignore = "Requires API updates to AgentCore" ]
138261async fn test_agent_queue_priority ( ) {
139- todo ! ( "API updates needed for AgentCore" )
262+ let prover: Box < dyn ProverBackend > = Box :: new ( MockProver :: new ( ProverKind :: Z3 , true ) ) ;
263+ let tmp = TempDir :: new ( ) . unwrap ( ) ;
264+ let memory = SqliteMemory :: new ( tmp. path ( ) . join ( "queue_test.db" ) ) ;
265+ memory. init ( ) . await . unwrap ( ) ;
266+ let planner = RulePlanner :: new ( ) ;
267+ let router = ProverRouter :: new ( ) ;
268+
269+ let config = AgentConfig :: default ( ) ;
270+ let agent = AgentCore :: new (
271+ Box :: new ( memory) ,
272+ Box :: new ( planner) ,
273+ router,
274+ vec ! [ prover] ,
275+ config,
276+ ) ;
277+
278+ // Add goals with different priorities
279+ let make_goal = |id : & str , priority : Priority | AgenticGoal {
280+ goal : Goal {
281+ id : id. to_string ( ) ,
282+ target : Term :: Var ( "X" . to_string ( ) ) ,
283+ hypotheses : vec ! [ ] ,
284+ } ,
285+ priority,
286+ attempts : 0 ,
287+ max_attempts : 3 ,
288+ preferred_prover : None ,
289+ aspects : vec ! [ ] ,
290+ parent : None ,
291+ } ;
292+
293+ agent. add_goal ( make_goal ( "low" , Priority :: Low ) ) . await . unwrap ( ) ;
294+ agent. add_goal ( make_goal ( "high" , Priority :: High ) ) . await . unwrap ( ) ;
295+ agent. add_goal ( make_goal ( "critical" , Priority :: Critical ) ) . await . unwrap ( ) ;
296+ agent. add_goal ( make_goal ( "medium" , Priority :: Medium ) ) . await . unwrap ( ) ;
297+
298+ // Critical should come first (highest priority at front)
299+ let first = agent. next_goal ( ) . await . expect ( "Should have a goal" ) ;
300+ assert_eq ! ( first. goal. id, "critical" ) ;
301+
302+ let second = agent. next_goal ( ) . await . expect ( "Should have a goal" ) ;
303+ assert_eq ! ( second. goal. id, "high" ) ;
304+
305+ let third = agent. next_goal ( ) . await . expect ( "Should have a goal" ) ;
306+ assert_eq ! ( third. goal. id, "medium" ) ;
307+
308+ let fourth = agent. next_goal ( ) . await . expect ( "Should have a goal" ) ;
309+ assert_eq ! ( fourth. goal. id, "low" ) ;
310+
311+ assert ! ( agent. next_goal( ) . await . is_none( ) , "Queue should be empty" ) ;
140312}
141313
142314#[ test]
@@ -200,15 +372,121 @@ fn test_explanation_generation() {
200372}
201373
202374#[ tokio:: test]
203- #[ ignore = "Requires API updates to SqliteMemory" ]
204375async fn test_memory_failure_tracking ( ) {
205- todo ! ( "API updates needed for SqliteMemory" )
376+ let tmp = TempDir :: new ( ) . unwrap ( ) ;
377+ let memory = SqliteMemory :: new ( tmp. path ( ) . join ( "failure_test.db" ) ) ;
378+ memory. init ( ) . await . unwrap ( ) ;
379+
380+ let goal1 = AgenticGoal {
381+ goal : Goal {
382+ id : "fail1" . to_string ( ) ,
383+ target : Term :: Var ( "P" . to_string ( ) ) ,
384+ hypotheses : vec ! [ ] ,
385+ } ,
386+ priority : Priority :: High ,
387+ attempts : 1 ,
388+ max_attempts : 3 ,
389+ preferred_prover : Some ( ProverKind :: Coq ) ,
390+ aspects : vec ! [ "inductive" . to_string( ) ] ,
391+ parent : None ,
392+ } ;
393+
394+ let goal2 = AgenticGoal {
395+ goal : Goal {
396+ id : "fail2" . to_string ( ) ,
397+ target : Term :: Var ( "Q" . to_string ( ) ) ,
398+ hypotheses : vec ! [ ] ,
399+ } ,
400+ priority : Priority :: Medium ,
401+ attempts : 1 ,
402+ max_attempts : 3 ,
403+ preferred_prover : None ,
404+ aspects : vec ! [ "arithmetic" . to_string( ) ] ,
405+ parent : None ,
406+ } ;
407+
408+ // Store failures
409+ memory. store_failure ( & goal1, "timeout" . to_string ( ) ) . await . unwrap ( ) ;
410+ memory. store_failure ( & goal2, "unsupported tactic" . to_string ( ) ) . await . unwrap ( ) ;
411+
412+ // Verify failures stored
413+ let failures = memory. get_failures ( ) . await . unwrap ( ) ;
414+ assert_eq ! ( failures. len( ) , 2 ) ;
415+ assert_eq ! ( failures[ 0 ] . goal_id, "fail1" ) ;
416+ assert_eq ! ( failures[ 0 ] . reason, "timeout" ) ;
417+ assert_eq ! ( failures[ 0 ] . prover, Some ( ProverKind :: Coq ) ) ;
418+ assert_eq ! ( failures[ 1 ] . goal_id, "fail2" ) ;
419+ assert_eq ! ( failures[ 1 ] . reason, "unsupported tactic" ) ;
420+
421+ // Stats should show 0% success rate
422+ let stats = memory. stats ( ) . await . unwrap ( ) ;
423+ assert_eq ! ( stats. total_proofs, 0 ) ;
424+ assert_eq ! ( stats. total_failures, 2 ) ;
425+ assert ! ( ( stats. success_rate - 0.0 ) . abs( ) < f64 :: EPSILON ) ;
206426}
207427
208428#[ tokio:: test]
209- #[ ignore = "Requires API updates to ProverRouter" ]
210429async fn test_router_aspect_scoring ( ) {
211- todo ! ( "API updates needed for ProverRouter" )
430+ let router = ProverRouter :: new ( ) ;
431+
432+ // Build history: Z3 is great at arithmetic, Coq is great at inductive
433+ let arith_goal = AgenticGoal {
434+ goal : Goal {
435+ id : "arith" . to_string ( ) ,
436+ target : Term :: Var ( "N" . to_string ( ) ) ,
437+ hypotheses : vec ! [ ] ,
438+ } ,
439+ priority : Priority :: Medium ,
440+ attempts : 0 ,
441+ max_attempts : 3 ,
442+ preferred_prover : None ,
443+ aspects : vec ! [ "arithmetic" . to_string( ) ] ,
444+ parent : None ,
445+ } ;
446+
447+ let inductive_goal = AgenticGoal {
448+ goal : Goal {
449+ id : "induct" . to_string ( ) ,
450+ target : Term :: Var ( "P" . to_string ( ) ) ,
451+ hypotheses : vec ! [ ] ,
452+ } ,
453+ priority : Priority :: Medium ,
454+ attempts : 0 ,
455+ max_attempts : 3 ,
456+ preferred_prover : None ,
457+ aspects : vec ! [ "inductive" . to_string( ) ] ,
458+ parent : None ,
459+ } ;
460+
461+ // Z3 succeeds on arithmetic
462+ for _ in 0 ..10 {
463+ router. record_success ( & arith_goal, ProverKind :: Z3 ) . await ;
464+ }
465+
466+ // Coq succeeds on inductive
467+ for _ in 0 ..10 {
468+ router. record_success ( & inductive_goal, ProverKind :: Coq ) . await ;
469+ }
470+
471+ // Z3 fails on inductive
472+ for _ in 0 ..10 {
473+ router. record_failure ( & inductive_goal, ProverKind :: Z3 ) . await ;
474+ }
475+
476+ // Verify aspect-specific routing works
477+ let all_stats = router. get_all_stats ( ) . await ;
478+
479+ // Z3 global: 10 successes (arithmetic) + 10 failures (inductive) = 20 attempts
480+ let z3 = all_stats. get ( & ProverKind :: Z3 ) . unwrap ( ) ;
481+ assert_eq ! ( z3. successes, 10 ) ;
482+ assert_eq ! ( z3. failures, 10 ) ;
483+ assert ! ( ( z3. success_rate( ) - 0.5 ) . abs( ) < f64 :: EPSILON ) ;
484+
485+ // Coq global: 10 successes (inductive), 0 failures = 100% success
486+ let coq = all_stats. get ( & ProverKind :: Coq ) . unwrap ( ) ;
487+ assert_eq ! ( coq. successes, 10 ) ;
488+ assert_eq ! ( coq. failures, 0 ) ;
489+ assert ! ( ( coq. success_rate( ) - 1.0 ) . abs( ) < f64 :: EPSILON ) ;
212490}
213491
214492#[ test]
0 commit comments