agentskills-testing
Conformance suite and test doubles for Agent Skills providers.
SkillProvider is an abstract base class, which enforces that five methods exist
and nothing whatsoever about what they do. The requirements that actually matter
are the ones an ABC cannot express: that an unknown skill ID raises
SkillNotFoundError rather than returning {}, that ../../etc/passwd is
refused rather than resolved, and that a provider advertising resource listing
can actually list. This package turns those into tests you inherit.
pip install agentskills-testing
Conformance suite
Subclass ProviderConformanceSuite, supply a provider fixture, and pytest
collects the entire provider contract against your implementation:
import pytest
from agentskills_testing import ProviderConformanceSuite
from my_package import MyProvider
class TestMyProvider(ProviderConformanceSuite):
@pytest.fixture
def provider(self):
return MyProvider(...)
Fixture contract
The provider fixture must expose exactly one skill:
| id | conformance-skill |
| metadata | name == "conformance-skill", plus a non-empty description |
| body | non-empty markdown |
references/ |
notes.md containing b"# Notes\n\nA reference document.\n" |
scripts/ |
run.sh containing b"#!/bin/sh\necho conformance\n" |
assets/ |
diagram.svg containing b"<svg></svg>\n" |
It must not define a skill called no-such-skill-anywhere, or any resource
called no-such-resource.txt.
Every name and byte string above is exported as a constant (SKILL_ID,
REFERENCE_NAME, REFERENCE_BYTES, and so on), so a fixture can be built from
them rather than from copied literals. CONTRACT holds the same description as
a string, and is what the default fixture prints when you forget to override it.
What it checks
- Metadata carries
nameand a non-emptydescription, and does not carry the body — a metadata call that includes the body has already spent the tokens progressive disclosure exists to save. - Metadata is not shared mutable state: one caller mutating the returned dict must not affect the next.
- Repeated reads agree. A provider that streams without buffering passes the first read and returns empty on the second; caching bugs look the same.
- Each resource getter returns exact
bytes, notstr. A resource may be a PNG, and decoding on the way out makes that unreachable. - Unknown skills raise
SkillNotFoundError; unknown resources raiseResourceNotFoundError. list_resources()agrees withsupports_resource_listing. Callers branch on that flag, so a provider whose flag and behaviour disagree breaks them whichever way it lies.discover()agrees withsupports_discovery, and everything it reports can actually be read.register_all()validates the whole list, so one phantom ID fails the entire registration.- Traversal identifiers are refused — parent traversal, absolute paths, Windows separators, percent-encoded traversal, and embedded NUL bytes, as both skill IDs and resource names. These are not opt-out.
- Concurrent reads through a single instance return consistent content, which
catches per-call state stored on
self.
Size limits
ContentLimitConformanceSuite is separate and opt-in. A size limit is not part
of the universal contract — an in-memory provider has no external source to
bound, and demanding one would assert a filesystem's constraints against a dict.
It is required of any provider that reads bytes it did not author: from
disk, from a network, from anywhere a caller can grow without asking.
from agentskills_testing import ContentLimitConformanceSuite
class TestMyProviderLimits(ContentLimitConformanceSuite):
@pytest.fixture
def limited_provider(self):
return MyProvider(..., max_bytes=8)
The fixture holds the same skill, with a limit small enough that the skill exceeds it.
Test doubles
InMemorySkillProvider is a real, spec-compliant provider backed by a dict — it
passes the conformance suite above. Prefer it to an AsyncMock: a mock agrees
with whatever the test asserts, including the assertions that are wrong.
from agentskills_core import SkillRegistry
from agentskills_testing import InMemorySkillProvider, build_skill
provider = InMemorySkillProvider(
{
"incident-response": build_skill(
"incident-response",
description="Diagnose and mitigate a production incident.",
body="# Incident Response\n\nPage the on-call engineer.\n",
references={"severity-levels.md": b"SEV1 is customer-visible.\n"},
)
}
)
registry = SkillRegistry()
await registry.register_all(provider)
A string value is taken as the body, for the common case where the content does not matter:
provider = InMemorySkillProvider({"a": "body of a", "b": "body of b"})
provider.add("c") # a default skill named "c"
build_skill() produces frontmatter that passes validate_skill(), so a test
that does not care about metadata does not have to invent any.
render_skill_md(skill) renders one back to SKILL.md text, which is how you
populate a temporary directory for the filesystem provider.
To emulate a backend that cannot enumerate — a static HTTP host without a
manifest, for instance — pass supports_resource_listing=False, or
supports_discovery=False, or both.
Fixtures
Installing the package registers a pytest plugin, so these are available with no
import and no conftest.py entry:
| Fixture | What you get |
|---|---|
sample_skill |
An InMemorySkill with one reference, one script, and one asset |
skill_provider |
An InMemorySkillProvider serving sample_skill |
skill_registry |
A SkillRegistry with that skill already registered |
async def test_my_agent(skill_registry):
catalog = await skill_registry.get_skills_catalog()
assert "incident-response" in catalog
License
MIT — see LICENSE.
Part of the Agent Skills SDK.
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file agentskills_testing-0.5.0.tar.gz.
File metadata
- Download URL: agentskills_testing-0.5.0.tar.gz
- Upload date:
- Size: 10.9 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
afc64831d37f0e6ca8beabb57eb334e9849a52e394e93ca2e70a9d483e15448f
|
|
| MD5 |
77189e92789a77f3ee6dd3beb2d645c4
|
|
| BLAKE2b-256 |
b54843d5e90d3132d2a965e76050f0535ece2f9b43c49737f7fb178d79712693
|
Provenance
The following attestation bundles were made for agentskills_testing-0.5.0.tar.gz:
Publisher:
publish.yml on pratikxpanda/agentskills-sdk
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
agentskills_testing-0.5.0.tar.gz -
Subject digest:
afc64831d37f0e6ca8beabb57eb334e9849a52e394e93ca2e70a9d483e15448f - Sigstore transparency entry: 2512840464
- Sigstore integration time:
-
Permalink:
pratikxpanda/agentskills-sdk@80407364f229b0e817fb028f2b2dc98ce6bab741 -
Branch / Tag:
refs/tags/v0.5.0 - Owner: https://github.com/pratikxpanda
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@80407364f229b0e817fb028f2b2dc98ce6bab741 -
Trigger Event:
push
-
Statement type:
File details
Details for the file agentskills_testing-0.5.0-py3-none-any.whl.
File metadata
- Download URL: agentskills_testing-0.5.0-py3-none-any.whl
- Upload date:
- Size: 12.6 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
10138c4f21fa776f2ad649ede3907104c41adf0c898fbf02caa1505ed815d048
|
|
| MD5 |
d80b890d2fd0f67b00bbf86ea31582b7
|
|
| BLAKE2b-256 |
090e77d8f62a87f2d03e9c237e6b60b0fc3030132bfe38d8c8aa38c031e9ec69
|
Provenance
The following attestation bundles were made for agentskills_testing-0.5.0-py3-none-any.whl:
Publisher:
publish.yml on pratikxpanda/agentskills-sdk
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
agentskills_testing-0.5.0-py3-none-any.whl -
Subject digest:
10138c4f21fa776f2ad649ede3907104c41adf0c898fbf02caa1505ed815d048 - Sigstore transparency entry: 2512840479
- Sigstore integration time:
-
Permalink:
pratikxpanda/agentskills-sdk@80407364f229b0e817fb028f2b2dc98ce6bab741 -
Branch / Tag:
refs/tags/v0.5.0 - Owner: https://github.com/pratikxpanda
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@80407364f229b0e817fb028f2b2dc98ce6bab741 -
Trigger Event:
push
-
Statement type: