Nelze vybrat více než 25 témat Téma musí začínat písmenem nebo číslem, může obsahovat pomlčky („-“) a může být dlouhé až 35 znaků.
 
 
 
 

81 řádky
4.9 KiB

  1. import assert from 'node:assert/strict'
  2. import test from 'node:test'
  3. import { readFileSync } from 'node:fs'
  4. import { createRequire } from 'node:module'
  5. const requireWeb = createRequire(new URL('../../unreal_tran_web/package.json', import.meta.url))
  6. const ts = requireWeb('typescript')
  7. const source = new URL('../../unreal_tran_web/src/features/', import.meta.url)
  8. const compile = path => ts.transpileModule(readFileSync(new URL(path, source), 'utf8'), {
  9. compilerOptions: { target: ts.ScriptTarget.ES2022, module: ts.ModuleKind.ESNext },
  10. }).outputText
  11. const url = text => `data:text/javascript;base64,${Buffer.from(text).toString('base64')}`
  12. const documentUrl = url(compile('training/trainingDocument.ts'))
  13. const bridgeUrl = url(compile('training-legacy/bridge.ts').replace(/from ['"]\.\.\/training\/trainingDocument['"]/, `from '${documentUrl}'`))
  14. const { trainingProjectMetadata } = await import(url(compile('training-legacy/trainingProjectMetadata.ts')))
  15. const { toFormalContent, toLegacyProject } = await import(bridgeUrl)
  16. const { saveTrainingWithCover } = await import(url(compile('training-legacy/trainingCoverPersistence.ts')))
  17. const current = () => ({ id: '161', type: 'TRAINING', name: 'Original name', description: 'Project purpose from creation',
  18. categoryCode: 'RESOURCE-TEST', trainingMode: 'VIRTUAL', content: {}, assets: [], coverUri: '', version: 3 })
  19. test('edited project name and task settings survive actual formal/legacy save roundtrip', async () => {
  20. const original = current()
  21. const legacy = { name: ' Fox inspection training ', trainingMode: 'virtual',
  22. task: { description: 'Inspect the visible fox', category: 'Task subject', duration: 12, level: 'L2' },
  23. runtime: { startMode: 'exam' }, steps: [] }
  24. const metadata = trainingProjectMetadata(legacy, original)
  25. const { content } = toFormalContent(legacy, metadata.trainingMode, original.content)
  26. let received
  27. const result = await saveTrainingWithCover(original, { ...original, ...metadata, content, dependencies: [] },
  28. async () => { throw new Error('No viewport in metadata unit test') }, {
  29. upload: async () => { throw new Error('unexpected upload') },
  30. update: async body => { received = structuredClone(body); return { ...body, version: 4 } },
  31. }, () => {})
  32. const reopened = toLegacyProject(result.project)
  33. assert.equal(received.name, 'Fox inspection training')
  34. assert.equal(reopened.name, 'Fox inspection training')
  35. assert.equal(received.description, original.description, 'project purpose is independent of task instructions')
  36. assert.equal(received.categoryCode, original.categoryCode, 'task category must not replace the project category')
  37. assert.equal(reopened.task.description, 'Inspect the visible fox')
  38. assert.equal(reopened.task.category, 'Task subject')
  39. assert.equal(reopened.runtime.startMode, 'exam', 'demo/practice/exam is runtime mode, not training channel')
  40. assert.equal(reopened.task.duration, 12)
  41. assert.equal(received.content.name, undefined, 'project name belongs only to the envelope')
  42. assert.equal(original.name, 'Original name')
  43. })
  44. test('supported document training modes agree across envelope, normalized content and reopened editor', () => {
  45. for (const mode of ['virtual', 'physical', 'confrontation']) {
  46. const previous = current()
  47. const legacy = { name: 'Edited', trainingMode: mode, steps: [], task: {} }
  48. const metadata = trainingProjectMetadata(legacy, previous)
  49. const { content } = toFormalContent(legacy, metadata.trainingMode, previous.content)
  50. const reopened = toLegacyProject({ ...previous, ...metadata, content })
  51. assert.equal(metadata.trainingMode, mode.toUpperCase())
  52. assert.equal(content.trainingMode, metadata.trainingMode)
  53. assert.ok(content.enabledChannels.includes(metadata.trainingMode))
  54. assert.equal(reopened.trainingMode, mode)
  55. }
  56. })
  57. test('missing envelope fields retain existing values while invalid supplied mode fails explicitly', () => {
  58. const previous = current()
  59. assert.deepEqual(trainingProjectMetadata({ task: { description: 'Default task prose' } }, previous), {
  60. name: previous.name, description: previous.description, categoryCode: previous.categoryCode, trainingMode: previous.trainingMode,
  61. })
  62. assert.throws(() => trainingProjectMetadata({ trainingMode: 'exam' }, previous), /训练形态无效/)
  63. })
  64. test('server mode rejection propagates without treating metadata as successfully saved', async () => {
  65. const previous = current()
  66. const metadata = trainingProjectMetadata({ name: 'Changed', trainingMode: 'physical' }, previous)
  67. const denied = new Error('模式修改不允许')
  68. await assert.rejects(saveTrainingWithCover(previous, { ...previous, ...metadata },
  69. async () => { throw new Error('No viewport') }, {
  70. upload: async () => { throw new Error('unexpected upload') },
  71. update: async () => { throw denied },
  72. }, () => {}), error => error === denied)
  73. assert.equal(previous.name, 'Original name')
  74. assert.equal(previous.trainingMode, 'VIRTUAL')
  75. })