dashboardSupervisor.test.ts 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157
  1. import { describe, expect, it } from 'vitest'
  2. import {
  3. DashboardSupervisor,
  4. DEFAULT_DASHBOARD_USERNAME,
  5. } from '../../src/server/dashboardSupervisor.js'
  6. function supervisor(overrides: {
  7. values?: Record<string, string>
  8. running?: boolean
  9. runningHost?: '127.0.0.1' | '0.0.0.0'
  10. allowLan?: boolean
  11. resolveLanProbeHost?: () => string | undefined
  12. } = {}) {
  13. const values = { ...overrides.values }
  14. let running = overrides.running ?? false
  15. let runningHost = overrides.runningHost ?? '127.0.0.1'
  16. const lanProbeHost = '192.168.1.20'
  17. const calls: string[][] = []
  18. const launches: string[][] = []
  19. const instance = new DashboardSupervisor({
  20. allowLan: overrides.allowLan ?? false,
  21. lanProbeHost: overrides.resolveLanProbeHost ? undefined : lanProbeHost,
  22. resolveLanProbeHost: overrides.resolveLanProbeHost,
  23. run(args) {
  24. calls.push([...args])
  25. if (args[0] === 'config' && args[1] === 'get') return values[args[2]] ?? ''
  26. if (args[0] === 'config' && args[1] === 'set') {
  27. values[args[2]] = args[3]
  28. return ''
  29. }
  30. if (args[0] === 'dashboard' && args[1] === '--stop') {
  31. running = false
  32. return ''
  33. }
  34. return ''
  35. },
  36. launch(args) {
  37. launches.push([...args])
  38. running = true
  39. runningHost = args[2] as typeof runningHost
  40. },
  41. probe: async (host) => running && (
  42. host === '127.0.0.1'
  43. || (host === lanProbeHost && runningHost === '0.0.0.0')
  44. ),
  45. log: () => undefined,
  46. })
  47. return { instance, calls, launches, values }
  48. }
  49. describe('DashboardSupervisor', () => {
  50. it('configures the requested default credentials and starts an unavailable dashboard', async () => {
  51. const value = supervisor()
  52. await value.instance.checkNow()
  53. expect(value.values['dashboard.basic_auth.username']).toBe(DEFAULT_DASHBOARD_USERNAME)
  54. expect(value.values['dashboard.basic_auth.password_hash']).toMatch(/^scrypt\$16384\$8\$1\$/)
  55. expect(value.values['dashboard.basic_auth.secret']).toMatch(/.{32,}/)
  56. expect(value.launches).toEqual([['dashboard', '--host', '127.0.0.1', '--no-open']])
  57. })
  58. it('does not overwrite configured dashboard credentials', async () => {
  59. const value = supervisor({
  60. running: true,
  61. values: {
  62. 'dashboard.basic_auth.username': 'operator',
  63. 'dashboard.basic_auth.password_hash': 'scrypt$existing',
  64. 'dashboard.basic_auth.secret': 'configured-secret',
  65. },
  66. })
  67. await value.instance.checkNow()
  68. expect(value.calls.filter((args) => args[1] === 'set')).toEqual([])
  69. expect(value.launches).toEqual([])
  70. })
  71. it('restarts a running dashboard after filling missing authentication and uses LAN binding when enabled', async () => {
  72. const value = supervisor({ running: true, allowLan: true })
  73. await value.instance.checkNow()
  74. expect(value.calls).toContainEqual(['dashboard', '--stop'])
  75. expect(value.launches).toEqual([['dashboard', '--host', '0.0.0.0', '--no-open']])
  76. })
  77. it('moves an authenticated loopback dashboard onto the LAN when managed installation requires it', async () => {
  78. const value = supervisor({
  79. running: true,
  80. runningHost: '127.0.0.1',
  81. allowLan: true,
  82. values: {
  83. 'dashboard.basic_auth.username': 'operator',
  84. 'dashboard.basic_auth.password_hash': 'scrypt$existing',
  85. 'dashboard.basic_auth.secret': 'configured-secret',
  86. },
  87. })
  88. await value.instance.checkNow()
  89. expect(value.calls).toContainEqual(['dashboard', '--stop'])
  90. expect(value.launches).toEqual([['dashboard', '--host', '0.0.0.0', '--no-open']])
  91. })
  92. it('moves an authenticated LAN dashboard back to loopback when unmanaged LAN is requested', async () => {
  93. const value = supervisor({
  94. running: true,
  95. runningHost: '0.0.0.0',
  96. allowLan: false,
  97. values: {
  98. 'dashboard.basic_auth.username': 'operator',
  99. 'dashboard.basic_auth.password_hash': 'scrypt$existing',
  100. 'dashboard.basic_auth.secret': 'configured-secret',
  101. },
  102. })
  103. await value.instance.checkNow()
  104. expect(value.calls).toContainEqual(['dashboard', '--stop'])
  105. expect(value.launches).toEqual([['dashboard', '--host', '127.0.0.1', '--no-open']])
  106. })
  107. it('rechecks LAN binding after the network interface becomes available', async () => {
  108. let networkReady = false
  109. const value = supervisor({
  110. running: true,
  111. runningHost: '127.0.0.1',
  112. allowLan: true,
  113. resolveLanProbeHost: () => networkReady ? '192.168.1.20' : undefined,
  114. values: {
  115. 'dashboard.basic_auth.username': 'operator',
  116. 'dashboard.basic_auth.password_hash': 'scrypt$existing',
  117. 'dashboard.basic_auth.secret': 'configured-secret',
  118. },
  119. })
  120. await value.instance.checkNow()
  121. expect(value.launches).toEqual([])
  122. networkReady = true
  123. await value.instance.checkNow()
  124. expect(value.calls).toContainEqual(['dashboard', '--stop'])
  125. expect(value.launches).toEqual([['dashboard', '--host', '0.0.0.0', '--no-open']])
  126. })
  127. it('explicitly restarts a healthy dashboard and waits for 9119 to return', async () => {
  128. const value = supervisor({
  129. running: true,
  130. values: {
  131. 'dashboard.basic_auth.username': 'operator',
  132. 'dashboard.basic_auth.password_hash': 'scrypt$existing',
  133. 'dashboard.basic_auth.secret': 'configured-secret',
  134. },
  135. })
  136. await value.instance.restart()
  137. expect(value.calls).toContainEqual(['dashboard', '--stop'])
  138. expect(value.launches).toEqual([['dashboard', '--host', '127.0.0.1', '--no-open']])
  139. })
  140. })