133 lines
4.3 KiB
TypeScript
133 lines
4.3 KiB
TypeScript
import assert from 'node:assert/strict'
|
|
|
|
import { test } from 'vitest'
|
|
|
|
import {
|
|
prepareProfileRenameLifecycle,
|
|
profileRenameFromRequest,
|
|
type ProfileRenameLifecycleDeps
|
|
} from './profile-rename-routing'
|
|
|
|
const renameRequest = {
|
|
body: { new_name: 'renamed-profile' },
|
|
method: 'PATCH',
|
|
path: '/api/profiles/primary-profile'
|
|
}
|
|
|
|
function lifecycleDeps(events: string[]): ProfileRenameLifecycleDeps {
|
|
return {
|
|
isValidProfileName: profile => /^[a-z0-9][a-z0-9_-]{0,63}$/.test(profile),
|
|
primaryProfileKey: () => 'primary-profile',
|
|
reloadPrimaryWindow: () => events.push('reload-primary-window'),
|
|
restartPrimaryBackend: async () => {
|
|
events.push('restart-primary-backend')
|
|
},
|
|
teardownPoolBackendAndWait: async profile => {
|
|
events.push(`teardown-pool:${profile}`)
|
|
},
|
|
teardownPrimaryBackendAndWait: async () => {
|
|
events.push('teardown-primary')
|
|
},
|
|
writeActiveDesktopProfile: profile => {
|
|
events.push(`write-active:${profile}`)
|
|
}
|
|
}
|
|
}
|
|
|
|
test('profileRenameFromRequest parses string and object JSON bodies', () => {
|
|
assert.deepEqual(profileRenameFromRequest(renameRequest), {
|
|
newName: 'renamed-profile',
|
|
oldName: 'primary-profile'
|
|
})
|
|
assert.deepEqual(profileRenameFromRequest({ ...renameRequest, body: JSON.stringify({ new_name: 'String-Body' }) }), {
|
|
newName: 'string-body',
|
|
oldName: 'primary-profile'
|
|
})
|
|
})
|
|
|
|
test('profileRenameFromRequest rejects malformed and reserved rename requests', () => {
|
|
assert.equal(profileRenameFromRequest({ ...renameRequest, method: 'DELETE' }), null)
|
|
assert.equal(profileRenameFromRequest({ ...renameRequest, body: '{' }), null)
|
|
assert.equal(profileRenameFromRequest({ ...renameRequest, body: { new_name: 'default' } }), null)
|
|
assert.equal(profileRenameFromRequest({ ...renameRequest, path: '/api/profiles/default' }), null)
|
|
})
|
|
|
|
test('prepareProfileRenameLifecycle tears down a pooled backend and routes through the primary', async () => {
|
|
const events: string[] = []
|
|
|
|
const lifecycle = await prepareProfileRenameLifecycle(
|
|
{ ...renameRequest, path: '/api/profiles/worker-profile' },
|
|
lifecycleDeps(events)
|
|
)
|
|
|
|
assert.equal(lifecycle?.kind, 'pool')
|
|
assert.equal(lifecycle?.routeProfile, null)
|
|
assert.deepEqual(events, ['teardown-pool:worker-profile'])
|
|
|
|
await lifecycle?.complete()
|
|
await lifecycle?.rollback()
|
|
assert.deepEqual(events, ['teardown-pool:worker-profile'])
|
|
})
|
|
|
|
test('prepareProfileRenameLifecycle re-homes a renamed primary after success', async () => {
|
|
const events: string[] = []
|
|
const lifecycle = await prepareProfileRenameLifecycle(renameRequest, lifecycleDeps(events))
|
|
|
|
assert.equal(lifecycle?.kind, 'primary')
|
|
assert.equal(lifecycle?.routeProfile, null)
|
|
assert.deepEqual(events, ['write-active:default', 'teardown-primary'])
|
|
|
|
await lifecycle?.complete()
|
|
assert.deepEqual(events, [
|
|
'write-active:default',
|
|
'teardown-primary',
|
|
'write-active:renamed-profile',
|
|
'teardown-primary',
|
|
'reload-primary-window'
|
|
])
|
|
})
|
|
|
|
test('prepareProfileRenameLifecycle restores the original primary after failure', async () => {
|
|
const events: string[] = []
|
|
const lifecycle = await prepareProfileRenameLifecycle(renameRequest, lifecycleDeps(events))
|
|
|
|
await lifecycle?.rollback()
|
|
assert.deepEqual(events, [
|
|
'write-active:default',
|
|
'teardown-primary',
|
|
'write-active:primary-profile',
|
|
'teardown-primary',
|
|
'restart-primary-backend'
|
|
])
|
|
})
|
|
|
|
test('prepareProfileRenameLifecycle restores the original primary when initial teardown fails', async () => {
|
|
const events: string[] = []
|
|
const deps = lifecycleDeps(events)
|
|
|
|
deps.teardownPrimaryBackendAndWait = async () => {
|
|
events.push('teardown-primary')
|
|
throw new Error('teardown failed')
|
|
}
|
|
|
|
await assert.rejects(prepareProfileRenameLifecycle(renameRequest, deps), /teardown failed/)
|
|
assert.deepEqual(events, [
|
|
'write-active:default',
|
|
'teardown-primary',
|
|
'write-active:primary-profile',
|
|
'restart-primary-backend'
|
|
])
|
|
})
|
|
|
|
test('prepareProfileRenameLifecycle ignores invalid profile names without side effects', async () => {
|
|
const events: string[] = []
|
|
|
|
const lifecycle = await prepareProfileRenameLifecycle(
|
|
{ ...renameRequest, body: { new_name: 'Not Valid!' } },
|
|
lifecycleDeps(events)
|
|
)
|
|
|
|
assert.equal(lifecycle, null)
|
|
assert.deepEqual(events, [])
|
|
})
|