mirror of
https://gitee.com/wanwujie/sub2api
synced 2026-04-12 10:54:45 +08:00
feat(sync): full code sync from release
This commit is contained in:
55
frontend/src/utils/__tests__/openaiWsMode.spec.ts
Normal file
55
frontend/src/utils/__tests__/openaiWsMode.spec.ts
Normal file
@@ -0,0 +1,55 @@
|
||||
import { describe, expect, it } from 'vitest'
|
||||
import {
|
||||
OPENAI_WS_MODE_DEDICATED,
|
||||
OPENAI_WS_MODE_OFF,
|
||||
OPENAI_WS_MODE_SHARED,
|
||||
isOpenAIWSModeEnabled,
|
||||
normalizeOpenAIWSMode,
|
||||
openAIWSModeFromEnabled,
|
||||
resolveOpenAIWSModeFromExtra
|
||||
} from '@/utils/openaiWsMode'
|
||||
|
||||
describe('openaiWsMode utils', () => {
|
||||
it('normalizes mode values', () => {
|
||||
expect(normalizeOpenAIWSMode('off')).toBe(OPENAI_WS_MODE_OFF)
|
||||
expect(normalizeOpenAIWSMode(' Shared ')).toBe(OPENAI_WS_MODE_SHARED)
|
||||
expect(normalizeOpenAIWSMode('DEDICATED')).toBe(OPENAI_WS_MODE_DEDICATED)
|
||||
expect(normalizeOpenAIWSMode('invalid')).toBeNull()
|
||||
})
|
||||
|
||||
it('maps legacy enabled flag to mode', () => {
|
||||
expect(openAIWSModeFromEnabled(true)).toBe(OPENAI_WS_MODE_SHARED)
|
||||
expect(openAIWSModeFromEnabled(false)).toBe(OPENAI_WS_MODE_OFF)
|
||||
expect(openAIWSModeFromEnabled('true')).toBeNull()
|
||||
})
|
||||
|
||||
it('resolves by mode key first, then enabled, then fallback enabled keys', () => {
|
||||
const extra = {
|
||||
openai_oauth_responses_websockets_v2_mode: 'dedicated',
|
||||
openai_oauth_responses_websockets_v2_enabled: false,
|
||||
responses_websockets_v2_enabled: false
|
||||
}
|
||||
const mode = resolveOpenAIWSModeFromExtra(extra, {
|
||||
modeKey: 'openai_oauth_responses_websockets_v2_mode',
|
||||
enabledKey: 'openai_oauth_responses_websockets_v2_enabled',
|
||||
fallbackEnabledKeys: ['responses_websockets_v2_enabled', 'openai_ws_enabled']
|
||||
})
|
||||
expect(mode).toBe(OPENAI_WS_MODE_DEDICATED)
|
||||
})
|
||||
|
||||
it('falls back to default when nothing is present', () => {
|
||||
const mode = resolveOpenAIWSModeFromExtra({}, {
|
||||
modeKey: 'openai_apikey_responses_websockets_v2_mode',
|
||||
enabledKey: 'openai_apikey_responses_websockets_v2_enabled',
|
||||
fallbackEnabledKeys: ['responses_websockets_v2_enabled', 'openai_ws_enabled'],
|
||||
defaultMode: OPENAI_WS_MODE_OFF
|
||||
})
|
||||
expect(mode).toBe(OPENAI_WS_MODE_OFF)
|
||||
})
|
||||
|
||||
it('treats off as disabled and shared/dedicated as enabled', () => {
|
||||
expect(isOpenAIWSModeEnabled(OPENAI_WS_MODE_OFF)).toBe(false)
|
||||
expect(isOpenAIWSModeEnabled(OPENAI_WS_MODE_SHARED)).toBe(true)
|
||||
expect(isOpenAIWSModeEnabled(OPENAI_WS_MODE_DEDICATED)).toBe(true)
|
||||
})
|
||||
})
|
||||
90
frontend/src/utils/__tests__/soraTokenParser.spec.ts
Normal file
90
frontend/src/utils/__tests__/soraTokenParser.spec.ts
Normal file
@@ -0,0 +1,90 @@
|
||||
import { describe, expect, it } from 'vitest'
|
||||
import { parseSoraRawTokens } from '@/utils/soraTokenParser'
|
||||
|
||||
describe('parseSoraRawTokens', () => {
|
||||
it('parses sessionToken and accessToken from JSON payload', () => {
|
||||
const payload = JSON.stringify({
|
||||
user: { id: 'u1' },
|
||||
accessToken: 'at-json-1',
|
||||
sessionToken: 'st-json-1'
|
||||
})
|
||||
|
||||
const result = parseSoraRawTokens(payload)
|
||||
|
||||
expect(result.sessionTokens).toEqual(['st-json-1'])
|
||||
expect(result.accessTokens).toEqual(['at-json-1'])
|
||||
})
|
||||
|
||||
it('supports plain session tokens (one per line)', () => {
|
||||
const result = parseSoraRawTokens('st-1\nst-2')
|
||||
|
||||
expect(result.sessionTokens).toEqual(['st-1', 'st-2'])
|
||||
expect(result.accessTokens).toEqual([])
|
||||
})
|
||||
|
||||
it('supports non-standard object snippets via regex', () => {
|
||||
const raw = "sessionToken: 'st-snippet', access_token: \"at-snippet\""
|
||||
const result = parseSoraRawTokens(raw)
|
||||
|
||||
expect(result.sessionTokens).toEqual(['st-snippet'])
|
||||
expect(result.accessTokens).toEqual(['at-snippet'])
|
||||
})
|
||||
|
||||
it('keeps unique tokens and extracts JWT-like plain line as AT too', () => {
|
||||
const jwt = 'eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIxIn0.signature'
|
||||
const raw = `st-dup\nst-dup\n${jwt}\n${JSON.stringify({ sessionToken: 'st-json', accessToken: jwt })}`
|
||||
const result = parseSoraRawTokens(raw)
|
||||
|
||||
expect(result.sessionTokens).toEqual(['st-json', 'st-dup'])
|
||||
expect(result.accessTokens).toEqual([jwt])
|
||||
})
|
||||
|
||||
it('parses session token from Set-Cookie line and strips cookie attributes', () => {
|
||||
const raw =
|
||||
'__Secure-next-auth.session-token.0=st-cookie-part-0; Domain=.chatgpt.com; Path=/; Expires=Thu, 28 May 2026 11:43:36 GMT; HttpOnly; Secure; SameSite=Lax'
|
||||
const result = parseSoraRawTokens(raw)
|
||||
|
||||
expect(result.sessionTokens).toEqual(['st-cookie-part-0'])
|
||||
expect(result.accessTokens).toEqual([])
|
||||
})
|
||||
|
||||
it('merges chunked session-token cookies by numeric suffix order', () => {
|
||||
const raw = [
|
||||
'Set-Cookie: __Secure-next-auth.session-token.1=part-1; Path=/; HttpOnly',
|
||||
'Set-Cookie: __Secure-next-auth.session-token.0=part-0; Path=/; HttpOnly'
|
||||
].join('\n')
|
||||
const result = parseSoraRawTokens(raw)
|
||||
|
||||
expect(result.sessionTokens).toEqual(['part-0part-1'])
|
||||
expect(result.accessTokens).toEqual([])
|
||||
})
|
||||
|
||||
it('prefers latest duplicate chunk values when multiple cookie groups exist', () => {
|
||||
const raw = [
|
||||
'Set-Cookie: __Secure-next-auth.session-token.0=old-0; Path=/; HttpOnly',
|
||||
'Set-Cookie: __Secure-next-auth.session-token.1=old-1; Path=/; HttpOnly',
|
||||
'Set-Cookie: __Secure-next-auth.session-token.0=new-0; Path=/; HttpOnly',
|
||||
'Set-Cookie: __Secure-next-auth.session-token.1=new-1; Path=/; HttpOnly'
|
||||
].join('\n')
|
||||
const result = parseSoraRawTokens(raw)
|
||||
|
||||
expect(result.sessionTokens).toEqual(['new-0new-1'])
|
||||
expect(result.accessTokens).toEqual([])
|
||||
})
|
||||
|
||||
it('uses latest complete chunk group and ignores incomplete latest group', () => {
|
||||
const raw = [
|
||||
'set-cookie',
|
||||
'__Secure-next-auth.session-token.0=ok-0; Domain=.chatgpt.com; Path=/',
|
||||
'set-cookie',
|
||||
'__Secure-next-auth.session-token.1=ok-1; Domain=.chatgpt.com; Path=/',
|
||||
'set-cookie',
|
||||
'__Secure-next-auth.session-token.0=partial-0; Domain=.chatgpt.com; Path=/'
|
||||
].join('\n')
|
||||
|
||||
const result = parseSoraRawTokens(raw)
|
||||
|
||||
expect(result.sessionTokens).toEqual(['ok-0ok-1'])
|
||||
expect(result.accessTokens).toEqual([])
|
||||
})
|
||||
})
|
||||
Reference in New Issue
Block a user