dify/knowledge-fs/packages/api/src/knowledge-space-profile-handler-behavior.test.ts

3083 lines
103 KiB
TypeScript

import { createNodePlatformAdapter } from "@knowledge/adapters/node";
import {
type KnowledgeSpaceManifest,
type KnowledgeSpaceModelSelection,
KnowledgeSpaceRetrievalProfileSchema,
createDefaultKnowledgeSpaceManifest,
createKnowledgeSpaceEmbeddingProfile,
createKnowledgeSpaceRetrievalProfile,
} from "@knowledge/core";
import { describe, expect, it, vi } from "vitest";
import type {
DifyCapabilityV2GatewayAuthenticator,
DifyCapabilityV2SanitizedGrant,
} from "./dify-capability-v2";
import {
KnowledgeSpaceAccessError,
type KnowledgeSpaceManifestRepository,
KnowledgeSpaceProfileTransitionError,
KnowledgeSpaceProvisioningIdempotencyConflictError,
KnowledgeSpaceProvisioningIncompleteReplayError,
KnowledgeSpaceUnpublishedProfileActivationError,
type ModelCapabilityKind,
type ModelCapabilityPreflight,
ModelCapabilityPreflightError,
type ModelCapabilitySnapshot,
createInMemoryDocumentAssetRepository,
createInMemoryKnowledgeSpaceAccessRepository,
createInMemoryKnowledgeSpaceManifestRepository,
createInMemoryKnowledgeSpaceProfileRepository,
createInMemoryKnowledgeSpaceRepository,
createKnowledgeGateway,
createKnowledgeSpaceAccessService,
createKnowledgeSpacePendingModelConfiguration,
createStaticAuthVerifier,
} from "./index";
import { ensureLegacyPublishedProfileTuple } from "./knowledge-space-handlers";
import { PublishedProjectionReadUnavailableError } from "./published-projection-read-snapshot";
const SPACE_ID = "018f0d60-7a49-7cc2-9c1b-5b36f18f2c42";
const NOW = "2026-07-21T12:00:00.000Z";
const WRITE_TOKEN = "write-token";
const EMBEDDING_V1: KnowledgeSpaceModelSelection = {
model: "embed-v1",
pluginId: "embedding-plugin",
provider: "plugin-daemon",
};
const EMBEDDING_V2: KnowledgeSpaceModelSelection = {
model: "embed-v2",
pluginId: "embedding-plugin",
provider: "plugin-daemon",
};
const REASONING_V1: KnowledgeSpaceModelSelection = {
model: "reasoning-v1",
pluginId: "reasoning-plugin",
provider: "plugin-daemon",
};
const REASONING_V2: KnowledgeSpaceModelSelection = {
model: "reasoning-v2",
pluginId: "reasoning-plugin",
provider: "plugin-daemon",
};
const RERANK_V1: KnowledgeSpaceModelSelection = {
model: "rerank-v1",
pluginId: "rerank-plugin",
provider: "plugin-daemon",
};
const RERANK_V2: KnowledgeSpaceModelSelection = {
model: "rerank-v2",
pluginId: "rerank-plugin",
provider: "plugin-daemon",
};
function headers() {
return {
authorization: `Bearer ${WRITE_TOKEN}`,
"content-type": "application/json",
};
}
function auth() {
return createStaticAuthVerifier({
subjectsByToken: {
[WRITE_TOKEN]: {
scopes: ["knowledge-spaces:*"],
subjectId: "user:owner",
tenantId: "tenant-1",
},
},
});
}
function capability(
kind: ModelCapabilityKind,
selection: KnowledgeSpaceModelSelection,
checkedAt = NOW,
): ModelCapabilitySnapshot {
const marker = kind === "embedding" ? "a" : kind === "reasoning" ? "b" : "c";
return {
capabilityDigest: `sha256:${marker.repeat(64)}`,
checkedAt,
...(kind === "embedding" ? { dimension: 1536, distanceMetric: "cosine" as const } : {}),
kind,
pluginUniqueIdentifier: `${selection.pluginId}@installed-1`,
schemaFingerprint: `sha256:${"d".repeat(64)}`,
selection,
};
}
function modelPreflight() {
const verify = vi.fn(async (input: Parameters<ModelCapabilityPreflight["verify"]>[0]) =>
capability(input.kind, input.selection),
);
return { preflight: { verify }, verify };
}
async function createSpace(app: ReturnType<typeof createKnowledgeGateway>) {
const response = await app.request("/knowledge-spaces", {
body: JSON.stringify({ name: "Profile behavior" }),
headers: headers(),
method: "POST",
});
expect(response.status).toBe(201);
}
async function seedActiveManifest(manifests: KnowledgeSpaceManifestRepository) {
const current = await manifests.get({ knowledgeSpaceId: SPACE_ID, tenantId: "tenant-1" });
if (!current) throw new Error("test manifest missing");
const embeddingProfile = await createKnowledgeSpaceEmbeddingProfile(EMBEDDING_V1);
const retrievalProfile = createKnowledgeSpaceRetrievalProfile({
defaultMode: "deep",
reasoningModel: REASONING_V1,
rerank: { enabled: true, model: RERANK_V1 },
scoreThreshold: { enabled: true, stage: "mode-final", value: 0.35 },
topK: 8,
});
const updated = await manifests.update({
expectedManifestVersion: current.manifestVersion,
knowledgeSpaceId: SPACE_ID,
patch: {
embeddingProfile,
manifestVersion: current.manifestVersion + 1,
retrievalProfile,
updatedAt: NOW,
},
tenantId: "tenant-1",
});
if (!updated) throw new Error("test manifest update failed");
return updated;
}
async function seedLegacyManifestWithoutRerank(manifests: KnowledgeSpaceManifestRepository) {
const current = await manifests.get({ knowledgeSpaceId: SPACE_ID, tenantId: "tenant-1" });
if (!current) throw new Error("test manifest missing");
const updated = await manifests.update({
expectedManifestVersion: current.manifestVersion,
knowledgeSpaceId: SPACE_ID,
patch: {
embeddingProfile: await createKnowledgeSpaceEmbeddingProfile(EMBEDDING_V1),
manifestVersion: current.manifestVersion + 1,
retrievalProfile: KnowledgeSpaceRetrievalProfileSchema.parse({
defaultMode: "deep",
reasoningModel: REASONING_V1,
revision: 1,
rerank: { enabled: false },
scoreThreshold: { enabled: false, stage: "mode-final" },
topK: 8,
}),
updatedAt: NOW,
},
tenantId: "tenant-1",
});
if (!updated) throw new Error("test manifest update failed");
return updated;
}
function retrievalUpdateBody() {
return {
expectedRevision: 1,
profile: {
defaultMode: "deep",
reasoningModel: REASONING_V2,
rerank: { enabled: true, model: RERANK_V2 },
scoreThreshold: { enabled: true, stage: "mode-final", value: 0.45 },
topK: 12,
},
} as const;
}
function rerankOnlyRetrievalUpdateBody(rerankModel = RERANK_V2) {
return {
expectedRevision: 1,
profile: {
defaultMode: "deep",
reasoningModel: REASONING_V1,
rerank: { enabled: true, model: rerankModel },
scoreThreshold: { enabled: false, stage: "mode-final" },
topK: 8,
},
} as const;
}
async function legacyManifest(): Promise<KnowledgeSpaceManifest> {
return createDefaultKnowledgeSpaceManifest({
createdAt: NOW,
embeddingProfile: await createKnowledgeSpaceEmbeddingProfile(EMBEDDING_V1),
id: "018f0d60-7a49-7cc2-9c1b-5b36f18f7a10",
knowledgeSpaceId: SPACE_ID,
retrievalProfile: createKnowledgeSpaceRetrievalProfile({
defaultMode: "deep",
reasoningModel: REASONING_V1,
rerank: { enabled: true, model: RERANK_V1 },
scoreThreshold: { enabled: true, stage: "mode-final", value: 0.35 },
topK: 8,
}),
tenantId: "tenant-1",
updatedAt: NOW,
});
}
function legacyResearchProfile() {
return KnowledgeSpaceRetrievalProfileSchema.parse({
defaultMode: "research",
reasoningModel: REASONING_V1,
rerank: { enabled: false },
revision: 1,
scoreThreshold: { enabled: false, stage: "mode-final" },
topK: 5,
});
}
function requireLegacyProfiles(manifest: KnowledgeSpaceManifest) {
const { embeddingProfile, retrievalProfile } = manifest;
if (!embeddingProfile || !retrievalProfile) {
throw new Error("legacy profile fixture is incomplete");
}
return { embeddingProfile, retrievalProfile };
}
function profileRepository() {
return createInMemoryKnowledgeSpaceProfileRepository({
maxListLimit: 10,
maxRevisions: 20,
});
}
type ProfileGatewayOptions = Omit<
Parameters<typeof createKnowledgeGateway>[0],
"adapter" | "auth" | "knowledgeSpaces" | "now"
>;
function profileApp(options: ProfileGatewayOptions = {}) {
return createKnowledgeGateway({
adapter: createNodePlatformAdapter({ env: {} }),
auth: auth(),
knowledgeSpaces: createInMemoryKnowledgeSpaceRepository({
generateId: () => SPACE_ID,
maxListLimit: 10,
maxSpaces: 10,
}),
now: () => NOW,
...options,
});
}
function publishedProfileApp(
manifests: KnowledgeSpaceManifestRepository,
profiles: ReturnType<typeof profileRepository>,
migrations: NonNullable<ProfileGatewayOptions["knowledgeSpaceProfileMigrations"]>,
) {
return profileApp({
knowledgeSpaceManifests: manifests,
knowledgeSpaceProfileMigrations: migrations,
knowledgeSpaceProfilePublications: {
activateCandidate: async () => ({}) as never,
bindCandidate: async () => ({}) as never,
bindCurrentPublished: async () => ({}) as never,
bindExistingPublished: async () => ({}) as never,
requireActivatedBinding: async () => ({}) as never,
},
knowledgeSpaceProfiles: profiles,
modelCapabilityPreflight: {
verify: async (input) =>
capability(input.kind, {
model: input.selection.model,
pluginId: input.selection.pluginId,
provider: input.selection.provider,
}),
},
});
}
function settingsCapabilityAuth(): {
auth: { authenticate: DifyCapabilityV2GatewayAuthenticator["authenticate"] };
grant: DifyCapabilityV2SanitizedGrant;
} {
const grant: DifyCapabilityV2SanitizedGrant = {
action: "knowledge_spaces.settings.update",
actor: "dify-account:account-1",
authzRevision: {
credential_revision: null,
external_access_epoch: 1,
membership_epoch: 1,
space_acl_epoch: 1,
},
azp: "dify-console",
callerKind: "interactive",
capVersion: 2,
contentPolicyRevision: 1,
contentScopeIds: [],
controlSpaceId: "control-space-1",
expiresAt: 2_000_000_060,
grantId: "settings-grant-1",
issuedAt: 2_000_000_000,
jtiHash: `sha256:${"a".repeat(64)}`,
namespaceId: "tenant-1",
notBefore: 2_000_000_000,
resource: { id: SPACE_ID, parent_id: null, type: "knowledge_space" },
subject: "dify-account:account-1",
traceId: "trace-1",
};
return {
auth: {
authenticate: async () => ({
callerKind: grant.callerKind,
claims: {} as never,
grant,
subject: {
scopes: ["knowledge-spaces:read"],
subjectId: grant.subject,
tenantId: grant.namespaceId,
},
}),
},
grant,
};
}
function bootstrapInput(
manifest: KnowledgeSpaceManifest,
profiles: ReturnType<typeof profileRepository>,
overrides: Partial<Parameters<typeof ensureLegacyPublishedProfileTuple>[0]> = {},
) {
return {
createdBySubjectId: "user:owner",
knowledgeSpaceId: SPACE_ID,
manifest,
modelCapabilityPreflight: {
verify: async (input: Parameters<ModelCapabilityPreflight["verify"]>[0]) =>
capability(input.kind, input.selection),
},
now: () => NOW,
profilePublicationBindings: {
bindCurrentPublished: async () => ({}) as never,
},
profiles,
tenantId: "tenant-1",
...overrides,
};
}
describe("knowledge-space profile handler behavior", () => {
it("aggregates pending first-document settings behind one revision-fenced product route", async () => {
const app = profileApp();
await createSpace(app);
const initial = await app.request(`/knowledge-spaces/${SPACE_ID}/product-settings`, {
headers: headers(),
});
expect(initial.status).toBe(200);
await expect(initial.json()).resolves.toEqual({
activeProfileAvailable: false,
activeProfileRevisions: {},
capabilities: {
deep: false,
index: false,
ingest: false,
query: false,
research: false,
sourceSync: false,
},
configurationState: "setup-required",
embedding: null,
issues: [
{ code: "missing", field: "embedding", retryable: false },
{ code: "missing", field: "reasoning", retryable: false },
{ code: "missing", field: "rerank", retryable: false },
],
retrieval: null,
revision: 1,
});
const updated = await app.request(`/knowledge-spaces/${SPACE_ID}/product-settings`, {
body: JSON.stringify({
embedding: EMBEDDING_V1,
expectedRevision: 1,
retrieval: {
defaultMode: "deep",
reasoningModel: REASONING_V1,
rerank: { enabled: true, model: RERANK_V1 },
scoreThreshold: { enabled: true, stage: "mode-final", value: 0.35 },
topK: 8,
},
}),
headers: headers(),
method: "PATCH",
});
expect(updated.status).toBe(200);
await expect(updated.json()).resolves.toMatchObject({
activeProfileAvailable: false,
activeProfileRevisions: {},
capabilities: {
deep: false,
index: false,
ingest: true,
query: false,
research: false,
sourceSync: true,
},
configurationState: "pending-validation",
embedding: EMBEDDING_V1,
issues: [],
retrieval: { defaultMode: "deep", reasoningModel: REASONING_V1, topK: 8 },
revision: 2,
});
const current = await app.request(`/knowledge-spaces/${SPACE_ID}/product-settings`, {
headers: headers(),
});
expect(current.status).toBe(200);
await expect(current.json()).resolves.toMatchObject({
configurationState: "pending-validation",
embedding: EMBEDDING_V1,
revision: 2,
});
const stale = await app.request(`/knowledge-spaces/${SPACE_ID}/product-settings`, {
body: JSON.stringify({ embedding: EMBEDDING_V2, expectedRevision: 1 }),
headers: headers(),
method: "PATCH",
});
expect(stale.status).toBe(409);
await expect(stale.json()).resolves.toMatchObject({
code: "PRODUCT_SETTINGS_REVISION_CONFLICT",
});
});
it("keeps active profile changes on the dedicated migration workflow", async () => {
const manifests = createInMemoryKnowledgeSpaceManifestRepository({
maxListLimit: 10,
maxManifests: 10,
});
const app = profileApp({ knowledgeSpaceManifests: manifests });
await createSpace(app);
const active = await seedActiveManifest(manifests);
const current = await app.request(`/knowledge-spaces/${SPACE_ID}/product-settings`, {
headers: headers(),
});
expect(current.status).toBe(200);
await expect(current.json()).resolves.toMatchObject({ configurationState: "active" });
const response = await app.request(`/knowledge-spaces/${SPACE_ID}/product-settings`, {
body: JSON.stringify({ embedding: EMBEDDING_V2, expectedRevision: active.manifestVersion }),
headers: headers(),
method: "PATCH",
});
expect(response.status).toBe(409);
await expect(response.json()).resolves.toEqual({
code: "PRODUCT_SETTINGS_PROFILE_MIGRATION_REQUIRED",
error: "Active profile changes require the dedicated profile migration workflow",
});
});
it("keeps active capabilities available when a replacement candidate fails validation", async () => {
const manifests = createInMemoryKnowledgeSpaceManifestRepository({
maxListLimit: 10,
maxManifests: 10,
});
const app = profileApp({ knowledgeSpaceManifests: manifests });
await createSpace(app);
const active = await seedActiveManifest(manifests);
const pending = createKnowledgeSpacePendingModelConfiguration({
embeddingSelection: EMBEDDING_V2,
retrievalProfile: {
defaultMode: "deep",
reasoningModel: REASONING_V2,
rerank: { enabled: true, model: RERANK_V2 },
scoreThreshold: { enabled: true, stage: "mode-final", value: 0.35 },
topK: 8,
},
});
const failed = await manifests.update({
expectedManifestVersion: active.manifestVersion,
knowledgeSpaceId: SPACE_ID,
patch: {
manifestVersion: active.manifestVersion + 1,
pendingModelConfiguration: {
...pending,
failure: {
code: "MODEL_SELECTION_NOT_FOUND",
failedAt: NOW,
field: "embedding",
retryable: false,
},
state: "validation-failed",
},
updatedAt: NOW,
},
tenantId: "tenant-1",
});
if (!failed) throw new Error("test manifest update failed");
const response = await app.request(`/knowledge-spaces/${SPACE_ID}/product-settings`, {
headers: headers(),
});
expect(response.status).toBe(200);
await expect(response.json()).resolves.toMatchObject({
activeProfileAvailable: true,
activeProfileRevisions: { embedding: 1, retrieval: 1 },
capabilities: {
deep: true,
index: true,
ingest: true,
query: true,
research: true,
sourceSync: true,
},
configurationState: "validation-failed",
issues: [{ code: "unavailable", field: "embedding", retryable: false }],
});
const legacyFailed = await manifests.update({
expectedManifestVersion: failed.manifestVersion,
knowledgeSpaceId: SPACE_ID,
patch: {
manifestVersion: failed.manifestVersion + 1,
pendingModelConfiguration: {
...pending,
failure: {
code: "MODEL_SELECTION_NOT_FOUND",
failedAt: NOW,
retryable: false,
},
state: "validation-failed",
},
updatedAt: NOW,
},
tenantId: "tenant-1",
});
if (!legacyFailed) throw new Error("legacy test manifest update failed");
const legacyResponse = await app.request(`/knowledge-spaces/${SPACE_ID}/product-settings`, {
headers: headers(),
});
expect(legacyResponse.status).toBe(200);
await expect(legacyResponse.json()).resolves.toMatchObject({
issues: [
{ code: "unavailable", field: "embedding", retryable: false },
{ code: "unavailable", field: "reasoning", retryable: false },
{ code: "unavailable", field: "rerank", retryable: false },
],
});
});
it("withholds published capabilities when the runtime snapshot is unavailable", async () => {
const manifests = createInMemoryKnowledgeSpaceManifestRepository({
maxListLimit: 10,
maxManifests: 10,
});
const app = profileApp({
knowledgeSpaceManifests: manifests,
runtimeSnapshotResolver: {
assertReady: vi.fn(async () => undefined),
resolve: async (input) => {
throw new PublishedProjectionReadUnavailableError(input);
},
},
});
await createSpace(app);
await seedActiveManifest(manifests);
const response = await app.request(`/knowledge-spaces/${SPACE_ID}/product-settings`, {
headers: headers(),
});
expect(response.status).toBe(200);
await expect(response.json()).resolves.toMatchObject({
capabilities: { deep: false, query: false, research: false },
issues: [{ code: "unavailable", field: "publication", retryable: true }],
});
});
it("reports query and research readiness from their runtime mode gates", async () => {
const manifests = createInMemoryKnowledgeSpaceManifestRepository({
maxListLimit: 10,
maxManifests: 10,
});
const assertReady = vi.fn(
async (input: { readonly resolvedMode?: "deep" | "fast" | "research" | undefined }) => {
if (input.resolvedMode === "deep" || input.resolvedMode === "research") {
throw new PublishedProjectionReadUnavailableError({
knowledgeSpaceId: SPACE_ID,
resolvedMode: input.resolvedMode,
tenantId: "tenant-1",
});
}
},
);
const app = profileApp({
knowledgeSpaceManifests: manifests,
runtimeSnapshotResolver: {
assertReady,
resolve: async () => ({}) as never,
},
});
await createSpace(app);
await seedActiveManifest(manifests);
const response = await app.request(`/knowledge-spaces/${SPACE_ID}/product-settings`, {
headers: headers(),
});
expect(response.status).toBe(200);
await expect(response.json()).resolves.toMatchObject({
capabilities: { deep: false, query: true, research: false },
issues: [{ code: "unavailable", field: "publication", retryable: true }],
});
expect(assertReady).toHaveBeenCalledWith({
knowledgeSpaceId: SPACE_ID,
resolvedMode: "fast",
tenantId: "tenant-1",
});
expect(assertReady).toHaveBeenCalledWith({
knowledgeSpaceId: SPACE_ID,
resolvedMode: "deep",
tenantId: "tenant-1",
});
expect(assertReady).toHaveBeenCalledWith({
knowledgeSpaceId: SPACE_ID,
resolvedMode: "research",
tenantId: "tenant-1",
});
});
it("atomically activates verified embedding and retrieval settings for an unpublished space", async () => {
const manifests = createInMemoryKnowledgeSpaceManifestRepository({
maxListLimit: 10,
maxManifests: 10,
});
const activate = vi.fn(async () => ({}) as never);
const { preflight, verify } = modelPreflight();
const app = createKnowledgeGateway({
adapter: createNodePlatformAdapter({ env: {} }),
auth: auth(),
knowledgeSpaceManifests: manifests,
knowledgeSpaceUnpublishedProfileActivations: {
activate,
activateInitialTuple: async () => ({}) as never,
},
knowledgeSpaces: createInMemoryKnowledgeSpaceRepository({
generateId: () => SPACE_ID,
maxListLimit: 10,
maxSpaces: 10,
}),
modelCapabilityPreflight: preflight,
now: () => NOW,
});
await createSpace(app);
await seedActiveManifest(manifests);
const embedding = await app.request(`/knowledge-spaces/${SPACE_ID}/embedding-profile`, {
body: JSON.stringify(EMBEDDING_V2),
headers: headers(),
method: "PUT",
});
expect(embedding.status).toBe(200);
await expect(embedding.json()).resolves.toMatchObject({
dimension: 1536,
model: "embed-v2",
revision: 2,
});
const retrieval = await app.request(`/knowledge-spaces/${SPACE_ID}/retrieval-profile`, {
body: JSON.stringify(retrievalUpdateBody()),
headers: headers(),
method: "PUT",
});
expect(retrieval.status).toBe(200);
await expect(retrieval.json()).resolves.toMatchObject({
reasoningModel: REASONING_V2,
revision: 2,
topK: 12,
});
expect(verify.mock.calls.map(([input]) => input.kind)).toEqual([
"embedding",
"reasoning",
"rerank",
]);
expect(activate).toHaveBeenCalledTimes(2);
expect(activate).toHaveBeenNthCalledWith(
1,
expect.objectContaining({ kind: "embedding", knowledgeSpaceId: SPACE_ID }),
);
expect(activate).toHaveBeenNthCalledWith(
2,
expect.objectContaining({ kind: "retrieval", knowledgeSpaceId: SPACE_ID }),
);
});
it("stages immutable candidates and replays published profile migration requests", async () => {
const manifests = createInMemoryKnowledgeSpaceManifestRepository({
maxListLimit: 10,
maxManifests: 10,
});
const profiles = createInMemoryKnowledgeSpaceProfileRepository({
maxListLimit: 10,
maxRevisions: 20,
});
const request = vi.fn(
async (input: { readonly changedKind: "embedding" | "retrieval" }) =>
({
changedKind: input.changedKind,
checkpoint: "queued",
createdAt: NOW,
id: input.changedKind === "embedding" ? "migration-embedding" : "migration-retrieval",
knowledgeSpaceId: SPACE_ID,
rebuildScope:
input.changedKind === "embedding" ? "full-vector-space" : "clone-publication",
runState: "queued",
updatedAt: NOW,
}) as never,
);
const bindCurrentPublished = vi.fn(async () => ({}) as never);
const { preflight } = modelPreflight();
const app = createKnowledgeGateway({
adapter: createNodePlatformAdapter({ env: {} }),
auth: auth(),
knowledgeSpaceManifests: manifests,
knowledgeSpaceProfileMigrations: {
cancel: async () => null,
findByCandidate: async () => null,
get: async () => null,
request,
requiresMigration: async () => true,
retry: async () => null,
},
knowledgeSpaceProfilePublications: {
activateCandidate: async () => ({}) as never,
bindCandidate: async () => ({}) as never,
bindCurrentPublished,
bindExistingPublished: async () => ({}) as never,
requireActivatedBinding: async () => ({}) as never,
},
knowledgeSpaceProfiles: profiles,
knowledgeSpaces: createInMemoryKnowledgeSpaceRepository({
generateId: () => SPACE_ID,
maxListLimit: 10,
maxSpaces: 10,
}),
modelCapabilityPreflight: preflight,
now: () => NOW,
});
await createSpace(app);
await seedActiveManifest(manifests);
const embedding = await app.request(`/knowledge-spaces/${SPACE_ID}/embedding-profile`, {
body: JSON.stringify(EMBEDDING_V2),
headers: headers(),
method: "PUT",
});
const embeddingBody = await embedding.json();
expect(embedding.status, JSON.stringify(embeddingBody)).toBe(202);
expect(embeddingBody).toMatchObject({
changedKind: "embedding",
id: "migration-embedding",
runState: "queued",
});
const retrievalManifests = createInMemoryKnowledgeSpaceManifestRepository({
maxListLimit: 10,
maxManifests: 10,
});
const retrievalProfiles = createInMemoryKnowledgeSpaceProfileRepository({
maxListLimit: 10,
maxRevisions: 20,
});
const retrievalApp = createKnowledgeGateway({
adapter: createNodePlatformAdapter({ env: {} }),
auth: auth(),
knowledgeSpaceManifests: retrievalManifests,
knowledgeSpaceProfileMigrations: {
cancel: async () => null,
findByCandidate: async () => null,
get: async () => null,
request,
requiresMigration: async () => true,
retry: async () => null,
},
knowledgeSpaceProfilePublications: {
activateCandidate: async () => ({}) as never,
bindCandidate: async () => ({}) as never,
bindCurrentPublished,
bindExistingPublished: async () => ({}) as never,
requireActivatedBinding: async () => ({}) as never,
},
knowledgeSpaceProfiles: retrievalProfiles,
knowledgeSpaces: createInMemoryKnowledgeSpaceRepository({
generateId: () => SPACE_ID,
maxListLimit: 10,
maxSpaces: 10,
}),
modelCapabilityPreflight: preflight,
now: () => NOW,
});
await createSpace(retrievalApp);
await seedActiveManifest(retrievalManifests);
const retrieval = await retrievalApp.request(
`/knowledge-spaces/${SPACE_ID}/retrieval-profile`,
{
body: JSON.stringify(retrievalUpdateBody()),
headers: headers(),
method: "PUT",
},
);
expect(retrieval.status).toBe(202);
await expect(retrieval.json()).resolves.toMatchObject({
changedKind: "retrieval",
id: "migration-retrieval",
runState: "queued",
});
expect(request).toHaveBeenCalledTimes(2);
expect(bindCurrentPublished).toHaveBeenCalledTimes(2);
await expect(
profiles.getRevision({
kind: "embedding",
knowledgeSpaceId: SPACE_ID,
revision: 2,
tenantId: "tenant-1",
}),
).resolves.toMatchObject({ state: "candidate" });
await expect(
retrievalProfiles.getRevision({
kind: "retrieval",
knowledgeSpaceId: SPACE_ID,
revision: 2,
tenantId: "tenant-1",
}),
).resolves.toMatchObject({ state: "candidate" });
});
it("supersedes an owned pending retrieval candidate when the rerank model changes", async () => {
const manifests = createInMemoryKnowledgeSpaceManifestRepository({
maxListLimit: 10,
maxManifests: 10,
});
const profiles = profileRepository();
const request = vi.fn(
async (input: { readonly candidateRevision: number }) =>
({
changedKind: "retrieval",
checkpoint: "queued",
createdAt: NOW,
id: `migration-retrieval-${input.candidateRevision}`,
knowledgeSpaceId: SPACE_ID,
rebuildScope: "clone-publication",
runState: "queued",
updatedAt: NOW,
}) as never,
);
const cancel = vi.fn(async (input: { readonly runId: string }) => {
expect(input.runId).toBe("migration-retrieval-2");
await profiles.failCandidate({
errorCode: "PROFILE_MIGRATION_CANCELED",
errorMessage: "Superseded by a newer retrieval settings update",
kind: "retrieval",
knowledgeSpaceId: SPACE_ID,
now: NOW,
revision: 2,
tenantId: "tenant-1",
});
return {
changedKind: "retrieval",
checkpoint: "queued",
completedAt: NOW,
createdAt: NOW,
id: input.runId,
knowledgeSpaceId: SPACE_ID,
rebuildScope: "clone-publication",
runState: "canceled",
updatedAt: NOW,
} as never;
});
const findByCandidate = vi.fn(async (input: { readonly candidateProfileId: string }) => {
const revision = await profiles.getRevision({
kind: "retrieval",
knowledgeSpaceId: SPACE_ID,
revision: 2,
tenantId: "tenant-1",
});
if (request.mock.calls.length === 0 || revision?.id !== input.candidateProfileId) return null;
return {
changedKind: "retrieval",
checkpoint: "queued",
createdAt: NOW,
id: "migration-retrieval-2",
knowledgeSpaceId: SPACE_ID,
rebuildScope: "clone-publication",
runState: "queued",
updatedAt: NOW,
} as never;
});
const app = publishedProfileApp(manifests, profiles, {
cancel,
findByCandidate,
get: async () => null,
request,
requiresMigration: async () => true,
retry: async () => null,
});
await createSpace(app);
await seedLegacyManifestWithoutRerank(manifests);
const first = await app.request(`/knowledge-spaces/${SPACE_ID}/retrieval-profile`, {
body: JSON.stringify(rerankOnlyRetrievalUpdateBody(RERANK_V1)),
headers: headers(),
method: "PUT",
});
expect(first.status).toBe(202);
const replacement = await app.request(`/knowledge-spaces/${SPACE_ID}/retrieval-profile`, {
body: JSON.stringify(rerankOnlyRetrievalUpdateBody(RERANK_V2)),
headers: headers(),
method: "PUT",
});
const replacementBody = await replacement.json();
expect(replacement.status, JSON.stringify(replacementBody)).toBe(202);
expect(replacementBody).toMatchObject({
id: "migration-retrieval-3",
runState: "queued",
});
expect(request).toHaveBeenCalledTimes(2);
expect(findByCandidate).toHaveBeenCalledTimes(3);
expect(cancel).toHaveBeenCalledOnce();
await expect(
profiles.getRevision({
kind: "retrieval",
knowledgeSpaceId: SPACE_ID,
revision: 2,
tenantId: "tenant-1",
}),
).resolves.toMatchObject({ state: "failed" });
await expect(
profiles.getRevision({
kind: "retrieval",
knowledgeSpaceId: SPACE_ID,
revision: 3,
tenantId: "tenant-1",
}),
).resolves.toMatchObject({
snapshot: { rerank: { enabled: true, model: RERANK_V2 } },
state: "candidate",
});
});
it("retries a nonterminal failed retrieval migration when settings are saved again", async () => {
const manifests = createInMemoryKnowledgeSpaceManifestRepository({
maxListLimit: 10,
maxManifests: 10,
});
const profiles = profileRepository();
const request = vi.fn(
async () =>
({
changedKind: "retrieval",
checkpoint: "queued",
createdAt: NOW,
id: "migration-retrieval-retry",
knowledgeSpaceId: SPACE_ID,
lastErrorCode: "MODEL_PROVIDER_TEMPORARY",
rebuildScope: "clone-publication",
runState: "failed",
updatedAt: NOW,
}) as never,
);
const retry = vi.fn(
async () =>
({
changedKind: "retrieval",
checkpoint: "queued",
createdAt: NOW,
id: "migration-retrieval-retry",
knowledgeSpaceId: SPACE_ID,
rebuildScope: "clone-publication",
runState: "queued",
updatedAt: NOW,
}) as never,
);
const app = publishedProfileApp(manifests, profiles, {
cancel: async () => null,
findByCandidate: async () => null,
get: async () => null,
request,
requiresMigration: async () => true,
retry,
});
await createSpace(app);
await seedLegacyManifestWithoutRerank(manifests);
const response = await app.request(`/knowledge-spaces/${SPACE_ID}/retrieval-profile`, {
body: JSON.stringify(rerankOnlyRetrievalUpdateBody()),
headers: headers(),
method: "PUT",
});
expect(response.status).toBe(202);
await expect(response.json()).resolves.toMatchObject({
id: "migration-retrieval-retry",
runState: "queued",
});
expect(request).toHaveBeenCalledWith(expect.objectContaining({ candidateRevision: 2 }));
expect(retry).toHaveBeenCalledWith(
expect.objectContaining({
knowledgeSpaceId: SPACE_ID,
runId: "migration-retrieval-retry",
}),
);
});
it("allocates retrieval settings after a terminally failed immutable candidate", async () => {
const manifests = createInMemoryKnowledgeSpaceManifestRepository({
maxListLimit: 10,
maxManifests: 10,
});
const profiles = profileRepository();
const migration = {
changedKind: "retrieval",
checkpoint: "queued",
createdAt: NOW,
id: "migration-retrieval",
knowledgeSpaceId: SPACE_ID,
rebuildScope: "clone-publication",
runState: "queued",
updatedAt: NOW,
} as never;
const request = vi.fn(async () => migration);
const app = publishedProfileApp(manifests, profiles, {
cancel: async () => null,
findByCandidate: async () => null,
get: async () => null,
request,
requiresMigration: async () => true,
retry: async () => null,
});
await createSpace(app);
await seedLegacyManifestWithoutRerank(manifests);
const first = await app.request(`/knowledge-spaces/${SPACE_ID}/retrieval-profile`, {
body: JSON.stringify(rerankOnlyRetrievalUpdateBody()),
headers: headers(),
method: "PUT",
});
expect(first.status).toBe(202);
await profiles.failCandidate({
errorCode: "PROFILE_MIGRATION_CANDIDATE_INVALID",
errorMessage: "Candidate failed terminal validation",
kind: "retrieval",
knowledgeSpaceId: SPACE_ID,
now: NOW,
revision: 2,
tenantId: "tenant-1",
});
const replay = await app.request(`/knowledge-spaces/${SPACE_ID}/retrieval-profile`, {
body: JSON.stringify(rerankOnlyRetrievalUpdateBody()),
headers: headers(),
method: "PUT",
});
const replayBody = await replay.json();
expect(replay.status, JSON.stringify(replayBody)).toBe(202);
expect(request).toHaveBeenNthCalledWith(2, expect.objectContaining({ candidateRevision: 3 }));
await expect(
profiles.getRevision({
kind: "retrieval",
knowledgeSpaceId: SPACE_ID,
revision: 3,
tenantId: "tenant-1",
}),
).resolves.toMatchObject({
snapshot: {
defaultMode: "deep",
reasoningModel: REASONING_V1,
rerank: { enabled: true, model: RERANK_V2 },
topK: 8,
},
state: "candidate",
});
});
it("replays a published settings candidate when only capability observation time changes", async () => {
const manifests = createInMemoryKnowledgeSpaceManifestRepository({
maxListLimit: 10,
maxManifests: 10,
});
const profiles = profileRepository();
let checkedAt = NOW;
let capabilityDigest = `sha256:${"c".repeat(64)}`;
const migration = {
changedKind: "retrieval",
checkpoint: "queued",
createdAt: NOW,
id: "migration-retrieval",
knowledgeSpaceId: SPACE_ID,
rebuildScope: "clone-publication",
runState: "queued",
updatedAt: NOW,
} as never;
const request = vi.fn(async () => migration);
const findByCandidate = vi.fn(async () => (request.mock.calls.length === 0 ? null : migration));
const app = profileApp({
knowledgeSpaceManifests: manifests,
knowledgeSpaceProfileMigrations: {
cancel: async () => null,
findByCandidate,
get: async () => null,
request,
requiresMigration: async () => true,
retry: async () => null,
},
knowledgeSpaceProfilePublications: {
activateCandidate: async () => ({}) as never,
bindCandidate: async () => ({}) as never,
bindCurrentPublished: async () => ({}) as never,
bindExistingPublished: async () => ({}) as never,
requireActivatedBinding: async () => ({}) as never,
},
knowledgeSpaceProfiles: profiles,
modelCapabilityPreflight: {
verify: async (input) => {
const selection = {
model: input.selection.model,
pluginId: input.selection.pluginId,
provider: input.selection.provider,
};
return {
...capability(input.kind, selection, checkedAt),
...(input.kind === "rerank" ? { capabilityDigest } : {}),
};
},
},
});
await createSpace(app);
await seedActiveManifest(manifests);
const first = await app.request(`/knowledge-spaces/${SPACE_ID}/retrieval-profile`, {
body: JSON.stringify(retrievalUpdateBody()),
headers: headers(),
method: "PUT",
});
expect(first.status).toBe(202);
checkedAt = "2026-07-21T12:05:00.000Z";
const replay = await app.request(`/knowledge-spaces/${SPACE_ID}/retrieval-profile`, {
body: JSON.stringify(retrievalUpdateBody()),
headers: headers(),
method: "PUT",
});
const replayBody = await replay.json();
expect(replay.status, JSON.stringify(replayBody)).toBe(202);
expect(replayBody).toMatchObject({
changedKind: "retrieval",
id: "migration-retrieval",
});
expect(request).toHaveBeenCalledOnce();
expect(findByCandidate).toHaveBeenCalledTimes(2);
capabilityDigest = `sha256:${"e".repeat(64)}`;
const changedCapability = await app.request(`/knowledge-spaces/${SPACE_ID}/retrieval-profile`, {
body: JSON.stringify(retrievalUpdateBody()),
headers: headers(),
method: "PUT",
});
expect(changedCapability.status).toBe(409);
await expect(changedCapability.json()).resolves.toMatchObject({
code: "KNOWLEDGE_SPACE_SETTINGS_CANDIDATE_CONFLICT",
});
expect(request).toHaveBeenCalledOnce();
});
it("carries an integrated settings Capability grant into the durable migration", async () => {
const manifests = createInMemoryKnowledgeSpaceManifestRepository({
maxListLimit: 10,
maxManifests: 10,
});
const profiles = createInMemoryKnowledgeSpaceProfileRepository({
maxListLimit: 10,
maxRevisions: 20,
});
const spaces = createInMemoryKnowledgeSpaceRepository({
generateId: () => SPACE_ID,
maxListLimit: 10,
maxSpaces: 10,
});
const setupApp = createKnowledgeGateway({
adapter: createNodePlatformAdapter({ env: {} }),
auth: auth(),
knowledgeSpaceManifests: manifests,
knowledgeSpaces: spaces,
now: () => NOW,
});
await createSpace(setupApp);
await seedActiveManifest(manifests);
const request = vi.fn(
async () =>
({
changedKind: "embedding",
checkpoint: "queued",
createdAt: NOW,
id: "migration-embedding",
knowledgeSpaceId: SPACE_ID,
rebuildScope: "full-vector-space",
runState: "queued",
updatedAt: NOW,
}) as never,
);
const capability = settingsCapabilityAuth();
const { preflight } = modelPreflight();
const app = createKnowledgeGateway({
adapter: createNodePlatformAdapter({ env: {} }),
difyCapabilityV2Auth: capability.auth,
knowledgeSpaceManifests: manifests,
knowledgeSpaceProfileMigrations: {
cancel: async () => null,
findByCandidate: async () => null,
get: async () => null,
request,
requiresMigration: async () => true,
retry: async () => null,
},
knowledgeSpaceProfilePublications: {
activateCandidate: async () => ({}) as never,
bindCandidate: async () => ({}) as never,
bindCurrentPublished: async () => ({}) as never,
bindExistingPublished: async () => ({}) as never,
requireActivatedBinding: async () => ({}) as never,
},
knowledgeSpaceProfiles: profiles,
knowledgeSpaces: spaces,
modelCapabilityPreflight: preflight,
now: () => NOW,
});
const response = await app.request(`/knowledge-spaces/${SPACE_ID}/embedding-profile`, {
body: JSON.stringify(EMBEDDING_V2),
headers: {
authorization: "Bearer capability",
"content-type": "application/json",
},
method: "PUT",
});
expect(response.status, await response.clone().text()).toBe(202);
expect(request).toHaveBeenCalledWith(
expect.objectContaining({
capabilityGrantId: capability.grant.grantId,
changedKind: "embedding",
knowledgeSpaceId: SPACE_ID,
}),
);
});
it("reuses an exact immutable settings candidate owned by the same subject", async () => {
const manifests = createInMemoryKnowledgeSpaceManifestRepository({
maxListLimit: 10,
maxManifests: 10,
});
const profiles = profileRepository();
const request = vi.fn(
async () =>
({
changedKind: "embedding",
checkpoint: "queued",
createdAt: NOW,
id: "migration-replayed-embedding",
knowledgeSpaceId: SPACE_ID,
rebuildScope: "full-vector-space",
runState: "queued",
updatedAt: NOW,
}) as never,
);
const { preflight } = modelPreflight();
const app = profileApp({
knowledgeSpaceManifests: manifests,
knowledgeSpaceProfileMigrations: {
cancel: async () => null,
findByCandidate: async () => null,
get: async () => null,
request,
requiresMigration: async () => true,
retry: async () => null,
},
knowledgeSpaceProfilePublications: {
activateCandidate: async () => ({}) as never,
bindCandidate: async () => ({}) as never,
bindCurrentPublished: async () => ({}) as never,
bindExistingPublished: async () => ({}) as never,
requireActivatedBinding: async () => ({}) as never,
},
knowledgeSpaceProfiles: profiles,
modelCapabilityPreflight: preflight,
});
await createSpace(app);
const manifest = await seedActiveManifest(manifests);
const { embeddingProfile, retrievalProfile } = requireLegacyProfiles(manifest);
const activeEmbedding = await profiles.createCandidate({
capabilitySnapshot: capability("embedding", EMBEDDING_V1),
createdBySubjectId: "user:owner",
kind: "embedding",
knowledgeSpaceId: SPACE_ID,
now: NOW,
preserveLegacyInitialRevision: true,
snapshot: { ...embeddingProfile, dimension: 1536 },
tenantId: "tenant-1",
});
const activeRetrieval = await profiles.createCandidate({
capabilitySnapshot: {
reasoning: capability("reasoning", REASONING_V1),
rerank: capability("rerank", RERANK_V1),
verification: "verified",
},
createdBySubjectId: "user:owner",
kind: "retrieval",
knowledgeSpaceId: SPACE_ID,
now: NOW,
preserveLegacyInitialRevision: true,
snapshot: retrievalProfile,
tenantId: "tenant-1",
});
await profiles.activateCandidate({
expectedActiveRevision: null,
kind: "embedding",
knowledgeSpaceId: SPACE_ID,
now: NOW,
revision: activeEmbedding.revision,
tenantId: "tenant-1",
});
await profiles.activateCandidate({
expectedActiveRevision: null,
kind: "retrieval",
knowledgeSpaceId: SPACE_ID,
now: NOW,
revision: activeRetrieval.revision,
tenantId: "tenant-1",
});
const embeddingCapability = capability("embedding", EMBEDDING_V2);
if (
embeddingCapability.kind !== "embedding" ||
embeddingCapability.dimension === undefined ||
!embeddingCapability.distanceMetric
) {
throw new Error("test embedding capability is incomplete");
}
await profiles.createCandidate({
capabilitySnapshot: embeddingCapability,
createdBySubjectId: "user:owner",
kind: "embedding",
knowledgeSpaceId: SPACE_ID,
now: NOW,
snapshot: {
...(await createKnowledgeSpaceEmbeddingProfile(EMBEDDING_V2, 2, {
capabilityDigest: embeddingCapability.capabilityDigest,
dimension: embeddingCapability.dimension,
distanceMetric: embeddingCapability.distanceMetric,
pluginUniqueIdentifier: embeddingCapability.pluginUniqueIdentifier,
schemaFingerprint: embeddingCapability.schemaFingerprint,
})),
dimension: embeddingCapability.dimension,
},
tenantId: "tenant-1",
});
const response = await app.request(`/knowledge-spaces/${SPACE_ID}/embedding-profile`, {
body: JSON.stringify(EMBEDDING_V2),
headers: headers(),
method: "PUT",
});
expect(response.status).toBe(202);
await expect(response.json()).resolves.toMatchObject({ id: "migration-replayed-embedding" });
expect(request).toHaveBeenCalledOnce();
});
});
describe("legacy published profile reconciliation behavior", () => {
it("reports each missing bootstrap prerequisite with an actionable conflict", async () => {
const manifest = await legacyManifest();
await expect(
ensureLegacyPublishedProfileTuple(
bootstrapInput(manifest, profileRepository(), {
profilePublicationBindings: undefined,
}),
),
).rejects.toMatchObject({ code: "PROFILE_PUBLICATION_BOOTSTRAP_UNAVAILABLE", status: 503 });
await expect(
ensureLegacyPublishedProfileTuple(
bootstrapInput({ ...manifest, retrievalProfile: undefined }, profileRepository()),
),
).rejects.toMatchObject({ code: "PROFILE_MIGRATION_BASE_PROFILE_MISSING", status: 409 });
await expect(
ensureLegacyPublishedProfileTuple(
bootstrapInput({ ...manifest, embeddingProfile: undefined }, profileRepository()),
),
).rejects.toMatchObject({ code: "PROFILE_MIGRATION_BASE_PROFILE_MISSING", status: 409 });
});
it("rejects capability selections and dimensions that cannot prove the legacy vector space", async () => {
const manifest = await legacyManifest();
const { embeddingProfile } = requireLegacyProfiles(manifest);
await expect(
ensureLegacyPublishedProfileTuple(
bootstrapInput(manifest, profileRepository(), {
modelCapabilityPreflight: {
verify: async ({ kind, selection }) =>
capability(kind, kind === "embedding" ? EMBEDDING_V2 : selection),
},
}),
),
).rejects.toMatchObject({ code: "PROFILE_PUBLICATION_BOOTSTRAP_CAPABILITY_MISMATCH" });
await expect(
ensureLegacyPublishedProfileTuple(
bootstrapInput(
{
...manifest,
embeddingProfile: { ...embeddingProfile, dimension: 768 },
},
profileRepository(),
),
),
).rejects.toMatchObject({ code: "PROFILE_PUBLICATION_BOOTSTRAP_DIMENSION_CONFLICT" });
});
it("rejects existing active heads that were not backed by verified capabilities", async () => {
const manifest = await legacyManifest();
const { embeddingProfile, retrievalProfile } = requireLegacyProfiles(manifest);
const embeddingProfiles = profileRepository();
const embeddingCandidate = await embeddingProfiles.createCandidate({
capabilitySnapshot: capability("embedding", EMBEDDING_V2),
createdBySubjectId: "user:owner",
kind: "embedding",
knowledgeSpaceId: SPACE_ID,
now: NOW,
preserveLegacyInitialRevision: true,
snapshot: { ...embeddingProfile, dimension: 1536 },
tenantId: "tenant-1",
});
await embeddingProfiles.activateCandidate({
expectedActiveRevision: null,
kind: "embedding",
knowledgeSpaceId: SPACE_ID,
now: NOW,
revision: embeddingCandidate.revision,
tenantId: "tenant-1",
});
await expect(
ensureLegacyPublishedProfileTuple(bootstrapInput(manifest, embeddingProfiles)),
).rejects.toMatchObject({ code: "PROFILE_PUBLICATION_BOOTSTRAP_PROFILE_UNVERIFIED" });
const retrievalProfiles = profileRepository();
const retrievalCandidate = await retrievalProfiles.createCandidate({
capabilitySnapshot: {
reasoning: capability("reasoning", REASONING_V2),
rerank: capability("rerank", RERANK_V2),
verification: "verified",
},
createdBySubjectId: "user:owner",
kind: "retrieval",
knowledgeSpaceId: SPACE_ID,
now: NOW,
preserveLegacyInitialRevision: true,
snapshot: retrievalProfile,
tenantId: "tenant-1",
});
await retrievalProfiles.activateCandidate({
expectedActiveRevision: null,
kind: "retrieval",
knowledgeSpaceId: SPACE_ID,
now: NOW,
revision: retrievalCandidate.revision,
tenantId: "tenant-1",
});
await expect(
ensureLegacyPublishedProfileTuple(bootstrapInput(manifest, retrievalProfiles)),
).rejects.toMatchObject({ code: "PROFILE_PUBLICATION_BOOTSTRAP_PROFILE_UNVERIFIED" });
});
it("accepts already-active verified heads and rebinds the publication tuple", async () => {
const manifest = await legacyManifest();
const { embeddingProfile, retrievalProfile } = requireLegacyProfiles(manifest);
const profiles = profileRepository();
const embeddingSnapshot = { ...embeddingProfile, dimension: 1536 };
const embeddingCandidate = await profiles.createCandidate({
capabilitySnapshot: capability("embedding", EMBEDDING_V1),
createdBySubjectId: "user:owner",
kind: "embedding",
knowledgeSpaceId: SPACE_ID,
now: NOW,
preserveLegacyInitialRevision: true,
snapshot: embeddingSnapshot,
tenantId: "tenant-1",
});
const retrievalCandidate = await profiles.createCandidate({
capabilitySnapshot: {
reasoning: capability("reasoning", REASONING_V1),
rerank: capability("rerank", RERANK_V1),
verification: "verified",
},
createdBySubjectId: "user:owner",
kind: "retrieval",
knowledgeSpaceId: SPACE_ID,
now: NOW,
preserveLegacyInitialRevision: true,
snapshot: retrievalProfile,
tenantId: "tenant-1",
});
await profiles.activateCandidate({
expectedActiveRevision: null,
kind: "embedding",
knowledgeSpaceId: SPACE_ID,
now: NOW,
revision: embeddingCandidate.revision,
tenantId: "tenant-1",
});
await profiles.activateCandidate({
expectedActiveRevision: null,
kind: "retrieval",
knowledgeSpaceId: SPACE_ID,
now: NOW,
revision: retrievalCandidate.revision,
tenantId: "tenant-1",
});
const bindCurrentPublished = vi.fn(async () => ({}) as never);
await ensureLegacyPublishedProfileTuple(
bootstrapInput(manifest, profiles, {
profilePublicationBindings: { bindCurrentPublished },
}),
);
expect(bindCurrentPublished).toHaveBeenCalledWith({
knowledgeSpaceId: SPACE_ID,
tenantId: "tenant-1",
verifiedAt: NOW,
});
});
it("converges when another bootstrap wins the profile-head activation race", async () => {
const manifest = await legacyManifest();
const baseProfiles = profileRepository();
let raceActivation = true;
const racingProfiles = {
...baseProfiles,
activateCandidate: async (input: Parameters<typeof baseProfiles.activateCandidate>[0]) => {
const activated = await baseProfiles.activateCandidate(input);
if (raceActivation) {
raceActivation = false;
throw new KnowledgeSpaceProfileTransitionError(
"KNOWLEDGE_SPACE_PROFILE_HEAD_CONFLICT",
"concurrent head activation",
);
}
return activated;
},
};
await ensureLegacyPublishedProfileTuple(bootstrapInput(manifest, racingProfiles));
await expect(
baseProfiles.getHead({
kind: "retrieval",
knowledgeSpaceId: SPACE_ID,
tenantId: "tenant-1",
}),
).resolves.toMatchObject({ activeRevision: 1 });
});
it("reuses a matching legacy Research candidate", async () => {
const manifest = await legacyManifest();
const researchProfile = legacyResearchProfile();
const researchProfiles = profileRepository();
await researchProfiles.createCandidate({
capabilitySnapshot: {
reasoning: capability("reasoning", REASONING_V1),
rerank: null,
verification: "verified",
},
createdBySubjectId: "user:owner",
kind: "retrieval",
knowledgeSpaceId: SPACE_ID,
now: NOW,
preserveLegacyInitialRevision: true,
snapshot: researchProfile,
tenantId: "tenant-1",
});
await ensureLegacyPublishedProfileTuple(
bootstrapInput(
{ ...manifest, embeddingProfile: undefined, retrievalProfile: researchProfile },
researchProfiles,
),
);
await expect(
researchProfiles.getHead({
kind: "retrieval",
knowledgeSpaceId: SPACE_ID,
tenantId: "tenant-1",
}),
).resolves.toMatchObject({ activeRevision: 1 });
});
it("refuses conflicting legacy candidates and non-convergent activation races", async () => {
const manifest = await legacyManifest();
const profiles = profileRepository();
await profiles.createCandidate({
capabilitySnapshot: capability("embedding", EMBEDDING_V2),
createdBySubjectId: "user:other",
kind: "embedding",
knowledgeSpaceId: SPACE_ID,
now: NOW,
preserveLegacyInitialRevision: true,
snapshot: {
...(await createKnowledgeSpaceEmbeddingProfile(EMBEDDING_V2)),
dimension: 1536,
},
tenantId: "tenant-1",
});
await expect(
ensureLegacyPublishedProfileTuple(bootstrapInput(manifest, profiles)),
).rejects.toMatchObject({ code: "KNOWLEDGE_SPACE_SETTINGS_CANDIDATE_CONFLICT" });
const racingBase = profileRepository();
const racingProfiles = {
...racingBase,
activateCandidate: async () => {
throw new KnowledgeSpaceProfileTransitionError(
"KNOWLEDGE_SPACE_PROFILE_HEAD_CONFLICT",
"the competing activation did not publish an equivalent head",
);
},
};
await expect(
ensureLegacyPublishedProfileTuple(bootstrapInput(manifest, racingProfiles)),
).rejects.toMatchObject({ code: "KNOWLEDGE_SPACE_PROFILE_HEAD_CONFLICT" });
});
it("bootstraps only the missing profile kind and reuses a reranked retrieval candidate", async () => {
const manifest = await legacyManifest();
const { embeddingProfile, retrievalProfile } = requireLegacyProfiles(manifest);
const retrievalHeadProfiles = profileRepository();
const activeRetrieval = await retrievalHeadProfiles.createCandidate({
capabilitySnapshot: {
reasoning: capability("reasoning", REASONING_V1),
rerank: capability("rerank", RERANK_V1),
verification: "verified",
},
createdBySubjectId: "user:owner",
kind: "retrieval",
knowledgeSpaceId: SPACE_ID,
now: NOW,
preserveLegacyInitialRevision: true,
snapshot: retrievalProfile,
tenantId: "tenant-1",
});
await retrievalHeadProfiles.activateCandidate({
expectedActiveRevision: null,
kind: "retrieval",
knowledgeSpaceId: SPACE_ID,
now: NOW,
revision: activeRetrieval.revision,
tenantId: "tenant-1",
});
await ensureLegacyPublishedProfileTuple(bootstrapInput(manifest, retrievalHeadProfiles));
await expect(
retrievalHeadProfiles.getHead({
kind: "embedding",
knowledgeSpaceId: SPACE_ID,
tenantId: "tenant-1",
}),
).resolves.toMatchObject({ activeRevision: 1 });
const embeddingHeadProfiles = profileRepository();
const activeEmbedding = await embeddingHeadProfiles.createCandidate({
capabilitySnapshot: capability("embedding", EMBEDDING_V1),
createdBySubjectId: "user:owner",
kind: "embedding",
knowledgeSpaceId: SPACE_ID,
now: NOW,
preserveLegacyInitialRevision: true,
snapshot: { ...embeddingProfile, dimension: 1536 },
tenantId: "tenant-1",
});
await embeddingHeadProfiles.activateCandidate({
expectedActiveRevision: null,
kind: "embedding",
knowledgeSpaceId: SPACE_ID,
now: NOW,
revision: activeEmbedding.revision,
tenantId: "tenant-1",
});
await embeddingHeadProfiles.createCandidate({
capabilitySnapshot: {
reasoning: capability("reasoning", REASONING_V1),
rerank: capability("rerank", RERANK_V1),
verification: "verified",
},
createdBySubjectId: "user:owner",
kind: "retrieval",
knowledgeSpaceId: SPACE_ID,
now: NOW,
preserveLegacyInitialRevision: true,
snapshot: retrievalProfile,
tenantId: "tenant-1",
});
await ensureLegacyPublishedProfileTuple(bootstrapInput(manifest, embeddingHeadProfiles));
await expect(
embeddingHeadProfiles.getHead({
kind: "retrieval",
knowledgeSpaceId: SPACE_ID,
tenantId: "tenant-1",
}),
).resolves.toMatchObject({ activeRevision: 1 });
});
it("converges candidate-creation races for both repository conflict codes", async () => {
const baseManifest = await legacyManifest();
const researchProfile = legacyResearchProfile();
const manifest = {
...baseManifest,
embeddingProfile: undefined,
retrievalProfile: researchProfile,
};
const conflictCodes = [
"KNOWLEDGE_SPACE_PROFILE_CANDIDATE_EXISTS",
"KNOWLEDGE_SPACE_PROFILE_REVISION_CONFLICT",
] as const;
for (const code of conflictCodes) {
const baseProfiles = profileRepository();
await baseProfiles.createCandidate({
capabilitySnapshot: {
reasoning: capability("reasoning", REASONING_V1),
rerank: null,
verification: "verified",
},
createdBySubjectId: "user:owner",
kind: "retrieval",
knowledgeSpaceId: SPACE_ID,
now: NOW,
preserveLegacyInitialRevision: true,
snapshot: researchProfile,
tenantId: "tenant-1",
});
let revisionReads = 0;
const racingProfiles = {
...baseProfiles,
createCandidate: async () => {
throw new KnowledgeSpaceProfileTransitionError(
code,
"candidate was written concurrently",
);
},
getRevision: async (input: Parameters<typeof baseProfiles.getRevision>[0]) => {
revisionReads += 1;
return revisionReads === 1 ? null : baseProfiles.getRevision(input);
},
};
await ensureLegacyPublishedProfileTuple(bootstrapInput(manifest, racingProfiles));
await expect(
baseProfiles.getHead({
kind: "retrieval",
knowledgeSpaceId: SPACE_ID,
tenantId: "tenant-1",
}),
).resolves.toMatchObject({ activeRevision: 1 });
}
const losingProfiles = profileRepository();
const nonConvergentProfiles = {
...losingProfiles,
createCandidate: async () => {
throw new KnowledgeSpaceProfileTransitionError(
"KNOWLEDGE_SPACE_PROFILE_CANDIDATE_EXISTS",
"the concurrent candidate does not match",
);
},
getRevision: async () => null,
};
await expect(
ensureLegacyPublishedProfileTuple(bootstrapInput(manifest, nonConvergentProfiles)),
).rejects.toMatchObject({ code: "KNOWLEDGE_SPACE_SETTINGS_CANDIDATE_CONFLICT" });
});
it("rejects replay candidates with unverified or mismatched reasoning capabilities", async () => {
const baseManifest = await legacyManifest();
const researchProfile = legacyResearchProfile();
const manifest = {
...baseManifest,
embeddingProfile: undefined,
retrievalProfile: researchProfile,
};
const capabilitySnapshots = [
{
reasoning: capability("reasoning", REASONING_V1),
rerank: null,
verification: "unverified",
},
{
reasoning: {
...capability("reasoning", REASONING_V2),
capabilityDigest: `sha256:${"e".repeat(64)}`,
},
rerank: null,
verification: "verified",
},
] as const;
for (const capabilitySnapshot of capabilitySnapshots) {
const profiles = profileRepository();
await profiles.createCandidate({
capabilitySnapshot,
createdBySubjectId: "user:owner",
kind: "retrieval",
knowledgeSpaceId: SPACE_ID,
now: NOW,
preserveLegacyInitialRevision: true,
snapshot: researchProfile,
tenantId: "tenant-1",
});
await expect(
ensureLegacyPublishedProfileTuple(bootstrapInput(manifest, profiles)),
).rejects.toMatchObject({ code: "KNOWLEDGE_SPACE_SETTINGS_CANDIDATE_CONFLICT" });
}
});
it("rejects explicitly unverified and inconsistent Research capability ledgers", async () => {
const manifest = await legacyManifest();
const { retrievalProfile } = requireLegacyProfiles(manifest);
const unverifiedProfiles = profileRepository();
const unverifiedCandidate = await unverifiedProfiles.createCandidate({
capabilitySnapshot: {
reasoning: capability("reasoning", REASONING_V1),
rerank: capability("rerank", RERANK_V1),
verification: "unverified",
},
createdBySubjectId: "user:owner",
kind: "retrieval",
knowledgeSpaceId: SPACE_ID,
now: NOW,
preserveLegacyInitialRevision: true,
snapshot: retrievalProfile,
tenantId: "tenant-1",
});
await unverifiedProfiles.activateCandidate({
expectedActiveRevision: null,
kind: "retrieval",
knowledgeSpaceId: SPACE_ID,
now: NOW,
revision: unverifiedCandidate.revision,
tenantId: "tenant-1",
});
await expect(
ensureLegacyPublishedProfileTuple(bootstrapInput(manifest, unverifiedProfiles)),
).rejects.toMatchObject({ code: "PROFILE_PUBLICATION_BOOTSTRAP_PROFILE_UNVERIFIED" });
const researchProfile = legacyResearchProfile();
const inconsistentProfiles = profileRepository();
const inconsistentCandidate = await inconsistentProfiles.createCandidate({
capabilitySnapshot: {
reasoning: capability("reasoning", REASONING_V1),
rerank: capability("rerank", RERANK_V1),
verification: "verified",
},
createdBySubjectId: "user:owner",
kind: "retrieval",
knowledgeSpaceId: SPACE_ID,
now: NOW,
preserveLegacyInitialRevision: true,
snapshot: researchProfile,
tenantId: "tenant-1",
});
await inconsistentProfiles.activateCandidate({
expectedActiveRevision: null,
kind: "retrieval",
knowledgeSpaceId: SPACE_ID,
now: NOW,
revision: inconsistentCandidate.revision,
tenantId: "tenant-1",
});
await expect(
ensureLegacyPublishedProfileTuple(
bootstrapInput(
{ ...manifest, embeddingProfile: undefined, retrievalProfile: researchProfile },
inconsistentProfiles,
),
),
).rejects.toMatchObject({ code: "PROFILE_PUBLICATION_BOOTSTRAP_PROFILE_UNVERIFIED" });
const malformedProfiles = profileRepository();
const malformedCandidate = await malformedProfiles.createCandidate({
capabilitySnapshot: {
reasoning: capability("reasoning", REASONING_V1),
rerank: capability("rerank", RERANK_V1),
verification: "verified",
},
createdBySubjectId: "user:owner",
kind: "retrieval",
knowledgeSpaceId: SPACE_ID,
now: NOW,
preserveLegacyInitialRevision: true,
snapshot: retrievalProfile,
tenantId: "tenant-1",
});
await malformedProfiles.activateCandidate({
expectedActiveRevision: null,
kind: "retrieval",
knowledgeSpaceId: SPACE_ID,
now: NOW,
revision: malformedCandidate.revision,
tenantId: "tenant-1",
});
const malformedHead = await malformedProfiles.getHead({
kind: "retrieval",
knowledgeSpaceId: SPACE_ID,
tenantId: "tenant-1",
});
if (!malformedHead) throw new Error("test retrieval head missing");
const malformedLedger = {
...malformedProfiles,
getHead: async (input: Parameters<typeof malformedProfiles.getHead>[0]) =>
input.kind === "retrieval"
? {
...malformedHead,
profile: {
...malformedHead.profile,
snapshot: { ...retrievalProfile, rerank: { enabled: true } },
},
}
: malformedProfiles.getHead(input),
};
await expect(
ensureLegacyPublishedProfileTuple(bootstrapInput(manifest, malformedLedger)),
).rejects.toMatchObject({ code: "PROFILE_PUBLICATION_BOOTSTRAP_PROFILE_UNVERIFIED" });
});
it("fails closed when a preflight adapter omits a required legacy capability", async () => {
const manifest = await legacyManifest();
await expect(
ensureLegacyPublishedProfileTuple(
bootstrapInput(manifest, profileRepository(), {
modelCapabilityPreflight: {
verify: async (input) =>
input.kind === "embedding"
? (undefined as never)
: capability(input.kind, input.selection),
},
}),
),
).rejects.toMatchObject({ code: "MODEL_PREFLIGHT_UNAVAILABLE" });
await expect(
ensureLegacyPublishedProfileTuple(
bootstrapInput(manifest, profileRepository(), {
modelCapabilityPreflight: {
verify: async (input) =>
input.kind === "reasoning"
? (undefined as never)
: capability(input.kind, input.selection),
},
}),
),
).rejects.toMatchObject({ code: "MODEL_PREFLIGHT_UNAVAILABLE" });
});
});
describe("knowledge-space profile failure behavior", () => {
it("fails pending settings and space updates when durable permission issuance is denied", async () => {
const baseAccess = createKnowledgeSpaceAccessService({
repository: createInMemoryKnowledgeSpaceAccessRepository({
maxApiKeysPerSpace: 10,
maxListLimit: 10,
maxMembersPerSpace: 10,
}),
});
const app = profileApp({
knowledgeSpaceAccess: {
...baseAccess,
createPermissionSnapshot: async () => {
throw new KnowledgeSpaceAccessError(
"space_access_forbidden",
"permission snapshots are disabled",
);
},
},
});
await createSpace(app);
const embedding = await app.request(`/knowledge-spaces/${SPACE_ID}/embedding-profile`, {
body: JSON.stringify(EMBEDDING_V2),
headers: headers(),
method: "PUT",
});
expect(embedding.status).toBe(403);
const retrieval = await app.request(`/knowledge-spaces/${SPACE_ID}/retrieval-profile`, {
body: JSON.stringify({
expectedRevision: 0,
profile: { ...retrievalUpdateBody().profile, defaultMode: "research" },
}),
headers: headers(),
method: "PUT",
});
expect(retrieval.status).toBe(403);
const update = await app.request(`/knowledge-spaces/${SPACE_ID}`, {
body: JSON.stringify({ expectedRevision: 1, name: "Denied rename" }),
headers: headers(),
method: "PATCH",
});
expect(update.status).toBe(403);
});
it("maps non-retryable preflight failures and activation-time access revocation", async () => {
const preflightManifests = createInMemoryKnowledgeSpaceManifestRepository({
maxListLimit: 10,
maxManifests: 10,
});
const preflightApp = profileApp({
knowledgeSpaceManifests: preflightManifests,
knowledgeSpaceUnpublishedProfileActivations: {
activate: async () => ({}) as never,
activateInitialTuple: async () => ({}) as never,
},
modelCapabilityPreflight: {
verify: async () => {
throw new ModelCapabilityPreflightError(
"MODEL_CAPABILITY_MISMATCH",
"selected model is incompatible",
);
},
},
});
await createSpace(preflightApp);
await seedActiveManifest(preflightManifests);
const embeddingPreflight = await preflightApp.request(
`/knowledge-spaces/${SPACE_ID}/embedding-profile`,
{
body: JSON.stringify(EMBEDDING_V2),
headers: headers(),
method: "PUT",
},
);
expect(embeddingPreflight.status).toBe(422);
const retrievalPreflight = await preflightApp.request(
`/knowledge-spaces/${SPACE_ID}/retrieval-profile`,
{
body: JSON.stringify(retrievalUpdateBody()),
headers: headers(),
method: "PUT",
},
);
expect(retrievalPreflight.status).toBe(422);
const activationManifests = createInMemoryKnowledgeSpaceManifestRepository({
maxListLimit: 10,
maxManifests: 10,
});
const { preflight } = modelPreflight();
const activationApp = profileApp({
knowledgeSpaceManifests: activationManifests,
knowledgeSpaceUnpublishedProfileActivations: {
activate: async () => {
throw new KnowledgeSpaceAccessError(
"space_access_forbidden",
"permission was revoked before activation",
);
},
activateInitialTuple: async () => ({}) as never,
},
modelCapabilityPreflight: preflight,
});
await createSpace(activationApp);
await seedActiveManifest(activationManifests);
const embeddingActivation = await activationApp.request(
`/knowledge-spaces/${SPACE_ID}/embedding-profile`,
{
body: JSON.stringify(EMBEDDING_V2),
headers: headers(),
method: "PUT",
},
);
expect(embeddingActivation.status).toBe(403);
const retrievalActivation = await activationApp.request(
`/knowledge-spaces/${SPACE_ID}/retrieval-profile`,
{
body: JSON.stringify(retrievalUpdateBody()),
headers: headers(),
method: "PUT",
},
);
expect(retrievalActivation.status).toBe(403);
});
it("keeps missing spaces and invalid pending retrieval updates explicit", async () => {
const app = profileApp();
const missingEmbedding = await app.request(`/knowledge-spaces/${SPACE_ID}/embedding-profile`, {
body: JSON.stringify(EMBEDDING_V2),
headers: headers(),
method: "PUT",
});
expect(missingEmbedding.status).toBe(404);
const missingRetrieval = await app.request(`/knowledge-spaces/${SPACE_ID}/retrieval-profile`, {
body: JSON.stringify(retrievalUpdateBody()),
headers: headers(),
method: "PUT",
});
expect(missingRetrieval.status).toBe(404);
await createSpace(app);
const revisionConflict = await app.request(`/knowledge-spaces/${SPACE_ID}/retrieval-profile`, {
body: JSON.stringify({ ...retrievalUpdateBody(), expectedRevision: 1 }),
headers: headers(),
method: "PUT",
});
expect(revisionConflict.status).toBe(409);
await expect(revisionConflict.json()).resolves.toMatchObject({
code: "PENDING_MODEL_CONFIGURATION_CONFLICT",
});
const embeddingRequired = await app.request(`/knowledge-spaces/${SPACE_ID}/retrieval-profile`, {
body: JSON.stringify({ ...retrievalUpdateBody(), expectedRevision: 0 }),
headers: headers(),
method: "PUT",
});
expect(embeddingRequired.status).toBe(409);
await expect(embeddingRequired.json()).resolves.toMatchObject({
code: "EMBEDDING_MODEL_REQUIRED",
});
const createWithoutEmbedding = await profileApp().request("/knowledge-spaces", {
body: JSON.stringify({
name: "Invalid deep space",
retrievalProfile: retrievalUpdateBody().profile,
}),
headers: headers(),
method: "POST",
});
expect(createWithoutEmbedding.status).toBe(422);
await expect(createWithoutEmbedding.json()).resolves.toMatchObject({
code: "MODEL_CAPABILITY_MISMATCH",
});
});
it("replays identical pending selections without starting duplicate validation", async () => {
const app = profileApp();
await createSpace(app);
const embedding = await app.request(`/knowledge-spaces/${SPACE_ID}/embedding-profile`, {
body: JSON.stringify(EMBEDDING_V2),
headers: headers(),
method: "PUT",
});
expect(embedding.status).toBe(202);
const retrievalBody = {
expectedRevision: 0,
profile: {
...retrievalUpdateBody().profile,
defaultMode: "research",
},
} as const;
const retrieval = await app.request(`/knowledge-spaces/${SPACE_ID}/retrieval-profile`, {
body: JSON.stringify(retrievalBody),
headers: headers(),
method: "PUT",
});
expect(retrieval.status).toBe(202);
const embeddingReplay = await app.request(`/knowledge-spaces/${SPACE_ID}/embedding-profile`, {
body: JSON.stringify(EMBEDDING_V2),
headers: headers(),
method: "PUT",
});
expect(embeddingReplay.status).toBe(202);
await expect(embeddingReplay.json()).resolves.toMatchObject({
configurationStatus: "pending-validation",
revision: 2,
});
const retrievalReplay = await app.request(`/knowledge-spaces/${SPACE_ID}/retrieval-profile`, {
body: JSON.stringify(retrievalBody),
headers: headers(),
method: "PUT",
});
expect(retrievalReplay.status).toBe(202);
await expect(retrievalReplay.json()).resolves.toMatchObject({ revision: 2 });
});
it("reports pending manifest CAS loss for both settings kinds", async () => {
const baseManifests = createInMemoryKnowledgeSpaceManifestRepository({
maxListLimit: 10,
maxManifests: 10,
});
let rejectUpdates = false;
const manifests: KnowledgeSpaceManifestRepository = {
...baseManifests,
update: async (input) => (rejectUpdates ? null : baseManifests.update(input)),
};
const app = profileApp({ knowledgeSpaceManifests: manifests });
await createSpace(app);
const manifest = await baseManifests.get({
knowledgeSpaceId: SPACE_ID,
tenantId: "tenant-1",
});
if (!manifest) throw new Error("test manifest missing");
await baseManifests.update({
expectedManifestVersion: manifest.manifestVersion,
knowledgeSpaceId: SPACE_ID,
patch: {
manifestVersion: manifest.manifestVersion + 1,
pendingModelConfiguration: createKnowledgeSpacePendingModelConfiguration({
embeddingSelection: EMBEDDING_V1,
}),
updatedAt: NOW,
},
tenantId: "tenant-1",
});
rejectUpdates = true;
const embedding = await app.request(`/knowledge-spaces/${SPACE_ID}/embedding-profile`, {
body: JSON.stringify(EMBEDDING_V2),
headers: headers(),
method: "PUT",
});
expect(embedding.status).toBe(409);
await expect(embedding.json()).resolves.toMatchObject({
code: "PENDING_MODEL_CONFIGURATION_CONFLICT",
});
const retrieval = await app.request(`/knowledge-spaces/${SPACE_ID}/retrieval-profile`, {
body: JSON.stringify({
expectedRevision: 0,
profile: { ...retrievalUpdateBody().profile, defaultMode: "research" },
}),
headers: headers(),
method: "PUT",
});
expect(retrieval.status).toBe(409);
await expect(retrieval.json()).resolves.toMatchObject({
code: "PENDING_MODEL_CONFIGURATION_CONFLICT",
});
});
it("fails closed when capability preflight or atomic activation is unavailable", async () => {
const unavailableManifests = createInMemoryKnowledgeSpaceManifestRepository({
maxListLimit: 10,
maxManifests: 10,
});
const preflightUnavailable = profileApp({
knowledgeSpaceManifests: unavailableManifests,
knowledgeSpaceUnpublishedProfileActivations: {
activate: async () => ({}) as never,
activateInitialTuple: async () => ({}) as never,
},
});
await createSpace(preflightUnavailable);
await seedActiveManifest(unavailableManifests);
const noEmbeddingPreflight = await preflightUnavailable.request(
`/knowledge-spaces/${SPACE_ID}/embedding-profile`,
{
body: JSON.stringify(EMBEDDING_V2),
headers: headers(),
method: "PUT",
},
);
expect(noEmbeddingPreflight.status).toBe(503);
await expect(noEmbeddingPreflight.json()).resolves.toMatchObject({
code: "MODEL_PREFLIGHT_UNAVAILABLE",
});
const noRetrievalPreflight = await preflightUnavailable.request(
`/knowledge-spaces/${SPACE_ID}/retrieval-profile`,
{
body: JSON.stringify(retrievalUpdateBody()),
headers: headers(),
method: "PUT",
},
);
expect(noRetrievalPreflight.status).toBe(503);
await expect(noRetrievalPreflight.json()).resolves.toMatchObject({
code: "MODEL_PREFLIGHT_UNAVAILABLE",
});
const activationManifests = createInMemoryKnowledgeSpaceManifestRepository({
maxListLimit: 10,
maxManifests: 10,
});
const { preflight } = modelPreflight();
const activationUnavailable = profileApp({
knowledgeSpaceManifests: activationManifests,
modelCapabilityPreflight: preflight,
});
await createSpace(activationUnavailable);
await seedActiveManifest(activationManifests);
const noEmbeddingActivation = await activationUnavailable.request(
`/knowledge-spaces/${SPACE_ID}/embedding-profile`,
{
body: JSON.stringify(EMBEDDING_V2),
headers: headers(),
method: "PUT",
},
);
expect(noEmbeddingActivation.status).toBe(503);
await expect(noEmbeddingActivation.json()).resolves.toMatchObject({
code: "UNPUBLISHED_PROFILE_ACTIVATION_UNAVAILABLE",
});
const noRetrievalActivation = await activationUnavailable.request(
`/knowledge-spaces/${SPACE_ID}/retrieval-profile`,
{
body: JSON.stringify(retrievalUpdateBody()),
headers: headers(),
method: "PUT",
},
);
expect(noRetrievalActivation.status).toBe(503);
await expect(noRetrievalActivation.json()).resolves.toMatchObject({
code: "UNPUBLISHED_PROFILE_ACTIVATION_UNAVAILABLE",
});
});
it("requires configured migration machinery before changing a published tuple", async () => {
const manifests = createInMemoryKnowledgeSpaceManifestRepository({
maxListLimit: 10,
maxManifests: 10,
});
const app = profileApp({
knowledgeSpaceManifests: manifests,
projectionSetPublications: {
getPublished: async () => ({}) as never,
} as never,
});
await createSpace(app);
await seedActiveManifest(manifests);
const embedding = await app.request(`/knowledge-spaces/${SPACE_ID}/embedding-profile`, {
body: JSON.stringify(EMBEDDING_V2),
headers: headers(),
method: "PUT",
});
expect(embedding.status).toBe(503);
await expect(embedding.json()).resolves.toMatchObject({
code: "PROFILE_MIGRATION_UNAVAILABLE",
});
const retrieval = await app.request(`/knowledge-spaces/${SPACE_ID}/retrieval-profile`, {
body: JSON.stringify(retrievalUpdateBody()),
headers: headers(),
method: "PUT",
});
expect(retrieval.status).toBe(503);
await expect(retrieval.json()).resolves.toMatchObject({
code: "PROFILE_MIGRATION_UNAVAILABLE",
});
});
it("blocks embedding replacement after the vector space is frozen or populated", async () => {
const frozenManifests = createInMemoryKnowledgeSpaceManifestRepository({
maxListLimit: 10,
maxManifests: 10,
});
const frozenApp = profileApp({ knowledgeSpaceManifests: frozenManifests });
await createSpace(frozenApp);
const frozenManifest = await seedActiveManifest(frozenManifests);
await frozenManifests.update({
expectedManifestVersion: frozenManifest.manifestVersion,
knowledgeSpaceId: SPACE_ID,
patch: {
embeddingProfileFrozenAt: NOW,
manifestVersion: frozenManifest.manifestVersion + 1,
updatedAt: NOW,
},
tenantId: "tenant-1",
});
const frozen = await frozenApp.request(`/knowledge-spaces/${SPACE_ID}/embedding-profile`, {
body: JSON.stringify(EMBEDDING_V2),
headers: headers(),
method: "PUT",
});
expect(frozen.status).toBe(409);
const assets = createInMemoryDocumentAssetRepository({ maxAssets: 10 });
const populatedManifests = createInMemoryKnowledgeSpaceManifestRepository({
maxListLimit: 10,
maxManifests: 10,
});
const populatedApp = profileApp({
documentAssets: assets,
knowledgeSpaceManifests: populatedManifests,
});
await createSpace(populatedApp);
await seedActiveManifest(populatedManifests);
await assets.create({
filename: "indexed.txt",
knowledgeSpaceId: SPACE_ID,
mimeType: "text/plain",
objectKey: `tenant-1/spaces/${SPACE_ID}/raw/indexed.txt`,
sha256: "e".repeat(64),
sizeBytes: 8,
tenantId: "tenant-1",
});
const populated = await populatedApp.request(
`/knowledge-spaces/${SPACE_ID}/embedding-profile`,
{
body: JSON.stringify(EMBEDDING_V2),
headers: headers(),
method: "PUT",
},
);
expect(populated.status).toBe(409);
});
it("maps atomic activation conflicts to stable settings responses", async () => {
const manifests = createInMemoryKnowledgeSpaceManifestRepository({
maxListLimit: 10,
maxManifests: 10,
});
const { preflight } = modelPreflight();
const app = profileApp({
knowledgeSpaceManifests: manifests,
knowledgeSpaceUnpublishedProfileActivations: {
activate: async () => {
throw new KnowledgeSpaceUnpublishedProfileActivationError(
"PROFILE_ACTIVATION_CONFLICT",
"profile activation lost its manifest fence",
);
},
activateInitialTuple: async () => ({}) as never,
},
modelCapabilityPreflight: preflight,
});
await createSpace(app);
await seedActiveManifest(manifests);
const embedding = await app.request(`/knowledge-spaces/${SPACE_ID}/embedding-profile`, {
body: JSON.stringify(EMBEDDING_V2),
headers: headers(),
method: "PUT",
});
expect(embedding.status).toBe(409);
await expect(embedding.json()).resolves.toMatchObject({ code: "PROFILE_ACTIVATION_CONFLICT" });
const retrieval = await app.request(`/knowledge-spaces/${SPACE_ID}/retrieval-profile`, {
body: JSON.stringify(retrievalUpdateBody()),
headers: headers(),
method: "PUT",
});
expect(retrieval.status).toBe(409);
await expect(retrieval.json()).resolves.toMatchObject({ code: "PROFILE_ACTIVATION_CONFLICT" });
const transitionManifests = createInMemoryKnowledgeSpaceManifestRepository({
maxListLimit: 10,
maxManifests: 10,
});
const transitionApp = profileApp({
knowledgeSpaceManifests: transitionManifests,
knowledgeSpaceUnpublishedProfileActivations: {
activate: async () => {
throw new KnowledgeSpaceProfileTransitionError(
"KNOWLEDGE_SPACE_PROFILE_HEAD_CONFLICT",
"profile head changed concurrently",
);
},
activateInitialTuple: async () => ({}) as never,
},
modelCapabilityPreflight: preflight,
});
await createSpace(transitionApp);
await seedActiveManifest(transitionManifests);
const transition = await transitionApp.request(
`/knowledge-spaces/${SPACE_ID}/embedding-profile`,
{
body: JSON.stringify(EMBEDDING_V2),
headers: headers(),
method: "PUT",
},
);
expect(transition.status).toBe(409);
await expect(transition.json()).resolves.toMatchObject({
code: "KNOWLEDGE_SPACE_PROFILE_HEAD_CONFLICT",
});
const retrievalTransition = await transitionApp.request(
`/knowledge-spaces/${SPACE_ID}/retrieval-profile`,
{
body: JSON.stringify(retrievalUpdateBody()),
headers: headers(),
method: "PUT",
},
);
expect(retrievalTransition.status).toBe(409);
await expect(retrievalTransition.json()).resolves.toMatchObject({
code: "KNOWLEDGE_SPACE_PROFILE_HEAD_CONFLICT",
});
});
it("maps published bootstrap prerequisites and unexpected preflight outages", async () => {
const manifests = createInMemoryKnowledgeSpaceManifestRepository({
maxListLimit: 10,
maxManifests: 10,
});
const { preflight } = modelPreflight();
const app = profileApp({
knowledgeSpaceManifests: manifests,
knowledgeSpaceProfileMigrations: {
cancel: async () => null,
findByCandidate: async () => null,
get: async () => null,
request: async () => ({}) as never,
requiresMigration: async () => true,
retry: async () => null,
},
knowledgeSpaceProfiles: profileRepository(),
modelCapabilityPreflight: preflight,
});
await createSpace(app);
await seedActiveManifest(manifests);
const embedding = await app.request(`/knowledge-spaces/${SPACE_ID}/embedding-profile`, {
body: JSON.stringify(EMBEDDING_V2),
headers: headers(),
method: "PUT",
});
expect(embedding.status).toBe(503);
await expect(embedding.json()).resolves.toMatchObject({
code: "PROFILE_PUBLICATION_BOOTSTRAP_UNAVAILABLE",
});
const retrieval = await app.request(`/knowledge-spaces/${SPACE_ID}/retrieval-profile`, {
body: JSON.stringify(retrievalUpdateBody()),
headers: headers(),
method: "PUT",
});
expect(retrieval.status).toBe(503);
await expect(retrieval.json()).resolves.toMatchObject({
code: "PROFILE_PUBLICATION_BOOTSTRAP_UNAVAILABLE",
});
const outageManifests = createInMemoryKnowledgeSpaceManifestRepository({
maxListLimit: 10,
maxManifests: 10,
});
const outageApp = profileApp({
knowledgeSpaceManifests: outageManifests,
knowledgeSpaceUnpublishedProfileActivations: {
activate: async () => ({}) as never,
activateInitialTuple: async () => ({}) as never,
},
modelCapabilityPreflight: {
verify: async () => {
throw new Error("plugin daemon transport crashed");
},
},
});
await createSpace(outageApp);
await seedActiveManifest(outageManifests);
const embeddingOutage = await outageApp.request(
`/knowledge-spaces/${SPACE_ID}/embedding-profile`,
{
body: JSON.stringify(EMBEDDING_V2),
headers: headers(),
method: "PUT",
},
);
expect(embeddingOutage.status).toBe(500);
const retrievalOutage = await outageApp.request(
`/knowledge-spaces/${SPACE_ID}/retrieval-profile`,
{
body: JSON.stringify(retrievalUpdateBody()),
headers: headers(),
method: "PUT",
},
);
expect(retrievalOutage.status).toBe(500);
});
it("preserves provisioning replay errors and rejects unusable embedding capabilities", async () => {
const provisioningCases = [
{
error: new KnowledgeSpaceProvisioningIdempotencyConflictError(),
status: 409,
},
{
error: new KnowledgeSpaceProvisioningIncompleteReplayError(),
status: 503,
},
] as const;
for (const { error, status } of provisioningCases) {
const app = profileApp({
knowledgeSpaceProvisioning: {
provision: async () => {
throw error;
},
},
});
const response = await app.request("/knowledge-spaces", {
body: JSON.stringify({ name: `Provisioning error ${status}` }),
headers: headers(),
method: "POST",
});
expect(response.status).toBe(status);
}
const manifests = createInMemoryKnowledgeSpaceManifestRepository({
maxListLimit: 10,
maxManifests: 10,
});
const invalidCapabilityApp = profileApp({
knowledgeSpaceManifests: manifests,
knowledgeSpaceUnpublishedProfileActivations: {
activate: async () => ({}) as never,
activateInitialTuple: async () => ({}) as never,
},
modelCapabilityPreflight: {
verify: async (input) =>
({
...capability(input.kind, input.selection),
dimension: undefined,
distanceMetric: undefined,
}) as never,
},
});
await createSpace(invalidCapabilityApp);
await seedActiveManifest(manifests);
const response = await invalidCapabilityApp.request(
`/knowledge-spaces/${SPACE_ID}/embedding-profile`,
{
body: JSON.stringify(EMBEDDING_V2),
headers: headers(),
method: "PUT",
},
);
expect(response.status).toBe(500);
});
it("surfaces legacy model preflight failures before staging published candidates", async () => {
const manifests = createInMemoryKnowledgeSpaceManifestRepository({
maxListLimit: 10,
maxManifests: 10,
});
const profiles = profileRepository();
const app = profileApp({
knowledgeSpaceManifests: manifests,
knowledgeSpaceProfileMigrations: {
cancel: async () => null,
findByCandidate: async () => null,
get: async () => null,
request: async () => ({}) as never,
requiresMigration: async () => true,
retry: async () => null,
},
knowledgeSpaceProfilePublications: {
activateCandidate: async () => ({}) as never,
bindCandidate: async () => ({}) as never,
bindCurrentPublished: async () => ({}) as never,
bindExistingPublished: async () => ({}) as never,
requireActivatedBinding: async () => ({}) as never,
},
knowledgeSpaceProfiles: profiles,
modelCapabilityPreflight: {
verify: async (input) => {
if (["embed-v1", "reasoning-v1", "rerank-v1"].includes(input.selection.model)) {
throw new ModelCapabilityPreflightError(
"MODEL_PREFLIGHT_FAILED",
"legacy model installation is offline",
{ retryable: true },
);
}
return capability(input.kind, input.selection);
},
},
});
await createSpace(app);
await seedActiveManifest(manifests);
const embedding = await app.request(`/knowledge-spaces/${SPACE_ID}/embedding-profile`, {
body: JSON.stringify(EMBEDDING_V2),
headers: headers(),
method: "PUT",
});
expect(embedding.status).toBe(503);
await expect(embedding.json()).resolves.toMatchObject({ code: "MODEL_PREFLIGHT_FAILED" });
const retrieval = await app.request(`/knowledge-spaces/${SPACE_ID}/retrieval-profile`, {
body: JSON.stringify(retrievalUpdateBody()),
headers: headers(),
method: "PUT",
});
expect(retrieval.status).toBe(503);
await expect(retrieval.json()).resolves.toMatchObject({ code: "MODEL_PREFLIGHT_FAILED" });
await expect(
profiles.getHead({ kind: "embedding", knowledgeSpaceId: SPACE_ID, tenantId: "tenant-1" }),
).resolves.toBeNull();
});
it("fails closed when the published profile ledger is unavailable during bootstrap", async () => {
const manifests = createInMemoryKnowledgeSpaceManifestRepository({
maxListLimit: 10,
maxManifests: 10,
});
const baseProfiles = profileRepository();
const unavailableProfiles = {
...baseProfiles,
getHead: async () => {
throw new Error("profile ledger unavailable");
},
};
const { preflight } = modelPreflight();
const app = profileApp({
knowledgeSpaceManifests: manifests,
knowledgeSpaceProfileMigrations: {
cancel: async () => null,
findByCandidate: async () => null,
get: async () => null,
request: async () => ({}) as never,
requiresMigration: async () => true,
retry: async () => null,
},
knowledgeSpaceProfilePublications: {
activateCandidate: async () => ({}) as never,
bindCandidate: async () => ({}) as never,
bindCurrentPublished: async () => ({}) as never,
bindExistingPublished: async () => ({}) as never,
requireActivatedBinding: async () => ({}) as never,
},
knowledgeSpaceProfiles: unavailableProfiles,
modelCapabilityPreflight: preflight,
});
await createSpace(app);
await seedActiveManifest(manifests);
const embedding = await app.request(`/knowledge-spaces/${SPACE_ID}/embedding-profile`, {
body: JSON.stringify(EMBEDDING_V2),
headers: headers(),
method: "PUT",
});
expect(embedding.status).toBe(503);
await expect(embedding.json()).resolves.toMatchObject({
code: "PROFILE_PUBLICATION_BOOTSTRAP_FAILED",
});
const retrieval = await app.request(`/knowledge-spaces/${SPACE_ID}/retrieval-profile`, {
body: JSON.stringify(retrievalUpdateBody()),
headers: headers(),
method: "PUT",
});
expect(retrieval.status).toBe(503);
await expect(retrieval.json()).resolves.toMatchObject({
code: "PROFILE_PUBLICATION_BOOTSTRAP_FAILED",
});
});
it("refuses to attach a published settings request to another immutable candidate", async () => {
const manifests = createInMemoryKnowledgeSpaceManifestRepository({
maxListLimit: 10,
maxManifests: 10,
});
const profiles = profileRepository();
const { preflight } = modelPreflight();
const app = profileApp({
knowledgeSpaceManifests: manifests,
knowledgeSpaceProfileMigrations: {
cancel: async () => null,
findByCandidate: async () => null,
get: async () => null,
request: async () => ({}) as never,
requiresMigration: async () => true,
retry: async () => null,
},
knowledgeSpaceProfilePublications: {
activateCandidate: async () => ({}) as never,
bindCandidate: async () => ({}) as never,
bindCurrentPublished: async () => ({}) as never,
bindExistingPublished: async () => ({}) as never,
requireActivatedBinding: async () => ({}) as never,
},
knowledgeSpaceProfiles: profiles,
modelCapabilityPreflight: preflight,
});
await createSpace(app);
const manifest = await seedActiveManifest(manifests);
const { embeddingProfile, retrievalProfile } = requireLegacyProfiles(manifest);
const activeEmbedding = await profiles.createCandidate({
capabilitySnapshot: capability("embedding", EMBEDDING_V1),
createdBySubjectId: "user:owner",
kind: "embedding",
knowledgeSpaceId: SPACE_ID,
now: NOW,
preserveLegacyInitialRevision: true,
snapshot: { ...embeddingProfile, dimension: 1536 },
tenantId: "tenant-1",
});
const activeRetrieval = await profiles.createCandidate({
capabilitySnapshot: {
reasoning: capability("reasoning", REASONING_V1),
rerank: capability("rerank", RERANK_V1),
verification: "verified",
},
createdBySubjectId: "user:owner",
kind: "retrieval",
knowledgeSpaceId: SPACE_ID,
now: NOW,
preserveLegacyInitialRevision: true,
snapshot: retrievalProfile,
tenantId: "tenant-1",
});
await profiles.activateCandidate({
expectedActiveRevision: null,
kind: "embedding",
knowledgeSpaceId: SPACE_ID,
now: NOW,
revision: activeEmbedding.revision,
tenantId: "tenant-1",
});
await profiles.activateCandidate({
expectedActiveRevision: null,
kind: "retrieval",
knowledgeSpaceId: SPACE_ID,
now: NOW,
revision: activeRetrieval.revision,
tenantId: "tenant-1",
});
const competingSelection: KnowledgeSpaceModelSelection = {
model: "embed-competing",
pluginId: "embedding-plugin",
provider: "plugin-daemon",
};
await profiles.createCandidate({
capabilitySnapshot: capability("embedding", competingSelection),
createdBySubjectId: "user:other",
kind: "embedding",
knowledgeSpaceId: SPACE_ID,
now: NOW,
snapshot: {
...(await createKnowledgeSpaceEmbeddingProfile(competingSelection, 2)),
dimension: 1536,
},
tenantId: "tenant-1",
});
const response = await app.request(`/knowledge-spaces/${SPACE_ID}/embedding-profile`, {
body: JSON.stringify(EMBEDDING_V2),
headers: headers(),
method: "PUT",
});
expect(response.status).toBe(409);
await expect(response.json()).resolves.toMatchObject({
code: "KNOWLEDGE_SPACE_SETTINGS_CANDIDATE_CONFLICT",
});
});
});
describe("knowledge-space configuration status behavior", () => {
it("derives ready modes from verified active profile heads", async () => {
const profiles = profileRepository();
const app = profileApp({ knowledgeSpaceProfiles: profiles });
await createSpace(app);
const embeddingCandidate = await profiles.createCandidate({
capabilitySnapshot: capability("embedding", EMBEDDING_V1),
createdBySubjectId: "user:owner",
kind: "embedding",
knowledgeSpaceId: SPACE_ID,
now: NOW,
snapshot: {
...(await createKnowledgeSpaceEmbeddingProfile(EMBEDDING_V1)),
dimension: 1536,
},
tenantId: "tenant-1",
});
const retrievalCandidate = await profiles.createCandidate({
capabilitySnapshot: {
reasoning: capability("reasoning", REASONING_V1),
rerank: capability("rerank", RERANK_V1),
verification: "verified",
},
createdBySubjectId: "user:owner",
kind: "retrieval",
knowledgeSpaceId: SPACE_ID,
now: NOW,
snapshot: createKnowledgeSpaceRetrievalProfile({
defaultMode: "deep",
reasoningModel: REASONING_V1,
rerank: { enabled: true, model: RERANK_V1 },
scoreThreshold: { enabled: true, stage: "mode-final", value: 0.35 },
topK: 8,
}),
tenantId: "tenant-1",
});
await profiles.activateCandidate({
expectedActiveRevision: null,
kind: "embedding",
knowledgeSpaceId: SPACE_ID,
now: NOW,
revision: embeddingCandidate.revision,
tenantId: "tenant-1",
});
await profiles.activateCandidate({
expectedActiveRevision: null,
kind: "retrieval",
knowledgeSpaceId: SPACE_ID,
now: NOW,
revision: retrievalCandidate.revision,
tenantId: "tenant-1",
});
const response = await app.request(`/knowledge-spaces/${SPACE_ID}/status`, {
headers: headers(),
});
expect(response.status).toBe(200);
await expect(response.json()).resolves.toMatchObject({
configuration: {
activeProfiles: { embeddingRevision: 1, retrievalRevision: 1 },
availableModes: ["fast", "research", "deep"],
status: "ready",
},
});
});
it("marks a legacy research-only active head as setup-required", async () => {
const profiles = profileRepository();
const app = profileApp({ knowledgeSpaceProfiles: profiles });
await createSpace(app);
const retrievalCandidate = await profiles.createCandidate({
capabilitySnapshot: {
reasoning: capability("reasoning", REASONING_V1),
rerank: null,
verification: "verified",
},
createdBySubjectId: "user:owner",
kind: "retrieval",
knowledgeSpaceId: SPACE_ID,
now: NOW,
snapshot: legacyResearchProfile(),
tenantId: "tenant-1",
});
await profiles.activateCandidate({
expectedActiveRevision: null,
kind: "retrieval",
knowledgeSpaceId: SPACE_ID,
now: NOW,
revision: retrievalCandidate.revision,
tenantId: "tenant-1",
});
const response = await app.request(`/knowledge-spaces/${SPACE_ID}/status`, {
headers: headers(),
});
expect(response.status).toBe(200);
await expect(response.json()).resolves.toMatchObject({
configuration: {
activeProfiles: { retrievalRevision: 1 },
availableModes: [],
status: "setup-required",
},
});
});
it("normalizes valid pending diagnostics and fails closed on corrupt metadata", async () => {
const manifests = createInMemoryKnowledgeSpaceManifestRepository({
maxListLimit: 10,
maxManifests: 10,
});
const app = profileApp({ knowledgeSpaceManifests: manifests });
await createSpace(app);
const updatePendingMetadata = async (value: unknown) => {
const current = await manifests.get({
knowledgeSpaceId: SPACE_ID,
tenantId: "tenant-1",
});
if (!current) throw new Error("test manifest missing");
const updated = await manifests.update({
expectedManifestVersion: current.manifestVersion,
knowledgeSpaceId: SPACE_ID,
patch: {
manifestVersion: current.manifestVersion + 1,
metadata: {
...current.metadata,
__knowledgeFsPendingModelConfiguration: value,
},
updatedAt: NOW,
},
tenantId: "tenant-1",
});
if (!updated) throw new Error("test manifest metadata update failed");
};
const readConfiguration = async () => {
const response = await app.request(`/knowledge-spaces/${SPACE_ID}/status`, {
headers: headers(),
});
expect(response.status).toBe(200);
return (await response.json()).configuration;
};
await updatePendingMetadata("corrupt");
await expect(readConfiguration()).resolves.toMatchObject({ status: "validation-failed" });
await updatePendingMetadata({ digest: "bad", revision: 0, state: "pending-validation" });
await expect(readConfiguration()).resolves.toMatchObject({ status: "validation-failed" });
await updatePendingMetadata({
digest: "a".repeat(64),
revision: 2,
state: "validating",
});
await expect(readConfiguration()).resolves.toMatchObject({
pendingModelConfiguration: {
digest: "a".repeat(64),
revision: 2,
state: "pending-validation",
},
status: "pending-validation",
});
await updatePendingMetadata({
digest: "b".repeat(64),
failure: { code: "MODEL_OFFLINE", failedAt: NOW, retryable: true },
revision: 3,
state: "validation-failed",
});
await expect(readConfiguration()).resolves.toMatchObject({
pendingModelConfiguration: {
failure: { code: "MODEL_OFFLINE", failedAt: NOW, retryable: true },
revision: 3,
state: "validation-failed",
},
status: "validation-failed",
});
await updatePendingMetadata({
digest: "c".repeat(64),
failure: { code: "not valid!", failedAt: "never", retryable: "yes" },
revision: 4,
state: "validation-failed",
});
await expect(readConfiguration()).resolves.toMatchObject({ status: "validation-failed" });
});
it("reports object storage health-check outages without failing status diagnostics", async () => {
const baseAdapter = createNodePlatformAdapter({ env: {} });
const app = createKnowledgeGateway({
adapter: {
...baseAdapter,
objectStorage: {
...baseAdapter.objectStorage,
health: async () => {
throw new Error("object storage health endpoint timed out");
},
},
},
auth: auth(),
knowledgeSpaces: createInMemoryKnowledgeSpaceRepository({
generateId: () => SPACE_ID,
maxListLimit: 10,
maxSpaces: 10,
}),
now: () => NOW,
});
await createSpace(app);
const response = await app.request(`/knowledge-spaces/${SPACE_ID}/status`, {
headers: headers(),
});
expect(response.status).toBe(200);
await expect(response.json()).resolves.toMatchObject({ storage: { healthy: false } });
});
});