diff --git a/.env.example b/.env.example index 641cfea..7419ca1 100644 --- a/.env.example +++ b/.env.example @@ -1 +1,2 @@ +APP_ENV=development DATABASE_URL="file:./dev.db" \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index c37832b..769d2d1 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,6 +10,7 @@ "license": "UNLICENSED", "dependencies": { "@nestjs/common": "^9.0.0", + "@nestjs/config": "^2.3.4", "@nestjs/core": "^9.0.0", "@nestjs/platform-express": "^9.0.0", "@prisma/client": "^4.15.0", @@ -1490,6 +1491,22 @@ } } }, + "node_modules/@nestjs/config": { + "version": "2.3.4", + "resolved": "https://registry.npmjs.org/@nestjs/config/-/config-2.3.4.tgz", + "integrity": "sha512-IGdSF+0F9MJO6dCRTEahdxPz4iVijjtolcFBxnY+2QYM3bXYQvAgzskGZi+WkAFJN/VzR3TEp60gN5sI74GxPA==", + "dependencies": { + "dotenv": "16.1.4", + "dotenv-expand": "10.0.0", + "lodash": "4.17.21", + "uuid": "9.0.0" + }, + "peerDependencies": { + "@nestjs/common": "^7.0.0 || ^8.0.0 || ^9.0.0", + "reflect-metadata": "^0.1.13", + "rxjs": "^6.0.0 || ^7.2.0" + } + }, "node_modules/@nestjs/core": { "version": "9.4.2", "resolved": "https://registry.npmjs.org/@nestjs/core/-/core-9.4.2.tgz", @@ -3646,6 +3663,25 @@ "node": ">=6.0.0" } }, + "node_modules/dotenv": { + "version": "16.1.4", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.1.4.tgz", + "integrity": "sha512-m55RtE8AsPeJBpOIFKihEmqUcoVncQIwo7x9U8ZwLEZw9ZpXboz2c+rvog+jUaJvVrZ5kBOeYQBX5+8Aa/OZQw==", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/motdotla/dotenv?sponsor=1" + } + }, + "node_modules/dotenv-expand": { + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/dotenv-expand/-/dotenv-expand-10.0.0.tgz", + "integrity": "sha512-GopVGCpVS1UKH75VKHGuQFqS1Gusej0z4FyQkPdwjil2gNIv+LNsqBlboOzpJFZKVT95GkCyWJbBSdFEFUWI2A==", + "engines": { + "node": ">=12" + } + }, "node_modules/ee-first": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", @@ -5956,8 +5992,7 @@ "node_modules/lodash": { "version": "4.17.21", "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", - "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", - "dev": true + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" }, "node_modules/lodash.memoize": { "version": "4.1.2", @@ -8681,6 +8716,14 @@ "node": ">= 0.4.0" } }, + "node_modules/uuid": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.0.tgz", + "integrity": "sha512-MXcSTerfPa4uqyzStbRoTgt5XIe3x5+42+q1sDuy3R5MDk66URdLMOZe5aPX/SQd+kuYAh0FdP/pO28IkQyTeg==", + "bin": { + "uuid": "dist/bin/uuid" + } + }, "node_modules/v8-compile-cache-lib": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz", diff --git a/package.json b/package.json index 2d0a765..99dd09d 100644 --- a/package.json +++ b/package.json @@ -17,11 +17,12 @@ "test:watch": "jest --watch", "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": "jest --runInBand --config ./test/jest-e2e.json --verbose --passWithNoTests --detectOpenHandles", "migrate": "prisma migrate dev" }, "dependencies": { "@nestjs/common": "^9.0.0", + "@nestjs/config": "^2.3.4", "@nestjs/core": "^9.0.0", "@nestjs/platform-express": "^9.0.0", "@prisma/client": "^4.15.0", diff --git a/src/app.module.ts b/src/app.module.ts index d81df9e..97db5d5 100644 --- a/src/app.module.ts +++ b/src/app.module.ts @@ -1,10 +1,17 @@ import { Module, ValidationPipe } from '@nestjs/common'; import { APP_PIPE } from '@nestjs/core'; import { CardsModule } from './cards/cards.module'; +import { ConfigModule } from '@nestjs/config'; +import configs from './configs'; @Module({ imports: [ CardsModule, + ConfigModule.forRoot({ + load: configs, + envFilePath: '.env', + isGlobal: true, + }), ], providers: [ { diff --git a/src/cards/cards.module.ts b/src/cards/cards.module.ts index 495efd9..25fff9f 100644 --- a/src/cards/cards.module.ts +++ b/src/cards/cards.module.ts @@ -1,7 +1,7 @@ import { Module } from '@nestjs/common'; import { CardsController } from './cards.controller'; import { CardsService } from './cards.service'; -import { PrismaModule } from 'prisma/prisma.module'; +import { PrismaModule } from './../../prisma/prisma.module'; @Module({ controllers: [CardsController], diff --git a/src/cards/cards.service.ts b/src/cards/cards.service.ts index 73661a0..adfd6b9 100644 --- a/src/cards/cards.service.ts +++ b/src/cards/cards.service.ts @@ -1,7 +1,7 @@ import { BadRequestException, Injectable, NotFoundException } from '@nestjs/common'; import { CreateCardDto } from './dtos/create-card.dto'; import { CardType } from './cards.enum'; -import { PrismaService } from 'prisma/prisma.service'; +import { PrismaService } from './../../prisma/prisma.service'; import { BugCard as BugCardModel, IssueCard as IssueCardModel, diff --git a/src/configs/app.config.ts b/src/configs/app.config.ts new file mode 100644 index 0000000..59bd906 --- /dev/null +++ b/src/configs/app.config.ts @@ -0,0 +1,8 @@ +import { registerAs } from "@nestjs/config"; + +export default registerAs( + 'app', + (): Record => ({ + env: process.env.APP_ENV || 'development', + }) +); \ No newline at end of file diff --git a/src/configs/index.ts b/src/configs/index.ts new file mode 100644 index 0000000..5c41b15 --- /dev/null +++ b/src/configs/index.ts @@ -0,0 +1,5 @@ +import AppConfig from './app.config'; + +export default [ + AppConfig +] \ No newline at end of file diff --git a/test/app.e2e-spec.ts b/test/app.e2e-spec.ts deleted file mode 100644 index 50cda62..0000000 --- a/test/app.e2e-spec.ts +++ /dev/null @@ -1,24 +0,0 @@ -import { Test, TestingModule } from '@nestjs/testing'; -import { INestApplication } from '@nestjs/common'; -import * as request from 'supertest'; -import { AppModule } from './../src/app.module'; - -describe('AppController (e2e)', () => { - let app: INestApplication; - - beforeEach(async () => { - const moduleFixture: TestingModule = await Test.createTestingModule({ - imports: [AppModule], - }).compile(); - - app = moduleFixture.createNestApplication(); - await app.init(); - }); - - it('/ (GET)', () => { - return request(app.getHttpServer()) - .get('/') - .expect(200) - .expect('Hello World!'); - }); -}); diff --git a/test/cards.e2e-spec.ts b/test/cards.e2e-spec.ts new file mode 100644 index 0000000..3ea4e77 --- /dev/null +++ b/test/cards.e2e-spec.ts @@ -0,0 +1,221 @@ +import { Test, TestingModule } from '@nestjs/testing'; +import { INestApplication } from '@nestjs/common'; +import * as request from 'supertest'; +import { AppModule } from '../src/app.module'; + +describe('Cards API Test', () => { + let app: INestApplication; + + beforeEach(async () => { + const moduleFixture: TestingModule = await Test.createTestingModule({ + imports: [AppModule], + }).compile(); + + app = moduleFixture.createNestApplication(); + await app.init(); + }) + + const issueCard = { + "type": "issue", + "title": "Test Issue Title", + "description": "Test Issue Description" + }; + + const taskCard = { + "type": "task", + "title": "Test Task Title", + "category": "test" + }; + + const bugCard = { + "type": "bug", + "description": "Test Bug Description" + }; + + it('Create a new IssueCard', () => { + const card = issueCard; + return request(app.getHttpServer()) + .post('/trello-manager') + .send(card) + .expect(201) + .then((res) => { + expect(res.body).toHaveProperty('id'); + expect(res.body).toHaveProperty('title'); + expect(res.body).toHaveProperty('description'); + const { title, description } = res.body; + expect(title).toBe(card.title); + expect(description).toBe(card.description); + }); + }); + + it('Get all IssueCards', () => { + return request(app.getHttpServer()) + .get('/trello-manager/issue') + .expect(200) + .then((res) => { + expect(res.body).toBeInstanceOf(Array); + }); + }); + + it('Get a IssueCard', () => { + const card = issueCard; + return request(app.getHttpServer()) + .post('/trello-manager') + .send(card) + .expect(201) + .then((res) => { + expect(res.body).toHaveProperty('id'); + const { id } = res.body; + return request(app.getHttpServer()) + .get(`/trello-manager/issue/${id}`) + .expect(200) + .then((res) => { + expect(res.body).toHaveProperty('id'); + expect(res.body).toHaveProperty('title'); + expect(res.body).toHaveProperty('description'); + const { title, description } = res.body; + expect(title).toBe(card.title); + expect(description).toBe(card.description); + }); + }); + }); + + it('Delete a IssueCard', () => { + const card = issueCard; + return request(app.getHttpServer()) + .post('/trello-manager') + .send(card) + .expect(201) + .then((res) => { + expect(res.body).toHaveProperty('id'); + const { id } = res.body; + return request(app.getHttpServer()) + .delete(`/trello-manager/issue/${id}`) + .expect(200); + }); + }); + + it('Create a new TaskCard', () => { + const card = taskCard; + return request(app.getHttpServer()) + .post('/trello-manager') + .send(card) + .expect(201) + .then((res) => { + expect(res.body).toHaveProperty('id'); + expect(res.body).toHaveProperty('title'); + expect(res.body).toHaveProperty('category'); + const { title, category } = res.body; + expect(title).toBe(card.title); + expect(category).toBe(card.category); + }); + }); + + it('Get all TaskCards', () => { + return request(app.getHttpServer()) + .get('/trello-manager/task') + .expect(200) + .then((res) => { + expect(res.body).toBeInstanceOf(Array); + }); + }); + + it('Get a TaskCard', () => { + const card = taskCard; + return request(app.getHttpServer()) + .post('/trello-manager') + .send(card) + .expect(201) + .then((res) => { + expect(res.body).toHaveProperty('id'); + const { id } = res.body; + return request(app.getHttpServer()) + .get(`/trello-manager/task/${id}`) + .expect(200) + .then((res) => { + expect(res.body).toHaveProperty('id'); + expect(res.body).toHaveProperty('title'); + expect(res.body).toHaveProperty('category'); + const { title, category } = res.body; + expect(title).toBe(card.title); + expect(category).toBe(card.category); + }); + }); + }); + + it('Delete a TaskCard', () => { + const card = taskCard; + return request(app.getHttpServer()) + .post('/trello-manager') + .send(card) + .expect(201) + .then((res) => { + expect(res.body).toHaveProperty('id'); + const { id } = res.body; + return request(app.getHttpServer()) + .delete(`/trello-manager/task/${id}`) + .expect(200); + }); + }); + + it('Create a new BugCard', () => { + const card = bugCard; + return request(app.getHttpServer()) + .post('/trello-manager') + .send(card) + .expect(201) + .then((res) => { + expect(res.body).toHaveProperty('id'); + expect(res.body).toHaveProperty('title'); + expect(res.body).toHaveProperty('description'); + const { description } = res.body; + expect(description).toBe(card.description); + }); + }); + + it('Get all BugCards', () => { + return request(app.getHttpServer()) + .get('/trello-manager/bug') + .expect(200) + .then((res) => { + expect(res.body).toBeInstanceOf(Array); + }); + }); + + it('Get a BugCard', () => { + const card = bugCard; + return request(app.getHttpServer()) + .post('/trello-manager') + .send(card) + .expect(201) + .then((res) => { + expect(res.body).toHaveProperty('id'); + const { id } = res.body; + return request(app.getHttpServer()) + .get(`/trello-manager/bug/${id}`) + .expect(200) + .then((res) => { + expect(res.body).toHaveProperty('id'); + expect(res.body).toHaveProperty('title'); + expect(res.body).toHaveProperty('description'); + const { description } = res.body; + expect(description).toBe(card.description); + }); + }); + }); + + it('Delete a BugCard', () => { + const card = bugCard; + return request(app.getHttpServer()) + .post('/trello-manager') + .send(card) + .expect(201) + .then((res) => { + expect(res.body).toHaveProperty('id'); + const { id } = res.body; + return request(app.getHttpServer()) + .delete(`/trello-manager/bug/${id}`) + .expect(200); + }); + }); +}); diff --git a/test/setup.ts b/test/setup.ts new file mode 100644 index 0000000..e142978 --- /dev/null +++ b/test/setup.ts @@ -0,0 +1,11 @@ +import { rm } from "fs/promises"; +import { join } from "path"; + +global.beforeEach(async () => { + try { + await rm(join(__dirname, '../prisma', 'test.db')) + } + catch (e) { + // ignore + } +}); \ No newline at end of file diff --git a/tsconfig.json b/tsconfig.json index adb614c..c7f1a7d 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -16,6 +16,9 @@ "noImplicitAny": false, "strictBindCallApply": false, "forceConsistentCasingInFileNames": false, - "noFallthroughCasesInSwitch": false + "noFallthroughCasesInSwitch": false, + "paths": { + "@/*": ["./src/*"] + } } }