use hyperdb_mcp::server::HyperMcpServer;
use tempfile::TempDir;
fn server_with_test_table() -> (HyperMcpServer, TempDir) {
let dir = TempDir::new().unwrap();
let path = dir.path().join("workspace.hyper");
let server = HyperMcpServer::with_no_daemon(Some(path.to_str().unwrap().into()), false, true);
let _ = server.resource_body_for_uri("hyper://workspace");
{
let handle = server.engine_handle();
let guard = handle.lock().expect("engine mutex");
let engine = guard.as_ref().expect("engine initialized");
engine
.execute_command("CREATE TABLE widgets (id INT NOT NULL, name TEXT)")
.unwrap();
engine
.execute_command("INSERT INTO widgets VALUES (1, 'Alpha')")
.unwrap();
engine
.execute_command("INSERT INTO widgets VALUES (2, 'Beta')")
.unwrap();
}
(server, dir)
}
#[test]
fn list_resources_includes_workspace_tables_readme_and_per_table() {
let (server, _dir) = server_with_test_table();
let uris = server.list_resource_uris();
assert!(uris.contains(&"hyper://workspace".to_string()));
assert!(uris.contains(&"hyper://tables".to_string()));
assert!(uris.contains(&"hyper://readme".to_string()));
assert!(uris.contains(&"hyper://tables/widgets/schema".to_string()));
assert!(uris.contains(&"hyper://tables/widgets/sample".to_string()));
assert!(uris.contains(&"hyper://tables/widgets/csv-sample".to_string()));
}
#[test]
fn read_workspace_resource_returns_status() {
let (server, _dir) = server_with_test_table();
let body = server
.resource_body_for_uri("hyper://workspace")
.unwrap()
.expect("workspace resource should exist");
assert_eq!(body.mime_type(), "application/json");
let json = body.as_json().expect("workspace is JSON");
assert_eq!(json["hyperd_running"], true);
assert!(json["ephemeral_path"].is_string());
assert_eq!(json["has_persistent"], true);
assert!(json["persistent_path"].is_string());
assert_eq!(json["table_count"], 1);
let version = json["hyper_rust_api_version"]
.as_str()
.expect("workspace resource carries hyper_rust_api_version");
assert!(version.contains(".r"));
}
#[test]
fn read_tables_resource_returns_all_tables() {
let (server, _dir) = server_with_test_table();
let body = server
.resource_body_for_uri("hyper://tables")
.unwrap()
.expect("tables resource should exist");
let json = body.as_json().expect("tables is JSON");
let tables = json["tables"].as_array().unwrap();
assert_eq!(tables.len(), 1);
assert_eq!(tables[0]["name"], "widgets");
assert_eq!(tables[0]["row_count"], 2);
}
#[test]
fn read_table_schema_resource_returns_single_table() {
let (server, _dir) = server_with_test_table();
let body = server
.resource_body_for_uri("hyper://tables/widgets/schema")
.unwrap()
.expect("widgets schema resource should exist");
let json = body.as_json().expect("schema is JSON");
assert_eq!(json["name"], "widgets");
assert_eq!(json["row_count"], 2);
let cols = json["columns"].as_array().unwrap();
assert_eq!(cols.len(), 2);
}
#[test]
fn read_table_sample_resource_returns_rows_and_schema() {
let (server, _dir) = server_with_test_table();
let body = server
.resource_body_for_uri("hyper://tables/widgets/sample")
.unwrap()
.expect("widgets sample resource should exist");
assert_eq!(body.mime_type(), "application/json");
let json = body.as_json().expect("sample is JSON");
assert_eq!(json["table"], "widgets");
assert_eq!(json["row_count"], 2);
let rows = json["rows"].as_array().unwrap();
assert_eq!(rows.len(), 2, "fixture has 2 rows, sample returns both");
let schema = json["schema"].as_array().unwrap();
assert!(
!schema.is_empty(),
"schema should be populated from catalog"
);
}
#[test]
fn read_table_csv_sample_resource_emits_csv() {
let (server, _dir) = server_with_test_table();
let body = server
.resource_body_for_uri("hyper://tables/widgets/csv-sample")
.unwrap()
.expect("widgets csv-sample resource should exist");
assert_eq!(body.mime_type(), "text/csv");
let text = body.to_text();
let mut lines = text.lines();
let header = lines.next().expect("CSV has a header row");
assert_eq!(header, "id,name");
let data: Vec<&str> = lines.collect();
assert_eq!(data.len(), 2);
assert!(data[0].starts_with("1,") && data[0].contains("Alpha"));
assert!(data[1].starts_with("2,") && data[1].contains("Beta"));
}
#[test]
fn read_readme_resource_lists_tables_in_markdown() {
let (server, _dir) = server_with_test_table();
let body = server
.resource_body_for_uri("hyper://readme")
.unwrap()
.expect("readme resource should exist");
assert_eq!(body.mime_type(), "text/markdown");
let text = body.to_text();
assert!(text.starts_with("# HyperDB workspace"));
assert!(text.contains("`widgets`"));
assert!(text.contains("hyper://tables/widgets/schema"));
assert!(text.contains("hyper://tables/widgets/sample"));
assert!(text.contains("hyper://tables/widgets/csv-sample"));
assert!(text.contains("## Tool hints"));
}
#[test]
fn read_readme_resource_handles_empty_workspace() {
let dir = TempDir::new().unwrap();
let path = dir.path().join("empty.hyper");
let server = HyperMcpServer::with_no_daemon(Some(path.to_str().unwrap().into()), false, true);
let body = server
.resource_body_for_uri("hyper://readme")
.unwrap()
.expect("readme resource should exist even with no tables");
let text = body.to_text();
assert!(text.contains("No tables loaded yet"));
assert!(text.contains("load_file"));
assert!(text.contains("inspect_file"));
}
#[test]
fn unknown_uri_returns_none() {
let (server, _dir) = server_with_test_table();
let result = server.resource_body_for_uri("hyper://bogus").unwrap();
assert!(result.is_none());
}
#[test]
fn missing_table_schema_returns_error() {
let (server, _dir) = server_with_test_table();
let result = server.resource_body_for_uri("hyper://tables/ghost/schema");
assert!(result.is_err());
let err = result.unwrap_err();
assert_eq!(err.code, hyperdb_mcp::error::ErrorCode::TableNotFound);
}
#[test]
fn missing_table_sample_returns_error() {
let (server, _dir) = server_with_test_table();
let result = server.resource_body_for_uri("hyper://tables/ghost/sample");
assert!(result.is_err());
assert_eq!(
result.unwrap_err().code,
hyperdb_mcp::error::ErrorCode::TableNotFound
);
}