updater.test.ts 3.1 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182
  1. import { lstatSync, mkdtempSync, rmSync } from 'node:fs'
  2. import { tmpdir } from 'node:os'
  3. import { join } from 'node:path'
  4. import { afterEach, describe, expect, it } from 'vitest'
  5. import {
  6. currentTarget,
  7. formatCommandFailure,
  8. serviceInstallInvocation,
  9. switchCurrent,
  10. validateManifest,
  11. } from '../bin/hermes-yaoyao-updater.mjs'
  12. const roots: string[] = []
  13. afterEach(() => {
  14. for (const root of roots.splice(0)) rmSync(root, { recursive: true, force: true })
  15. })
  16. describe('standalone updater primitives', () => {
  17. it('atomically switches the stable current symlink', () => {
  18. const root = mkdtempSync(join(tmpdir(), 'hermes-updater-'))
  19. roots.push(root)
  20. const first = join(root, 'releases', '0.2.0-a')
  21. const second = join(root, 'releases', '0.3.0-b')
  22. switchCurrent(root, first, 'first')
  23. expect(currentTarget(root)).toBe(first)
  24. switchCurrent(root, second, 'second')
  25. expect(currentTarget(root)).toBe(second)
  26. expect(lstatSync(join(root, 'current')).isSymbolicLink()).toBe(true)
  27. })
  28. it('rejects a mismatched release manifest before any service transition', () => {
  29. expect(() => validateManifest({
  30. schemaVersion: 1,
  31. releaseVersion: '0.3.0',
  32. webVersion: '0.3.1',
  33. pluginVersion: '1.8.0',
  34. gitTag: 'v0.3.0',
  35. })).toThrow('版本组合不一致')
  36. })
  37. it('keeps useful command output while redacting credentials', () => {
  38. const error = Object.assign(new Error('Command failed'), {
  39. status: 127,
  40. stdout: '\u001B[31m发布版本校验通过\u001B[0m',
  41. stderr: 'https://secret@example.test/release.git\nsh: vue-tsc: command not found',
  42. })
  43. const message = formatCommandFailure('npm', ['run', 'build'], error)
  44. expect(message).toContain('npm run build 失败,退出码 127')
  45. expect(message).toContain('发布版本校验通过')
  46. expect(message).toContain('https://***@example.test/release.git')
  47. expect(message).toContain('vue-tsc: command not found')
  48. expect(message).not.toContain('secret')
  49. expect(message).not.toContain('\u001B')
  50. })
  51. it('uses the new lifecycle code while starting a rolled-back release', () => {
  52. const releaseRoot = '/Users/test/.local/share/hermes-yaoyao'
  53. const oldRelease = join(releaseRoot, 'releases', '0.2.11-old')
  54. const newRelease = join(releaseRoot, 'releases', '0.2.12-new')
  55. const invocation = serviceInstallInvocation(
  56. oldRelease,
  57. { releaseRoot, source: 'https://example.test/hermes-yaoyao.git' },
  58. true,
  59. newRelease,
  60. )
  61. expect(invocation.cli).toBe(join(newRelease, 'bin', 'hermes-yaoyao.mjs'))
  62. expect(invocation.env.HERMES_YAOYAO_SERVICE_ROOT).toBe(join(releaseRoot, 'current'))
  63. expect(invocation.env.HERMES_YAOYAO_RELEASE_SOURCE).toBe('https://example.test/hermes-yaoyao.git')
  64. const sourceRollback = serviceInstallInvocation(
  65. oldRelease,
  66. { releaseRoot, source: 'https://example.test/hermes-yaoyao.git' },
  67. false,
  68. newRelease,
  69. )
  70. expect(sourceRollback.cli).toBe(join(newRelease, 'bin', 'hermes-yaoyao.mjs'))
  71. expect(sourceRollback.env.HERMES_YAOYAO_SERVICE_ROOT).toBeUndefined()
  72. })
  73. })