1
Some checks failed
CI / verify (push) Has been cancelled

This commit is contained in:
2026-04-11 15:43:32 +08:00
parent f19e482c8f
commit 0981d6ee1b
78 changed files with 1102 additions and 8510 deletions

View File

@@ -14,12 +14,25 @@ import { requestIdMiddleware } from './middleware/requestId.ts';
import { createAppContext } from './server.ts';
import { httpRequest, type TestRequestInit } from './testHttp.ts';
function createTestConfig(testName: string): AppConfig {
type TestConfigOverrides = Partial<
Omit<AppConfig, 'llm' | 'dashScope' | 'smsAuth' | 'wechatAuth' | 'authSession'>
> & {
llm?: Partial<AppConfig['llm']>;
dashScope?: Partial<AppConfig['dashScope']>;
smsAuth?: Partial<AppConfig['smsAuth']>;
wechatAuth?: Partial<AppConfig['wechatAuth']>;
authSession?: Partial<AppConfig['authSession']>;
};
function createTestConfig(
testName: string,
overrides: TestConfigOverrides = {},
): AppConfig {
const tempRoot = fs.mkdtempSync(
path.join(os.tmpdir(), `genarrative-server-node-${testName}-`),
);
return {
const baseConfig: AppConfig = {
nodeEnv: 'test',
projectRoot: tempRoot,
publicDir: path.join(tempRoot, 'public'),
@@ -99,13 +112,39 @@ function createTestConfig(testName: string): AppConfig {
refreshCookiePath: '/api/auth',
},
};
return {
...baseConfig,
...overrides,
llm: {
...baseConfig.llm,
...overrides.llm,
},
dashScope: {
...baseConfig.dashScope,
...overrides.dashScope,
},
smsAuth: {
...baseConfig.smsAuth,
...overrides.smsAuth,
},
wechatAuth: {
...baseConfig.wechatAuth,
...overrides.wechatAuth,
},
authSession: {
...baseConfig.authSession,
...overrides.authSession,
},
};
}
async function withTestServer<T>(
testName: string,
run: (options: { baseUrl: string }) => Promise<T>,
overrides: TestConfigOverrides = {},
) {
const context = await createAppContext(createTestConfig(testName));
const context = await createAppContext(createTestConfig(testName, overrides));
const app = createApp(context);
const server = await new Promise<import('node:http').Server>((resolve) => {
const nextServer = app.listen(0, '127.0.0.1', () => resolve(nextServer));
@@ -348,6 +387,130 @@ test('auth entry auto-registers, me works, logout invalidates old token', async
});
});
test('login options expose enabled methods without authentication', async () => {
await withTestServer('auth-login-options', async ({ baseUrl }) => {
const response = await httpRequest(`${baseUrl}/api/auth/login-options`);
const payload = (await response.json()) as {
availableLoginMethods: string[];
};
assert.equal(response.status, 200);
assert.deepEqual(payload.availableLoginMethods, ['phone', 'wechat']);
});
});
test('wechat start uses qrconnect for desktop browsers', async () => {
await withTestServer(
'wechat-start-desktop',
async ({ baseUrl }) => {
const response = await httpRequest(
`${baseUrl}/api/auth/wechat/start?redirectPath=${encodeURIComponent('/')}`,
{
headers: {
'User-Agent':
'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 Chrome/135.0.0.0 Safari/537.36',
},
},
);
const payload = (await response.json()) as {
authorizationUrl: string;
};
const authorizationUrl = new URL(payload.authorizationUrl);
assert.equal(response.status, 200);
assert.equal(
`${authorizationUrl.origin}${authorizationUrl.pathname}`,
'https://open.weixin.qq.com/connect/qrconnect',
);
assert.equal(authorizationUrl.searchParams.get('scope'), 'snsapi_login');
assert.equal(authorizationUrl.hash, '#wechat_redirect');
},
{
wechatAuth: {
enabled: true,
provider: 'wechat',
appId: 'wx-test-app-id',
appSecret: 'wx-test-app-secret',
},
},
);
});
test('wechat start uses oauth authorize inside wechat browser', async () => {
await withTestServer(
'wechat-start-in-app',
async ({ baseUrl }) => {
const response = await httpRequest(
`${baseUrl}/api/auth/wechat/start?redirectPath=${encodeURIComponent('/')}`,
{
headers: {
'User-Agent':
'Mozilla/5.0 (iPhone; CPU iPhone OS 18_0 like Mac OS X) AppleWebKit/605.1.15 Mobile/15E148 MicroMessenger/8.0.54',
},
},
);
const payload = (await response.json()) as {
authorizationUrl: string;
};
const authorizationUrl = new URL(payload.authorizationUrl);
assert.equal(response.status, 200);
assert.equal(
`${authorizationUrl.origin}${authorizationUrl.pathname}`,
'https://open.weixin.qq.com/connect/oauth2/authorize',
);
assert.equal(authorizationUrl.searchParams.get('scope'), 'snsapi_userinfo');
assert.equal(authorizationUrl.hash, '#wechat_redirect');
},
{
wechatAuth: {
enabled: true,
provider: 'wechat',
appId: 'wx-test-app-id',
appSecret: 'wx-test-app-secret',
},
},
);
});
test('wechat start rejects unsupported mobile browsers for real provider', async () => {
await withTestServer(
'wechat-start-mobile-browser',
async ({ baseUrl }) => {
const response = await httpRequest(
`${baseUrl}/api/auth/wechat/start?redirectPath=${encodeURIComponent('/')}`,
{
headers: {
'User-Agent':
'Mozilla/5.0 (iPhone; CPU iPhone OS 18_0 like Mac OS X) AppleWebKit/605.1.15 Version/18.0 Mobile/15E148 Safari/604.1',
},
},
);
const payload = (await response.json()) as {
error: {
code: string;
message: string;
};
};
assert.equal(response.status, 400);
assert.equal(payload.error.code, 'BAD_REQUEST');
assert.equal(
payload.error.message,
'当前浏览器请使用手机号登录,或在微信内打开后再使用微信登录',
);
},
{
wechatAuth: {
enabled: true,
provider: 'wechat',
appId: 'wx-test-app-id',
appSecret: 'wx-test-app-secret',
},
},
);
});
test('phone login sends code, creates a user and returns masked profile info', async () => {
await withTestServer('phone-login', async ({ baseUrl }) => {
const sendResult = await sendPhoneCode(baseUrl, '13800138000');