Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

added altair-api e2e tests #2199

Merged
merged 21 commits into from May 29, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
64 changes: 64 additions & 0 deletions .github/workflows/pr-api-e2e.yml
@@ -0,0 +1,64 @@
name: API E2E Testing

on:
push:
branches:
- master
pull_request:

jobs:
# Label of the container job
container-job:
# Containers must run in Linux based operating systems
runs-on: ubuntu-latest
# Docker Hub image that `container-job` executes in
# container: node:20

# Service containers to run with `container-job`
services:
# Label used to access the service container
postgres:
image: postgres:13
env:
POSTGRES_USER: prisma
POSTGRES_PASSWORD: prisma
POSTGRES_DB: tests
ports:
- 5434:5432
# Set health checks to wait until postgres has started
options: >-
--health-cmd pg_isready
--health-interval 10s
--health-timeout 5s
--health-retries 5

steps:
# Downloads a copy of the code in your repository before running CI tests
- name: Check out repository code
uses: actions/checkout@v3
- name: Use Node.js ${{ env.NODE_VERSION }} on ${{ matrix.os }}
uses: actions/setup-node@v1
with:
node-version: ${{ env.NODE_VERSION }}
- name: restore lerna
uses: actions/cache@v2
with:
path: |
node_modules
*/*/node_modules
key: ${{ runner.os }}-${{ hashFiles('**/yarn.lock') }}
- run: yarn --frozen-lockfile --network-timeout 1000000
- name: Build apps (with retries)
uses: nick-invision/retry@v2
with:
timeout_minutes: 15
max_attempts: 3
command: yarn build:ci
# - name: Update env file
# run: |
# echo DATABASE_URL="postgresql://prisma:prisma@postgres:5434/tests?schema=public" >> packages/altair-api/.env.e2e
# cat packages/altair-api/.env.e2e
- name: Migration
run: yarn --cwd packages/altair-api migrate:e2e
- name: Run E2E
run: yarn --cwd packages/altair-api test:e2e
50 changes: 25 additions & 25 deletions .github/workflows/pr-e2e.yml
Expand Up @@ -17,28 +17,28 @@ jobs:
os: [macos-latest, ubuntu-latest]
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v2
- name: Use Node.js ${{ env.NODE_VERSION }} on ${{ matrix.os }}
uses: actions/setup-node@v1
with:
node-version: ${{ env.NODE_VERSION }}
- name: restore lerna
uses: actions/cache@v2
with:
path: |
node_modules
*/*/node_modules
key: ${{ runner.os }}-${{ hashFiles('**/yarn.lock') }}
- run: yarn --frozen-lockfile --network-timeout 1000000
- uses: browser-actions/setup-chrome@latest
- name: Build apps (with retries)
uses: nick-invision/retry@v2
with:
timeout_minutes: 15
max_attempts: 3
command: yarn build:ci
- run: npx playwright install
- name: Run headless e2e test
uses: GabrielBB/xvfb-action@v1
with:
run: yarn playwright test --retries 2
- uses: actions/checkout@v2
- name: Use Node.js ${{ env.NODE_VERSION }} on ${{ matrix.os }}
uses: actions/setup-node@v1
with:
node-version: ${{ env.NODE_VERSION }}
- name: restore lerna
uses: actions/cache@v2
with:
path: |
node_modules
*/*/node_modules
key: ${{ runner.os }}-${{ hashFiles('**/yarn.lock') }}
- run: yarn --frozen-lockfile --network-timeout 1000000
- uses: browser-actions/setup-chrome@latest
- name: Build apps (with retries)
uses: nick-invision/retry@v2
with:
timeout_minutes: 15
max_attempts: 3
command: yarn build:ci
- run: npx playwright install
- name: Run headless e2e test
uses: GabrielBB/xvfb-action@v1
with:
run: yarn playwright test --retries 2
1 change: 1 addition & 0 deletions package.json
Expand Up @@ -15,6 +15,7 @@
"chalk": "^4.1.0",
"compare-versions": "^4.1.3",
"cwex": "^1.0.4",
"dotenv-cli": "^7.2.1",
"eslint": "8.18.0",
"eslint-config-altair": "^5.0.22",
"eslint-config-prettier": "8.5.0",
Expand Down
14 changes: 14 additions & 0 deletions packages/altair-api/.env.e2e
@@ -0,0 +1,14 @@
JWT_ACCESS_SECRET=super-secret-access-secret
EVENTS_JWT_ACCESS_SECRET=events-jwt-access-secret
JWT_REFRESH_SECRET=jwt-refresh-secret
GOOGLE_OAUTH_CLIENT_ID=test.apps.googleusercontent.com
GOOGLE_OAUTH_CLIENT_SECRET=GS-53CR37
POSTGRES_DB=tests
POSTGRES_USER=prisma
POSTGRES_PASSWORD=prisma
DATABASE_URL=postgresql://prisma:prisma@localhost:5434/tests?schema=public
NEW_RELIC_APP_NAME=altairgraphql.test
NEW_RELIC_LICENSE_KEY=test-key
NEW_RELIC_APPLICATION_LOGGING_FORWARDING_ENABLED=true
STRIPE_SECRET_KEY=sk_test_xxx
STRIPE_WEBHOOK_SECRET=
17 changes: 17 additions & 0 deletions packages/altair-api/bin/e2e.sh
@@ -0,0 +1,17 @@
#!/bin/bash

# set -euo pipefail

SCRIPT_DIR=$(dirname "$0")

# start e2e test database
docker compose -f "$SCRIPT_DIR/../docker-compose.e2e.yml" up -d --wait

# migrate e2e test database
yarn migrate:e2e

# run e2e test
yarn test:e2e --watch

# shutdown e2e test database
docker compose -f "$SCRIPT_DIR/../docker-compose.e2e.yml" down -v
25 changes: 25 additions & 0 deletions packages/altair-api/docker-compose.e2e.yml
@@ -0,0 +1,25 @@
# Set the version of docker compose to use
version: '3.9'

# The containers that compose the project
services:
db:
image: postgres:13
restart: always
container_name: integration-tests-prisma
ports:
- '5434:5432'
volumes:
- postgres-e2e:/var/lib/postgresql/data-e2e
environment:
POSTGRES_USER: prisma
POSTGRES_PASSWORD: prisma
POSTGRES_DB: tests
networks:
- test-network

networks:
test-network:

volumes:
postgres-e2e:
4 changes: 3 additions & 1 deletion packages/altair-api/package.json
Expand Up @@ -53,6 +53,7 @@
"eslint-config-prettier": "^8.3.0",
"eslint-plugin-prettier": "^4.0.0",
"jest": "29.4.1",
"passport-custom": "^1.1.1",
"pino-pretty": "^9.2.0",
"prettier": "^2.3.2",
"prettier-config-altair": "^5.0.25",
Expand All @@ -79,10 +80,11 @@
"start:debug": "nest start --debug --watch",
"start:dev": "nest start --watch",
"start:prod": "node dist/main",
"migrate:e2e": "dotenv -e .env.e2e -- prisma migrate deploy --schema ../altair-db/prisma/schema.prisma",
"test": "jest",
"test:cov": "jest --coverage",
"test:debug": "node --inspect-brk -r tsconfig-paths/register -r ts-node/register node_modules/.bin/jest --runInBand",
"test:e2e": "jest --config ./test/jest-e2e.json",
"test:e2e": "dotenv -e .env.e2e -- jest --config ./test/jest-e2e.config.js --detectOpenHandles --forceExit",
"test:watch": "jest --watch"
}
}
58 changes: 58 additions & 0 deletions packages/altair-api/src/app-bootstrap.ts
@@ -0,0 +1,58 @@
import { INestApplication, ValidationPipe } from '@nestjs/common';
import { ConfigService } from '@nestjs/config';
import { HttpAdapterHost } from '@nestjs/core';
import { DocumentBuilder, SwaggerModule } from '@nestjs/swagger';
import { Logger, LoggerErrorInterceptor } from 'nestjs-pino';
import { PrismaClientExceptionFilter, PrismaService } from 'nestjs-prisma';
import { CorsConfig, SwaggerConfig } from './common/config';
import { NewrelicInterceptor } from './newrelic/newrelic.interceptor';

export const bootstrapApp = async (app: INestApplication) => {
// Logger
if (process.env.NODE_ENV === 'production') {
// Use pino logger in production
app.useLogger(app.get(Logger));
}

// Validation
app.useGlobalPipes(new ValidationPipe({ whitelist: true, transform: true }));

// Interceptors
if (process.env.NODE_ENV === 'production') {
app.useGlobalInterceptors(new NewrelicInterceptor(app.get(Logger)));
}
if (process.env.NODE_ENV === 'production') {
app.useGlobalInterceptors(new LoggerErrorInterceptor());
}

// enable shutdown hook
const prismaService: PrismaService = app.get(PrismaService);
await prismaService.enableShutdownHooks(app);

// Prisma Client Exception Filter for unhandled exceptions
const { httpAdapter } = app.get(HttpAdapterHost);
app.useGlobalFilters(new PrismaClientExceptionFilter(httpAdapter));

const configService = app.get(ConfigService);
const corsConfig = configService.get<CorsConfig>('cors');
const swaggerConfig = configService.get<SwaggerConfig>('swagger');

// Swagger Api
if (swaggerConfig.enabled) {
const options = new DocumentBuilder()
.setTitle(swaggerConfig.title || 'Altair')
.setDescription(swaggerConfig.description || 'The Altair API description')
.setVersion(swaggerConfig.version || '1.0')
.build();
const document = SwaggerModule.createDocument(app, options);

SwaggerModule.setup(swaggerConfig.path || 'swagger', app, document);
}

// Cors
if (corsConfig.enabled) {
app.enableCors();
}

return app;
};
25 changes: 18 additions & 7 deletions packages/altair-api/src/app.controller.ts
@@ -1,4 +1,12 @@
import { Controller, Get, Req, Res, Sse, UseGuards } from '@nestjs/common';
import {
Controller,
Get,
OnModuleDestroy,
Req,
Res,
Sse,
UseGuards,
} from '@nestjs/common';
import { EventEmitter2 } from '@nestjs/event-emitter';
import { Request, Response } from 'express';
import { PrismaService } from 'nestjs-prisma';
Expand All @@ -24,11 +32,11 @@ export class AppController {
@Sse('events')
handleUserEvents(@Req() req: Request): Observable<unknown> {
const subject$ = new Subject();
const userId = req.user.id;
const userId = req?.user?.id;

// TODO: Create events
// TODO: Emit events from prisma middleware
this.eventService.on([EVENTS.COLLECTION_UPDATE], async ({ id }) => {
const collectionUpdateListener = async ({ id }: any) => {
const validUserCollection = await this.prisma.queryCollection.findFirst({
select: {
id: true,
Expand Down Expand Up @@ -60,8 +68,10 @@ export class AppController {
if (validUserCollection) {
subject$.next({ uid: userId, collectionId: id });
}
});
this.eventService.on([EVENTS.QUERY_UPDATE], async ({ id }) => {
};
this.eventService.on([EVENTS.COLLECTION_UPDATE], collectionUpdateListener);

const queryUpdateListener = async ({ id }: any) => {
// check query workspace owner
const validQueryItem = await this.prisma.queryItem.findFirst({
where: {
Expand Down Expand Up @@ -93,9 +103,10 @@ export class AppController {
},
});
if (validQueryItem) {
subject$.next({ uid: req.user.id, queryId: id });
subject$.next({ uid: req?.user?.id, queryId: id });
}
});
};
this.eventService.on([EVENTS.QUERY_UPDATE], queryUpdateListener);

return subject$.pipe(map((data) => ({ data })));
}
Expand Down
12 changes: 8 additions & 4 deletions packages/altair-api/src/app.module.ts
Expand Up @@ -16,20 +16,24 @@ import { StripeModule } from './stripe/stripe.module';
import { StripeWebhookController } from './stripe-webhook/stripe-webhook.controller';
import { WorkspacesModule } from './workspaces/workspaces.module';

// eslint-disable-next-line @typescript-eslint/no-var-requires
const newrelicPino = require('@newrelic/pino-enricher');
if (process.env.NEW_RELIC_APP_NAME && process.env.NODE_ENV === 'production') {
// eslint-disable-next-line @typescript-eslint/no-var-requires
const newrelicPino = require('@newrelic/pino-enricher');
}

@Module({
imports: [
LoggerModule.forRoot(),
...(process.env.NODE_ENV !== 'test' ? [LoggerModule.forRoot()] : []),
ConfigModule.forRoot({ isGlobal: true, load: [config] }),
PrismaModule.forRoot({
isGlobal: true,
prismaServiceOptions: {
middlewares: [], // configure your prisma middleware
},
}),
EventEmitterModule.forRoot(),
EventEmitterModule.forRoot({
verboseMemoryLeak: true,
}),
AuthModule,
QueriesModule,
QueryCollectionsModule,
Expand Down
7 changes: 6 additions & 1 deletion packages/altair-api/src/auth/auth.controller.ts
Expand Up @@ -51,6 +51,11 @@ export class AuthController {
@Get('slt')
@UseGuards(JwtAuthGuard)
getShortlivedEventsToken(@Req() req: Request) {
return { slt: this.authService.getShortLivedEventsToken(req.user.id) };
const userId = req?.user?.id;
if (!userId) {
throw new BadRequestException('User not found');
}

return { slt: this.authService.getShortLivedEventsToken(userId) };
}
}
2 changes: 1 addition & 1 deletion packages/altair-api/src/auth/auth.module.ts
Expand Up @@ -25,7 +25,7 @@ import { QueryCollectionsService } from 'src/query-collections/query-collections
return {
secret: configService.get<string>('JWT_ACCESS_SECRET'),
signOptions: {
expiresIn: securityConfig.expiresIn,
expiresIn: securityConfig?.expiresIn,
},
};
},
Expand Down