#![cfg(feature = "conformance")]
use std::sync::Arc;
use pamin_kit::schemars::JsonSchema;
use pamin_kit::{Audience, Ctx, Feature, Hardware, Manifest, Module, Ops, Routing};
use serde::Deserialize;
#[path = "../examples/echo/main.rs"]
mod echo;
#[test]
fn the_example_conforms() {
pamin_kit::conformance!(echo::Echo, "examples/echo/SKILL.md");
}
struct Leaky;
#[derive(Deserialize, JsonSchema)]
struct Read {
#[allow(dead_code)]
project: String,
}
impl Module for Leaky {
fn manifest(&self) -> Manifest {
Manifest {
name: "leaky",
contract_version: "1.0.0",
routing: Routing::Stateless,
long_running: false,
scopes: vec![],
metering_unit: "call",
hardware: Hardware::default(),
audience: Audience::Model,
}
}
fn register(&self, ops: &mut Ops<Self>) {
ops.tool(
"leaky_read",
Feature::Core,
|_: Arc<Self>, _: Ctx, _: Read| async { Ok::<_, String>(()) },
);
}
}
#[test]
#[should_panic(expected = "leaky_read: argument \"project\" names a tenant")]
fn a_project_argument_fails() {
pamin_kit::conformance::check(Leaky, std::path::Path::new("examples/echo/SKILL.md"));
}
#[test]
#[should_panic(expected = "leaky_read: SKILL.md does not name it")]
fn a_tool_missing_from_the_skill_fails() {
pamin_kit::conformance::check(Leaky, std::path::Path::new("examples/echo/SKILL.md"));
}
struct Slow;
impl Module for Slow {
fn manifest(&self) -> Manifest {
Manifest {
long_running: true,
..Leaky.manifest()
}
}
fn register(&self, _ops: &mut Ops<Self>) {}
}
#[test]
#[should_panic(expected = "the manifest says long_running: true, but no operation")]
fn a_long_running_manifest_without_tasks_fails() {
pamin_kit::conformance::check(Slow, std::path::Path::new("examples/echo/SKILL.md"));
}
struct Counting;
#[derive(Deserialize, JsonSchema)]
struct Count {
#[allow(dead_code)]
to: u32,
}
impl Module for Counting {
fn manifest(&self) -> Manifest {
Leaky.manifest()
}
fn register(&self, ops: &mut Ops<Self>) {
ops.prompt(
"count",
Feature::Core,
|_: Arc<Self>, _: Ctx, _: Count| async { Ok::<_, String>(Vec::new()) },
);
}
}
#[test]
#[should_panic(expected = "count: argument \"to\" is not a string")]
fn a_prompt_argument_that_is_not_a_string_fails() {
pamin_kit::conformance::check(Counting, std::path::Path::new("examples/echo/SKILL.md"));
}
struct Sloppy;
impl Module for Sloppy {
fn manifest(&self) -> Manifest {
Manifest {
name: "sloppy",
..Leaky.manifest()
}
}
fn register(&self, ops: &mut Ops<Self>) {
ops.tool(
"ReadNotes",
Feature::Core,
|_: Arc<Self>, _: Ctx, _: Count| async { Ok::<_, String>(()) },
);
}
}
#[test]
#[should_panic(expected = "ReadNotes: a tool is named sloppy_<action> in snake case")]
fn a_tool_outside_the_naming_convention_fails() {
pamin_kit::conformance::check(Sloppy, std::path::Path::new("examples/echo/SKILL.md"));
}
struct Numbered;
impl Module for Numbered {
fn manifest(&self) -> Manifest {
Manifest {
name: "9notes",
..Leaky.manifest()
}
}
fn register(&self, _: &mut Ops<Self>) {}
}
#[test]
#[should_panic(expected = "module name \"9notes\" is not a lowercase letter")]
fn a_module_outside_the_naming_convention_fails() {
pamin_kit::conformance::check(Numbered, std::path::Path::new("examples/echo/SKILL.md"));
}