oxynote/web/app/composables/api/useCapabilitiesAPI.nuxt.test.ts

195 lines
5.6 KiB
TypeScript

import { setResponseStatus } from "h3"
import { afterEach, beforeEach, describe, it } from "vitest"
import {
clearQueryCache,
disposeMockEndpoints,
mockEndpoint,
runInApp,
} from "./test-helpers"
import useCapabilitiesAPI from "./useCapabilitiesAPI"
function makeCapabilitiesAPI() {
return runInApp(() => useCapabilitiesAPI())
}
function stubCapabilities(overrides: Partial<Capabilities> = {}): Capabilities {
return {
github: true,
slack: true,
changeDetection: true,
mcp: true,
aiAssistant: { status: AssistantStatus.Active, model: "claude-opus-5" },
...overrides,
}
}
// the composable is created once per test and its query loaded eagerly;
// refresh() joins that in-flight load rather than forcing a second request
describe("useCapabilitiesAPI", { concurrent: false }, () => {
// the tests share the app-wide query cache and the test-time endpoint
// registry, so they cannot interleave
beforeEach(clearQueryCache)
afterEach(disposeMockEndpoints)
describe("fetchCapabilities", () => {
it("fetches the deployment's capabilities", async ({ expect }) => {
const capabilities = stubCapabilities()
const calls = mockEndpoint("GET", "/api/capabilities", () => capabilities)
const api = makeCapabilitiesAPI()
const result = await api.fetchCapabilities.refresh()
expect(result.data).toEqual(capabilities)
expect(calls).toHaveLength(1)
})
})
describe("service flags", () => {
it("maps each flag to its own field", async ({ expect }) => {
mockEndpoint("GET", "/api/capabilities", () =>
stubCapabilities({
github: true,
slack: false,
changeDetection: true,
mcp: false,
}),
)
const api = makeCapabilitiesAPI()
await api.fetchCapabilities.refresh()
expect(api.isGithubEnabled.value).toBe(true)
expect(api.isSlackEnabled.value).toBe(false)
expect(api.isChangeDetectionEnabled.value).toBe(true)
expect(api.isMcpEnabled.value).toBe(false)
})
it("reports every service enabled before the request resolves", ({
expect,
}) => {
const api = makeCapabilitiesAPI()
expect(api.isGithubEnabled.value).toBe(true)
expect(api.isSlackEnabled.value).toBe(true)
expect(api.isChangeDetectionEnabled.value).toBe(true)
expect(api.isMcpEnabled.value).toBe(true)
expect(api.isAssistantEnabled.value).toBe(true)
})
it("reports every service enabled when the request fails", async ({
expect,
}) => {
mockEndpoint("GET", "/api/capabilities", (_call, event) => {
setResponseStatus(event, 500)
return { error: "boom" }
})
const api = makeCapabilitiesAPI()
await api.fetchCapabilities.refresh()
expect(api.fetchCapabilities.error.value).not.toBeNull()
expect(api.isGithubEnabled.value).toBe(true)
expect(api.isSlackEnabled.value).toBe(true)
expect(api.isChangeDetectionEnabled.value).toBe(true)
expect(api.isMcpEnabled.value).toBe(true)
expect(api.isAssistantEnabled.value).toBe(true)
})
})
describe("isAnyAppEnabled", () => {
const cc: Record<string, { github: boolean; slack: boolean }> = {
"both apps are available": { github: true, slack: true },
"only github is available": { github: true, slack: false },
"only slack is available": { github: false, slack: true },
}
Object.entries(cc).forEach(([name, c]) => {
it(`reports enabled when ${name}`, async ({ expect }) => {
mockEndpoint("GET", "/api/capabilities", () => stubCapabilities(c))
const api = makeCapabilitiesAPI()
await api.fetchCapabilities.refresh()
expect(api.isAnyAppEnabled.value).toBe(true)
})
})
it("reports disabled when neither app is available", async ({ expect }) => {
mockEndpoint("GET", "/api/capabilities", () =>
stubCapabilities({ github: false, slack: false }),
)
const api = makeCapabilitiesAPI()
await api.fetchCapabilities.refresh()
expect(api.isAnyAppEnabled.value).toBe(false)
})
})
describe("isAssistantEnabled", () => {
const cc: Record<string, { status: AssistantStatus; enabled: boolean }> = {
"runs a full-strength model": {
status: AssistantStatus.Active,
enabled: true,
},
"runs a mid-tier model with limitations": {
status: AssistantStatus.ActiveButWeak,
enabled: true,
},
"is disabled because the model is too weak": {
status: AssistantStatus.InactiveTooWeak,
enabled: false,
},
"is disabled because no provider is configured": {
status: AssistantStatus.Inactive,
enabled: false,
},
}
Object.entries(cc).forEach(([name, c]) => {
it(`reports ${c.enabled ? "enabled" : "disabled"} when the assistant ${name}`, async ({
expect,
}) => {
mockEndpoint("GET", "/api/capabilities", () =>
stubCapabilities({
aiAssistant: { status: c.status, model: "some-model" },
}),
)
const api = makeCapabilitiesAPI()
await api.fetchCapabilities.refresh()
expect(api.isAssistantEnabled.value).toBe(c.enabled)
})
})
})
describe("assistantStatus / assistantModel", () => {
it("passes the status and configured model through", async ({ expect }) => {
mockEndpoint("GET", "/api/capabilities", () =>
stubCapabilities({
aiAssistant: {
status: AssistantStatus.ActiveButWeak,
model: "gpt-4o-mini",
},
}),
)
const api = makeCapabilitiesAPI()
await api.fetchCapabilities.refresh()
expect(api.assistantStatus.value).toBe(AssistantStatus.ActiveButWeak)
expect(api.assistantModel.value).toBe("gpt-4o-mini")
})
it("reports no status and an empty model while unknown", ({ expect }) => {
const api = makeCapabilitiesAPI()
expect(api.assistantStatus.value).toBeNull()
expect(api.assistantModel.value).toBe("")
})
})
})