Files
lingxi-ai/tests/server/xai-auth-controller.test.ts
T

124 lines
4.1 KiB
TypeScript
Raw Normal View History

2026-06-05 11:29:11 +08:00
import { afterEach, beforeEach, describe, expect, it } from 'vitest'
import { existsSync, mkdirSync, mkdtempSync, readFileSync, rmSync, writeFileSync } from 'fs'
import { dirname, join } from 'path'
import { tmpdir } from 'os'
import YAML from 'js-yaml'
import { applyXaiOAuthDefaultModel, saveXaiOAuthTokensForProfile, status } from '../../packages/server/src/controllers/hermes/xai-auth'
let hermesHome = ''
function writeFile(relativePath: string, content: string) {
const target = join(hermesHome, relativePath)
mkdirSync(dirname(target), { recursive: true })
writeFileSync(target, content)
}
function readYaml(relativePath: string) {
return YAML.load(readFileSync(join(hermesHome, relativePath), 'utf-8')) as any
}
function readJson(relativePath: string) {
return JSON.parse(readFileSync(join(hermesHome, relativePath), 'utf-8'))
}
function makeCtx(profile: string): any {
return {
state: { profile: { name: profile } },
query: {},
request: { body: {} },
get: () => '',
status: 200,
body: undefined as unknown,
}
}
describe('xAI auth controller', () => {
beforeEach(() => {
hermesHome = mkdtempSync(join(tmpdir(), 'hwui-xai-auth-'))
process.env.HERMES_HOME = hermesHome
})
afterEach(() => {
delete process.env.HERMES_HOME
if (hermesHome) rmSync(hermesHome, { recursive: true, force: true })
hermesHome = ''
})
it('does not keep a non-xAI model when switching the default provider to xai-oauth', () => {
const config = applyXaiOAuthDefaultModel({
model: {
default: 'glm-5-turbo',
provider: 'custom:glm-coding-plan',
base_url: 'https://api.z.ai/api/anthropic',
api_key: 'secret',
},
})
expect(config.model).toEqual({
default: 'grok-4.3',
provider: 'xai-oauth',
})
})
it('preserves an existing Grok model when refreshing xai-oauth credentials', () => {
const config = applyXaiOAuthDefaultModel({
model: {
default: 'grok-4.20-reasoning',
provider: 'xai-oauth',
},
})
expect(config.model).toEqual({
default: 'grok-4.20-reasoning',
provider: 'xai-oauth',
})
})
it('persists OAuth credentials and default model in the request-scoped profile only', async () => {
mkdirSync(join(hermesHome, 'profiles', 'research'), { recursive: true })
writeFile('config.yaml', 'model:\n provider: deepseek\n default: deepseek-chat\n')
writeFile('profiles/research/config.yaml', 'model:\n provider: openrouter\n default: openrouter-model\n')
await saveXaiOAuthTokensForProfile(
'research',
{
discovery: { token_endpoint: 'https://auth.x.ai/oauth/token' },
redirectUri: 'http://127.0.0.1:56121/callback',
},
{
access_token: 'research-access-token',
refresh_token: 'research-refresh-token',
expires_in: 3600,
token_type: 'Bearer',
},
)
expect(existsSync(join(hermesHome, 'auth.json'))).toBe(false)
const auth = readJson('profiles/research/auth.json')
expect(auth.providers['xai-oauth'].tokens.access_token).toBe('research-access-token')
expect(auth.credential_pool['xai-oauth'][0].refresh_token).toBe('research-refresh-token')
expect(readYaml('config.yaml').model).toEqual({ provider: 'deepseek', default: 'deepseek-chat' })
expect(readYaml('profiles/research/config.yaml').model).toEqual({ provider: 'xai-oauth', default: 'grok-4.3' })
})
it('checks xAI OAuth status against the request-scoped profile', async () => {
mkdirSync(join(hermesHome, 'profiles', 'research'), { recursive: true })
writeFile('auth.json', JSON.stringify({ version: 1, providers: {}, credential_pool: {} }, null, 2))
writeFile('profiles/research/auth.json', JSON.stringify({
version: 1,
providers: {
'xai-oauth': {
last_refresh: '2026-06-02T00:00:00.000Z',
tokens: { access_token: 'research-access-token' },
},
},
}, null, 2))
const ctx = makeCtx('research')
await status(ctx)
expect(ctx.body).toEqual({ authenticated: true, last_refresh: '2026-06-02T00:00:00.000Z' })
})
})