1
1
/* eslint-disable @typescript-eslint/no-non-null-assertion */
2
2
/* eslint-disable @typescript-eslint/unbound-method */
3
- import { setupDefaultJobEnvironment } from '../../__mocks__/context'
3
+ import { MockJobContext , setupDefaultJobEnvironment } from '../../__mocks__/context'
4
4
import { setupMockShowStyleCompound } from '../../__mocks__/presetCollections'
5
5
import { findInstancesToSync , PartInstanceToSync , SyncChangesToPartInstancesWorker } from '../syncChangesToPartInstance'
6
6
import { mock } from 'jest-mock-extended'
@@ -10,6 +10,17 @@ import type { PlayoutRundownModel } from '../../playout/model/PlayoutRundownMode
10
10
import type { PlayoutPartInstanceModel } from '../../playout/model/PlayoutPartInstanceModel'
11
11
import type { DBPart } from '@sofie-automation/corelib/dist/dataModel/Part'
12
12
import { protectString } from '@sofie-automation/corelib/dist/protectedString'
13
+ import { PlayoutModelImpl } from '../../playout/model/implementation/PlayoutModelImpl'
14
+ import { PlaylistTimingType , ShowStyleBlueprintManifest } from '@sofie-automation/blueprints-integration'
15
+ import { RundownPlaylistId } from '@sofie-automation/corelib/dist/dataModel/Ids'
16
+ import { DBRundownPlaylist , SelectedPartInstance } from '@sofie-automation/corelib/dist/dataModel/RundownPlaylist'
17
+ import { PlayoutRundownModelImpl } from '../../playout/model/implementation/PlayoutRundownModelImpl'
18
+ import { DBRundown } from '@sofie-automation/corelib/dist/dataModel/Rundown'
19
+ import { DBSegment } from '@sofie-automation/corelib/dist/dataModel/Segment'
20
+ import { PlayoutSegmentModelImpl } from '../../playout/model/implementation/PlayoutSegmentModelImpl'
21
+ import { DBPartInstance } from '@sofie-automation/corelib/dist/dataModel/PartInstance'
22
+ import { ProcessedShowStyleCompound } from '../../jobs'
23
+ import { PartialDeep , ReadonlyDeep } from 'type-fest'
13
24
14
25
jest . mock ( '../../playout/adlibTesting' )
15
26
import { validateAdlibTestingPartInstanceProperties } from '../../playout/adlibTesting'
@@ -165,5 +176,280 @@ describe('SyncChangesToPartInstancesWorker', () => {
165
176
expect ( syncIngestUpdateToPartInstanceFn ) . toHaveBeenCalledTimes ( 1 )
166
177
expect ( validateAdlibTestingPartInstanceProperties ) . toHaveBeenCalledTimes ( 1 )
167
178
} )
179
+
180
+ test ( 'removePartInstance for next calls recreateNextPartInstance' , async ( ) => {
181
+ const context = setupDefaultJobEnvironment ( )
182
+ const showStyleCompound = await setupMockShowStyleCompound ( context )
183
+
184
+ type TsyncIngestUpdateToPartInstanceFn = jest . MockedFunction <
185
+ Required < ShowStyleBlueprintManifest > [ 'syncIngestUpdateToPartInstance' ]
186
+ >
187
+ const syncIngestUpdateToPartInstanceFn : TsyncIngestUpdateToPartInstanceFn = jest . fn ( )
188
+ context . updateShowStyleBlueprint ( {
189
+ syncIngestUpdateToPartInstance : syncIngestUpdateToPartInstanceFn ,
190
+ } )
191
+ const blueprint = await context . getShowStyleBlueprint ( showStyleCompound . _id )
192
+
193
+ const partInstance = createMockPartInstance ( 'mockPartInstanceId' )
194
+ const part = createMockPart ( 'mockPartId' )
195
+
196
+ const playoutModel = createMockPlayoutModel ( { nextPartInstance : partInstance } )
197
+ const ingestModel = createMockIngestModelReadonly ( )
198
+ const rundownModel = createMockPlayoutRundownModel ( )
199
+
200
+ const worker = new SyncChangesToPartInstancesWorker (
201
+ context ,
202
+ playoutModel ,
203
+ ingestModel ,
204
+ showStyleCompound ,
205
+ blueprint
206
+ )
207
+ // Mock the method, we can test it separately
208
+ worker . recreateNextPartInstance = jest . fn ( )
209
+
210
+ const instanceToSync : PartInstanceToSync = {
211
+ playoutRundownModel : rundownModel ,
212
+ existingPartInstance : partInstance ,
213
+ previousPartInstance : null ,
214
+ playStatus : 'next' ,
215
+ newPart : part ,
216
+ proposedPieceInstances : Promise . resolve ( [ ] ) ,
217
+ }
218
+
219
+ syncIngestUpdateToPartInstanceFn . mockImplementationOnce ( ( context ) => {
220
+ // Remove the partInstance
221
+ context . removePartInstance ( )
222
+ } )
223
+
224
+ await worker . syncChangesToPartInstance ( instanceToSync )
225
+
226
+ expect ( partInstance . snapshotMakeCopy ) . toHaveBeenCalledTimes ( 1 )
227
+ expect ( partInstance . snapshotRestore ) . toHaveBeenCalledTimes ( 0 )
228
+ expect ( syncIngestUpdateToPartInstanceFn ) . toHaveBeenCalledTimes ( 1 )
229
+ expect ( validateAdlibTestingPartInstanceProperties ) . toHaveBeenCalledTimes ( 0 )
230
+ expect ( worker . recreateNextPartInstance ) . toHaveBeenCalledTimes ( 1 )
231
+ expect ( worker . recreateNextPartInstance ) . toHaveBeenCalledWith ( part )
232
+ } )
233
+ } )
234
+
235
+ describe ( 'recreateNextPartInstance' , ( ) => {
236
+ async function createSimplePlayoutModel (
237
+ context : MockJobContext ,
238
+ showStyleCompound : ReadonlyDeep < ProcessedShowStyleCompound >
239
+ ) {
240
+ const playlistId = protectString < RundownPlaylistId > ( 'mockPlaylistId' )
241
+ const playlistLock = await context . lockPlaylist ( playlistId )
242
+
243
+ const rundown : DBRundown = {
244
+ _id : protectString ( 'mockRundownId' ) ,
245
+ externalId : 'mockExternalId' ,
246
+ playlistId : playlistId ,
247
+ showStyleBaseId : showStyleCompound . _id ,
248
+ showStyleVariantId : showStyleCompound . showStyleVariantId ,
249
+ name : 'mockName' ,
250
+ organizationId : null ,
251
+ studioId : context . studioId ,
252
+ source : {
253
+ type : 'http' ,
254
+ } ,
255
+ created : 0 ,
256
+ modified : 0 ,
257
+ importVersions : {
258
+ blueprint : '' ,
259
+ core : '' ,
260
+ showStyleBase : '' ,
261
+ showStyleVariant : '' ,
262
+ studio : '' ,
263
+ } ,
264
+ timing : { type : PlaylistTimingType . None } ,
265
+ }
266
+
267
+ const segment : DBSegment = {
268
+ _id : protectString ( 'mockSegmentId' ) ,
269
+ rundownId : rundown . _id ,
270
+ name : 'mockSegmentName' ,
271
+ externalId : 'mockSegmentExternalId' ,
272
+ _rank : 0 ,
273
+ }
274
+
275
+ const part0 : DBPart = {
276
+ _id : protectString ( 'mockPartId0' ) ,
277
+ segmentId : segment . _id ,
278
+ rundownId : rundown . _id ,
279
+ title : 'mockPartTitle0' ,
280
+ _rank : 0 ,
281
+ expectedDuration : 0 ,
282
+ expectedDurationWithTransition : 0 ,
283
+ externalId : 'mockPartExternalId0' ,
284
+ }
285
+
286
+ const nextPartInstance : DBPartInstance = {
287
+ _id : protectString ( 'mockPartInstanceId' ) ,
288
+ part : part0 ,
289
+ segmentId : segment . _id ,
290
+ rundownId : rundown . _id ,
291
+ takeCount : 0 ,
292
+ rehearsal : false ,
293
+ playlistActivationId : protectString ( 'mockPlaylistActivationId' ) ,
294
+ segmentPlayoutId : protectString ( 'mockSegmentPlayoutId' ) ,
295
+ }
296
+
297
+ const playlist : DBRundownPlaylist = {
298
+ _id : playlistId ,
299
+ externalId : 'mockExternalId' ,
300
+ activationId : protectString ( 'mockActivationId' ) ,
301
+ currentPartInfo : null ,
302
+ nextPartInfo : {
303
+ rundownId : nextPartInstance . rundownId ,
304
+ partInstanceId : nextPartInstance . _id ,
305
+ manuallySelected : false ,
306
+ consumesQueuedSegmentId : false ,
307
+ } ,
308
+ previousPartInfo : null ,
309
+ studioId : context . studioId ,
310
+ name : 'mockName' ,
311
+ created : 0 ,
312
+ modified : 0 ,
313
+ timing : { type : PlaylistTimingType . None } ,
314
+ rundownIdsInOrder : [ ] ,
315
+ }
316
+
317
+ const segmentModel = new PlayoutSegmentModelImpl ( segment , [ part0 ] )
318
+ const rundownModel = new PlayoutRundownModelImpl ( rundown , [ segmentModel ] , [ ] )
319
+ const playoutModel = new PlayoutModelImpl (
320
+ context ,
321
+ playlistLock ,
322
+ playlistId ,
323
+ [ ] ,
324
+ playlist ,
325
+ [ nextPartInstance ] ,
326
+ new Map ( ) ,
327
+ [ rundownModel ] ,
328
+ undefined
329
+ )
330
+
331
+ return { playlistId, playoutModel, part0, nextPartInstance }
332
+ }
333
+
334
+ function createMockIngestModelReadonly ( ) : IngestModelReadonly {
335
+ return mock < IngestModelReadonly > (
336
+ {
337
+ findPart : jest . fn ( ( ) => undefined ) ,
338
+ getGlobalPieces : jest . fn ( ( ) => [ ] ) ,
339
+ } ,
340
+ mockOptions
341
+ )
342
+ }
343
+
344
+ test ( 'clear auto chosen partInstance' , async ( ) => {
345
+ const context = setupDefaultJobEnvironment ( )
346
+ const showStyleCompound = await setupMockShowStyleCompound ( context )
347
+ const blueprint = await context . getShowStyleBlueprint ( showStyleCompound . _id )
348
+
349
+ const { playoutModel } = await createSimplePlayoutModel ( context , showStyleCompound )
350
+
351
+ const ingestModel = createMockIngestModelReadonly ( )
352
+
353
+ const worker = new SyncChangesToPartInstancesWorker (
354
+ context ,
355
+ playoutModel ,
356
+ ingestModel ,
357
+ showStyleCompound ,
358
+ blueprint
359
+ )
360
+
361
+ expect ( playoutModel . nextPartInstance ) . toBeTruthy ( )
362
+ expect ( playoutModel . playlist . nextPartInfo ) . toEqual ( {
363
+ partInstanceId : playoutModel . nextPartInstance ! . partInstance . _id ,
364
+ rundownId : playoutModel . nextPartInstance ! . partInstance . rundownId ,
365
+ consumesQueuedSegmentId : false ,
366
+ manuallySelected : false ,
367
+ } satisfies SelectedPartInstance )
368
+
369
+ await worker . recreateNextPartInstance ( undefined )
370
+
371
+ expect ( playoutModel . nextPartInstance ) . toBeFalsy ( )
372
+ } )
373
+
374
+ test ( 'clear manually chosen partInstance' , async ( ) => {
375
+ const context = setupDefaultJobEnvironment ( )
376
+ const showStyleCompound = await setupMockShowStyleCompound ( context )
377
+ const blueprint = await context . getShowStyleBlueprint ( showStyleCompound . _id )
378
+
379
+ const { playoutModel } = await createSimplePlayoutModel ( context , showStyleCompound )
380
+
381
+ const ingestModel = createMockIngestModelReadonly ( )
382
+
383
+ const worker = new SyncChangesToPartInstancesWorker (
384
+ context ,
385
+ playoutModel ,
386
+ ingestModel ,
387
+ showStyleCompound ,
388
+ blueprint
389
+ )
390
+
391
+ expect ( playoutModel . nextPartInstance ) . toBeTruthy ( )
392
+ // Force the next part to be manually selected, and verify
393
+ playoutModel . setPartInstanceAsNext ( playoutModel . nextPartInstance , true , false )
394
+ expect ( playoutModel . playlist . nextPartInfo ) . toEqual ( {
395
+ partInstanceId : playoutModel . nextPartInstance ! . partInstance . _id ,
396
+ rundownId : playoutModel . nextPartInstance ! . partInstance . rundownId ,
397
+ consumesQueuedSegmentId : false ,
398
+ manuallySelected : true ,
399
+ } satisfies SelectedPartInstance )
400
+
401
+ await worker . recreateNextPartInstance ( undefined )
402
+
403
+ expect ( playoutModel . nextPartInstance ) . toBeFalsy ( )
404
+ } )
405
+
406
+ test ( 'clear manually chosen partInstance with replacement part' , async ( ) => {
407
+ const context = setupDefaultJobEnvironment ( )
408
+ const showStyleCompound = await setupMockShowStyleCompound ( context )
409
+ const blueprint = await context . getShowStyleBlueprint ( showStyleCompound . _id )
410
+
411
+ const { playoutModel, part0 } = await createSimplePlayoutModel ( context , showStyleCompound )
412
+
413
+ const ingestModel = createMockIngestModelReadonly ( )
414
+
415
+ const worker = new SyncChangesToPartInstancesWorker (
416
+ context ,
417
+ playoutModel ,
418
+ ingestModel ,
419
+ showStyleCompound ,
420
+ blueprint
421
+ )
422
+
423
+ expect ( playoutModel . nextPartInstance ) . toBeTruthy ( )
424
+ const partInstanceIdBefore = playoutModel . nextPartInstance ! . partInstance . _id
425
+
426
+ // Force the next part to be manually selected, and verify
427
+ playoutModel . setPartInstanceAsNext ( playoutModel . nextPartInstance , true , false )
428
+ expect ( playoutModel . playlist . nextPartInfo ) . toEqual ( {
429
+ partInstanceId : playoutModel . nextPartInstance ! . partInstance . _id ,
430
+ rundownId : playoutModel . nextPartInstance ! . partInstance . rundownId ,
431
+ consumesQueuedSegmentId : false ,
432
+ manuallySelected : true ,
433
+ } satisfies SelectedPartInstance )
434
+
435
+ await worker . recreateNextPartInstance ( part0 )
436
+
437
+ expect ( playoutModel . nextPartInstance ) . toBeTruthy ( )
438
+ // Must have been regenerated
439
+ expect ( playoutModel . nextPartInstance ! . partInstance . _id ) . not . toEqual ( partInstanceIdBefore )
440
+ expect ( playoutModel . nextPartInstance ! . partInstance ) . toMatchObject ( {
441
+ part : {
442
+ _id : part0 . _id ,
443
+ } ,
444
+ } satisfies PartialDeep < DBPartInstance > )
445
+
446
+ // Make sure the part is still manually selected
447
+ expect ( playoutModel . playlist . nextPartInfo ) . toEqual ( {
448
+ partInstanceId : playoutModel . nextPartInstance ! . partInstance . _id ,
449
+ rundownId : playoutModel . nextPartInstance ! . partInstance . rundownId ,
450
+ consumesQueuedSegmentId : false ,
451
+ manuallySelected : true ,
452
+ } satisfies SelectedPartInstance )
453
+ } )
168
454
} )
169
455
} )
0 commit comments