Skip to content

Commit

Permalink
Merge pull request #57 from DevKor-github/staging
Browse files Browse the repository at this point in the history
Staging
  • Loading branch information
astorverse authored Aug 7, 2024
2 parents ead770d + 7dad4f4 commit 0884592
Show file tree
Hide file tree
Showing 12 changed files with 574 additions and 150 deletions.
37 changes: 37 additions & 0 deletions .github/workflows/pr-tests.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
name: PR Tests

on:
pull_request:
branches:
- staging
- master

jobs:
test:
runs-on: ubuntu-latest

steps:
- name: Checkout repository
uses: actions/checkout@v2

- name: Set up Node.js
uses: actions/setup-node@v4
with:
node-version: '21.7.2'

- name: Install dependencies
run: npm install

- name: Run tests
run: npm test

# - name: Upload test results
# if: always()
# uses: actions/upload-artifact@v2
# with:
# name: test-results
# path: test-results # Adjust the path according to your test results directory

- name: Notify test results
if: failure()
run: echo "Tests failed!"
193 changes: 193 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -205,3 +205,196 @@ erDiagram
sticker_category_mapper ||--o{ sticker : maps_to
sticker_category_mapper ||--o{ sticker_category : categorized_as
```

## 커밋 컨벤션

## 네이밍 룰의 우선사항

**엔티티 중심의 클래스 작명**: {Entity}{Action}{Purpose}{Layer}

`UserCreateResponseDto, ArticlesCreateService`

**Layer 중심의 인스턴스 작명**: {Layer}\_{Entity}{Action}{Purpose}

`svc_usersCreate, dto_userCreateResponse`

**선택적 도메인과 접두사 활용해 지역변수 및 필드 작명**: ({prefix})({Domain}){Name}

`isPubluished, userId, articleId`

## DTO 네이밍 룰

**Common DTO**: 모든 계층에서 공통으로 사용할 수 있는 기본 DTO.

`{Entity}Dto`

**Request DTO**: 클라이언트 -> 컨트롤러 간의 데이터 전송.

`{Entity}{Action}RequestDto`

**Response DTO**: 컨트롤러 -> 클라이언트 간의 데이터 전송. 혹은 공통된 response일 때 사용.

`{Entity}{Action}ResponseDto`

**Service Output DTO**: 서비스 -> 컨트롤러 간의 별도의 데이터 전송.

`{Entity}{Action}SvcOutputDto`

**Repository Output DTO**: 레포지토리 -> 서비스 간의 별도의 데이터 전송.

`{Entity}{Action}RepoOutputDto`

**Service Interface**: 컨트롤러 -> 서비스 간의 데이터 전송.

`I{Entity}{Layer}{Action}`

**Repository Interface**: 서비스 -> 레포지토리 간의 데이터 전송.

`I{Entity}{Layer}{Action}`

## 메서드 네이밍 룰

- **컨트롤러 메서드**: HTTP 요청 중점 네이밍
- 생성: `create{Entity}`
- 조회: `get{Entity}`, `get{Entities}`
- 업데이트: `patch{Entity}`, `put{Entity}`
- 삭제: `delete{Entity}`
- **서비스 메서드**: Entity 명시
- 생성: `create{Entity}`
- 조회: `find{Entities}`, `find{Entity}By{Criteria}`
- 복합 조회: `read{Object}`
- 업데이트: `update{Entity}`
- 삭제: `delete{Entity}`
- **레포지토리 메서드**: Entity 생략
- 생성: `save`, `create`
- 조회: `findById`, `findAll`, `findBy{Criteria}`
- 업데이트: `update`
- 삭제: `delete`

## 테스트 코드 네이밍 룰

### jest 코드

```typescript
describe('{layer}', () => {
describe('{method}', async () => {
it('should {result}{condition}', () => {});
});
});
```

### 예시

```typescript
AgreementsService
createAgreement
should return AgreementDto with valid input (6 ms)
existCheck
should throw exception when agreement does not exist (5 ms)
should return AgreementDto when agreement exists (2 ms)
```

## 접두사 정리

### 엔티티 (Entity)

- **접두사:** `ent_`
- **예시:** `ent_agreement`

### 모듈 (Module)

- **접두사:** `mod_`
- **예시:** `mod_`

### 서비스 (Service)

- **접두사:** `svc_`
- **예시:** `svc_agreements`

### 레포지토리 (Repository)

- **접두사:** `repo_`
- **예시:** `repo_agreements`

### 컨트롤러 (Controller)

- **접두사:** `ctrl_`
- **예시:** `ctrl_user`

### DTO (Data Transfer Object)

- **접두사:** `dto_`
- **예시:** `dto_agreement`

### DB 관련 변수 (db connection pool 등)

- **접두사:** `db_`
- **예시:** `db_redisQueue`, `db_dataSource`

커밋 메세지과 머지 룰

### 커밋 컨벤션

#### 커밋 메시지 양식

- 커밋 메시지는 다음 형식을 따릅니다:
```
type(scope): subject
```
- 예시:
```
refactor(user): change variables name
```

#### 커밋 메시지 구성 요소

- **type**: 커밋의 종류를 나타냅니다.
- `feat`: 새로운 기능 추가
- `fix`: 버그 수정
- `docs`: 문서 변경
- `style`: 코드 포맷팅, 세미콜론 누락 등 (비즈니스 로직에 변화가 없는 경우)
- `refactor`: 코드 리팩토링 (기능 변화 없음)
- `perf`: 성능 개선
- `test`: 테스트 추가 또는 수정
- `chore`: 빌드 과정 또는 보조 도구 수정 (라이브러리, 환경 설정 파일 등)
- **scope**: 커밋 범위를 나타냅니다.
- **subject**: 변경 사항에 대한 간결한 설명 (필수).

#### 본문 내용 (선택 사항)

- 필요한 경우, 본문에 추가 설명을 작성할 수 있습니다.
- 왜 이 변경을 했는지, 무엇을 변경했는지에 대한 배경 정보를 포함할 수 있습니다.

### 머지 룰

#### 브랜치 구조

- `develop`: 개발 중인 브랜치
- `staging`: 테스트 및 검증을 위한 브랜치
- `master`: 프로덕션 배포를 위한 브랜치
- `{type}/{subject}`: 세부 기능 개발을 위한 브랜치

#### 머지 절차

1. **개발 작업**

- 새로운 기능 또는 버그 수정은 개별 기능 브랜치에서 작업합니다.
- 작업이 완료되면 `develop` 브랜치에 pr을 올립니다.

2. **검토 및 테스트**

- 모든 변경 사항은 `develop` 브랜치에서 충분히 검토하고 테스트합니다.
- 변경 사항이 검토되고 테스트가 완료되면 `develop` 브랜치를 `staging` 브랜치로 머지합니다.

3. **스테이징 환경에서 검증**

- `staging` 브랜치에서 애플리케이션이 정상적으로 작동하는지 확인합니다.
- 테스트가 완료되고 모든 기능이 정상 작동하면 `staging` 브랜치를 `master` 브랜치로 머지합니다.

https://staging.api.blccu.com

4. **프로덕션 배포**

- `master` 브랜치에 머지된 변경 사항은 프로덕션 환경에 배포됩니다.

https://api.blccu.com
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
"deploy:staging": "./deploy/deploy-staging.sh",
"deploy:prod": "./deploy/deploy-prod.sh",
"lint": "eslint \"{src,apps,libs,test}/**/*.ts\" --fix",
"test": "jest",
"test": "jest --verbose",
"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",
Expand Down
114 changes: 114 additions & 0 deletions src/APIs/agreements/__test__/agreements.controller.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
import { Test, TestingModule } from '@nestjs/testing';
import { AgreementsService } from '../agreements.service';
import { AgreementDto } from '../dtos/common/agreement.dto';
import {
MockService,
MockServiceFactory,
TEST_DATE_FIELDS,
} from '@/utils/test.utils';
import { AgreementType } from '@/common/enums/agreement-type.enum';
import { AgreementsController } from '../agreements.controller';
import { AgreementCreateRequestDto } from '../dtos/request/agreement-create-request.dto';
import { Request } from 'express';
import { AgreementPatchRequestDto } from '../dtos/request/agreement-patch-request.dto';

describe('AgreementsService', () => {
let ctrl_agreements: AgreementsController;
let svc_agreements: MockService<AgreementsService>;
let dto_agreement: AgreementDto;

beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
controllers: [AgreementsController],
providers: [
{
provide: AgreementsService,
useValue: MockServiceFactory.getMockService(AgreementsService),
},
],
}).compile();

ctrl_agreements = module.get<AgreementsController>(AgreementsController);
svc_agreements =
module.get<MockService<AgreementsService>>(AgreementsService);
dto_agreement = {
id: 1,
userId: 1,
agreementType: AgreementType.TERMS_OF_SERVICE,
isAgreed: true,
...TEST_DATE_FIELDS,
};
});

describe('agree', () => {
it('should return AgreementDto with valid input', async () => {
const req = { user: { userId: 1 } } as Request;
const dto: AgreementCreateRequestDto = {
agreementType: AgreementType.TERMS_OF_SERVICE,
isAgreed: true,
};
svc_agreements.createAgreement.mockResolvedValue(dto_agreement);

const result = await ctrl_agreements.agree(req, dto);
expect(result).toEqual(dto_agreement);
expect(svc_agreements.createAgreement).toHaveBeenCalledWith({
...dto,
userId: 1,
});
});
});

describe('fetchAgreements', () => {
it('should fetch agreements for the user', async () => {
const req = { user: { userId: 1 } } as Request;
svc_agreements.findAgreements.mockResolvedValue([dto_agreement]);

const result = await ctrl_agreements.fetchAgreements(req);
expect(result).toEqual([dto_agreement]);
expect(svc_agreements.findAgreements).toHaveBeenCalledWith({ userId: 1 });
});
});

describe('fetchAgreementAdmin', () => {
it('should fetch agreements for the target user as admin', async () => {
const req = { user: { userId: 1 } } as Request;
const targetUserKakaoId = 2;
svc_agreements.adminCheck.mockResolvedValue(true);
svc_agreements.findAgreements.mockResolvedValue([dto_agreement]);

const result = await ctrl_agreements.fetchAgreementAdmin(
req,
targetUserKakaoId,
);
expect(result).toEqual([dto_agreement]);
expect(svc_agreements.adminCheck).toHaveBeenCalledWith({ userId: 1 });
expect(svc_agreements.findAgreements).toHaveBeenCalledWith({
userId: targetUserKakaoId,
});
});
});
describe('patchAgreement', () => {
it('should patch an agreement', async () => {
const req = { user: { userId: 1 } } as Request;
const agreementId = 1;
const patchAgreementInput: AgreementPatchRequestDto = { isAgreed: false };
const patchAgreementOutput: AgreementDto = {
...dto_agreement,
isAgreed: patchAgreementInput.isAgreed,
};
svc_agreements.patchAgreement.mockResolvedValue(patchAgreementOutput);

const result = await ctrl_agreements.patchAgreement(
req,
agreementId,
patchAgreementInput,
);
expect(result).toEqual(patchAgreementOutput);
expect(svc_agreements.patchAgreement).toHaveBeenCalledWith({
...patchAgreementInput,
agreementId,
userId: req.user.userId,
});
});
});
});
Loading

0 comments on commit 0884592

Please sign in to comment.