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((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) }) })