@@ -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');
|
||||
|
||||
Reference in New Issue
Block a user