import { existsSync } from 'node:fs'; import type { INestApplication } from '@nestjs/common'; import { Test } from '@nestjs/testing'; import request from 'supertest'; import { AppModule } from '../src/app.module.js'; import { applyMigrations } from '../src/prisma/apply-migrations.js'; import { PrismaService } from '../src/prisma/prisma.service.js'; type GraphqlBody = { data?: T; errors?: Array<{ message: string }>; }; function readBody(response: request.Response): GraphqlBody { const payload: unknown = response.body; if (typeof payload !== 'object' || payload === null) { throw new Error('GraphQL response was not an object'); } const record = payload as Record; const errors = Array.isArray(record.errors) ? record.errors.filter(isErrorItem) : undefined; return { data: record.data as T | undefined, errors, }; } function isErrorItem(value: unknown): value is { message: string } { return ( typeof value === 'object' && value !== null && 'message' in value && typeof value.message === 'string' ); } function http(app: INestApplication) { // The Nest server handle is typed as any. // eslint-disable-next-line @typescript-eslint/no-unsafe-argument return request(app.getHttpServer()); } if (existsSync('.env')) { process.loadEnvFile('.env'); } const EXAMPLE = ` query { profile { name description skills { name } experience { company position } projects { name } } } `; describe('digital card', () => { let app: INestApplication; beforeAll(async () => { await applyMigrations(); const moduleRef = await Test.createTestingModule({ imports: [AppModule], }).compile(); app = moduleRef.createNestApplication(); await app.init(); }); afterAll(async () => { await app.close(); }); it('opens Apollo Sandbox and a health check', async () => { const sandbox = await http(app).get('/graphql').set('Accept', 'text/html'); expect(sandbox.status).toBe(200); expect(sandbox.text.toLowerCase()).toMatch(/sandbox|apollo/); const health = await http(app).get('/health'); expect(health.status).toBe(200); expect(health.body).toEqual({ status: 'ok' }); }); it('returns the profile for the assignment query', async () => { const prisma = app.get(PrismaService); prisma.takeQueries(); const response = await http(app).post('/graphql').send({ query: EXAMPLE }); expect(response.status).toBe(200); const body = readBody<{ profile: { name: string; description: string; skills: Array<{ name: string }>; experience: Array<{ company: string; position: string }>; projects: Array<{ name: string }>; }; }>(response); expect(body.errors).toBeUndefined(); const profile = body.data?.profile; expect(profile?.name).toBe('Илья Братских'); expect(profile?.description.length).toBeGreaterThan(0); expect(profile?.skills.map((skill) => skill.name)).toContain('NestJS'); expect(profile?.experience).toEqual( expect.arrayContaining([ expect.objectContaining({ company: 'АО «Синтелс»', position: 'Fullstack Developer & Researcher', }), ]), ); expect(profile?.projects.map((project) => project.name)).toContain( 'AI-обучение и чат-боты', ); const statements = prisma.takeQueries().filter(isApplicationSql); expect(statements.length).toBeGreaterThan(0); expect(statements.length).toBeLessThanOrEqual(6); expect(statements.filter((sql) => /Achievement/i.test(sql))).toHaveLength( 0, ); }); it('loads nested achievements without a query per job', async () => { const prisma = app.get(PrismaService); prisma.takeQueries(); const response = await http(app) .post('/graphql') .send({ query: ` query { profile { experience { company achievements { description } } projects { name url skills { name } highlights } } } `, }); expect(response.status).toBe(200); const body = readBody<{ profile: { experience: Array<{ company: string; achievements: Array<{ description: string }>; }>; projects: Array<{ name: string; url: string | null; skills: Array<{ name: string }>; }>; }; }>(response); expect(body.errors).toBeUndefined(); const jobs = body.data?.profile.experience ?? []; const sintels = jobs.find((job) => job.company.includes('Синтелс')); expect(sintels?.achievements).toHaveLength(4); const projects = body.data?.profile.projects ?? []; const ai = projects.find((project) => project.name.startsWith('AI-')); expect(ai?.url).toBe('https://github.com/KBAHTNET/ai_learning'); expect(ai?.skills.map((skill) => skill.name)).toEqual( expect.arrayContaining(['Python', 'PyTorch']), ); const statements = prisma.takeQueries().filter(isApplicationSql); expect(statements.filter((sql) => /Achievement/i.test(sql))).toHaveLength( 1, ); expect(statements.length).toBeLessThanOrEqual(8); }); it('rejects an unknown or malformed slug', async () => { const missing = await http(app).post('/graphql').send({ query: 'query { profile(slug: "nobody") { name } }', }); expect(readBody(missing).errors?.[0]?.message).toContain('не найден'); const invalid = await http(app).post('/graphql').send({ query: 'query { profile(slug: "Bad Slug") { name } }', }); expect(readBody(invalid).errors?.[0]?.message).toContain('Некорректный'); }); }); function isApplicationSql(sql: string): boolean { return /profile|skill|experience|achievement|project|education|award|link/i.test( sql, ); }