mirror of
https://github.com/NeteaseCloudMusicApiEnhanced/api-enhanced.git
synced 2026-10-20 14:41:23 +08:00
236 lines
7.7 KiB
JavaScript
236 lines
7.7 KiB
JavaScript
const assert = require('assert')
|
||
|
||
describe('util/runtimeState', () => {
|
||
let runtimeState
|
||
|
||
beforeEach(() => {
|
||
// 每个用例从干净状态开始,并清掉可能影响 seed 的环境变量
|
||
delete process.env.NETEASE_ANONYMOUS_TOKEN
|
||
delete process.env.NETEASE_XEAPI_PUBLIC_KEY
|
||
delete require.cache[require.resolve('./runtimeState')]
|
||
runtimeState = require('./runtimeState')
|
||
})
|
||
|
||
afterEach(() => {
|
||
delete process.env.NETEASE_ANONYMOUS_TOKEN
|
||
delete process.env.NETEASE_XEAPI_PUBLIC_KEY
|
||
})
|
||
|
||
describe('anonymous token', () => {
|
||
it('set/get 往返', () => {
|
||
runtimeState.setAnonymousToken('TOKEN_A')
|
||
assert.strictEqual(runtimeState.getAnonymousToken(), 'TOKEN_A')
|
||
})
|
||
|
||
it('set 忽略空值,不覆盖已有 token', () => {
|
||
runtimeState.setAnonymousToken('TOKEN_A')
|
||
runtimeState.setAnonymousToken('')
|
||
runtimeState.setAnonymousToken(null)
|
||
assert.strictEqual(runtimeState.getAnonymousToken(), 'TOKEN_A')
|
||
})
|
||
|
||
it('优先从环境变量读取种子(需显式 loadSeed)', () => {
|
||
process.env.NETEASE_ANONYMOUS_TOKEN = 'ENV_TOKEN'
|
||
assert.strictEqual(runtimeState.getAnonymousToken(), '')
|
||
runtimeState.loadSeed()
|
||
assert.strictEqual(runtimeState.getAnonymousToken(), 'ENV_TOKEN')
|
||
})
|
||
|
||
it('缺失时 ensureAnonymousToken 调用 refresh 并写入', async () => {
|
||
let calls = 0
|
||
const token = await runtimeState.ensureAnonymousToken(async () => {
|
||
calls++
|
||
return 'FRESH_TOKEN'
|
||
})
|
||
assert.strictEqual(token, 'FRESH_TOKEN')
|
||
assert.strictEqual(runtimeState.getAnonymousToken(), 'FRESH_TOKEN')
|
||
assert.strictEqual(calls, 1)
|
||
})
|
||
|
||
it('已有 token 时 ensureAnonymousToken 不再调用 refresh', async () => {
|
||
runtimeState.setAnonymousToken('CACHED')
|
||
let calls = 0
|
||
const token = await runtimeState.ensureAnonymousToken(async () => {
|
||
calls++
|
||
return 'FRESH'
|
||
})
|
||
assert.strictEqual(token, 'CACHED')
|
||
assert.strictEqual(calls, 0)
|
||
})
|
||
|
||
it('并发调用共享同一个刷新任务(去重)', async () => {
|
||
let calls = 0
|
||
const slowRefresh = () =>
|
||
new Promise((resolve) =>
|
||
setTimeout(() => {
|
||
calls++
|
||
resolve('SHARED_TOKEN')
|
||
}, 20),
|
||
)
|
||
|
||
const results = await Promise.all([
|
||
runtimeState.ensureAnonymousToken(slowRefresh),
|
||
runtimeState.ensureAnonymousToken(slowRefresh),
|
||
runtimeState.ensureAnonymousToken(slowRefresh),
|
||
])
|
||
|
||
assert.deepStrictEqual(results, [
|
||
'SHARED_TOKEN',
|
||
'SHARED_TOKEN',
|
||
'SHARED_TOKEN',
|
||
])
|
||
assert.strictEqual(calls, 1)
|
||
})
|
||
|
||
it('refresh 失败后任务被清理,可再次重试', async () => {
|
||
await assert.rejects(
|
||
runtimeState.ensureAnonymousToken(async () => {
|
||
throw new Error('boom')
|
||
}),
|
||
)
|
||
assert.strictEqual(runtimeState.isAnonymousTokenRefreshing(), false)
|
||
|
||
const token = await runtimeState.ensureAnonymousToken(
|
||
async () => 'RETRY_OK',
|
||
)
|
||
assert.strictEqual(token, 'RETRY_OK')
|
||
})
|
||
|
||
it('刷新期间 isAnonymousTokenRefreshing 为 true', async () => {
|
||
let sawRefreshing = false
|
||
await runtimeState.ensureAnonymousToken(async () => {
|
||
sawRefreshing = runtimeState.isAnonymousTokenRefreshing()
|
||
return 'X'
|
||
})
|
||
assert.strictEqual(sawRefreshing, true)
|
||
assert.strictEqual(runtimeState.isAnonymousTokenRefreshing(), false)
|
||
})
|
||
})
|
||
|
||
describe('xeapi public key', () => {
|
||
it('set/get 往返', () => {
|
||
runtimeState.setXeapiPublicKey({ publicKey: 'PK', version: 'v1' })
|
||
assert.deepStrictEqual(runtimeState.getXeapiPublicKey(), {
|
||
publicKey: 'PK',
|
||
version: 'v1',
|
||
})
|
||
})
|
||
|
||
it('优先从环境变量读取 JSON 种子(需显式 loadSeed)', () => {
|
||
process.env.NETEASE_XEAPI_PUBLIC_KEY = JSON.stringify({
|
||
publicKey: 'ENV_PK',
|
||
version: 'env-v',
|
||
})
|
||
assert.strictEqual(runtimeState.getXeapiPublicKey(), null)
|
||
runtimeState.loadSeed()
|
||
assert.deepStrictEqual(runtimeState.getXeapiPublicKey(), {
|
||
publicKey: 'ENV_PK',
|
||
version: 'env-v',
|
||
})
|
||
})
|
||
|
||
it('环境变量中的非法 JSON 被忽略', () => {
|
||
process.env.NETEASE_XEAPI_PUBLIC_KEY = 'not-json'
|
||
runtimeState.loadSeed()
|
||
assert.strictEqual(runtimeState.getXeapiPublicKey(), null)
|
||
})
|
||
|
||
it('mergeXeapiPublicKey 保留旧 sk(服务端未下发时)', () => {
|
||
runtimeState.setXeapiPublicKey({ publicKey: 'PK1', sk: 'SK1' })
|
||
const merged = runtimeState.mergeXeapiPublicKey({ publicKey: 'PK2' })
|
||
assert.strictEqual(merged.sk, 'SK1')
|
||
assert.strictEqual(merged.publicKey, 'PK2')
|
||
})
|
||
|
||
it('mergeXeapiPublicKey 优先使用新 sk', () => {
|
||
runtimeState.setXeapiPublicKey({ publicKey: 'PK1', sk: 'SK1' })
|
||
const merged = runtimeState.mergeXeapiPublicKey({
|
||
publicKey: 'PK2',
|
||
sk: 'SK2',
|
||
})
|
||
assert.strictEqual(merged.sk, 'SK2')
|
||
})
|
||
|
||
it('缺失时 ensureXeapiPublicKey 调用 refresh 并合并写入', async () => {
|
||
let calls = 0
|
||
const result = await runtimeState.ensureXeapiPublicKey(async () => {
|
||
calls++
|
||
return { publicKey: 'FRESH_PK', version: 'v2' }
|
||
})
|
||
assert.strictEqual(calls, 1)
|
||
assert.deepStrictEqual(result, { publicKey: 'FRESH_PK', version: 'v2' })
|
||
assert.deepStrictEqual(runtimeState.getXeapiPublicKey(), {
|
||
publicKey: 'FRESH_PK',
|
||
version: 'v2',
|
||
})
|
||
})
|
||
|
||
it('已有 key 且未 force 时不刷新', async () => {
|
||
runtimeState.setXeapiPublicKey({ publicKey: 'CACHED' })
|
||
let calls = 0
|
||
const result = await runtimeState.ensureXeapiPublicKey(async () => {
|
||
calls++
|
||
return { publicKey: 'FRESH' }
|
||
})
|
||
assert.strictEqual(calls, 0)
|
||
assert.strictEqual(result.publicKey, 'CACHED')
|
||
})
|
||
|
||
it('force 时即使已有 key 也刷新', async () => {
|
||
runtimeState.setXeapiPublicKey({ publicKey: 'CACHED', sk: 'OLD_SK' })
|
||
let calls = 0
|
||
const result = await runtimeState.ensureXeapiPublicKey(
|
||
async () => {
|
||
calls++
|
||
return { publicKey: 'FRESH' }
|
||
},
|
||
{ force: true },
|
||
)
|
||
assert.strictEqual(calls, 1)
|
||
assert.strictEqual(result.publicKey, 'FRESH')
|
||
// 旧 sk 被保留
|
||
assert.strictEqual(result.sk, 'OLD_SK')
|
||
})
|
||
|
||
it('并发 ensureXeapiPublicKey 共享同一刷新任务', async () => {
|
||
let calls = 0
|
||
const slowRefresh = () =>
|
||
new Promise((resolve) =>
|
||
setTimeout(() => {
|
||
calls++
|
||
resolve({ publicKey: 'SHARED_PK' })
|
||
}, 20),
|
||
)
|
||
|
||
const results = await Promise.all([
|
||
runtimeState.ensureXeapiPublicKey(slowRefresh),
|
||
runtimeState.ensureXeapiPublicKey(slowRefresh),
|
||
])
|
||
|
||
assert.strictEqual(calls, 1)
|
||
assert.strictEqual(results[0].publicKey, 'SHARED_PK')
|
||
assert.strictEqual(results[1].publicKey, 'SHARED_PK')
|
||
})
|
||
|
||
it('刷新期间 isXeapiPublicKeyRefreshing 为 true', async () => {
|
||
let sawRefreshing = false
|
||
await runtimeState.ensureXeapiPublicKey(async () => {
|
||
sawRefreshing = runtimeState.isXeapiPublicKeyRefreshing()
|
||
return { publicKey: 'X' }
|
||
})
|
||
assert.strictEqual(sawRefreshing, true)
|
||
assert.strictEqual(runtimeState.isXeapiPublicKeyRefreshing(), false)
|
||
})
|
||
})
|
||
|
||
describe('reset', () => {
|
||
it('清空 token 与 publicKey', () => {
|
||
runtimeState.setAnonymousToken('TOKEN')
|
||
runtimeState.setXeapiPublicKey({ publicKey: 'PK' })
|
||
runtimeState.reset()
|
||
assert.strictEqual(runtimeState.getAnonymousToken(), '')
|
||
assert.strictEqual(runtimeState.getXeapiPublicKey(), null)
|
||
})
|
||
})
|
||
})
|