Files
oh-my-opencode/packages/prompts-core/src/loader.test.ts
T

129 lines
4.2 KiB
TypeScript
Raw Normal View History

2026-05-24 17:17:23 +09:00
import { describe, expect, test } from "bun:test"
import { dirname, join } from "node:path"
import { fileURLToPath } from "node:url"
import { loadPrompt, PromptFileNotFoundError, PromptPathTraversalError } from "./loader"
2026-05-24 17:17:23 +09:00
import type { PromptSource } from "./types"
const fixtureSource: PromptSource = {
baseDir: join(dirname(fileURLToPath(import.meta.url)), "__test_fixtures__"),
}
class ResolverFailureError extends Error {
readonly name = "ResolverFailureError"
}
class ExpectedErrorMissingError extends Error {
readonly name = "ExpectedErrorMissingError"
}
describe("loadPrompt", () => {
test("#given markdown fixture #then returns markdown body verbatim", async () => {
const prompt = await loadPrompt({ source: fixtureSource, name: "test-prompt", variant: "default" })
expect(prompt.body).toBe("Default prompt body with {X}.\nSecond line remains verbatim.\n")
})
test("#given frontmatter fixture #then returns parsed frontmatter", async () => {
const prompt = await loadPrompt<{ readonly title: string; readonly enabled: boolean }>({
source: fixtureSource,
name: "test-prompt",
variant: "default",
})
expect(prompt.frontmatter.title).toBe("Test Prompt")
expect(prompt.frontmatter.enabled).toBe(true)
})
test("#given empty frontmatter #then parses without crashing", async () => {
const prompt = await loadPrompt({ source: fixtureSource, name: "test-prompt", variant: "gpt" })
expect(prompt.frontmatter).toEqual({})
expect(prompt.body).toBe("GPT prompt body with {A} and {B}.\n")
})
test("#given missing file #then error mentions prompt name and variant", async () => {
const error = await captureError(() =>
loadPrompt({ source: fixtureSource, name: "test-prompt", variant: "missing" })
)
expect(error).toBeInstanceOf(PromptFileNotFoundError)
expect(expectError(error).message).toContain("test-prompt/missing")
})
test("#given prompt name escapes source directory #then rejects path traversal", async () => {
const error = await captureError(() =>
loadPrompt({ source: fixtureSource, name: "../test-prompt", variant: "default" })
)
expect(error).toBeInstanceOf(PromptPathTraversalError)
})
test("#given variant escapes source directory #then rejects path traversal", async () => {
const error = await captureError(() =>
loadPrompt({ source: fixtureSource, name: "test-prompt", variant: "../../outside" })
)
expect(error).toBeInstanceOf(PromptPathTraversalError)
})
2026-05-24 17:17:23 +09:00
test("#given runtime injection #then replaces placeholder in body", async () => {
const prompt = await loadPrompt({
source: fixtureSource,
name: "test-prompt",
variant: "default",
inject: [{ placeholder: "{X}", resolver: () => "Y" }],
})
expect(prompt.body).toBe("Default prompt body with Y.\nSecond line remains verbatim.\n")
})
test("#given multiple runtime injections #then applies all and ignores absent placeholders", async () => {
const prompt = await loadPrompt({
source: fixtureSource,
name: "test-prompt",
variant: "gpt",
inject: [
{ placeholder: "{A}", resolver: () => "Alpha" },
{ placeholder: "{B}", resolver: () => "Beta" },
{ placeholder: "{ABSENT}", resolver: () => "No-op" },
],
})
expect(prompt.body).toBe("GPT prompt body with Alpha and Beta.\n")
})
test("#given injection resolver throws #then propagates the error", async () => {
const error = await captureError(() =>
loadPrompt({
source: fixtureSource,
name: "test-prompt",
variant: "default",
inject: [
{
placeholder: "{X}",
resolver: () => {
throw new ResolverFailureError("resolver failed")
},
},
],
})
)
expect(error).toBeInstanceOf(ResolverFailureError)
})
})
async function captureError(operation: () => Promise<unknown>): Promise<unknown> {
try {
await operation()
return undefined
} catch (error) {
return error
}
}
function expectError(error: unknown): Error {
if (error instanceof Error) return error
throw new ExpectedErrorMissingError("Expected operation to throw an Error instance")
}