@@ -4,7 +4,13 @@ import { MeteorMock } from '../../__mocks__/meteor'
44import { logger } from '../logging'
55import { getRandomId , getRandomString , literal , protectString } from '../lib/tempLib'
66import { SnapshotType } from '@sofie-automation/meteor-lib/dist/collections/Snapshots'
7- import { IBlueprintPieceType , PieceLifespan , StatusCode , TSR } from '@sofie-automation/blueprints-integration'
7+ import {
8+ IBlueprintPieceType ,
9+ PieceLifespan ,
10+ PlaylistTimingType ,
11+ StatusCode ,
12+ TSR ,
13+ } from '@sofie-automation/blueprints-integration'
814import {
915 PeripheralDeviceType ,
1016 PeripheralDeviceCategory ,
@@ -27,6 +33,8 @@ import {
2733 SegmentId ,
2834 SnapshotId ,
2935 UserActionsLogItemId ,
36+ StudioId ,
37+ RundownPlaylistId ,
3038} from '@sofie-automation/corelib/dist/dataModel/Ids'
3139
3240// Set up mocks for tests in this suite
@@ -53,6 +61,8 @@ import {
5361 UserActionsLog ,
5462 Segments ,
5563 SofieIngestDataCache ,
64+ Studios ,
65+ RundownPlaylists ,
5666} from '../collections'
5767import { NrcsIngestCacheType } from '@sofie-automation/corelib/dist/dataModel/NrcsIngestDataCache'
5868import { JSONBlobStringify } from '@sofie-automation/shared-lib/dist/lib/JSONBlob'
@@ -64,7 +74,7 @@ import {
6474import { DBSegment } from '@sofie-automation/corelib/dist/dataModel/Segment'
6575import { Settings } from '../Settings'
6676import { SofieIngestCacheType } from '@sofie-automation/corelib/dist/dataModel/SofieIngestDataCache'
67- import { ObjectOverrideSetOp } from '@sofie-automation/corelib/dist/settings/objectWithOverrides'
77+ import { ObjectOverrideSetOp , ObjectWithOverrides } from '@sofie-automation/corelib/dist/settings/objectWithOverrides'
6878
6979describe ( 'cronjobs' , ( ) => {
7080 let env : DefaultEnvironment
@@ -476,7 +486,10 @@ describe('cronjobs', () => {
476486 expect ( await Snapshots . findOneAsync ( snapshot1 ) ) . toBeUndefined ( )
477487 } )
478488 async function insertPlayoutDevice (
479- props : Pick < PeripheralDevice , 'subType' | 'deviceName' | 'lastSeen' | 'parentDeviceId' > &
489+ props : Pick <
490+ PeripheralDevice ,
491+ 'subType' | 'deviceName' | 'lastSeen' | 'parentDeviceId' | 'studioAndConfigId'
492+ > &
480493 Partial < Pick < PeripheralDevice , 'token' > >
481494 ) : Promise < PeripheralDeviceId > {
482495 const deviceId = protectString < PeripheralDeviceId > ( getRandomString ( ) )
@@ -504,7 +517,10 @@ describe('cronjobs', () => {
504517 return deviceId
505518 }
506519
507- async function createMockPlayoutGatewayAndDevices ( lastSeen : number ) : Promise < {
520+ async function createMockPlayoutGatewayAndDevices (
521+ lastSeen : number ,
522+ studioId ?: StudioId
523+ ) : Promise < {
508524 deviceToken : string
509525 mockPlayoutGw : PeripheralDeviceId
510526 mockCasparCg : PeripheralDeviceId
@@ -516,6 +532,12 @@ describe('cronjobs', () => {
516532 lastSeen : lastSeen ,
517533 subType : PERIPHERAL_SUBTYPE_PROCESS ,
518534 token : deviceToken ,
535+ studioAndConfigId : studioId
536+ ? {
537+ configId : '' ,
538+ studioId,
539+ }
540+ : undefined ,
519541 } )
520542 const mockCasparCg = await insertPlayoutDevice ( {
521543 deviceName : 'CasparCG' ,
@@ -540,6 +562,73 @@ describe('cronjobs', () => {
540562 }
541563 }
542564
565+ async function createMockStudioAndRundown ( ) : Promise < {
566+ studioId : StudioId
567+ rundownPlaylistId : RundownPlaylistId
568+ } > {
569+ function newObjectWithOverrides < T extends { } > ( defaults : T ) : ObjectWithOverrides < T > {
570+ return {
571+ defaults,
572+ overrides : [ ] ,
573+ }
574+ }
575+ const studioId = protectString < StudioId > ( getRandomString ( ) )
576+ await Studios . insertAsync ( {
577+ _id : studioId ,
578+ organizationId : null ,
579+ name : 'Studio' ,
580+ blueprintConfigWithOverrides : newObjectWithOverrides ( { } ) ,
581+ _rundownVersionHash : '' ,
582+ lastBlueprintConfig : undefined ,
583+ lastBlueprintFixUpHash : undefined ,
584+ mappingsWithOverrides : newObjectWithOverrides ( { } ) ,
585+ supportedShowStyleBase : [ ] ,
586+ settingsWithOverrides : newObjectWithOverrides ( {
587+ allowHold : true ,
588+ allowPieceDirectPlay : true ,
589+ enableBuckets : true ,
590+ enableEvaluationForm : true ,
591+ frameRate : 25 ,
592+ mediaPreviewsUrl : '' ,
593+ minimumTakeSpan : 1000 ,
594+ } ) ,
595+ routeSetsWithOverrides : newObjectWithOverrides ( { } ) ,
596+ routeSetExclusivityGroupsWithOverrides : newObjectWithOverrides ( { } ) ,
597+ packageContainersWithOverrides : newObjectWithOverrides ( { } ) ,
598+ previewContainerIds : [ ] ,
599+ thumbnailContainerIds : [ ] ,
600+ peripheralDeviceSettings : {
601+ deviceSettings : newObjectWithOverrides ( { } ) ,
602+ ingestDevices : newObjectWithOverrides ( { } ) ,
603+ inputDevices : newObjectWithOverrides ( { } ) ,
604+ playoutDevices : newObjectWithOverrides ( { } ) ,
605+ } ,
606+ } )
607+
608+ const rundownPlaylistId = protectString < RundownPlaylistId > ( getRandomString ( ) )
609+ await RundownPlaylists . mutableCollection . insertAsync ( {
610+ _id : rundownPlaylistId ,
611+ created : Date . now ( ) ,
612+ currentPartInfo : null ,
613+ nextPartInfo : null ,
614+ externalId : '' ,
615+ modified : Date . now ( ) ,
616+ name : 'Rundown' ,
617+ previousPartInfo : null ,
618+ rundownIdsInOrder : [ ] ,
619+ studioId,
620+ timing : {
621+ type : PlaylistTimingType . None ,
622+ } ,
623+ activationId : protectString ( '' ) ,
624+ } )
625+
626+ return {
627+ studioId,
628+ rundownPlaylistId,
629+ }
630+ }
631+
543632 test ( 'Attempts to restart CasparCG when job is enabled' , async ( ) => {
544633 const { mockCasparCg, deviceToken } = await createMockPlayoutGatewayAndDevices ( Date . now ( ) ) // Some time after the threshold
545634
@@ -605,6 +694,28 @@ describe('cronjobs', () => {
605694 expect ( logger . info ) . toHaveBeenLastCalledWith ( 'Nightly cronjob: done' )
606695 } , MAX_WAIT_TIME )
607696 } )
697+ test ( 'Skips CasparCG in Studios with active Playlists when job is enabled' , async ( ) => {
698+ const { studioId } = await createMockStudioAndRundown ( )
699+ await createMockPlayoutGatewayAndDevices ( Date . now ( ) , studioId ) // Some time after the threshold
700+ ; ( logger . info as jest . Mock ) . mockClear ( )
701+ // set time to 2020/07/{date} 04:05 Local Time, should be more than 24 hours after 2020/07/19 00:00 UTC
702+ mockCurrentTime = new Date ( 2020 , 6 , date ++ , 4 , 5 , 0 ) . getTime ( )
703+ // cronjob is checked every 5 minutes, so advance 6 minutes
704+ await jest . advanceTimersByTimeAsync ( 6 * 60 * 1000 )
705+
706+ await waitUntil ( async ( ) => {
707+ // Run timers, so that all promises in the cronjob has a chance to resolve:
708+ const pendingCommands = await PeripheralDeviceCommands . findFetchAsync ( { } )
709+ expect ( pendingCommands ) . toHaveLength ( 0 )
710+ } , MAX_WAIT_TIME )
711+
712+ // make sure that the cronjob ends
713+ await waitUntil ( async ( ) => {
714+ // Run timers, so that all promises in the cronjob has a chance to resolve:
715+ await runAllTimers ( )
716+ expect ( logger . info ) . toHaveBeenLastCalledWith ( 'Nightly cronjob: done' )
717+ } , MAX_WAIT_TIME )
718+ } )
608719 test ( 'Does not attempt to restart CasparCG when job is disabled' , async ( ) => {
609720 await createMockPlayoutGatewayAndDevices ( Date . now ( ) ) // Some time after the threshold
610721 await setCasparCGCronEnabled ( false )
0 commit comments