File size: 3,533 Bytes
00a912e
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
import assert from 'node:assert/strict'
import { mkdtempSync, rmSync } from 'node:fs'
import { tmpdir } from 'node:os'
import path from 'node:path'
import test, { after } from 'node:test'

const testDataDir = mkdtempSync(path.join(tmpdir(), 'music-together-tencent-refresh-'))
process.env.DATABASE_URL = `file:${path.join(testDataDir, 'test.db')}`
process.env.LOG_LEVEL = 'silent'

const { db } = await import('../src/repositories/database.js')
const { platformAuthRepo } = await import('../src/repositories/platformAuthRepository.js')
const authService = await import('../src/services/authService.js')
const tencentAuth = await import('../src/services/tencentAuthService.js')
const { refreshDueTencentCredentials, TENCENT_CREDENTIAL_REFRESH_INTERVAL_MS } =
  await import('../src/services/tencentCredentialRefreshService.js')

const now = 1_800_000_000_000
for (const userId of ['qq-refresh-success', 'qq-refresh-failure']) {
  db.prepare('INSERT INTO users (id, nickname, created_at, updated_at, last_seen_at) VALUES (?, ?, ?, ?, ?)').run(
    userId,
    userId,
    now,
    now,
    now,
  )
}

function credential(musicid: number, musickey: string) {
  return tencentAuth.buildTencentCredentialCookie('', {
    musicid,
    musickey,
    refresh_token: `refresh-token-${musicid}`,
    refresh_key: `refresh-key-${musicid}`,
    loginType: 2,
  })
}

const successCookie = credential(101, 'old-success-key')
const failureCookie = credential(202, 'old-failure-key')
for (const [userId, cookie] of [
  ['qq-refresh-success', successCookie],
  ['qq-refresh-failure', failureCookie],
] as const) {
  platformAuthRepo.save({
    userId,
    platform: 'tencent',
    cookie,
    nickname: userId,
    vipType: 1,
    credentialRefreshAttemptedAt: now - TENCENT_CREDENTIAL_REFRESH_INTERVAL_MS,
  })
  authService.restoreUserCookies('refresh-room', userId)
}

after(() => {
  authService.cleanupRoom('refresh-room')
  db.close()
  rmSync(testDataDir, { recursive: true, force: true })
})

test('refreshes due credentials every 24 hours and updates active room copies', async () => {
  const summary = await refreshDueTencentCredentials(now, async (cookie) => {
    if (cookie === failureCookie) throw new Error('provider unavailable')
    return tencentAuth.buildTencentCredentialCookie(cookie, {
      musicid: 101,
      musickey: 'new-success-key',
      refresh_token: 'rotated-refresh-token',
      refresh_key: 'rotated-refresh-key',
      loginType: 2,
    })
  })

  assert.deepEqual(summary, { checked: 2, refreshed: 1, failed: 1, skipped: 0 })
  const persistedSuccess = platformAuthRepo.loadUser('qq-refresh-success')[0]
  const persistedFailure = platformAuthRepo.loadUser('qq-refresh-failure')[0]
  assert.equal(tencentAuth.parseTencentCredential(persistedSuccess.cookie)?.musickey, 'new-success-key')
  assert.equal(tencentAuth.parseTencentCredential(persistedSuccess.cookie)?.refreshToken, 'rotated-refresh-token')
  assert.equal(persistedSuccess.credentialRefreshAttemptedAt, now)
  assert.equal(persistedFailure.cookie, failureCookie)
  assert.equal(persistedFailure.credentialRefreshAttemptedAt, now)
  assert.equal(
    tencentAuth.parseTencentCredential(authService.getUserCookie('qq-refresh-success', 'tencent', 'refresh-room')!)
      ?.musickey,
    'new-success-key',
  )
  assert.equal(authService.getUserCookie('qq-refresh-failure', 'tencent', 'refresh-room'), failureCookie)

  assert.equal(platformAuthRepo.loadDueTencent(now - 1).length, 0)
  assert.equal(platformAuthRepo.loadDueTencent(now).length, 2)
})