use std::path::{Path, PathBuf};
use async_trait::async_trait;
use supercode_harness::mcp::cache_churn_notice;
use supercode_harness::session::Session;
use supercode_harness::tools::{Tool, ToolContext};
use supercode_harness::{Agent, CachePlan, ChatMessage, ChatRequest, Config, Provider, Usage};
#[test]
fn cache_churn_notice_is_a_pure_function_naming_server_and_c2() {
let msg = cache_churn_notice("github", 7);
assert!(msg.contains("github"));
assert!(msg.contains('7'));
assert!(msg.contains("C2"));
}
fn fixture(name: &str) -> PathBuf {
Path::new(env!("CARGO_MANIFEST_DIR"))
.join("tests/fixtures")
.join(name)
}
fn load_codex() -> Session {
Session::from_codex(fixture("codex_session.jsonl")).unwrap()
}
struct PlainAnswerProvider;
#[async_trait]
impl Provider for PlainAnswerProvider {
async fn complete(
&self,
_req: &ChatRequest,
_on_delta: &(dyn for<'a> Fn(&'a str) + Send + Sync),
) -> supercode_harness::Result<(ChatMessage, Usage)> {
Ok((ChatMessage::assistant("ok"), Usage::default()))
}
}
struct NoopTool;
#[async_trait]
impl Tool for NoopTool {
fn name(&self) -> &str {
"noop_extra_tool"
}
fn description(&self) -> &str {
"does nothing"
}
fn parameters(&self) -> serde_json::Value {
serde_json::json!({"type": "object", "properties": {}})
}
async fn execute(
&self,
_args: serde_json::Value,
_ctx: &ToolContext,
) -> supercode_harness::Result<String> {
Ok(String::new())
}
}
#[tokio::test]
async fn registering_a_tool_before_any_turn_leaves_cache_established_false() {
let config = Config::builder().model("test-model").build();
let mut agent = Agent::with_provider(config, Box::new(PlainAnswerProvider));
assert!(!agent.cache_established());
agent.register_tool(NoopTool);
assert!(!agent.cache_established());
assert!(!agent.request_issued());
}
#[tokio::test]
async fn registering_a_tool_after_the_cache_is_established_invalidates_it() {
let config = Config::builder()
.model("test-model")
.cache_plan(CachePlan::ImportedPrefix)
.build();
let mut agent = Agent::with_provider(config, Box::new(PlainAnswerProvider));
agent.load_session(load_codex());
agent.send("continue where we left off").await.unwrap();
assert!(
agent.cache_established(),
"sanity: the first ImportedPrefix-annotated turn must establish the cache"
);
agent.register_tool(NoopTool);
assert!(
!agent.cache_established(),
"registering a tool after the cache was warm must invalidate it (C2)"
);
}