@@ -13,6 +13,7 @@ import {
1313 schemaMock ,
1414} from '@sim/testing'
1515import { generateShortId } from '@sim/utils/id'
16+ import { DrizzleQueryError } from 'drizzle-orm/errors'
1617import { afterEach , beforeEach , describe , expect , it , vi } from 'vitest'
1718import * as connectorTokens from '@/lib/knowledge/connectors/access-token'
1819import { executeSync , isConnectorRunnableStatus } from '@/lib/knowledge/connectors/sync-engine'
@@ -83,6 +84,12 @@ const { mockGetDocument, mockMapTags, mockListDocuments } = vi.hoisted(() => ({
8384 mockListDocuments : vi . fn ( ) ,
8485} ) )
8586
87+ const { mockLogError } = vi . hoisted ( ( ) => ( { mockLogError : vi . fn ( ) } ) )
88+ vi . mock ( '@sim/logger' , async ( ) => {
89+ const { createMockLogger } = await import ( '@sim/testing/mocks/logger.mock' )
90+ return { createLogger : ( ) => ( { ...createMockLogger ( ) , error : mockLogError } ) }
91+ } )
92+
8693vi . mock ( '@/lib/billing/core/billing-attribution' , ( ) => ( {
8794 assertBillingAttributionOwner : vi . fn ( ) ,
8895 assertBillingAttributionSnapshot : ( snapshot : unknown ) => snapshot ,
@@ -2840,6 +2847,46 @@ describe('executeSync heartbeats during the listing phase', () => {
28402847 dbChainMockFns . returning . mockResolvedValueOnce ( [ { id : 'c-1' , accessMode : 'workspace' } ] )
28412848 }
28422849
2850+ it . each ( [ 'workspace' , 'admin' ] as const ) (
2851+ 'diagnoses a %s tombstone query failure without continuing the crawl' ,
2852+ async ( accessMode ) => {
2853+ primeSyncUpToListing ( )
2854+ dbChainMockFns . returning . mockReset ( )
2855+ dbChainMockFns . returning . mockResolvedValueOnce ( [ { ...CONNECTOR , accessMode } ] )
2856+ const error = new DrizzleQueryError (
2857+ 'select private SQL' ,
2858+ [ 'private bound content' ] ,
2859+ Object . assign ( new Error ( 'canceling statement due to statement timeout' ) , { code : '57014' } )
2860+ )
2861+ dbChainMockFns . limit
2862+ . mockResolvedValueOnce ( [ CONNECTOR ] )
2863+ . mockResolvedValueOnce ( [ { userId : 'u-1' , workspaceId : 'ws-1' } ] )
2864+ . mockRejectedValueOnce ( error )
2865+
2866+ const result = await executeSync ( 'c-1' , {
2867+ billingAttribution : { workspaceId : 'ws-1' } as never ,
2868+ } )
2869+
2870+ expect ( result . error ) . toBe ( 'Database request failed (SQLSTATE 57014).' )
2871+ expect ( mockLogError ) . toHaveBeenCalledWith ( 'Connector tombstone check failed' , {
2872+ connectorId : 'c-1' ,
2873+ operation : 'document.tombstone-check' ,
2874+ elapsedMs : expect . any ( Number ) ,
2875+ diagnostic : {
2876+ category : 'database' ,
2877+ code : '57014' ,
2878+ databaseReason : 'statement_timeout' ,
2879+ message : 'Database request failed (SQLSTATE 57014).' ,
2880+ } ,
2881+ } )
2882+ expect ( mockListDocuments ) . not . toHaveBeenCalled ( )
2883+ expect ( JSON . stringify ( mockLogError . mock . calls ) ) . not . toContain ( 'private' )
2884+ expect ( dbChainMockFns . set ) . toHaveBeenCalledWith (
2885+ expect . objectContaining ( { status : 'error' , consecutiveFailures : 1 } )
2886+ )
2887+ }
2888+ )
2889+
28432890 it . each ( [ 'workspace' , 'admin' ] as const ) (
28442891 'uses the locked source mode %s when resolving its token' ,
28452892 async ( accessMode ) => {
0 commit comments