Files
Cli-Proxy-API-Management-Ce…/tests/authFileIdentity.test.ts
T
Supra4E8C 7c072c4ab4 feat(authFiles): enhance identity handling and search functionality
- Introduced deriveAuthFileIdentity to manage display of account email and project ID.
- Updated search logic to include account email and project ID in search criteria.
- Improved styling for file names and account display in AuthFileCard.
- Normalized email and project ID fields in API responses.
- Added tests for new identity logic and search functionalities.
2026-07-31 12:23:00 +08:00

187 lines
6.0 KiB
TypeScript

import { describe, expect, test } from 'bun:test';
import { deriveAuthFileIdentity, stripJsonExtension } from '../src/features/authFiles/identity';
import type { AuthFileItem } from '../src/types';
const authFile = (overrides: Partial<AuthFileItem> = {}): AuthFileItem => ({
name: 'credential.json',
type: 'codex',
...overrides,
});
describe('stripJsonExtension', () => {
test('strips the extension from a real codex file name', () => {
expect(stripJsonExtension('codex-abc12345-user@example.com-team.json')).toBe(
'codex-abc12345-user@example.com-team'
);
});
test('matches the extension case-insensitively', () => {
expect(stripJsonExtension('Antigravity-User@Example.com.JSON')).toBe(
'Antigravity-User@Example.com'
);
});
test('leaves an extension-less runtime-only id untouched', () => {
expect(stripJsonExtension('aistudio-channel-a')).toBe('aistudio-channel-a');
});
test('never strips down to an empty string', () => {
expect(stripJsonExtension('.json')).toBe('.json');
});
test('trims surrounding whitespace', () => {
expect(stripJsonExtension(' kimi-1712345678901.json ')).toBe('kimi-1712345678901');
});
});
describe('deriveAuthFileIdentity', () => {
test('leads with the account email and keeps the full name as the secondary row', () => {
expect(
deriveAuthFileIdentity(
authFile({
name: 'codex-abc12345-user@example.com-team.json',
email: 'user@example.com',
})
)
).toEqual({
primary: 'user@example.com',
kind: 'email',
secondary: 'codex-abc12345-user@example.com-team',
fullName: 'codex-abc12345-user@example.com-team.json',
});
});
test('treats a whitespace-only email as absent', () => {
const identity = deriveAuthFileIdentity(
authFile({ name: 'kimi-1712345678901.json', email: ' ' })
);
expect(identity.kind).toBe('fileName');
expect(identity.primary).toBe('kimi-1712345678901');
});
test('treats a non-string email as absent (index signature guard)', () => {
const identity = deriveAuthFileIdentity(
authFile({ name: 'kimi-1712345678901.json', email: 123 as unknown as string })
);
expect(identity.kind).toBe('fileName');
expect(identity.primary).toBe('kimi-1712345678901');
});
test('falls back to the file name and drops the duplicate secondary row', () => {
expect(
deriveAuthFileIdentity(authFile({ name: 'kimi-1712345678901.json', type: 'kimi' }))
).toEqual({
primary: 'kimi-1712345678901',
kind: 'fileName',
secondary: null,
fullName: 'kimi-1712345678901.json',
});
});
test('uses the project id when there is no email', () => {
expect(
deriveAuthFileIdentity(
authFile({ name: 'vertex-my-proj.json', type: 'vertex', projectId: 'my-proj' })
)
).toEqual({
primary: 'my-proj',
kind: 'projectId',
secondary: 'vertex-my-proj',
fullName: 'vertex-my-proj.json',
});
});
test('prefers the email over the project id', () => {
const identity = deriveAuthFileIdentity(
authFile({
name: 'vertex-my-proj.json',
email: 'sa@project.iam.gserviceaccount.com',
projectId: 'my-proj',
})
);
expect(identity.kind).toBe('email');
expect(identity.primary).toBe('sa@project.iam.gserviceaccount.com');
});
test('never surfaces the account field — it can be a raw API key', () => {
const identity = deriveAuthFileIdentity(
authFile({
name: 'gemini-apikey.json',
type: 'gemini',
account: 'sk-live-abcd1234',
account_type: 'api_key',
})
);
expect(identity.kind).toBe('fileName');
expect(identity.primary).toBe('gemini-apikey');
expect(JSON.stringify(identity)).not.toContain('sk-live');
});
test('ignores an oauth account even when it looks like an email (do not add it back to the chain)', () => {
const identity = deriveAuthFileIdentity(
authFile({
name: 'codex-abc12345-user@example.com-team.json',
account: 'user@example.com',
account_type: 'oauth',
})
);
expect(identity.kind).toBe('fileName');
expect(identity.primary).toBe('codex-abc12345-user@example.com-team');
expect(identity.secondary).toBeNull();
});
test('suppresses the secondary row for runtime-only entries whose name is the account', () => {
expect(
deriveAuthFileIdentity(
authFile({
name: 'aistudio-channel-a',
type: 'aistudio',
email: 'aistudio-channel-a',
runtimeOnly: true,
})
).secondary
).toBeNull();
});
test('the duplicate guard is case-insensitive', () => {
expect(
deriveAuthFileIdentity(authFile({ name: 'USER@X.COM.json', email: 'user@x.com' })).secondary
).toBeNull();
});
test('keeps the disambiguating secondary row for two credentials sharing one email', () => {
const team = deriveAuthFileIdentity(
authFile({ name: 'codex-abc12345-user@example.com-team.json', email: 'user@example.com' })
);
const plus = deriveAuthFileIdentity(
authFile({ name: 'codex-abc12345-user@example.com-plus.json', email: 'user@example.com' })
);
expect(team.primary).toBe(plus.primary);
expect(team.secondary).not.toBeNull();
expect(team.secondary).not.toBe(plus.secondary);
});
test('does not strip a provider prefix from the secondary row', () => {
expect(
deriveAuthFileIdentity(
authFile({ name: '-abc12345-user@example.com-team.json', email: 'user@example.com' })
).secondary
).toBe('-abc12345-user@example.com-team');
});
test('handles an empty file name without inventing a placeholder', () => {
expect(deriveAuthFileIdentity(authFile({ name: '', email: 'user@x.com' }))).toEqual({
primary: 'user@x.com',
kind: 'email',
secondary: null,
fullName: '',
});
expect(deriveAuthFileIdentity(authFile({ name: '' }))).toEqual({
primary: '',
kind: 'fileName',
secondary: null,
fullName: '',
});
});
});