Pudu programming language
Menu
Package

@chrismichaelps / pudu-lang-mcp

Model Context Protocol server for Pudu language documentation and compiler tools

0.1.1Apache-2.01

InstallClose

ToolsTest.pudu

Pudu90 lines5.5 KB

GitHub ↗
1/** @Test.Domain.Catalog.ToolsTest.Suite — catalogue invariants and handler coverage */2module PuduLangMcp.Domain.Catalog.ToolsTest34import Std.Io as Io5import Std.Json as Json6import Std.List as List7import Std.Map as Map8import Std.Test as Test9import Std.Text as Text10import PuduLangMcp.App.Tools.Registry as Registry11import PuduLangMcp.Domain.Catalog.ToolSpec as ToolSpec12import PuduLangMcp.Domain.Catalog.Tools as Tools13import PuduLangMcp.Utils.JsonAccess as Access1415/// Whether a tool name uses only the characters the specification recommends.16fn wellFormed(name: Str) -> Bool {17  name.length() >= 1 && name.length() <= 128 && Text.allChars(name, |c: Char| (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9') || c == '_' || c == '-' || c == '.')18}1920/// Each tool's hints as read-only, destructive, idempotent, open-world, then its required arguments.21fn expected() -> Array[(Str, (Bool, Bool, Bool, Bool), Array[Str])] {22  let lookup = (true, false, true, false)23  let position = ["line", "character"]24  [25    ("pudu_docs_search", lookup, ["query"]),26    ("pudu_docs_read", lookup, ["slug"]),27    ("pudu_reference_search", lookup, ["query"]),28    ("pudu_module_reference", lookup, ["module"]),29    ("pudu_check", lookup, []),30    ("pudu_format", lookup, []),31    ("pudu_lint", lookup, []),32    ("pudu_run", (true, false, false, false), ["source"]),33    ("pudu_test", (false, false, false, true), []),34    ("pudu_hover", lookup, position),35    ("pudu_definition", lookup, position),36    ("pudu_references", lookup, position),37    ("pudu_completion", lookup, position),38    ("pudu_signature_help", lookup, position),39    ("pudu_code_actions", lookup, position),40    ("pudu_rename_preview", lookup, ["line", "character", "newName"]),41    ("pudu_document_symbols", lookup, []),42    ("pudu_workspace_symbols", lookup, ["query"]),43    ("pudu_toolchain", lookup, [])44  ]45}4647/// A tool's hints and required arguments as the catalogue declares them.48fn declared(spec: &ToolSpec.ToolSpec) -> (Str, (Bool, Bool, Bool, Bool), Array[Str]) {49  let hints = (spec.annotations.readOnly, spec.annotations.destructive, spec.annotations.idempotent, spec.annotations.openWorld)50  (spec.name, hints, spec.properties.filter(|held: ToolSpec.Property| held.required).map(|held: ToolSpec.Property| held.name))51}5253/// Runs the suite.54fn main() -> Int {55  let all = Tools.all()56  let names = all.map(|spec: ToolSpec.ToolSpec| spec.name)57  let described = all.map(|spec: ToolSpec.ToolSpec| ToolSpec.toJson(&spec))58  let handlers = Registry.handlers()59  let rename = Tools.find("pudu_rename_preview")60  let docs = described.filter(|tool: Json.Json| Access.text(&tool, "name") == Some("pudu_docs_search"))[0]61  let check = described.filter(|tool: Json.Json| Access.text(&tool, "name") == Some("pudu_check"))[0]62  let bare = described.filter(|tool: Json.Json| Access.text(&tool, "name") == Some("pudu_toolchain"))[0]63  let checks = Test.suite("Domain.Catalog.Tools", &[64      Test.equals("names are unique", &List.distinct(&names).length(), &names.length()),65      Test.all("names are well formed", &names, |name: Str| wellFormed(name)),66      Test.all("names carry the pudu_ prefix", &names, |name: Str| name.startsWith("pudu_")),67      Test.all("every tool has a handler", &names, |name: Str| Map.containsKey(&handlers, name)),68      Test.equals("every handler has a tool", &Map.size(&handlers), &names.length()),69      Test.all("every schema is a closed object", &described, |tool: Json.Json| Access.pathText(&tool, &["inputSchema", "type"]) == Some("object") && Json.path(&tool, &["inputSchema", "additionalProperties"]) == Some(Json.Boolean(false))),70      Test.all("every tool declares all four hints", &described, |tool: Json.Json| ["readOnlyHint", "destructiveHint", "idempotentHint", "openWorldHint"].filter(|hint: Str| Json.path(&tool, &["annotations", hint]) == None).isEmpty()),71      Test.all("no tool is destructive", &all, |spec: ToolSpec.ToolSpec| !spec.annotations.destructive),72      Test.all("a description is present", &all, |spec: ToolSpec.ToolSpec| !Text.isBlank(spec.description)),73      Test.all("no exclusive name is also required", &all, |spec: ToolSpec.ToolSpec| spec.properties.filter(|held: ToolSpec.Property| held.required && spec.exactlyOne.contains(held.name)).isEmpty()),74      Test.all("exclusive names are declared properties", &all, |spec: ToolSpec.ToolSpec| spec.exactlyOne.filter(|name: Str| !spec.properties.map(|held: ToolSpec.Property| held.name).contains(name)).isEmpty()),75      Test.present("a rename preview is offered", &rename),76      Test.equals("every tool declares its hints and required arguments", &all.map(|spec: ToolSpec.ToolSpec| declared(&spec)), &expected()),77      Test.equals("a tool without required arguments has no required member", &Json.path(&described.filter(|tool: Json.Json| Access.text(&tool, "name") == Some("pudu_test"))[0], &["inputSchema", "required"]), &None),78      Test.equals("required arguments reach the schema", &Json.path(&docs, &["inputSchema", "required"]), &Some(Access.texts(&["query"]))),79      Test.that("an exclusive pair is described as oneOf", Json.path(&check, &["inputSchema", "oneOf"]) != None),80      Test.equals("bounds reach the schema", &Access.pathInt(&docs, &["inputSchema", "properties", "limit", "maximum"]), &Some(25)),81      Test.absent("an unknown tool is not found", &Tools.find("pudu_nope")),82      Test.equals("a tool without arguments has no properties member", &Json.path(&bare, &["inputSchema", "properties"]), &None)83    ])84  let ran = Test.run(&checks)85  for failure in Test.failuresOf(&ran) {86    let _reported = Io.writeErrorLine(failure)87  }88  Test.report(&ran)89}90