1use super::*;
20use crate::on_demand;
21use frame_support::{
22 migrations::VersionedMigration, pallet_prelude::ValueQuery, storage_alias,
23 traits::UncheckedOnRuntimeUpgrade, weights::Weight,
24};
25
26use super::assigner_coretime::{CoreDescriptor, Schedule};
29
30pub(super) mod v3 {
32 use super::*;
33 use frame_support::pallet_prelude::{OptionQuery, Twox256};
34
35 #[derive(Encode, Decode, TypeInfo, Debug, Clone, PartialEq)]
37 pub(crate) enum Assignment {
38 Pool { para_id: ParaId, core_index: CoreIndex },
40 Bulk(ParaId),
42 }
43
44 impl Assignment {
45 pub fn para_id(&self) -> ParaId {
46 match self {
47 Self::Pool { para_id, .. } => *para_id,
48 Self::Bulk(para_id) => *para_id,
49 }
50 }
51 }
52
53 #[storage_alias]
54 pub(crate) type ClaimQueue<T: Config> =
55 StorageValue<Pallet<T>, BTreeMap<CoreIndex, VecDeque<Assignment>>, ValueQuery>;
56
57 #[storage_alias]
65 pub(crate) type CoreSchedules<T: Config> = StorageMap<
66 CoretimeAssignmentProvider,
67 Twox256,
68 (BlockNumberFor<T>, CoreIndex),
69 Schedule<BlockNumberFor<T>>,
70 OptionQuery,
71 >;
72
73 #[storage_alias]
78 pub(crate) type CoreDescriptors<T: Config> = StorageMap<
79 CoretimeAssignmentProvider,
80 Twox256,
81 CoreIndex,
82 CoreDescriptor<BlockNumberFor<T>>,
83 ValueQuery,
84 >;
85}
86
87pub struct UncheckedMigrateToV4<T>(core::marker::PhantomData<T>);
111
112#[cfg(any(feature = "try-runtime", test))]
113impl<T: Config> UncheckedMigrateToV4<T> {
114 pub fn pre_upgrade() -> Result<Vec<u8>, sp_runtime::DispatchError> {
115 let num_cores = configuration::ActiveConfig::<T>::get().scheduler_params.num_cores;
117 let mut schedule_count = 0u32;
118 let mut descriptor_count = 0u32;
119
120 for core_idx in 0..num_cores {
121 let core_index = CoreIndex(core_idx);
122 let descriptor = v3::CoreDescriptors::<T>::get(core_index);
123
124 if descriptor.queue().is_some() || descriptor.current_work().is_some() {
125 descriptor_count += 1;
126
127 if let Some(queue) = descriptor.queue() {
129 let mut current_block = Some(queue.first);
130 while let Some(block_number) = current_block {
131 let key = (block_number, core_index);
132 if let Some(schedule) = v3::CoreSchedules::<T>::get(key) {
133 schedule_count += 1;
134 current_block = schedule.next_schedule();
135 } else {
136 log::warn!(
139 target: super::LOG_TARGET,
140 "Broken linked list detected for core {:?} at block {:?}",
141 core_index,
142 block_number
143 );
144 break;
145 }
146 }
147 }
148 }
149 }
150
151 let claim_queue = v3::ClaimQueue::<T>::get();
152 let mut total_assignments = 0u32;
153 let mut pool_assignments = 0u32;
154
155 for (_core_idx, assignments) in claim_queue.iter() {
156 for assignment in assignments {
157 total_assignments = total_assignments.saturating_add(1);
158 if matches!(assignment, v3::Assignment::Pool { .. }) {
159 pool_assignments = pool_assignments.saturating_add(1);
160 }
161 }
162 }
163
164 log::info!(
165 target: super::LOG_TARGET,
166 "Before migration v4: {} CoreSchedules, {} CoreDescriptors, {} ClaimQueue assignments ({} pool, {} bulk)",
167 schedule_count,
168 descriptor_count,
169 total_assignments,
170 pool_assignments,
171 total_assignments.saturating_sub(pool_assignments)
172 );
173
174 if descriptor_count == 0 && schedule_count == 0 {
177 log::error!(
178 target: super::LOG_TARGET,
179 "Migration found no data to migrate! This likely means the storage alias pallet name \
180 (CoretimeAssignmentProvider) doesn't match the actual pallet name in construct_runtime. \
181 Check the runtime's construct_runtime macro for the correct pallet name."
182 );
183 return Err("No data found to migrate - wrong pallet name in storage alias?".into());
184 }
185
186 Ok((schedule_count, descriptor_count, total_assignments, pool_assignments).encode())
187 }
188
189 pub fn post_upgrade(state: Vec<u8>) -> Result<(), sp_runtime::DispatchError> {
190 log::info!(target: super::LOG_TARGET, "Running post_upgrade() for v4");
191
192 let (
193 expected_schedule_count,
194 expected_descriptor_count,
195 total_assignments,
196 expected_pool_assignments,
197 ): (u32, u32, u32, u32) =
198 Decode::decode(&mut &state[..]).map_err(|_| "Failed to decode pre_upgrade state")?;
199
200 ensure!(!v3::ClaimQueue::<T>::exists(), "ClaimQueue storage should have been removed");
202
203 let num_cores = configuration::ActiveConfig::<T>::get().scheduler_params.num_cores;
205 for core_idx in 0..num_cores {
206 let core_index = CoreIndex(core_idx);
207
208 let old_descriptor = v3::CoreDescriptors::<T>::get(core_index);
210 ensure!(
211 old_descriptor.queue().is_none() && old_descriptor.current_work().is_none(),
212 "Old CoreDescriptors should be empty"
213 );
214 }
215
216 let new_schedule_count = super::CoreSchedules::<T>::iter().count() as u32;
218 ensure!(
219 new_schedule_count == expected_schedule_count,
220 "CoreSchedules count mismatch after migration"
221 );
222
223 let new_descriptor_count = super::CoreDescriptors::<T>::get().len() as u32;
224 ensure!(
225 new_descriptor_count == expected_descriptor_count,
226 "CoreDescriptors count mismatch after migration"
227 );
228
229 log::info!(
230 target: super::LOG_TARGET,
231 "Successfully migrated v4: {} CoreSchedules, {} CoreDescriptors from AssignerCoretime to Scheduler; {} ClaimQueue assignments ({} pool pushed to on-demand)",
232 new_schedule_count,
233 new_descriptor_count,
234 total_assignments,
235 expected_pool_assignments
236 );
237
238 Ok(())
239 }
240}
241
242impl<T: Config> UncheckedOnRuntimeUpgrade for UncheckedMigrateToV4<T> {
243 fn on_runtime_upgrade() -> Weight {
244 let mut weight: Weight = Weight::zero();
245
246 let num_cores = configuration::ActiveConfig::<T>::get().scheduler_params.num_cores;
248 weight.saturating_accrue(T::DbWeight::get().reads(1));
249
250 let mut schedule_count = 0u64;
252 let mut descriptor_count = 0u64;
253 let mut new_descriptors: BTreeMap<CoreIndex, CoreDescriptor<BlockNumberFor<T>>> =
254 BTreeMap::new();
255
256 for core_idx in 0..num_cores {
257 let core_index = CoreIndex(core_idx);
258
259 let old_descriptor = v3::CoreDescriptors::<T>::take(core_index);
261 weight.saturating_accrue(T::DbWeight::get().reads_writes(1, 1));
262
263 if old_descriptor.queue().is_none() && old_descriptor.current_work().is_none() {
265 continue; }
267
268 descriptor_count += 1;
269
270 if let Some(queue) = old_descriptor.queue() {
272 let mut current_block = Some(queue.first);
273
274 while let Some(block_number) = current_block {
275 let key = (block_number, core_index);
276
277 if let Some(schedule) = v3::CoreSchedules::<T>::take(key) {
278 schedule_count += 1;
279 weight.saturating_accrue(T::DbWeight::get().reads_writes(1, 1));
280
281 let next = schedule.next_schedule();
283
284 super::CoreSchedules::<T>::insert(key, schedule);
286 weight.saturating_accrue(T::DbWeight::get().writes(1));
287
288 current_block = next;
290 } else {
291 log::error!(
294 target: super::LOG_TARGET,
295 "Next queue entry was missing - this is unexpected, (core, block): {:?}",
296 key,
297 );
298 break;
299 }
300 }
301 }
302
303 new_descriptors.insert(core_index, old_descriptor);
305 }
306
307 if descriptor_count > 0 && new_descriptors.len() as u64 != descriptor_count {
309 log::error!(
310 target: super::LOG_TARGET,
311 "Descriptor count mismatch during migration: expected {} but got {}",
312 descriptor_count,
313 new_descriptors.len()
314 );
315 }
316
317 super::CoreDescriptors::<T>::put(new_descriptors);
319 weight.saturating_accrue(T::DbWeight::get().writes(1));
320
321 let _ = v3::CoreDescriptors::<T>::clear(u32::MAX, None);
324 let _ = v3::CoreSchedules::<T>::clear(u32::MAX, None);
325 weight.saturating_accrue(T::DbWeight::get().writes(2));
326
327 let old_claim_queue = v3::ClaimQueue::<T>::take();
329 weight.saturating_accrue(T::DbWeight::get().reads_writes(1, 1));
330
331 let mut total_assignments = 0u32;
332 let mut migrated_pool_assignments = 0u32;
333
334 for (_core_idx, assignments) in old_claim_queue.iter() {
337 for assignment in assignments {
338 total_assignments = total_assignments.saturating_add(1);
339 if let v3::Assignment::Pool { para_id, .. } = assignment {
340 on_demand::Pallet::<T>::push_back_order(*para_id);
343 migrated_pool_assignments = migrated_pool_assignments.saturating_add(1);
344 }
345 }
351 }
352
353 weight.saturating_accrue(T::DbWeight::get().writes(migrated_pool_assignments as u64));
355
356 log::info!(
357 target: super::LOG_TARGET,
358 "Migrated para scheduler storage to v4: {} CoreSchedules, {} CoreDescriptors migrated from AssignerCoretime to Scheduler; removed ClaimQueue ({} total assignments, {} pool assignments migrated to on-demand)",
359 schedule_count,
360 descriptor_count,
361 total_assignments,
362 migrated_pool_assignments
363 );
364
365 weight
366 }
367
368 #[cfg(feature = "try-runtime")]
369 fn pre_upgrade() -> Result<Vec<u8>, sp_runtime::DispatchError> {
370 Self::pre_upgrade()
371 }
372
373 #[cfg(feature = "try-runtime")]
374 fn post_upgrade(state: Vec<u8>) -> Result<(), sp_runtime::DispatchError> {
375 Self::post_upgrade(state)
376 }
377}
378
379pub type MigrateV3ToV4<T> = VersionedMigration<
381 3,
382 4,
383 UncheckedMigrateToV4<T>,
384 Pallet<T>,
385 <T as frame_system::Config>::DbWeight,
386>;
387
388#[cfg(test)]
389mod v4_tests {
390 use super::*;
391 use crate::{
392 configuration,
393 mock::{new_test_ext, MockGenesisConfig, System, Test},
394 on_demand, scheduler,
395 };
396 use alloc::collections::BTreeMap;
397 use frame_support::traits::StorageVersion;
398 use pallet_broker::CoreAssignment as BrokerCoreAssignment;
399 use polkadot_primitives::{CoreIndex, Id as ParaId};
400
401 use super::assigner_coretime::{
402 AssignmentState, CoreDescriptor, PartsOf57600, QueueDescriptor, Schedule, WorkState,
403 };
404
405 #[test]
406 fn basic_migration_works() {
407 new_test_ext(MockGenesisConfig::default()).execute_with(|| {
408 configuration::ActiveConfig::<Test>::mutate(|c| {
410 c.scheduler_params.num_cores = 1;
411 });
412
413 let core = CoreIndex(0);
415 let block_number = 10u32;
416 let para_id = ParaId::from(1000);
417
418 let old_schedule = Schedule::new(
420 vec![(
421 BrokerCoreAssignment::Task(para_id.into()),
422 PartsOf57600::new_saturating(28800),
423 )],
424 Some(100u32),
425 None,
426 );
427
428 let old_descriptor = CoreDescriptor::new(
430 Some(QueueDescriptor { first: block_number, last: block_number }),
431 None,
432 );
433
434 v3::CoreSchedules::<Test>::insert((block_number, core), old_schedule);
436 v3::CoreDescriptors::<Test>::insert(core, old_descriptor);
437
438 StorageVersion::new(3).put::<super::Pallet<Test>>();
440
441 let state =
443 UncheckedMigrateToV4::<Test>::pre_upgrade().expect("pre_upgrade should succeed");
444 let _weight = UncheckedMigrateToV4::<Test>::on_runtime_upgrade();
445 UncheckedMigrateToV4::<Test>::post_upgrade(state).expect("post_upgrade should succeed");
446
447 let new_schedule = super::CoreSchedules::<Test>::get((block_number, core))
449 .expect("Schedule should be migrated");
450 assert_eq!(new_schedule.assignments().len(), 1);
451 assert_eq!(new_schedule.end_hint(), Some(100u32));
452 assert_eq!(new_schedule.next_schedule(), None);
453
454 let new_descriptors = super::CoreDescriptors::<Test>::get();
455 let new_descriptor = new_descriptors.get(&core).expect("Descriptor should be migrated");
456 assert!(new_descriptor.queue().is_some());
457 assert!(new_descriptor.current_work().is_none());
458
459 assert!(
461 v3::CoreSchedules::<Test>::get((block_number, core)).is_none(),
462 "Old CoreSchedules should be cleared"
463 );
464
465 let old_descriptor = v3::CoreDescriptors::<Test>::get(core);
467 assert!(
468 old_descriptor.queue().is_none() && old_descriptor.current_work().is_none(),
469 "Old CoreDescriptor should be cleared after migration"
470 );
471 });
472 }
473
474 #[test]
475 fn multi_core_migration_works() {
476 new_test_ext(MockGenesisConfig::default()).execute_with(|| {
477 configuration::ActiveConfig::<Test>::mutate(|c| {
479 c.scheduler_params.num_cores = 3;
480 });
481
482 let block_number = 10u32;
483
484 for core_idx in 0..3 {
486 let core = CoreIndex(core_idx);
487 let para_id = ParaId::from(1000 + core_idx);
488
489 let old_schedule = Schedule::new(
490 vec![(
491 BrokerCoreAssignment::Task(para_id.into()),
492 PartsOf57600::new_saturating(57600),
493 )],
494 None,
495 None,
496 );
497
498 let old_descriptor = CoreDescriptor::new(
499 Some(QueueDescriptor { first: block_number, last: block_number }),
500 None,
501 );
502
503 v3::CoreSchedules::<Test>::insert((block_number, core), old_schedule);
504 v3::CoreDescriptors::<Test>::insert(core, old_descriptor);
505 }
506
507 StorageVersion::new(3).put::<super::Pallet<Test>>();
508
509 let state =
511 UncheckedMigrateToV4::<Test>::pre_upgrade().expect("pre_upgrade should succeed");
512 UncheckedMigrateToV4::<Test>::on_runtime_upgrade();
513 UncheckedMigrateToV4::<Test>::post_upgrade(state).expect("post_upgrade should succeed");
514
515 let new_descriptors = super::CoreDescriptors::<Test>::get();
517 assert_eq!(new_descriptors.len(), 3);
518
519 for core_idx in 0..3 {
520 let core = CoreIndex(core_idx);
521 assert!(new_descriptors.contains_key(&core));
522 assert!(super::CoreSchedules::<Test>::get((block_number, core)).is_some());
523 }
524 });
525 }
526
527 #[test]
528 fn linked_list_migration_works() {
529 new_test_ext(MockGenesisConfig::default()).execute_with(|| {
530 configuration::ActiveConfig::<Test>::mutate(|c| {
532 c.scheduler_params.num_cores = 1;
533 });
534
535 let core = CoreIndex(0);
536 let para_id = ParaId::from(1000);
537
538 let schedule_30 = Schedule::new(
540 vec![(
541 BrokerCoreAssignment::Task(para_id.into()),
542 PartsOf57600::new_saturating(57600),
543 )],
544 None,
545 None,
546 );
547
548 let schedule_20 = Schedule::new(
549 vec![(
550 BrokerCoreAssignment::Task(para_id.into()),
551 PartsOf57600::new_saturating(57600),
552 )],
553 None,
554 Some(30u32),
555 );
556
557 let schedule_10 = Schedule::new(
558 vec![(
559 BrokerCoreAssignment::Task(para_id.into()),
560 PartsOf57600::new_saturating(57600),
561 )],
562 None,
563 Some(20u32),
564 );
565
566 v3::CoreSchedules::<Test>::insert((10u32, core), schedule_10);
568 v3::CoreSchedules::<Test>::insert((20u32, core), schedule_20);
569 v3::CoreSchedules::<Test>::insert((30u32, core), schedule_30);
570
571 let old_descriptor =
573 CoreDescriptor::new(Some(QueueDescriptor { first: 10u32, last: 30u32 }), None);
574
575 v3::CoreDescriptors::<Test>::insert(core, old_descriptor);
576
577 StorageVersion::new(3).put::<super::Pallet<Test>>();
578
579 let state =
581 UncheckedMigrateToV4::<Test>::pre_upgrade().expect("pre_upgrade should succeed");
582 UncheckedMigrateToV4::<Test>::on_runtime_upgrade();
583 UncheckedMigrateToV4::<Test>::post_upgrade(state).expect("post_upgrade should succeed");
584
585 assert!(super::CoreSchedules::<Test>::get((10u32, core)).is_some());
587 assert!(super::CoreSchedules::<Test>::get((20u32, core)).is_some());
588 assert!(super::CoreSchedules::<Test>::get((30u32, core)).is_some());
589
590 let new_10 = super::CoreSchedules::<Test>::get((10u32, core)).unwrap();
592 assert_eq!(new_10.next_schedule(), Some(20u32));
593
594 let new_20 = super::CoreSchedules::<Test>::get((20u32, core)).unwrap();
595 assert_eq!(new_20.next_schedule(), Some(30u32));
596
597 let new_30 = super::CoreSchedules::<Test>::get((30u32, core)).unwrap();
598 assert_eq!(new_30.next_schedule(), None);
599 });
600 }
601
602 #[test]
603 fn claim_queue_migration_works() {
604 new_test_ext(MockGenesisConfig::default()).execute_with(|| {
608 configuration::ActiveConfig::<Test>::mutate(|c| {
610 c.scheduler_params.num_cores = 1;
611 });
612
613 let core = CoreIndex(0);
614 let pool_para_1 = ParaId::from(1000);
615 let pool_para_2 = ParaId::from(1001);
616 let bulk_para_claimqueue = ParaId::from(2000);
617 let bulk_para_descriptor = ParaId::from(3000);
618 let block_number = 10u32;
619
620 let descriptor = CoreDescriptor::new(
622 Some(QueueDescriptor { first: block_number, last: block_number }),
623 None,
624 );
625 v3::CoreDescriptors::<Test>::insert(core, descriptor);
626
627 let schedule = Schedule::new(
629 vec![(
630 BrokerCoreAssignment::Task(bulk_para_descriptor.into()),
631 PartsOf57600::new_saturating(57600),
632 )],
633 None,
634 None,
635 );
636 v3::CoreSchedules::<Test>::insert((block_number, core), schedule);
637
638 let mut claim_queue = BTreeMap::new();
642 let mut assignments = VecDeque::new();
643 assignments.push_back(v3::Assignment::Pool { para_id: pool_para_1, core_index: core });
644 assignments.push_back(v3::Assignment::Bulk(bulk_para_claimqueue)); assignments.push_back(v3::Assignment::Pool { para_id: pool_para_2, core_index: core });
646 claim_queue.insert(core, assignments);
647
648 v3::ClaimQueue::<Test>::put(claim_queue);
649
650 StorageVersion::new(3).put::<super::Pallet<Test>>();
651
652 let claim_queue_before = super::Pallet::<Test>::claim_queue();
654 assert_eq!(claim_queue_before.len(), 1, "Should have 1 core in claim queue");
655 let core_queue = claim_queue_before.get(&core).expect("Core should be in claim queue");
656 assert_eq!(core_queue.len(), 3, "Core should have 3 assignments");
657 assert_eq!(core_queue[0], pool_para_1);
658 assert_eq!(core_queue[1], bulk_para_claimqueue);
659 assert_eq!(core_queue[2], pool_para_2);
660
661 let state =
663 UncheckedMigrateToV4::<Test>::pre_upgrade().expect("pre_upgrade should succeed");
664 UncheckedMigrateToV4::<Test>::on_runtime_upgrade();
665 UncheckedMigrateToV4::<Test>::post_upgrade(state).expect("post_upgrade should succeed");
666
667 assert!(!v3::ClaimQueue::<Test>::exists());
669
670 let mut on_demand_queue = on_demand::Pallet::<Test>::peek_order_queue();
675 let now = System::block_number().saturating_add(2); let popped: Vec<ParaId> =
677 on_demand_queue.pop_assignment_for_cores::<Test>(now, 2).collect();
678
679 assert_eq!(popped.len(), 2, "Should have 2 pool assignments in on-demand queue");
680 assert!(popped.contains(&pool_para_1), "pool_para_1 should be in queue");
681 assert!(popped.contains(&pool_para_2), "pool_para_2 should be in queue");
682
683 frame_system::Pallet::<Test>::set_block_number(block_number);
687 let peeked = scheduler::assigner_coretime::peek_next_block::<Test>(10);
688 let core_assignments = peeked.get(&core).expect("Core should have assignments");
689 let para_ids: Vec<ParaId> = core_assignments.iter().copied().collect();
690
691 assert!(
693 para_ids.contains(&bulk_para_descriptor),
694 "Should contain bulk para from descriptor"
695 );
696 assert!(
697 !para_ids.contains(&bulk_para_claimqueue),
698 "Should NOT contain bulk para from old ClaimQueue"
699 );
700 });
701 }
702
703 #[test]
704 fn empty_storage_migration_works() {
705 new_test_ext(MockGenesisConfig::default()).execute_with(|| {
706 StorageVersion::new(3).put::<super::Pallet<Test>>();
708
709 assert!(UncheckedMigrateToV4::<Test>::pre_upgrade().is_err());
711
712 let _weight = UncheckedMigrateToV4::<Test>::on_runtime_upgrade();
714
715 let new_descriptors = super::CoreDescriptors::<Test>::get();
717 assert!(new_descriptors.is_empty());
718
719 let state = (0u32, 0u32, 0u32, 0u32).encode();
722 UncheckedMigrateToV4::<Test>::post_upgrade(state)
723 .expect("post_upgrade should succeed with empty state");
724 });
725 }
726
727 #[test]
728 fn parts_of_57600_conversion_works() {
729 new_test_ext(MockGenesisConfig::default()).execute_with(|| {
730 configuration::ActiveConfig::<Test>::mutate(|c| {
732 c.scheduler_params.num_cores = 1;
733 });
734
735 let core = CoreIndex(0);
736 let block_number = 10u32;
737 let para_id = ParaId::from(1000);
738
739 let old_schedule = Schedule::new(
741 vec![
742 (
743 BrokerCoreAssignment::Task(para_id.into()),
744 PartsOf57600::new_saturating(14400), ),
746 (
747 BrokerCoreAssignment::Task(ParaId::from(1001).into()),
748 PartsOf57600::new_saturating(28800), ),
750 (
751 BrokerCoreAssignment::Task(ParaId::from(1002).into()),
752 PartsOf57600::new_saturating(14400), ),
754 ],
755 None,
756 None,
757 );
758
759 let old_descriptor = CoreDescriptor::new(
760 Some(QueueDescriptor { first: block_number, last: block_number }),
761 None,
762 );
763
764 v3::CoreSchedules::<Test>::insert((block_number, core), old_schedule);
765 v3::CoreDescriptors::<Test>::insert(core, old_descriptor);
766
767 StorageVersion::new(3).put::<super::Pallet<Test>>();
768
769 let state =
771 UncheckedMigrateToV4::<Test>::pre_upgrade().expect("pre_upgrade should succeed");
772 UncheckedMigrateToV4::<Test>::on_runtime_upgrade();
773 UncheckedMigrateToV4::<Test>::post_upgrade(state).expect("post_upgrade should succeed");
774
775 let new_schedule = super::CoreSchedules::<Test>::get((block_number, core))
777 .expect("Schedule should be migrated");
778 assert_eq!(new_schedule.assignments().len(), 3);
779
780 let sum: u16 = new_schedule.assignments().iter().map(|(_, parts)| parts.value()).sum();
782 assert_eq!(sum, 57600, "Sum of parts should equal full allocation");
783 });
784 }
785
786 #[test]
787 fn current_work_state_migrated() {
788 new_test_ext(MockGenesisConfig::default()).execute_with(|| {
789 configuration::ActiveConfig::<Test>::mutate(|c| {
791 c.scheduler_params.num_cores = 1;
792 });
793
794 let core = CoreIndex(0);
795 let para_id = ParaId::from(1000);
796
797 let old_descriptor = CoreDescriptor::new(
799 None,
800 Some(WorkState {
801 assignments: vec![(
802 BrokerCoreAssignment::Task(para_id.into()),
803 AssignmentState {
804 ratio: PartsOf57600::new_saturating(57600),
805 remaining: PartsOf57600::new_saturating(28800),
806 },
807 )],
808 end_hint: Some(100u32),
809 pos: 0,
810 step: PartsOf57600::new_saturating(1),
811 }),
812 );
813
814 v3::CoreDescriptors::<Test>::insert(core, old_descriptor);
815
816 StorageVersion::new(3).put::<super::Pallet<Test>>();
817
818 let state =
820 UncheckedMigrateToV4::<Test>::pre_upgrade().expect("pre_upgrade should succeed");
821 UncheckedMigrateToV4::<Test>::on_runtime_upgrade();
822 UncheckedMigrateToV4::<Test>::post_upgrade(state).expect("post_upgrade should succeed");
823
824 let new_descriptors = super::CoreDescriptors::<Test>::get();
826 let new_descriptor = new_descriptors.get(&core).expect("Descriptor should exist");
827 assert!(new_descriptor.current_work().is_some());
828
829 let work = new_descriptor.current_work().unwrap();
830 assert_eq!(work.assignments.len(), 1);
831
832 let (assignment, state) = &work.assignments[0];
834 match assignment {
835 BrokerCoreAssignment::Task(task_id) => {
836 assert_eq!(ParaId::from(*task_id), para_id, "ParaId should match");
837 },
838 _ => panic!("Expected Task assignment"),
839 }
840
841 assert_eq!(state.ratio.value(), 57600, "Ratio should be full allocation");
843 assert_eq!(state.remaining.value(), 28800, "Remaining should be half");
844
845 assert_eq!(work.end_hint, Some(100u32));
847 assert_eq!(work.pos, 0);
848 assert_eq!(work.step.value(), 1, "Step should be 1");
849 });
850 }
851}