claude-code-best/src/commands/login/__tests__/AuthPlaneSummary.test.tsx
claude-code-best ee63c17697 feat: 添加登录认证增强(workspace key、host guard、auth status)
- hostGuard: workspace API key 仅限 api.anthropic.com,OAuth 限定 subscription plane
- saveWorkspaceKey: sk-ant-api03- 前缀校验,安全写入缓存
- AuthPlaneSummary/WorkspaceKeyInput: 登录 UI 组件
- getAuthStatus: 认证状态查询

Co-Authored-By: glm-5-turbo <zai-org@claude-code-best.win>
2026-05-09 23:04:15 +08:00

112 lines
4.1 KiB
TypeScript

/**
* Tests for AuthPlaneSummary.tsx
* Uses staticRender to render Ink components to strings.
* Covers all 4 mode combinations + long provider list + key preview masking.
*/
import { describe, expect, test, mock } from 'bun:test';
import * as React from 'react';
import { logMock } from '../../../../tests/mocks/log';
import { debugMock } from '../../../../tests/mocks/debug';
mock.module('src/utils/log.ts', logMock);
mock.module('src/utils/debug.ts', debugMock);
mock.module('bun:bundle', () => ({ feature: () => false }));
mock.module('src/utils/settings/settings.js', () => ({
getCachedOrDefaultSettings: () => ({}),
getSettings: () => ({}),
}));
mock.module('src/utils/config.ts', () => ({
isConfigEnabled: () => true,
getGlobalConfig: () => ({ workspaceApiKey: undefined }),
saveGlobalConfig: (_updater: unknown) => undefined,
}));
import { renderToString } from '../../../utils/staticRender.js';
import type { AuthStatus } from '../getAuthStatus.js';
// Helper to build minimal AuthStatus fixtures
function makeStatus(overrides: Partial<AuthStatus> = {}): AuthStatus {
return {
subscription: {
active: false,
plan: null,
accountEmail: null,
},
workspaceKey: {
set: false,
prefixValid: false,
keyPreview: null,
source: null,
},
...overrides,
};
}
describe('AuthPlaneSummary', () => {
test('renders subscription as inactive (☐) when not logged in', async () => {
const { AuthPlaneSummary } = await import('../AuthPlaneSummary.js');
const status = makeStatus();
const out = await renderToString(<AuthPlaneSummary status={status} />);
expect(out).toContain('Subscription');
// Subscription inactive symbol or "not logged in" indicator
expect(out.toLowerCase()).toMatch(/not logged in|☐/);
});
test('renders subscription as active (☑) with plan label when subscribed', async () => {
const { AuthPlaneSummary } = await import('../AuthPlaneSummary.js');
const status = makeStatus({
subscription: { active: true, plan: 'pro', accountEmail: null },
});
const out = await renderToString(<AuthPlaneSummary status={status} />);
expect(out).toContain('pro');
// Active symbol present
expect(out).toContain('☑');
});
test('renders workspace key as set+valid (☑) when prefixValid=true', async () => {
const { AuthPlaneSummary } = await import('../AuthPlaneSummary.js');
const status = makeStatus({
workspaceKey: {
set: true,
prefixValid: true,
keyPreview: 'sk-a...67 (48 chars)',
source: 'env',
},
});
const out = await renderToString(<AuthPlaneSummary status={status} />);
// Key preview may be word-wrapped across lines in terminal output
expect(out).toContain('sk-a...67');
expect(out).toContain('☑');
});
test('renders workspace key warning (⚠) when set but prefix invalid', async () => {
const { AuthPlaneSummary } = await import('../AuthPlaneSummary.js');
const status = makeStatus({
workspaceKey: {
set: true,
prefixValid: false,
keyPreview: 'sk-w...ng (40 chars)',
source: 'env',
},
});
const out = await renderToString(<AuthPlaneSummary status={status} />);
// Warning indicator present
expect(out).toContain('⚠');
expect(out.toLowerCase()).toContain('sk-ant-api03-');
});
test('shows workspace key 4-step setup instructions when key not set and subscription active', async () => {
const { AuthPlaneSummary } = await import('../AuthPlaneSummary.js');
const status = makeStatus({
subscription: { active: true, plan: 'pro', accountEmail: null },
workspaceKey: { set: false, prefixValid: false, keyPreview: null, source: null },
});
const out = await renderToString(<AuthPlaneSummary status={status} />);
expect(out).toContain('console.anthropic.com');
});
// Third-party provider rendering tests removed 2026-05-06 — that section
// was deleted from AuthPlaneSummary to defer to fork's existing /login form
// for OpenAI-compat configuration. See AuthPlaneSummary.tsx for the rationale.
});