@@ -41,6 +41,26 @@ const payload = {
4141 workspaceId : binding . workspaceId ,
4242 organizationId : null ,
4343}
44+ const SOURCE_WORKSPACE_ID = 'workspace-1'
45+ const SOURCE_KEY = `workspace/${ SOURCE_WORKSPACE_ID } /source.pdf`
46+ const SOURCE_URL = `/api/files/serve/${ encodeURIComponent ( SOURCE_KEY ) } ?context=workspace`
47+ const SOURCE_UPDATED_AT = new Date ( '2026-08-05T12:00:00.000Z' )
48+ const SOURCE_BINDING = {
49+ id : 'source-file-1' ,
50+ key : SOURCE_KEY ,
51+ userId : 'source-user' ,
52+ workspaceId : SOURCE_WORKSPACE_ID ,
53+ context : 'workspace' ,
54+ originalName : 'source.pdf' ,
55+ displayName : 'source.pdf' ,
56+ contentType : 'application/pdf' ,
57+ size : 512 ,
58+ folderId : null ,
59+ uploadedAt : SOURCE_UPDATED_AT ,
60+ contentUpdatedAt : SOURCE_UPDATED_AT ,
61+ deletedAt : null ,
62+ secretProvenanceVersion : 1 ,
63+ }
4464function context ( ) : OutboxEventContext {
4565 return {
4666 eventId : 'cleanup-1' ,
@@ -179,4 +199,51 @@ describe('durable knowledge storage cleanup', () => {
179199 expect ( dbChainMockFns . transaction ) . not . toHaveBeenCalled ( )
180200 expect ( mockDeleteFile ) . not . toHaveBeenCalled ( )
181201 } )
202+ it ( 'never deletes a referenced workspace source as knowledge-base storage' , async ( ) => {
203+ await enqueueKnowledgeStorageCleanup (
204+ db ,
205+ [ { id : 'document-1' , fileUrl : SOURCE_URL , workspaceId : SOURCE_WORKSPACE_ID } ] ,
206+ 'request-1'
207+ )
208+
209+ expect ( mockGetBindings ) . not . toHaveBeenCalled ( )
210+ expect ( mockDeleteFile ) . not . toHaveBeenCalled ( )
211+ expect ( mockDeleteMetadata ) . not . toHaveBeenCalled ( )
212+ } )
213+
214+ it . each ( [ 'org-1' , 'org-2' , null ] ) (
215+ 'only queues an organization cache for its exact owner: %s' ,
216+ async ( organizationId ) => {
217+ const storageKey = 'kb/org-source.pdf'
218+ mockGetBindings . mockResolvedValue ( [
219+ {
220+ ...SOURCE_BINDING ,
221+ key : storageKey ,
222+ context : 'knowledge-base' ,
223+ workspaceId : null ,
224+ organizationId : 'org-1' ,
225+ } ,
226+ ] )
227+ const cleanup = enqueueKnowledgeStorageCleanup (
228+ db ,
229+ [
230+ {
231+ id : 'org-doc' ,
232+ fileUrl : `/api/files/serve/${ encodeURIComponent ( storageKey ) } ` ,
233+ workspaceId : null ,
234+ organizationId,
235+ } ,
236+ ] ,
237+ 'request-1'
238+ )
239+ if ( organizationId === 'org-1' ) {
240+ await cleanup
241+ expect ( dbChainMockFns . values ) . toHaveBeenCalledOnce ( )
242+ } else {
243+ await expect ( cleanup ) . rejects . toThrow ( )
244+ expect ( dbChainMockFns . values ) . not . toHaveBeenCalled ( )
245+ }
246+ expect ( mockDeleteFile ) . not . toHaveBeenCalled ( )
247+ }
248+ )
182249} )
0 commit comments