feat(gateway): include production Telegram user gateway

Version the NestJS TDLib gateway with Docker deployment, migrations, automated tests and platform integration documentation while excluding all account sessions, credentials, runtime databases and backups.
This commit is contained in:
wmq
2026-08-01 18:38:20 +08:00
parent af49d22ca7
commit 06112602ec
142 changed files with 23107 additions and 7 deletions
+119
View File
@@ -0,0 +1,119 @@
import { INestApplication } from '@nestjs/common';
import { Test } from '@nestjs/testing';
import request from 'supertest';
import { AppModule } from '../src/app.module';
import { ValidationPipe } from '../src/common/pipes/validation.pipe';
import { AllExceptionsFilter } from '../src/common/filters/all-exceptions.filter';
import { RequestIdInterceptor } from '../src/common/interceptors/request-id.interceptor';
import { ResponseInterceptor } from '../src/common/interceptors/response.interceptor';
describe('Gateway public endpoints (e2e)', () => {
let app: INestApplication;
beforeAll(async () => {
const moduleRef = await Test.createTestingModule({
imports: [AppModule],
}).compile();
app = moduleRef.createNestApplication();
app.setGlobalPrefix('api');
app.useGlobalFilters(new AllExceptionsFilter());
app.useGlobalInterceptors(
new RequestIdInterceptor(),
new ResponseInterceptor(),
);
app.useGlobalPipes(new ValidationPipe());
await app.init();
});
afterAll(async () => {
if (app) await app.close();
});
it('reports health without authentication', async () => {
const response = await request(app.getHttpServer())
.get('/api/admin/health')
.expect(200);
expect(response.body.data.status).toBe('ok');
});
it('protects admin metrics', async () => {
await request(app.getHttpServer())
.get('/api/admin/metrics')
.expect(401);
});
it('validates registration input through the real HTTP pipeline', async () => {
await request(app.getHttpServer())
.post('/api/auth/register')
.send({ email: 'not-an-email', password: 'x' })
.expect(400);
});
it('completes tenant signup, registration, login, and isolation checks', async () => {
const suffix = Date.now().toString(36);
const tenantResponse = await request(app.getHttpServer())
.post('/api/tenants')
.send({ name: `e2e-${suffix}`, maxAccounts: 2 })
.expect(201);
const tenantId = tenantResponse.body.data.id;
const credentials = {
email: `owner-${suffix}@example.com`,
password: 'StrongPass123!',
tenantId,
};
const registerResponse = await request(app.getHttpServer())
.post('/api/auth/register')
.send(credentials)
.expect(201);
expect(registerResponse.body.data.token).toBeTruthy();
expect(registerResponse.body.data.user.passwordHash).toBeUndefined();
const loginResponse = await request(app.getHttpServer())
.post('/api/auth/login')
.send(credentials)
.expect(201);
const token = loginResponse.body.data.token;
expect(loginResponse.body.data.user.passwordHash).toBeUndefined();
const tenantList = await request(app.getHttpServer())
.get('/api/tenants')
.set('Authorization', `Bearer ${token}`)
.set('x-tenant-id', tenantId)
.expect(200);
expect(tenantList.body.data).toHaveLength(1);
expect(tenantList.body.data[0].id).toBe(tenantId);
await request(app.getHttpServer())
.put(`/api/tenants/${tenantId}`)
.set('Authorization', `Bearer ${token}`)
.set('x-tenant-id', tenantId)
.send({ maxAccounts: 3 })
.expect(200);
const auditResponse = await request(app.getHttpServer())
.get('/api/admin/audit-logs?action=put')
.set('Authorization', `Bearer ${token}`)
.expect(200);
expect(auditResponse.body.data.total).toBeGreaterThanOrEqual(1);
expect(auditResponse.body.data.logs[0].tenantId).toBe(tenantId);
await request(app.getHttpServer())
.get('/api/tenants')
.set('Authorization', `Bearer ${token}`)
.set('x-tenant-id', 'another-tenant')
.expect(403);
});
it('reports database readiness when Redis is disabled for tests', async () => {
const response = await request(app.getHttpServer())
.get('/api/admin/ready')
.expect(200);
expect(response.body.data).toEqual({
status: 'ready',
checks: { database: 'up', redis: 'disabled' },
});
});
});