Skip to content

Commit

Permalink
Merge pull request #42 from boostcampwm-2024/feature/api/stockindex-#6
Browse files Browse the repository at this point in the history
[BE] 3.03 주식 차트 정보 기능 테스트 #9
  • Loading branch information
uuuo3o authored Nov 7, 2024
2 parents 98b875c + 9d8ff33 commit 096161f
Show file tree
Hide file tree
Showing 7 changed files with 258 additions and 48 deletions.
96 changes: 65 additions & 31 deletions BE/src/stock/index/stock.index.service.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { Injectable } from '@nestjs/common';
import axios from 'axios';
import { StockIndexListChartElementDto } from './dto/stock.index.list.chart.element.dto';
import { StockIndexListElementDto } from './dto/stock.index.list.element.dto';
import { StockIndexValueElementDto } from './dto/stock.index.value.element.dto';
Expand All @@ -10,23 +11,13 @@ import {
@Injectable()
export class StockIndexService {
async getDomesticStockIndexListByCode(code: string, accessToken: string) {
const url = `${process.env.KOREA_INVESTMENT_BASE_URL}/uapi/domestic-stock/v1/quotations/inquire-index-timeprice`;
const queryParams = `?FID_INPUT_HOUR_1=300&FID_COND_MRKT_DIV_CODE=U&FID_INPUT_ISCD=${code}`;

const response = await fetch(url + queryParams, {
method: 'GET',
headers: {
'content-type': 'application/json; charset=utf-8',
authorization: `Bearer ${accessToken}`,
appkey: process.env.KOREA_INVESTMENT_APP_KEY,
appsecret: process.env.KOREA_INVESTMENT_APP_SECRET,
tr_id: 'FHPUP02110200',
custtype: 'P',
},
});
const result = await this.requestDomesticStockIndexListApi(
code,
accessToken,
);

const result: StockIndexChartInterface = await response.json();
if (result.rt_cd !== '0') throw new Error('유효하지 않은 토큰');
if (result.rt_cd !== '0')
throw new Error('데이터를 정상적으로 조회하지 못했습니다.');

return new StockIndexListElementDto(
code,
Expand All @@ -40,28 +31,71 @@ export class StockIndexService {
}

async getDomesticStockIndexValueByCode(code: string, accessToken: string) {
const url = `${process.env.KOREA_INVESTMENT_BASE_URL}/uapi/domestic-stock/v1/quotations/inquire-index-price`;
const queryParams = `?FID_COND_MRKT_DIV_CODE=U&FID_INPUT_ISCD=${code}`;
const result = await this.requestDomesticStockIndexValueApi(
code,
accessToken,
);

const response = await fetch(url + queryParams, {
method: 'GET',
headers: {
'content-type': 'application/json; charset=utf-8',
authorization: `Bearer ${accessToken}`,
appkey: process.env.KOREA_INVESTMENT_APP_KEY,
appsecret: process.env.KOREA_INVESTMENT_APP_SECRET,
tr_id: 'FHPUP02100000',
custtype: 'P',
},
});
if (result.rt_cd !== '0')
throw new Error('데이터를 정상적으로 조회하지 못했습니다.');

const result: StockIndexValueInterface = await response.json();
return new StockIndexValueElementDto(
code,
result.output.bstp_nmix_prpr,
result.output.bstp_nmix_prdy_vrss,
result.output.bstp_nmix_prdy_vrss,
result.output.bstp_nmix_prdy_ctrt,
result.output.prdy_vrss_sign,
);
}

private async requestDomesticStockIndexListApi(
code: string,
accessToken: string,
) {
const response = await axios.get<StockIndexChartInterface>(
`${process.env.KOREA_INVESTMENT_BASE_URL}/uapi/domestic-stock/v1/quotations/inquire-index-timeprice`,
{
headers: {
'content-type': 'application/json; charset=utf-8',
authorization: `Bearer ${accessToken}`,
appkey: process.env.KOREA_INVESTMENT_APP_KEY,
appsecret: process.env.KOREA_INVESTMENT_APP_SECRET,
tr_id: 'FHPUP02110200',
custtype: 'P',
},
params: {
fid_input_hour_1: 300,
fid_cond_mrkt_div_code: 'U',
fid_input_iscd: code,
},
},
);

return response.data;
}

private async requestDomesticStockIndexValueApi(
code: string,
accessToken: string,
) {
const response = await axios.get<StockIndexValueInterface>(
`${process.env.KOREA_INVESTMENT_BASE_URL}/uapi/domestic-stock/v1/quotations/inquire-index-price`,
{
headers: {
'content-type': 'application/json; charset=utf-8',
authorization: `Bearer ${accessToken}`,
appkey: process.env.KOREA_INVESTMENT_APP_KEY,
appsecret: process.env.KOREA_INVESTMENT_APP_SECRET,
tr_id: 'FHPUP02100000',
custtype: 'P',
},
params: {
fid_cond_mrkt_div_code: 'U',
fid_input_iscd: code,
},
},
);

return response.data;
}
}
3 changes: 3 additions & 0 deletions BE/src/websocket/interface/socket.interface.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export interface SocketConnectTokenInterface {
approval_key: string;
}
26 changes: 9 additions & 17 deletions BE/src/websocket/socket.service.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
import { Injectable, OnModuleInit } from '@nestjs/common';
import { WebSocket } from 'ws';
import axios from 'axios';
import { SocketGateway } from './socket.gateway';
import { StockIndexValueElementDto } from '../stock/index/dto/stock.index.value.element.dto';
import { SocketConnectTokenInterface } from './interface/socket.interface';

@Injectable()
export class SocketService implements OnModuleInit {
Expand Down Expand Up @@ -50,20 +52,16 @@ export class SocketService implements OnModuleInit {
}

private async getSocketConnectionKey() {
const url = `${process.env.KOREA_INVESTMENT_BASE_URL}/oauth2/Approval`;

const response = await fetch(url, {
method: 'POST',
headers: {
'Content-Type': 'application/json; charset=UTF-8',
},
body: JSON.stringify({
const response = await axios.post<SocketConnectTokenInterface>(
`${process.env.KOREA_INVESTMENT_BASE_URL}/oauth2/Approval`,
{
grant_type: 'client_credentials',
appkey: process.env.KOREA_INVESTMENT_APP_KEY,
secretkey: process.env.KOREA_INVESTMENT_APP_SECRET,
}),
});
const result: SocketConnectTokenInterface = await response.json();
},
);

const result = response.data;
return result.approval_key;
}

Expand All @@ -86,9 +84,3 @@ export class SocketService implements OnModuleInit {
);
}
}

// interfaces

interface SocketConnectTokenInterface {
approval_key: string;
}
29 changes: 29 additions & 0 deletions BE/test/stock/index/mockdata/stock.index.list.mockdata.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
export const STOCK_INDEX_LIST_MOCK = {
VALID_DATA: {
data: {
output: [
{
bsop_hour: '100600',
bstp_nmix_prpr: '916.77',
bstp_nmix_prdy_vrss: '11.27',
prdy_vrss_sign: '2',
bstp_nmix_prdy_ctrt: '1.24',
acml_tr_pbmn: '3839797',
acml_vol: '313374',
cntg_vol: '870',
},
],
rt_cd: '0',
msg_cd: 'MCA00000',
msg1: '정상처리 되었습니다.',
},
},
INVALID_DATA: {
data: {
output: [],
rt_cd: '1',
msg_cd: 'MCA00000',
msg1: '유효하지 않은 토큰입니다.',
},
},
};
55 changes: 55 additions & 0 deletions BE/test/stock/index/mockdata/stock.index.value.mockdata.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
export const STOCK_INDEX_VALUE_MOCK = {
VALID_DATA: {
data: {
output: {
bstp_nmix_prpr: '857.60',
bstp_nmix_prdy_vrss: '-1.61',
prdy_vrss_sign: '5',
bstp_nmix_prdy_ctrt: '-0.19',
acml_vol: '1312496',
prdy_vol: '1222188',
acml_tr_pbmn: '11507962',
prdy_tr_pbmn: '11203385',
bstp_nmix_oprc: '863.69',
prdy_nmix_vrss_nmix_oprc: '4.48',
oprc_vrss_prpr_sign: '2',
bstp_nmix_oprc_prdy_ctrt: '0.52',
bstp_nmix_hgpr: '864.24',
prdy_nmix_vrss_nmix_hgpr: '5.03',
hgpr_vrss_prpr_sign: '2',
bstp_nmix_hgpr_prdy_ctrt: '0.59',
bstp_nmix_lwpr: '854.72',
prdy_clpr_vrss_lwpr: '-4.49',
lwpr_vrss_prpr_sign: '5',
prdy_clpr_vrss_lwpr_rate: '-0.52',
ascn_issu_cnt: '828',
uplm_issu_cnt: '5',
stnr_issu_cnt: '94',
down_issu_cnt: '716',
lslm_issu_cnt: '1',
dryy_bstp_nmix_hgpr: '890.06',
dryy_hgpr_vrss_prpr_rate: '3.65',
dryy_bstp_nmix_hgpr_date: '20240109',
dryy_bstp_nmix_lwpr: '786.28',
dryy_lwpr_vrss_prpr_rate: '-9.07',
dryy_bstp_nmix_lwpr_date: '20240201',
total_askp_rsqn: '24146999',
total_bidp_rsqn: '40450437',
seln_rsqn_rate: '37.38',
shnu_rsqn_rate: '62.62',
ntby_rsqn: '16303438',
},
rt_cd: '0',
msg_cd: 'MCA00000',
msg1: '정상처리 되었습니다.',
},
},
INVALID_DATA: {
data: {
output: {},
rt_cd: '1',
msg_cd: 'MCA00000',
msg1: '유효하지 않은 토큰입니다.',
},
},
};
49 changes: 49 additions & 0 deletions BE/test/stock/index/stock.index.list.e2e-spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import { Test } from '@nestjs/testing';
import axios from 'axios';
import { StockIndexService } from '../../../src/stock/index/stock.index.service';
import { STOCK_INDEX_LIST_MOCK } from './mockdata/stock.index.list.mockdata';

jest.mock('axios');

describe('stock index list test', () => {
let stockIndexService: StockIndexService;

beforeEach(async () => {
const module = await Test.createTestingModule({
providers: [StockIndexService],
}).compile();

stockIndexService = module.get(StockIndexService);
});

it('주가 지수 차트 조회 API에서 정상적인 데이터를 조회한 경우, 형식에 맞춰 정상적으로 반환한다.', async () => {
(axios.get as jest.Mock).mockResolvedValue(
STOCK_INDEX_LIST_MOCK.VALID_DATA,
);

expect(
await stockIndexService.getDomesticStockIndexListByCode(
'code',
'accessToken',
),
).toEqual({
code: 'code',
chart: [
{
time: STOCK_INDEX_LIST_MOCK.VALID_DATA.data.output[0].bsop_hour,
value: STOCK_INDEX_LIST_MOCK.VALID_DATA.data.output[0].bstp_nmix_prpr,
},
],
});
});

it('주가 지수 차트 조회 API에서 데이터를 조회하지 못한 경우, 에러를 발생시킨다.', async () => {
(axios.get as jest.Mock).mockResolvedValue(
STOCK_INDEX_LIST_MOCK.INVALID_DATA,
);

await expect(
stockIndexService.getDomesticStockIndexListByCode('code', 'accessToken'),
).rejects.toThrow('데이터를 정상적으로 조회하지 못했습니다.');
});
});
48 changes: 48 additions & 0 deletions BE/test/stock/index/stock.index.value.e2e-spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import { Test } from '@nestjs/testing';
import axios from 'axios';
import { StockIndexService } from '../../../src/stock/index/stock.index.service';
import { STOCK_INDEX_VALUE_MOCK } from './mockdata/stock.index.value.mockdata';

jest.mock('axios');

describe('stock index list test', () => {
let stockIndexService: StockIndexService;

beforeEach(async () => {
const module = await Test.createTestingModule({
providers: [StockIndexService],
}).compile();

stockIndexService = module.get(StockIndexService);
});

it('주가 지수 값 조회 API에서 정상적인 데이터를 조회한 경우, 형식에 맞춰 정상적으로 반환한다.', async () => {
(axios.get as jest.Mock).mockResolvedValue(
STOCK_INDEX_VALUE_MOCK.VALID_DATA,
);

expect(
await stockIndexService.getDomesticStockIndexValueByCode(
'code',
'accessToken',
),
).toEqual({
code: 'code',
value: STOCK_INDEX_VALUE_MOCK.VALID_DATA.data.output.bstp_nmix_prpr,
diff: STOCK_INDEX_VALUE_MOCK.VALID_DATA.data.output.bstp_nmix_prdy_vrss,
diffRate:
STOCK_INDEX_VALUE_MOCK.VALID_DATA.data.output.bstp_nmix_prdy_ctrt,
sign: STOCK_INDEX_VALUE_MOCK.VALID_DATA.data.output.prdy_vrss_sign,
});
});

it('주가 지수 값 조회 API에서 데이터를 조회하지 못한 경우, 에러를 발생시킨다.', async () => {
(axios.get as jest.Mock).mockResolvedValue(
STOCK_INDEX_VALUE_MOCK.INVALID_DATA,
);

await expect(
stockIndexService.getDomesticStockIndexValueByCode('code', 'accessToken'),
).rejects.toThrow('데이터를 정상적으로 조회하지 못했습니다.');
});
});

0 comments on commit 096161f

Please sign in to comment.