11import _ from 'underscore'
2- import { setupEmptyEnvironment } from '../../../__mocks__/helpers/database'
2+ import { setupEmptyEnvironment , setupMockStudio } from '../../../__mocks__/helpers/database'
33import { ICoreSystem , GENESIS_SYSTEM_VERSION } from '@sofie-automation/meteor-lib/dist/collections/CoreSystem'
44import { clearMigrationSteps , addMigrationSteps , prepareMigration , PreparedMigration } from '../databaseMigration'
55import { CURRENT_SYSTEM_VERSION } from '../currentSystemVersion'
66import { RunMigrationResult , GetMigrationStatusResult } from '@sofie-automation/meteor-lib/dist/api/migration'
77import { literal } from '@sofie-automation/corelib/dist/lib'
88import { protectString } from '@sofie-automation/corelib/dist/protectedString'
9- import { MigrationStepInputResult } from '@sofie-automation/blueprints-integration'
9+ import { MigrationStepCore , MigrationStepInputResult } from '@sofie-automation/blueprints-integration'
1010import { DBStudio } from '@sofie-automation/corelib/dist/dataModel/Studio'
1111import { MeteorCall } from '../../api/methods'
1212import { wrapDefaultObject } from '@sofie-automation/corelib/dist/settings/objectWithOverrides'
1313import { ShowStyleBases , ShowStyleVariants , Studios } from '../../collections'
1414import { getCoreSystemAsync } from '../../coreSystem/collection'
15- import { DEFAULT_MINIMUM_TAKE_SPAN } from '@sofie-automation/shared-lib/dist/core/constants'
1615import fs from 'fs'
1716
1817require ( '../../api/peripheralDevice.ts' ) // include in order to create the Meteor methods needed
@@ -107,35 +106,8 @@ describe('Migrations', () => {
107106 return false
108107 } ,
109108 migrate : async ( ) => {
110- await Studios . insertAsync ( {
109+ await setupMockStudio ( {
111110 _id : protectString ( 'studioMock2' ) ,
112- name : 'Default studio' ,
113- supportedShowStyleBase : [ ] ,
114- settingsWithOverrides : wrapDefaultObject ( {
115- mediaPreviewsUrl : '' ,
116- frameRate : 25 ,
117- minimumTakeSpan : DEFAULT_MINIMUM_TAKE_SPAN ,
118- allowHold : true ,
119- allowPieceDirectPlay : true ,
120- enableBuckets : true ,
121- enableEvaluationForm : true ,
122- } ) ,
123- mappingsWithOverrides : wrapDefaultObject ( { } ) ,
124- blueprintConfigWithOverrides : wrapDefaultObject ( { } ) ,
125- _rundownVersionHash : '' ,
126- routeSetsWithOverrides : wrapDefaultObject ( { } ) ,
127- routeSetExclusivityGroupsWithOverrides : wrapDefaultObject ( { } ) ,
128- packageContainersWithOverrides : wrapDefaultObject ( { } ) ,
129- previewContainerIds : [ ] ,
130- thumbnailContainerIds : [ ] ,
131- peripheralDeviceSettings : {
132- deviceSettings : wrapDefaultObject ( { } ) ,
133- playoutDevices : wrapDefaultObject ( { } ) ,
134- ingestDevices : wrapDefaultObject ( { } ) ,
135- inputDevices : wrapDefaultObject ( { } ) ,
136- } ,
137- lastBlueprintConfig : undefined ,
138- lastBlueprintFixUpHash : undefined ,
139111 } )
140112 } ,
141113 } ,
@@ -149,35 +121,8 @@ describe('Migrations', () => {
149121 return false
150122 } ,
151123 migrate : async ( ) => {
152- await Studios . insertAsync ( {
124+ await setupMockStudio ( {
153125 _id : protectString ( 'studioMock3' ) ,
154- name : 'Default studio' ,
155- supportedShowStyleBase : [ ] ,
156- settingsWithOverrides : wrapDefaultObject ( {
157- mediaPreviewsUrl : '' ,
158- frameRate : 25 ,
159- minimumTakeSpan : DEFAULT_MINIMUM_TAKE_SPAN ,
160- allowHold : true ,
161- allowPieceDirectPlay : true ,
162- enableBuckets : true ,
163- enableEvaluationForm : true ,
164- } ) ,
165- mappingsWithOverrides : wrapDefaultObject ( { } ) ,
166- blueprintConfigWithOverrides : wrapDefaultObject ( { } ) ,
167- _rundownVersionHash : '' ,
168- routeSetsWithOverrides : wrapDefaultObject ( { } ) ,
169- routeSetExclusivityGroupsWithOverrides : wrapDefaultObject ( { } ) ,
170- packageContainersWithOverrides : wrapDefaultObject ( { } ) ,
171- previewContainerIds : [ ] ,
172- thumbnailContainerIds : [ ] ,
173- peripheralDeviceSettings : {
174- deviceSettings : wrapDefaultObject ( { } ) ,
175- playoutDevices : wrapDefaultObject ( { } ) ,
176- ingestDevices : wrapDefaultObject ( { } ) ,
177- inputDevices : wrapDefaultObject ( { } ) ,
178- } ,
179- lastBlueprintConfig : undefined ,
180- lastBlueprintFixUpHash : undefined ,
181126 } )
182127 } ,
183128 } ,
@@ -191,35 +136,8 @@ describe('Migrations', () => {
191136 return false
192137 } ,
193138 migrate : async ( ) => {
194- await Studios . insertAsync ( {
139+ await setupMockStudio ( {
195140 _id : protectString ( 'studioMock1' ) ,
196- name : 'Default studio' ,
197- supportedShowStyleBase : [ ] ,
198- settingsWithOverrides : wrapDefaultObject ( {
199- mediaPreviewsUrl : '' ,
200- frameRate : 25 ,
201- minimumTakeSpan : DEFAULT_MINIMUM_TAKE_SPAN ,
202- allowHold : true ,
203- allowPieceDirectPlay : true ,
204- enableBuckets : true ,
205- enableEvaluationForm : true ,
206- } ) ,
207- mappingsWithOverrides : wrapDefaultObject ( { } ) ,
208- blueprintConfigWithOverrides : wrapDefaultObject ( { } ) ,
209- _rundownVersionHash : '' ,
210- routeSetsWithOverrides : wrapDefaultObject ( { } ) ,
211- routeSetExclusivityGroupsWithOverrides : wrapDefaultObject ( { } ) ,
212- packageContainersWithOverrides : wrapDefaultObject ( { } ) ,
213- previewContainerIds : [ ] ,
214- thumbnailContainerIds : [ ] ,
215- peripheralDeviceSettings : {
216- deviceSettings : wrapDefaultObject ( { } ) ,
217- playoutDevices : wrapDefaultObject ( { } ) ,
218- ingestDevices : wrapDefaultObject ( { } ) ,
219- inputDevices : wrapDefaultObject ( { } ) ,
220- } ,
221- lastBlueprintConfig : undefined ,
222- lastBlueprintFixUpHash : undefined ,
223141 } )
224142 } ,
225143 } ,
@@ -322,4 +240,48 @@ describe('Migrations', () => {
322240 expect(steps.indexOf(myShowStyleMockStep3)).toEqual(8)
323241 */
324242 } )
243+
244+ test ( 'Class-based migration steps work with proper binding' , async ( ) => {
245+ await MeteorCall . migration . resetDatabaseVersions ( )
246+ clearMigrationSteps ( )
247+
248+ // Create a migration step class that uses instance properties
249+ class TestClassMigrationStep implements Omit < MigrationStepCore , 'version' > {
250+ public readonly id = 'classBasedMigrationTest'
251+ public readonly canBeRunAutomatically = true
252+ public testValue = 'initialized'
253+
254+ public async validate ( ) : Promise < boolean | string > {
255+ // If 'this' is not bound, testValue will be undefined
256+ return this . testValue === 'initialized' ? 'Migration needed' : false
257+ }
258+
259+ public async migrate ( ) : Promise < void > {
260+ // If 'this' is not bound, this will throw or fail to update the correct instance
261+ this . testValue = 'migrated'
262+ }
263+ }
264+
265+ // Instantiate the step so we can check it later
266+ const step = new TestClassMigrationStep ( )
267+ addMigrationSteps ( '1.0.0' , [ step ] ) ( )
268+
269+ // Prepare migration to ensure it's detected
270+ const migration = await prepareMigration ( true )
271+ expect ( migration . migrationNeeded ) . toEqual ( true )
272+ expect ( _ . find ( migration . steps , ( s ) => s . id === 'classBasedMigrationTest' ) ) . toBeTruthy ( )
273+
274+ // Run the migration to verify that methods are properly bound
275+ const migrationStatus : GetMigrationStatusResult = await MeteorCall . migration . getMigrationStatus ( )
276+ const migrationResult : RunMigrationResult = await MeteorCall . migration . runMigration (
277+ migrationStatus . migration . chunks ,
278+ migrationStatus . migration . hash ,
279+ userInput ( migrationStatus )
280+ )
281+
282+ expect ( migrationResult . migrationCompleted ) . toEqual ( true )
283+
284+ // Verify that migrate() was called and 'this' was correctly bound
285+ expect ( step . testValue ) . toEqual ( 'migrated' )
286+ } )
325287} )
0 commit comments