vmDataMigration.test.ts 2.7 KB

12345678910111213141516171819202122232425262728293031323334
  1. // @vitest-environment node
  2. import {it,expect,vi} from 'vitest'
  3. import {mkdtemp,mkdir,writeFile,rm} from 'node:fs/promises'
  4. import {tmpdir} from 'node:os'
  5. import {join} from 'node:path'
  6. import {createCipheriv,randomBytes,randomUUID} from 'node:crypto'
  7. import {DatabaseSync} from 'node:sqlite'
  8. import {ContainerComputerProvider} from '../../src/runner/computers/container'
  9. import {preserveManagedVmData} from '../../src/runner/computers/migrateUserData'
  10. it('preserves the quiesced managed runner before old service shutdown, without changing its databases',async()=>{
  11. const home=await mkdtemp(join(tmpdir(),'yaoyao-migration-')),runnerId=randomUUID(),key=randomBytes(32),nonce=randomBytes(12)
  12. const cipher=createCipheriv('aes-256-gcm',key,nonce),payload=Buffer.concat([cipher.update(JSON.stringify({runnerId,token:'private-fixture',computers:{runtime:'docker'}})),cipher.final()])
  13. const sealed=Buffer.concat([nonce,cipher.getAuthTag(),payload]).toString('base64')
  14. const workspace=new DatabaseSync(join(home,'workspace.sqlite3'))
  15. workspace.exec('CREATE TABLE workspace_entities(owner TEXT,kind TEXT,id TEXT,data TEXT)')
  16. workspace.prepare('INSERT INTO workspace_entities VALUES(?,?,?,?)').run('_system','local-vm-managed','local',JSON.stringify({sealed}));workspace.close()
  17. await writeFile(join(home,'workspace-key.bin'),key,{mode:0o600})
  18. const directory=join(home,'runner-state','local-vm',runnerId);await mkdir(directory,{recursive:true})
  19. const db=new DatabaseSync(join(directory,'runner-commands.sqlite3')),spec={id:randomUUID(),ownerKey:'fixture',imageId:'sha256:'+'a'.repeat(64)}
  20. db.exec('CREATE TABLE computer_environments(id TEXT,value TEXT)')
  21. const entry=JSON.stringify({spec,status:'idle'});db.prepare('INSERT INTO computer_environments VALUES(?,?)').run(spec.id,entry)
  22. const spy=vi.spyOn(ContainerComputerProvider.prototype,'preserveUserData').mockResolvedValue(true)
  23. try{
  24. expect(await preserveManagedVmData(home)).toEqual({preserved:1})
  25. expect(spy).toHaveBeenCalledWith(spec)
  26. expect(db.prepare('SELECT value FROM computer_environments').get()?.value).toBe(entry)
  27. spy.mockClear();db.prepare('UPDATE computer_environments SET value=?').run(JSON.stringify({spec,status:'active'}))
  28. await expect(preserveManagedVmData(home)).rejects.toThrow('仍在使用')
  29. expect(spy).not.toHaveBeenCalled()
  30. spy.mockRejectedValue(new Error('disk full'));db.prepare('UPDATE computer_environments SET value=?').run(entry)
  31. await expect(preserveManagedVmData(home)).rejects.toThrow('disk full')
  32. expect(db.prepare('SELECT value FROM computer_environments').get()?.value).toBe(entry)
  33. }finally{spy.mockRestore();db.close();await rm(home,{recursive:true,force:true})}
  34. })