mirror of
https://github.com/whit3rabbit/anyllm-proxy.git
synced 2026-09-22 08:00:51 +00:00
135 lines
4.4 KiB
JavaScript
135 lines
4.4 KiB
JavaScript
import assert from 'node:assert/strict'
|
|
import { mkdtemp, readFile, writeFile } from 'node:fs/promises'
|
|
import { tmpdir } from 'node:os'
|
|
import { basename, join } from 'node:path'
|
|
import { pathToFileURL } from 'node:url'
|
|
import test from 'node:test'
|
|
import { transformWithOxc } from 'vite'
|
|
|
|
async function importTs(sourcePath) {
|
|
const source = await readFile(new URL(sourcePath, import.meta.url), 'utf8')
|
|
const result = await transformWithOxc(source, sourcePath, { lang: 'ts' })
|
|
const dir = await mkdtemp(join(tmpdir(), 'anyllm-admin-ui-test-'))
|
|
const outputPath = join(dir, basename(sourcePath).replace(/\.ts$/, '.mjs'))
|
|
await writeFile(outputPath, result.code)
|
|
return import(pathToFileURL(outputPath).href)
|
|
}
|
|
|
|
function jsonResponse(body, init = {}) {
|
|
return {
|
|
ok: init.ok ?? true,
|
|
status: init.status ?? 200,
|
|
headers: {
|
|
get(name) {
|
|
return name.toLowerCase() === 'content-length' ? (init.contentLength ?? null) : null
|
|
},
|
|
},
|
|
async json() {
|
|
return body
|
|
},
|
|
async text() {
|
|
return typeof body === 'string' ? body : JSON.stringify(body)
|
|
},
|
|
}
|
|
}
|
|
|
|
async function waitFor(predicate) {
|
|
for (let i = 0; i < 20; i += 1) {
|
|
if (predicate()) return
|
|
await new Promise((resolve) => setTimeout(resolve, 0))
|
|
}
|
|
assert.ok(predicate(), 'condition was not met')
|
|
}
|
|
|
|
test('key creation payload uses enforced max_budget_usd instead of legacy spend_limit', async () => {
|
|
const { buildCreateKeyPayload } = await importTs('./src/tabs/keys/keyPayload.ts')
|
|
|
|
const payload = buildCreateKeyPayload({
|
|
description: '',
|
|
spendLimit: '12.50',
|
|
rpmLimit: '',
|
|
})
|
|
|
|
assert.deepEqual(payload, {
|
|
description: null,
|
|
max_budget_usd: 12.5,
|
|
rpm_limit: null,
|
|
})
|
|
assert.equal('spend_limit' in payload, false)
|
|
})
|
|
|
|
test('csrf mutations run FIFO and fetch a fresh token inside each queued mutation', async () => {
|
|
const { createMutationQueue, runCsrfMutation } = await importTs('./src/api/csrf.ts')
|
|
const queueMutation = createMutationQueue()
|
|
const calls = []
|
|
const releaseMutations = []
|
|
let csrfCount = 0
|
|
|
|
const fetchImpl = async (path, init = {}) => {
|
|
const headers = init.headers ?? {}
|
|
if (path === '/admin/csrf-token') {
|
|
csrfCount += 1
|
|
calls.push({ kind: 'csrf', token: `csrf-${csrfCount}` })
|
|
return jsonResponse({ csrf_token: `csrf-${csrfCount}` })
|
|
}
|
|
calls.push({ kind: 'mutation', path, token: headers['X-CSRF-Token'] })
|
|
await new Promise((resolve) => releaseMutations.push(resolve))
|
|
return jsonResponse({ path })
|
|
}
|
|
|
|
const deps = {
|
|
fetchImpl,
|
|
getToken: () => 'admin-token',
|
|
handleAuthAndErrors: async () => {},
|
|
}
|
|
const first = queueMutation(() => runCsrfMutation('POST', '/admin/api/one', undefined, 'application/json', deps))
|
|
const second = queueMutation(() => runCsrfMutation('POST', '/admin/api/two', undefined, 'application/json', deps))
|
|
|
|
await waitFor(() => calls.length === 2)
|
|
assert.deepEqual(calls, [
|
|
{ kind: 'csrf', token: 'csrf-1' },
|
|
{ kind: 'mutation', path: '/admin/api/one', token: 'csrf-1' },
|
|
])
|
|
|
|
releaseMutations.shift()()
|
|
assert.deepEqual(await first, { path: '/admin/api/one' })
|
|
await waitFor(() => calls.length === 4)
|
|
assert.deepEqual(calls, [
|
|
{ kind: 'csrf', token: 'csrf-1' },
|
|
{ kind: 'mutation', path: '/admin/api/one', token: 'csrf-1' },
|
|
{ kind: 'csrf', token: 'csrf-2' },
|
|
{ kind: 'mutation', path: '/admin/api/two', token: 'csrf-2' },
|
|
])
|
|
|
|
releaseMutations.shift()()
|
|
assert.deepEqual(await second, { path: '/admin/api/two' })
|
|
})
|
|
|
|
test('csrf mutation retries one 403 with a new token', async () => {
|
|
const { runCsrfMutation } = await importTs('./src/api/csrf.ts')
|
|
const mutationTokens = []
|
|
let csrfCount = 0
|
|
let mutationCount = 0
|
|
|
|
const fetchImpl = async (path, init = {}) => {
|
|
const headers = init.headers ?? {}
|
|
if (path === '/admin/csrf-token') {
|
|
csrfCount += 1
|
|
return jsonResponse({ csrf_token: `csrf-${csrfCount}` })
|
|
}
|
|
mutationCount += 1
|
|
mutationTokens.push(headers['X-CSRF-Token'])
|
|
if (mutationCount === 1) return jsonResponse('stale csrf', { ok: false, status: 403 })
|
|
return jsonResponse({ ok: true })
|
|
}
|
|
|
|
const result = await runCsrfMutation('PUT', '/admin/api/config', '{"a":1}', 'application/json', {
|
|
fetchImpl,
|
|
getToken: () => 'admin-token',
|
|
handleAuthAndErrors: async () => {},
|
|
})
|
|
|
|
assert.deepEqual(result, { ok: true })
|
|
assert.deepEqual(mutationTokens, ['csrf-1', 'csrf-2'])
|
|
})
|