@@ -33,13 +33,22 @@ jest.mock('@trycompai/integration-platform', () => ({
3333 getManifest : jest . fn ( ) ,
3434 getActiveManifests : jest . fn ( ) ,
3535 runAllChecks : jest . fn ( ) ,
36+ // Default: not a code manifest, so isDynamic falls through to the
37+ // dynamicIntegration lookup (the pre-guard behavior these tests rely on).
38+ // A code-manifest case is exercised explicitly below.
39+ isCodeManifest : jest . fn ( ( ) => false ) ,
3640} ) ) ;
3741
3842import { db } from '@db' ;
39- import { getManifest , runAllChecks } from '@trycompai/integration-platform' ;
43+ import {
44+ getManifest ,
45+ isCodeManifest ,
46+ runAllChecks ,
47+ } from '@trycompai/integration-platform' ;
4048
4149const mockedGetManifest = getManifest as jest . Mock ;
4250const mockedRunAllChecks = runAllChecks as jest . Mock ;
51+ const mockedIsCodeManifest = isCodeManifest as jest . Mock ;
4352// Grab through the module reference to avoid the `unbound-method` lint rule.
4453const mockedTask = db . task as unknown as {
4554 findUnique : jest . Mock ;
@@ -265,6 +274,12 @@ describe('TaskIntegrationsController', () => {
265274 slug : 'aws' ,
266275 } ) ;
267276 mockedGetManifest . mockReturnValue ( MANIFEST ) ;
277+ // Default: treat the provider as NOT a code manifest, so isDynamic falls
278+ // through to the dynamicIntegration lookup (pre-guard behavior). The
279+ // code-manifest case is exercised explicitly in its own test. Set here so a
280+ // test that opts into `true` never leaks into the next (clearAllMocks keeps
281+ // implementations).
282+ mockedIsCodeManifest . mockReturnValue ( false ) ;
268283 // Default: no active exceptions (existing tests behave as before).
269284 mockFindingExceptionFindMany . mockResolvedValue ( [ ] ) ;
270285 mockCheckRunRepository . create . mockImplementation ( ( ) =>
@@ -509,6 +524,49 @@ describe('TaskIntegrationsController', () => {
509524 ) ;
510525 } ) ;
511526
527+ it ( 'treats a CODE-BASED provider as static even when a dynamic row shares its slug — shows the real fail, never held (CS-715)' , async ( ) => {
528+ // A code manifest wins over a dynamic integration of the same slug, so the
529+ // check is static. Pre-fix, the mere existence of the dynamic row forced the
530+ // run to be HELD as 'inconclusive' (hidden from the customer). It must be
531+ // classified static: a real finding shows 'failed' and fails the task.
532+ mockedIsCodeManifest . mockReturnValue ( true ) ;
533+ mockProviderRepository . findById . mockResolvedValue ( {
534+ id : 'prov_gh' ,
535+ slug : 'github' ,
536+ } ) ;
537+ // An active dynamic 'github' row exists — pre-fix this alone forced the hold.
538+ mockDynamicIntegrationFindFirst . mockResolvedValue ( { id : 'din_github' } ) ;
539+ mockConnectionRepository . findById . mockResolvedValue ( {
540+ id : 'conn_1' ,
541+ organizationId : 'org_1' ,
542+ providerId : 'prov_gh' ,
543+ status : 'active' ,
544+ } ) ;
545+ mockConnectionRepository . findActiveByProviderAndOrg . mockResolvedValue ( [
546+ {
547+ id : 'conn_1' ,
548+ organizationId : 'org_1' ,
549+ providerId : 'prov_gh' ,
550+ metadata : { } ,
551+ variables : { } ,
552+ } ,
553+ ] ) ;
554+ mockedRunAllChecks . mockResolvedValue ( failingResult ( ) ) ;
555+
556+ const result = await controller . runCheckForTask ( 'task_1' , 'org_1' , {
557+ connectionId : 'conn_1' ,
558+ checkId : 'aws-s3-encryption' ,
559+ } ) ;
560+
561+ // Not held: the finding fails the task and the run row is 'failed' (shown),
562+ // even though an active dynamic row exists for the same slug.
563+ expect ( result . taskStatus ) . toBe ( 'failed' ) ;
564+ expect ( mockCheckRunRepository . complete ) . toHaveBeenCalledWith (
565+ 'icr_x' ,
566+ expect . objectContaining ( { status : 'failed' } ) ,
567+ ) ;
568+ } ) ;
569+
512570 it ( 'does NOT fail the task when the only finding is excepted (goes done)' , async ( ) => {
513571 mockConnectionRepository . findById . mockResolvedValue ( {
514572 id : 'conn_1' ,
0 commit comments