testutil

package
v1.40.1 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Sep 10, 2026 License: MIT Imports: 14 Imported by: 0

Documentation

Overview

Package testutil provides the product-neutral building blocks for MCP server tests: engines and HTTP servers that answer with a canned response while capturing what was sent, an in-memory MCP server assembled from any toolset group, and a helper that calls one tool through it.

Nothing here knows about a particular Teamwork product. A product's test helpers wire their own toolset group on top — see internal/testutil for this server's, which is also the shape a server built on this repo should follow.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func CheckMessage

func CheckMessage(t *testing.T, result mcp.Result)

CheckMessage validates that a message represents a successful tool execution.

func DeskClientMock

func DeskClientMock(status int, response []byte) (*deskclient.Client, *httptest.Server)

DeskClientMock creates a Desk SDK client pointed at a test server answering with the given status and body. The caller owns closing the server.

func EngineMock

func EngineMock(status int, response []byte) *twapi.Engine

EngineMock creates a mock twapi.Engine that answers every request with the given HTTP response.

func EngineMockWithRequestBody

func EngineMockWithRequestBody(status int, response []byte) (*twapi.Engine, *[]byte)

EngineMockWithRequestBody is like EngineMock but also captures the body of the most recent request the engine sent, so tests can assert on the serialized request payload. The returned pointer is populated once a tool calls out.

func EngineMockWithRequestURL

func EngineMockWithRequestURL(status int, response []byte) (*twapi.Engine, *url.URL)

EngineMockWithRequestURL is like EngineMock but also captures the URL of the most recent request the engine sent, so tests can assert on the query string a tool actually builds.

Asserting the response alone cannot catch a filter or pagination parameter that is never sent: the mock replies with the same canned body regardless, so a dropped parameter looks identical to a working one.

func EngineMockWithRequestURLs

func EngineMockWithRequestURLs(status int, response []byte) (*twapi.Engine, *[]url.URL)

EngineMockWithRequestURLs is like EngineMockWithRequestURL but captures every request rather than only the last one.

Use it when the tool under test makes more than one call, because the last URL is then the follow-up rather than the one being asserted on: a tool that resolves a schema after listing has already discarded its list query string by the time it returns, and a test reading the last URL reports a parameter missing from a request that never carried it.

func ExecuteToolRequest

func ExecuteToolRequest(
	t *testing.T,
	mcpServer *mcp.Server,
	toolName string,
	args map[string]any,
	optFuncs ...ExecuteToolRequestOption,
)

ExecuteToolRequest executes a tool request and validates the response.

func HTTPServerMock

func HTTPServerMock(status int, response []byte) *httptest.Server

HTTPServerMock starts a test server answering every request with the given status and body. Tools reached through an *http.Client rather than a twapi.Engine are tested against one of these.

func MCPServer

func MCPServer(t *testing.T, groups ...*toolsets.ToolsetGroup) *mcp.Server

MCPServer assembles an in-memory MCP server with every toolset of the given groups enabled, including their write and delete tools.

func MCPServerWithCustomerURL

func MCPServerWithCustomerURL(
	t *testing.T,
	customerURL string,
	groups ...*toolsets.ToolsetGroup,
) *mcp.Server

MCPServerWithCustomerURL is like MCPServer but injects a customer URL into every request's context. Tools that resolve their endpoint from the caller's installation — anything built on an *http.Client rather than a pre-configured engine — reach the test server only because of this.

func NewMockHTTPResponse

func NewMockHTTPResponse(status int, body []byte) *http.Response

NewMockHTTPResponse builds a complete http.Response around a status and body, as an engine middleware must return one.

func RecordingEngineMock

func RecordingEngineMock(
	routes []MockRoute,
	fallbackStatus int,
	fallbackBody []byte,
) (*twapi.Engine, *[]RecordedRequest)

RecordingEngineMock is like RoutedEngineMock but records every request in order rather than only the last body. Tools that fan one call out into many writes need this: the order of those writes is part of the contract, and a single captured body cannot show it.

func RecordingHTTPServerMock

func RecordingHTTPServerMock(status int, response []byte) (*httptest.Server, func() (string, url.URL))

RecordingHTTPServerMock is like HTTPServerMock but also reports the method and URL of the most recent request.

The method is what separates some tools from each other: a link and an unlink tool may address the same path and differ only in POST versus DELETE, so a test that checks the URL alone passes when the two are swapped.

The request is returned through an accessor rather than a pointer because the capture happens on the test server's own goroutine.

func RoutedEngineMock

func RoutedEngineMock(routes []MockRoute, fallbackStatus int, fallbackBody []byte) *twapi.Engine

RoutedEngineMock creates a mock engine that returns different responses based on a substring match against the request URL path. Use this when a single tool dispatches calls to multiple endpoints that need distinct status codes (e.g. record create, which lists fields with 200 before posting the record with 201). Requests matching no route fall back to fallbackStatus/fallbackBody.

func RoutedEngineMockWithRequestBody

func RoutedEngineMockWithRequestBody(
	routes []MockRoute,
	fallbackStatus int,
	fallbackBody []byte,
) (*twapi.Engine, *[]byte)

RoutedEngineMockWithRequestBody is like RoutedEngineMock but also captures the body of the most recent request that carried one, so tests can assert on the serialized payload of the final write while still serving distinct responses per endpoint (e.g. a field-type GET at 200 followed by a value POST at 201).

func SequencedEngineMock

func SequencedEngineMock(t *testing.T, status int, responses ...[]byte) *twapi.Engine

SequencedEngineMock creates a mock engine that returns the given response bodies in order, one per request. Once the sequence is exhausted the final body is repeated. This lets tests drive a tool's internal pagination loop with a distinct body per page, or exercise a never-ending "hasMore" by supplying a single always-more body. All responses share the same status code.

Types

type ExecuteToolRequestOption

type ExecuteToolRequestOption func(*ExecuteToolRequestOptions)

ExecuteToolRequestOption is a function that modifies ExecuteToolRequestOptions.

func ExecuteToolRequestWithCheckMessage

func ExecuteToolRequestWithCheckMessage(f func(t *testing.T, result mcp.Result)) ExecuteToolRequestOption

ExecuteToolRequestWithCheckMessage executes a tool request and validates the response with a custom check function. Any nil function will be ignored.

type ExecuteToolRequestOptions

type ExecuteToolRequestOptions struct {
	// contains filtered or unexported fields
}

ExecuteToolRequestOptions represents options for ExecuteToolRequest.

type MockRoute

type MockRoute struct {
	Match string
	// Method restricts the route to a single HTTP method. An empty value matches
	// any method, which is what path-only routing needs; set it when one path
	// serves several verbs, as /tasks/{id}.json does for get and update.
	Method string
	Status int
	Body   []byte
}

MockRoute pairs a substring match against the request URL path with the status and body to return when it matches.

type RecordedRequest

type RecordedRequest struct {
	Method string
	// URL is the whole request URL, not only its path: a tool that reaches an
	// endpoint outside the API, as the pre-signed file upload does, is only
	// identifiable by its host, and a step that carries its parameters in the
	// query string has nothing in its body to assert on.
	URL  url.URL
	Body []byte
}

RecordedRequest is one HTTP request captured by RecordingEngineMock.

type SessionMock

type SessionMock struct{}

SessionMock implements a twapi session that authenticates every request against a fixed example host.

func (SessionMock) Authenticate

func (s SessionMock) Authenticate(context.Context, *http.Request) error

Authenticate implements the Authenticate method for SessionMock.

func (SessionMock) Server

func (s SessionMock) Server() string

Server implements the Server method for SessionMock.

type ToolRequest

type ToolRequest struct {
	mcp.CallToolRequest

	JSONRPC string `json:"jsonrpc"`
	ID      int64  `json:"id"`
}

ToolRequest represents a tool request for testing.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL