Skip to content
← Public packages

@kody/openapi

Bind an OpenAPI spec and call selected operations with saved integration or secret names.

src/smoke-test.ts

95 lines · 2.5 KB · TypeScript
import bind from './bind.ts'
import get from './get.ts'
import list from './list.ts'
import { parseOpenApiSpec } from './parse-spec-lib.ts'
import { resolveOpenApiSelection } from './selection.ts'
import unbind from './unbind.ts'
import { assertPinnedToApiBaseUrl, buildOperationUrl } from './url.ts'

const PING_SPEC = {
	openapi: '3.0.3',
	info: { title: 'Smoke API', version: '1.0.0' },
	servers: [{ url: 'https://example.com' }],
	paths: {
		'/ping': {
			get: {
				operationId: 'ping',
				summary: 'Ping',
				responses: { '200': { description: 'ok' } },
			},
		},
		'/items/{id}': {
			delete: {
				operationId: 'delete_item',
				summary: 'Delete',
				parameters: [
					{
						name: 'id',
						in: 'path',
						required: true,
						schema: { type: 'string' },
					},
				],
				responses: { '204': { description: 'gone' } },
			},
		},
	},
}

/**
 * Bind, list, get, and unbind an inline ping spec without calling a live API.
 * Use after publish to confirm storage and selection still work.
 *
 * @returns ok plus the selected ping slug
 *
 * @example
 * import smokeTest from 'kody:@<username>/openapi/smoke-test'
 * const result = await smokeTest()
 */
export default async function smokeTest(): Promise<{
	ok: boolean
	operationSlugs: Array<string>
	listed: number
	url: string
}> {
	const specText = JSON.stringify(PING_SPEC)
	const parsed = parseOpenApiSpec(specText)
	const resolved = resolveOpenApiSelection({
		operations: parsed.operations,
		selection: { operationIds: ['ping'] },
		includeDestructive: false,
	})
	const bound = await bind({
		name: 'smoke-ping',
		specUrl: 'https://example.com/openapi.json',
		apiBaseUrl: 'https://example.com',
		auth: { kind: 'none' },
		selection: { operationIds: ['ping'] },
		specText,
	})
	const listed = await list()
	const detail = await get({ name: 'smoke-ping' })
	const url = buildOperationUrl({
		apiBaseUrl: 'https://example.com',
		path: '/ping',
		params: {},
		query: { n: 1 },
	})
	assertPinnedToApiBaseUrl(url, 'https://example.com')
	await unbind({ name: 'smoke-ping' })
	const after = await list()
	const ok =
		resolved.operations.length === 1 &&
		bound.operationSlugs.includes('ping') &&
		listed.bindings.some((binding) => binding.name === 'smoke-ping') &&
		detail.operations[0]?.slug === 'ping' &&
		url.toString() === 'https://example.com/ping?n=1' &&
		after.bindings.every((binding) => binding.name !== 'smoke-ping')

	return {
		ok,
		operationSlugs: bound.operationSlugs,
		listed: listed.bindings.length,
		url: url.toString(),
	}
}