Skip to content
This repository has been archived by the owner on Aug 16, 2024. It is now read-only.

Commit

Permalink
feat(server): beta.12 - koa adapter (#192)
Browse files Browse the repository at this point in the history
  • Loading branch information
serhiisol authored Aug 24, 2023
1 parent 4928d6e commit 1dc26bd
Show file tree
Hide file tree
Showing 22 changed files with 1,733 additions and 49 deletions.
4 changes: 4 additions & 0 deletions server/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,10 @@ Or
```
npm install fastify @fastify/cookie @fastify/static @fastify/view --save
```
Or
```
npm install koa koa-bodyparser koa-mount koa-static koa-views --save
```

## Example
Fully working example can be found in [example](example) folder.
Expand Down
3 changes: 2 additions & 1 deletion server/example/app.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { APP_VERSION, GLOBAL_PIPE, Module } from '@server';
import { ExpressAdapter } from '@server/express';
import { FastifyAdapter } from '@server/fastify';
import { HttpModule } from '@server/http';
import { KoaAdapter } from '@server/koa';
import { SwaggerModule } from '@server/swagger';

import { MiscModule, PostsModule } from './modules';
Expand All @@ -10,7 +11,7 @@ import { ServerPipe } from './pipes';
@Module({
modules: [
HttpModule.create(
process.env.USE_FASTIFY ? FastifyAdapter : ExpressAdapter,
process.env.USE_FASTIFY ? FastifyAdapter : process.env.USE_KOA ? KoaAdapter : ExpressAdapter,
),
SwaggerModule.forRoot({
description: 'Decorators Example App',
Expand Down
8 changes: 8 additions & 0 deletions server/example/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ import * as FastifyView from '@fastify/view';
import { Application } from '@server';
import { HttpModule } from '@server/http';
import { json } from 'body-parser';
import * as koaBodyparser from 'koa-bodyparser';
import * as koaViews from 'koa-views';
import { join } from 'path';

import { AppModule } from './app.module';
Expand All @@ -17,6 +19,12 @@ async function bootstrap() {
},
root: join(__dirname, 'views'),
});
} else if (process.env.USE_KOA) {
module.use(koaViews(join(__dirname, 'views'), {
autoRender: false,
extension: 'ejs',
}));
module.use(koaBodyparser());
} else {
module.set('view engine', 'ejs');
module.set('views', join(__dirname, 'views'));
Expand Down
34 changes: 34 additions & 0 deletions server/integration/core/app-version/test/koa.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import { Application, HttpStatus, Module } from '@server';
import { HttpModule } from '@server/http';
import { KoaAdapter } from '@server/koa';
import * as request from 'supertest';

import { AppModule } from '../src/app.module';

@Module({
modules: [
HttpModule.create(KoaAdapter),
AppModule,
],
})
class TestModule { }

describe('Koa :: App Version', () => {
let app: Application;
let module: HttpModule;

beforeEach(async () => {
app = await Application.create(TestModule);
module = await app.inject<HttpModule>(HttpModule);

await module.listen();
});

afterEach(() => module.close());

it('registers `get` request with app version prefix', async () => {
return request(module.getHttpServer())
.get('/app-version/get')
.expect(HttpStatus.NO_CONTENT);
});
});
44 changes: 44 additions & 0 deletions server/integration/core/custom-decorators/test/koa.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import { Application, Module, Reflector } from '@server';
import { HttpModule } from '@server/http';
import { KoaAdapter } from '@server/koa';
import * as request from 'supertest';

import { AppModule } from '../src/app.module';

@Module({
modules: [
HttpModule.create(KoaAdapter),
AppModule,
],
})
class TestModule { }

describe('Koa :: Custom Decorators', () => {
let app: Application;
let module: HttpModule;

beforeEach(async () => {
app = await Application.create(TestModule);
module = await app.inject<HttpModule>(HttpModule);

await module.listen();
});

afterEach(() => module.close());

it('checks availability of reflector', async () => {
expect(await app.inject(Reflector)).toBeDefined();
});

it('decorates `get` request and its params', async () => {
return request(module.getHttpServer())
.get('/?param=decorated')
.expect('decorated');
});

it('throws error during `get` request', async () => {
return request(module.getHttpServer())
.get('/?param=failure')
.expect(({ body }) => expect(body.message).toEqual('decorated-error'));
});
});
71 changes: 71 additions & 0 deletions server/integration/core/pipes/test/koa.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
import { Application, Module } from '@server';
import { HttpModule } from '@server/http';
import { KoaAdapter } from '@server/koa';
import * as request from 'supertest';

import { AppModule } from '../src/app.module';
import { Sequence } from '../src/sequence';

@Module({
modules: [
HttpModule.create(KoaAdapter),
AppModule,
],
})
class TestModule { }

describe('Koa :: Pipes', () => {
let app: Application;
let module: HttpModule;
let seq: Sequence;

beforeEach(async () => {
app = await Application.create(TestModule);
module = await app.inject<HttpModule>(HttpModule);
seq = await app.inject<Sequence>(Sequence);

jest.spyOn(seq, 'push');

await module.listen();
});

afterEach(() => module.close());

it('executes pipes', async () => {
return request(module.getHttpServer())
.get('/')
.expect(() => {
expect(seq.push).toBeCalledWith('server');
expect(seq.push).toBeCalledWith('controller');
expect(seq.push).toBeCalledWith('method');
expect(seq.push).toBeCalledWith('method');
expect(seq.push).toBeCalledWith('controller');
expect(seq.push).toBeCalledWith('server');
});
});

it('executes pipes with method error', async () => {
return request(module.getHttpServer())
.get('/with-method-error')
.expect((res) => {
expect(res.body.message).toBe('method-error');
expect(seq.push).toBeCalledWith('server');
expect(seq.push).toBeCalledWith('controller');
expect(seq.push).toBeCalledWith('method');
expect(seq.push).toBeCalledWith('method');
expect(seq.push).toBeCalledWith('controller');
expect(seq.push).toBeCalledWith('server');
});
});

it('executes pipes with pipe error', async () => {
return request(module.getHttpServer())
.get('/with-pipe-error')
.expect((res) => {
expect(res.body.message).toBe('pipe-error');
expect(seq.push).toBeCalledWith('server');
expect(seq.push).toBeCalledWith('controller');
expect(seq.push).toBeCalledWith('server');
});
});
});
25 changes: 25 additions & 0 deletions server/integration/core/scopes/test/koa.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import { Application, Module } from '@server';
import { HttpModule } from '@server/http';
import { KoaAdapter } from '@server/koa';

import { AppModule } from '../src/app.module';

@Module({
modules: [
HttpModule.create(KoaAdapter),
AppModule,
],
})
class TestModule { }

describe('Koa :: Scopes', () => {
let app: Application;

beforeEach(async () => {
app = await Application.create(TestModule);
});

it('creates app without errors', () => {
expect(app).toBeDefined();
});
});
39 changes: 39 additions & 0 deletions server/integration/http/metadata-scanner/test/koa.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import { Application, Module } from '@server';
import { HttpModule } from '@server/http';
import { MetadataScanner } from '@server/http';
import { KoaAdapter } from '@server/koa';

import { AppModule } from '../src/app.module';

@Module({
modules: [
HttpModule.create(KoaAdapter),
AppModule,
],
})
class TestModule { }

describe('Koa :: Metadata Scanner', () => {
let app: Application;
let scanner: MetadataScanner;

beforeEach(async () => {
app = await Application.create(TestModule);
scanner = await app.inject<MetadataScanner>(MetadataScanner);
});

it('provides access to the metadata', () => {
const routesMetadata = scanner.scan();

expect(routesMetadata).toEqual(expect.arrayContaining([expect.objectContaining({
methodName: 'post',
params: expect.arrayContaining([expect.objectContaining({
argName: 'body',
index: 0,
methodName: 'post',
})]),
type: 'post',
url: '/',
})]));
});
});
113 changes: 113 additions & 0 deletions server/integration/http/params/test/koa.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
import { Application, Module } from '@server';
import { HttpModule } from '@server/http';
import { KoaAdapter } from '@server/koa';
import * as koaBodyparser from 'koa-bodyparser';
import * as request from 'supertest';

import { AppModule } from '../src/app.module';

@Module({
modules: [
HttpModule.create(KoaAdapter),
AppModule,
],
})
class TestModule { }

describe('Koa :: Params', () => {
let app: Application;
let module: HttpModule;

beforeEach(async () => {
app = await Application.create(TestModule);
module = await app.inject<HttpModule>(HttpModule);

module.use(koaBodyparser());

await module.listen();
});

afterEach(() => module.close());

it('receives `body` params', async () => {
return request(module.getHttpServer())
.post('/body')
.send({ example: 'param' })
.expect({ example: 'param', param: 'param' });
});

it('receives `cookies` params', async () => {
return request(module.getHttpServer())
.post('/cookies')
.set('Cookie', ['example=param'])
.expect({ cookie: 'param', example: 'param' });
});

it('receives `headers` params', async () => {
return request(module.getHttpServer())
.post('/headers')
.set({ example: 'param' })
.expect(({ body }) => expect(body).toEqual(expect.objectContaining({ example: 'param', header: 'param' })));
});

it('receives `params` params', async () => {
return request(module.getHttpServer())
.post('/params/param')
.expect({ example: 'param', param: 'param' });
});

it('receives `query` params', async () => {
return request(module.getHttpServer())
.post('/query?example=param')
.expect({ example: 'param', param: 'param' });
});

it('receives `request` param', async () => {
return request(module.getHttpServer())
.post('/request')
.expect('/request');
});

it('receives `response` param', async () => {
return request(module.getHttpServer())
.post('/response')
.expect('/response');
});

describe('with class validator', () => {
it('passes validation', () => {
return request(module.getHttpServer())
.post('/with-class-validator')
.send({ example: 'param' })
.expect({ example: 'param' });
});

it('fails validation', () => {
return request(module.getHttpServer())
.post('/with-class-validator')
.send({ example: 100 })
.expect(({ body }) => {
expect(body.message).toBeDefined();
expect(body.errors).toBeDefined();
});
});
});

describe('with custom validator', () => {
it('passes validation', () => {
return request(module.getHttpServer())
.post('/with-custom-validator')
.send({ example: 'param' })
.expect('param');
});

it('fails validation', () => {
return request(module.getHttpServer())
.post('/with-custom-validator')
.send({ example: 100 })
.expect(({ body }) => {
expect(body.message).toBeDefined();
});
});
});
});
Loading

0 comments on commit 1dc26bd

Please sign in to comment.