Skip to content

Commit fc5efa8

Browse files
committed
fix
1 parent a2176b1 commit fc5efa8

2 files changed

Lines changed: 186 additions & 1 deletion

File tree

Lines changed: 185 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,185 @@
1+
/**
2+
* @vitest-environment node
3+
*/
4+
import { dbChainMockFns, resetDbChainMock } from '@sim/testing'
5+
import { beforeEach, describe, expect, it, vi } from 'vitest'
6+
7+
const {
8+
mockCheckActorUsageLimits,
9+
mockGenerateEmbeddings,
10+
mockGetBoundWorkspaceFileSecretProvenanceByMetadata,
11+
mockGetFileMetadataByKeys,
12+
mockProcessDocument,
13+
} = vi.hoisted(() => ({
14+
mockCheckActorUsageLimits: vi.fn(),
15+
mockGenerateEmbeddings: vi.fn(),
16+
mockGetBoundWorkspaceFileSecretProvenanceByMetadata: vi.fn(),
17+
mockGetFileMetadataByKeys: vi.fn(),
18+
mockProcessDocument: vi.fn(),
19+
}))
20+
21+
vi.mock('@/lib/billing/calculations/usage-monitor', () => ({
22+
checkActorUsageLimits: mockCheckActorUsageLimits,
23+
}))
24+
25+
vi.mock('@/lib/knowledge/documents/document-processor', () => ({
26+
processDocument: mockProcessDocument,
27+
}))
28+
29+
vi.mock('@/lib/knowledge/embedding-models', () => ({
30+
getEmbeddingModelInfo: vi.fn(() => ({ tokenizerProvider: 'openai' })),
31+
}))
32+
33+
vi.mock('@/lib/knowledge/embeddings', () => ({
34+
generateEmbeddings: mockGenerateEmbeddings,
35+
}))
36+
37+
vi.mock('@/lib/uploads/contexts/workspace/workspace-file-secret-provenance', () => ({
38+
getBoundWorkspaceFileSecretProvenanceByMetadata:
39+
mockGetBoundWorkspaceFileSecretProvenanceByMetadata,
40+
}))
41+
42+
vi.mock('@/lib/uploads/core/storage-service', () => ({
43+
deleteFile: vi.fn(),
44+
}))
45+
46+
vi.mock('@/lib/uploads/server/metadata', () => ({
47+
deleteFileMetadataByIdentity: vi.fn(),
48+
getFileMetadataByKeys: mockGetFileMetadataByKeys,
49+
}))
50+
51+
import { processDocumentAsync } from '@/lib/knowledge/documents/service'
52+
53+
const PERSISTED_KEY = 'workspace/workspace-1/persisted.pdf'
54+
const PERSISTED_URL = `/api/files/serve/${encodeURIComponent(PERSISTED_KEY)}?context=workspace`
55+
const CONTENT_UPDATED_AT = new Date('2026-08-05T12:00:00.000Z')
56+
57+
const PERSISTED_CONTEXT = {
58+
workspaceId: null,
59+
knowledgeBaseUserId: 'knowledge-owner',
60+
chunkingConfig: null,
61+
embeddingModel: 'text-embedding-3-small',
62+
billedAccountUserId: null,
63+
uploadedBy: 'uploader-1',
64+
filename: 'persisted.pdf',
65+
fileUrl: PERSISTED_URL,
66+
fileSize: 512,
67+
mimeType: 'application/pdf',
68+
tag1: null,
69+
tag2: null,
70+
tag3: null,
71+
tag4: null,
72+
tag5: null,
73+
tag6: null,
74+
tag7: null,
75+
number1: null,
76+
number2: null,
77+
number3: null,
78+
number4: null,
79+
number5: null,
80+
date1: null,
81+
date2: null,
82+
boolean1: null,
83+
boolean2: null,
84+
boolean3: null,
85+
}
86+
87+
const PERSISTED_PROVENANCE_ROW = {
88+
id: 'document-1',
89+
secretProvenanceVersion: null,
90+
filename: PERSISTED_CONTEXT.filename,
91+
fileUrl: PERSISTED_CONTEXT.fileUrl,
92+
contentHash: null,
93+
sourceUrl: null,
94+
tag1: null,
95+
tag2: null,
96+
tag3: null,
97+
tag4: null,
98+
tag5: null,
99+
tag6: null,
100+
tag7: null,
101+
number1: null,
102+
number2: null,
103+
number3: null,
104+
number4: null,
105+
number5: null,
106+
date1: null,
107+
date2: null,
108+
boolean1: null,
109+
boolean2: null,
110+
boolean3: null,
111+
provenanceSourceHash: null,
112+
status: null,
113+
entries: null,
114+
}
115+
116+
const SOURCE_BINDING = {
117+
id: 'source-file-1',
118+
key: PERSISTED_KEY,
119+
userId: 'uploader-1',
120+
workspaceId: 'workspace-1',
121+
context: 'workspace',
122+
originalName: PERSISTED_CONTEXT.filename,
123+
displayName: PERSISTED_CONTEXT.filename,
124+
contentType: PERSISTED_CONTEXT.mimeType,
125+
size: PERSISTED_CONTEXT.fileSize,
126+
folderId: null,
127+
uploadedAt: CONTENT_UPDATED_AT,
128+
contentUpdatedAt: CONTENT_UPDATED_AT,
129+
deletedAt: null,
130+
secretProvenanceVersion: null,
131+
}
132+
133+
describe('knowledge document processing source', () => {
134+
beforeEach(() => {
135+
vi.clearAllMocks()
136+
resetDbChainMock()
137+
dbChainMockFns.limit
138+
.mockResolvedValueOnce([PERSISTED_CONTEXT])
139+
.mockResolvedValueOnce([PERSISTED_PROVENANCE_ROW])
140+
.mockResolvedValueOnce([{ id: 'document-1' }])
141+
mockCheckActorUsageLimits.mockResolvedValue({ isExceeded: false })
142+
mockGetFileMetadataByKeys.mockImplementation(async (_keys: string[], context: string) =>
143+
context === 'workspace' ? [SOURCE_BINDING] : []
144+
)
145+
mockGetBoundWorkspaceFileSecretProvenanceByMetadata.mockResolvedValue(
146+
new Map([[SOURCE_BINDING.id, { status: 'exact', entries: [] }]])
147+
)
148+
mockProcessDocument.mockResolvedValue({
149+
chunks: [],
150+
metadata: { chunkCount: 0, tokenCount: 0, characterCount: 0 },
151+
})
152+
})
153+
154+
it('uses the persisted document source instead of stale queued source fields', async () => {
155+
await processDocumentAsync('knowledge-base-1', 'document-1', {
156+
filename: 'stale.pdf',
157+
fileUrl: 'https://example.com/stale.pdf',
158+
fileSize: 1,
159+
mimeType: 'text/plain',
160+
})
161+
162+
expect(mockGetFileMetadataByKeys).toHaveBeenCalledWith(
163+
[PERSISTED_KEY],
164+
'workspace',
165+
expect.anything()
166+
)
167+
expect(mockGetBoundWorkspaceFileSecretProvenanceByMetadata).toHaveBeenCalledWith(
168+
expect.anything(),
169+
[SOURCE_BINDING]
170+
)
171+
expect(mockProcessDocument).toHaveBeenCalledWith(
172+
PERSISTED_CONTEXT.fileUrl,
173+
PERSISTED_CONTEXT.filename,
174+
PERSISTED_CONTEXT.mimeType,
175+
1024,
176+
200,
177+
100,
178+
PERSISTED_CONTEXT.uploadedBy,
179+
null,
180+
undefined,
181+
undefined
182+
)
183+
expect(mockGenerateEmbeddings).not.toHaveBeenCalled()
184+
})
185+
})

apps/sim/lib/knowledge/documents/service.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2567,7 +2567,7 @@ export async function deleteDocumentStorageFiles(
25672567
}
25682568

25692569
const binding = bindingByKey.get(storageKey)
2570-
if (!binding?.workspaceId) {
2570+
if (!binding?.workspaceId || binding.context !== 'knowledge-base') {
25712571
logger.warn(`[${requestId}] Skipping storage delete: no ownership binding for key`, {
25722572
documentId: doc.id,
25732573
storageKey,

0 commit comments

Comments
 (0)