Files
oci-portal-dash/src/api/instances.test.ts
T
2026-07-30 12:23:16 +08:00

78 lines
2.6 KiB
TypeScript

import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
import { createPinia, setActivePinia } from 'pinia'
import { terminateInstance, terminatingInstances, terminatingKey } from './instances'
/** 可控完成时机的 fetch stub;fail 控制 resolve 后是成功还是 500 */
function stubTerminateFetch(fail = false) {
const calls = { count: 0 }
let release!: () => void
const gate = new Promise<void>((r) => (release = r))
vi.stubGlobal('fetch', async () => {
calls.count++
await gate
if (fail) {
return {
ok: false,
status: 500,
url: '/api/v1/x',
json: async () => ({ error: '终止失败' }),
} as unknown as Response
}
return { ok: true, status: 200, text: async () => '' } as unknown as Response
})
return { calls, release }
}
beforeEach(() => {
localStorage.clear()
setActivePinia(createPinia())
terminatingInstances.clear()
})
afterEach(() => {
vi.unstubAllGlobals()
})
describe('共享终止锁', () => {
it('请求飞行中 key 在集合内,完成后移除', async () => {
const { release } = stubTerminateFetch()
const p = terminateInstance(3, 'ocid1.instance..x', false)
expect(terminatingInstances.has(terminatingKey(3, 'ocid1.instance..x'))).toBe(true)
release()
await p
expect(terminatingInstances.size).toBe(0)
})
it('请求失败同样释放锁,不留脏状态', async () => {
const { release } = stubTerminateFetch(true)
const p = terminateInstance(3, 'ocid1.instance..x', true)
expect(terminatingInstances.size).toBe(1)
release()
await expect(p).rejects.toMatchObject({ message: '终止失败' })
expect(terminatingInstances.size).toBe(0)
})
it('同一物理实例跨配置共享 key,后到调用被拒', async () => {
const { calls, release } = stubTerminateFetch()
const instanceId = 'ocid1.instance.oc1..same'
const first = terminateInstance(1, instanceId, false)
await expect(terminateInstance(2, instanceId, true)).rejects.toMatchObject({ status: 409 })
expect(terminatingKey(1, instanceId)).toBe(terminatingKey(2, instanceId))
expect(calls.count).toBe(1)
release()
await first
})
it('不同实例互不影响', async () => {
const { release } = stubTerminateFetch()
const pa = terminateInstance(1, 'a', false)
const pb = terminateInstance(2, 'b', false)
expect(terminatingInstances.has(terminatingKey(1, 'a'))).toBe(true)
expect(terminatingInstances.has(terminatingKey(2, 'b'))).toBe(true)
expect(terminatingKey(1, 'a')).not.toBe(terminatingKey(2, 'b'))
release()
await Promise.all([pa, pb])
expect(terminatingInstances.size).toBe(0)
})
})