Files
gongxue-base/scripts/tenant-resolve-contract-test.js
2026-07-12 19:26:57 +08:00

171 lines
4.4 KiB
JavaScript

import assert from 'node:assert/strict';
import { HttpError } from '../apps/api/src/core/errors.ts';
import { normalizeTenantHost, selectTenantLocator } from '../apps/api/src/features/tenant/locator.ts';
import { resolveTenantRoute } from '../apps/api/src/features/tenant/routes.ts';
const TENANT_ID = '00000000-0000-4000-8000-000000000001';
function tenantRow(overrides = {}) {
return {
id: TENANT_ID,
slug: 'campus-a',
name: 'Campus A',
status: 'active',
mode: 'saas',
host: 'campus-a.example.com',
brand_name: 'Campus A',
short_name: 'Campus A',
slogan: null,
logo_url: null,
favicon_url: null,
service_wechat: null,
service_account_name: null,
theme: {},
public_assets: {},
feature_flags: {},
admin_feature_flags: {},
public_config: {},
...overrides,
};
}
function requestContext(url, headers = {}) {
return {
url: new URL(url),
req: { headers },
res: {},
};
}
async function expectHttpError(run, statusCode, code) {
await assert.rejects(run, error => {
assert.ok(error instanceof HttpError);
assert.equal(error.statusCode, statusCode);
assert.equal(error.code, code);
return true;
});
}
assert.equal(normalizeTenantHost('Example.COM.:443'), 'example.com');
assert.equal(normalizeTenantHost('[::1]:8787'), '::1');
assert.equal(normalizeTenantHost('::1'), '::1');
assert.equal(normalizeTenantHost('bad,forwarded.example.com'), '');
assert.deepEqual(selectTenantLocator({
origin: 'https://Campus-A.Example.com:443',
requestedHost: 'campus-a.example.com.',
requestHost: 'api.example.com',
isProduction: true,
}), {
ok: true,
locator: {
kind: 'host',
host: 'campus-a.example.com',
expectedTenantCode: null,
source: 'browser',
},
});
assert.deepEqual(selectTenantLocator({
origin: 'null',
referer: 'https://campus-a.example.com/pages/student/home',
requestHost: 'api.example.com',
isProduction: true,
}), {
ok: true,
locator: {
kind: 'host',
host: 'campus-a.example.com',
expectedTenantCode: null,
source: 'browser',
},
});
assert.equal(selectTenantLocator({
origin: 'https://campus-a.example.com',
requestedHost: 'campus-b.example.com',
isProduction: true,
}).code, 'TENANT_HOST_CONFLICT');
assert.deepEqual(selectTenantLocator({
origin: 'http://localhost:5173',
requestedHost: 'localhost:5173',
tenantCode: 'campus-a',
requestHost: 'localhost:8787',
isProduction: false,
}), {
ok: true,
locator: { kind: 'tenantCode', tenantCode: 'campus-a', source: 'local-development' },
});
assert.deepEqual(selectTenantLocator({
tenantCode: 'campus-a',
requestHost: 'api.example.com',
isProduction: true,
}), {
ok: true,
locator: { kind: 'tenantCode', tenantCode: 'campus-a', source: 'headless-client' },
});
assert.equal(selectTenantLocator({
requestedHost: 'campus-a.example.com',
requestHost: 'api.example.com',
isProduction: true,
}).code, 'TENANT_HOST_UNTRUSTED');
{
const calls = [];
const result = await resolveTenantRoute(
requestContext('https://api.example.com/api/tenant/resolve?host=campus-a.example.com', {
host: 'api.example.com',
origin: 'https://campus-a.example.com',
'x-forwarded-host': 'attacker.example.com',
}),
async (sql, params) => {
calls.push({ sql, params });
return tenantRow();
},
);
assert.equal(result.tenant.id, TENANT_ID);
assert.deepEqual(calls[0].params, ['campus-a.example.com']);
assert.match(calls[0].sql, /d\.status = 'active' and t\.status = 'active'/);
}
await expectHttpError(
() => resolveTenantRoute(
requestContext('https://api.example.com/api/tenant/resolve?host=unknown.example.com', {
host: 'api.example.com',
origin: 'https://unknown.example.com',
}),
async () => null,
),
404,
'TENANT_DOMAIN_NOT_BOUND',
);
await expectHttpError(
() => resolveTenantRoute(
requestContext('https://api.example.com/api/tenant/resolve?host=campus-a.example.com&tenantCode=campus-b', {
host: 'api.example.com',
origin: 'https://campus-a.example.com',
}),
async () => tenantRow(),
),
409,
'TENANT_LOCATOR_CONFLICT',
);
await expectHttpError(
() => resolveTenantRoute(
requestContext('https://api.example.com/api/tenant/resolve?host=campus-a.example.com', {
host: 'api.example.com',
origin: 'https://campus-b.example.com',
}),
async () => tenantRow(),
),
409,
'TENANT_HOST_CONFLICT',
);
console.log('[PASS] Tenant resolve fail-closed contract');