don't stop believing

blockchain 테스트에 사용한 python 함수 - callRPC 본문

Python/Basic

blockchain 테스트에 사용한 python 함수 - callRPC

Tongchun 2018. 12. 20. 12:06

blockchain 테스트에 사용한 python 함수를 설명합니다.

  • getDateStrings - 날짜 - 년, 월, 일, 시, 분, 초를 확인하는 함수입니다.
  • getLogfileNameWithPath - 로그 기록을 위해 폴더를 생성하고 경로를 반환합니다.
  • getRequestId - ramdom으로 숫자를 반환합니다.
  • moveFile - 특정 파일을 로그 폴더로 이동시킵니다.
  • saveResultData - 데이터를 파일에 저장합니다.
  • convertToHex - String을 hex로 변환합니다.
  • leftPad64 - 왼쪽에 (또는 오른쪽에) 0을 채워 고정된 길이로 만듭니다.
  • getTestConfig - json 형태의 config 파일을 만들고 불러옵니다.
  • waitingCount - time.sleep() 할 때 동적으로 카운트를 셉니다.
  • callRPC - requests 모듈을 사용해 http API를 호출합니다.
  • callWS - websocket 모듈을 사용해 API 통신을 합니다.


이번에는 requests 모듈을 이용해 http post method를 호출하는 함수입니다.

Blockchain의 RPC에 연결해 어러 API를 호출하고 결과를 가져옵니다.


그리고 로그를 기록하는 함수까지 확인해 보겠습니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
# coding=utf-8
import requests
import unittest
import json, random
import time, datetime
import sys, pathlib
from pyunitreport import HTMLTestRunner
class nGleUtils():
def getRequestId(self):
'''API request dat id int 1 99 .'''
return random.randint(1,100)
def getDateStrings(self):
''' .'''
i = datetime.datetime.now()
dt = datetime.datetime(i.year, i.month, i.day, i.hour, i.minute, i.second)
ts = time.mktime(dt.timetuple())
return (i, dt, ts)
def getLogfileNameWithPath(self):
'''
log file .
.
'''
# .
i, dt, ts = self.getDateStrings()
# . (:'%Y%m%d%H%M%S')
logFolderName = datetime.datetime.fromtimestamp(int(ts)).strftime('%Y%m%d%H%M%S')
# .
logPath = F"./reports/{logFolderName}"
pathlib.Path(logPath).mkdir(parents=True, exist_ok=True)
# .
logFileName = F"{logPath}/klaytn.log"
# .
return logPath, logFolderName, logFileName
def waitingCount(self, frontString, second, rearString):
'''
.
ex) self.ngle.waitingCount("Waiting for", 5, "seconds until writing in block a transaction.")
'''
for i in range(second, -1, -1):
sys.stdout.write(F"\r{frontString} {i} {rearString}")
sys.stdout.flush()
time.sleep(1)
sys.stdout.write("\n\n")
def callRPC(self, nodeHost, rpcPort, methodName, params, logfile, saveResult=False):
# methodName param Klaytn RPC request .
HOST = F"http://{nodeHost}:{str(rpcPort)}"
HEADERS = {'Content-Type': 'application/json'}
isErrer = False
# blockchain RPC . ( RESTful API .)
payload = {'jsonrpc':'2.0','method':methodName,'params':params,'id':self.getRequestId()}
# post method . method input .
response = requests.post(HOST, data=json.dumps(payload), headers=HEADERS, timeout=300)
# request response .
self.writeLog(logfile, methodName, HOST, payload, response.text, saveFile=saveResult)
try:
# result key .
resultText = json.loads(response.text)['result']
except KeyError:
# errer key .
resultText = json.loads(response.text)['error']
isErrer = True
return (response.status_code, resultText, isErrer)
def writeLog(self, logFileName, klayMethod, host, data, resultText, testingStatus="", testingUnit="", saveFile=False):
'''
Testing .
debug_dumpBlock result .
'''
# result .
if saveFile:
resultText = F"{{\"nGle_Message\": \"{klayMethod} data was saved at a specific file.\"}}"
# testingStatus "Start" "Finish" .
if testingStatus == "Start":
logString = F"------------ Testing Start ({testingUnit}) ------------- \n\n"
elif testingStatus == "Finish":
logString = F"------------ Testing Finish ({testingUnit}) ------------ \n\n"
else:
# testingStatus "Start" "Finish" requests request, response .
# curl curl command .
curlString = F"curl -H \"Content-Type: application/json\" --data '{str(json.dumps(data))}' {host}"
jsonResult = json.loads(resultText)
logString = (
F"[{str(datetime.datetime.now())}] [{klayMethod}]\n"
F"{curlString}\n"
F"{json.dumps(jsonResult, indent=4, sort_keys=True)} \n\n"
)
print(logString)
with open(logFileName, 'at') as f:
f.write(F"\n{logString}")
class BlockchainSample(unittest.TestCase):
ngle = nGleUtils()
LOGFOLDER, LOGDATE, LOGPATH = ngle.getLogfileNameWithPath()
blockHost = "10.10.1.168"
rpcPort = 8551
def setUp(self):
# unittest TestCase .
print('Test Start')
def test_Accounts_RPC(self):
logTitle = "Account_RPC"
# test log Unit Test .
self.ngle.writeLog(self.LOGPATH, "", "", "", "", testingStatus="Start", testingUnit=logTitle)
# klay_accounts ##############################################################################
methodName = "klay_accounts"
params = []
statusCode, result, isError = self.ngle.callRPC(self.blockHost, self.rpcPort, methodName, params, self.LOGPATH)
self.assertEqual(200, statusCode, "Status Code is not 200.")
self.assertFalse(isError, result)
# 5 RPC .
self.ngle.waitingCount("Waiting for", 5, "seconds until writing in block a transaction.")
# klay_blockNumber ##############################################################################
methodName = "klay_blockNumber"
params = []
statusCode, result, isError = self.ngle.callRPC(self.blockHost, self.rpcPort, methodName, params, self.LOGPATH)
self.assertEqual(200, statusCode, "Status Code is not 200.")
self.assertFalse(isError, result)
# test log Unit Test .
self.ngle.writeLog(self.LOGPATH, "", "", "", "", testingStatus="Finish", testingUnit=logTitle)
def tearDown(self):
# unittest TestCase .
print('Test Finish')
def suite():
# TestSuite Test addTest .
suite = unittest.TestSuite()
suite.addTest(BlockchainSample('test_Accounts_RPC'))
return suite
# testcase .
if __name__== "__main__":
runner = unittest.TextTestRunner()
# HTMLTestRunner .
kwargs = {
"output": BlockchainSample.LOGDATE,
"report_name": "klayTestReport",
"failfast": True
}
runner = HTMLTestRunner(**kwargs)
runner.run(suite())
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX

먼저 nGleUtils 클래스를 확인해 보겠습니다.


getRequestId()

앞서 설명한 random 함수를 이용해 1에서 99까지의 id를 리턴합니다.

http://dejavuqa.tistory.com/294


getDateStrings()

여러 형태로 날짜와 시간을 확인하는 함수입니다.

http://dejavuqa.tistory.com/292


getLogfileNameWithPath()

로그를 기록할 폴더와 파일명 (경로)를 확인하는 함수입니다.

http://dejavuqa.tistory.com/293


waitingCount()

두 API 호출 사이 시간 간격을 주기위해 카운트를 셈니다.

http://dejavuqa.tistory.com/300


callRPC()

이번에 설명할 함수입니다. 호출은 아래와 같이 합니다.

callRPC(nodeHost, rpcPort, methodName, params, logfile, saveResult=False)

nodeHost는 실제 RPC(http)가 실행된 blockchain node IP 입니다.

rpcPort는 말그대로 접속 port 입니다. port는 node 실행할때 설정되며 node마다 다를 수 있습니다.

methodName은 API 종류입니다.

params는 API에 따른 input parameter입니다. params의 타입은 list 입니다.

logfile은 로그가 기록된 파일 경로입니다. getLogfileNameWithPath() 함수에서 가져옵니다.

saveResult는 API 호출 후 결과가 파일로 남을때 로그 기록용으로 사용합니다. False일때는 API의 결과를 그대로 사용하고 True라면 지정된 결과를 기록합니다.


리턴되는 데이터는 아래와 같습니다.

return (response.status_code, resultText, isErrer)

response.status_code는 http 결과 코드 입니다. 성공이라면 200, 경로를 찾을 수 없다면 404, 내부 오류라면 500 등이 리턴되게 됩니다.

resultText는 response.text 데이터 중 'result'에 해당하는 데이터를 반환합니다.

isError는 에러 유무를 리턴합니다. blockchain 로직에 대한 에러가 발생할 경우 status_code는 200으로 리턴되지만 request.text의 데이터는 errer message가 옵니다. json parsing 중 에러가 난다면 error message로 판단하고 isError를 True로 반환합니다.


writeLog()

request와 response를 그대로 로그파일에 기록합니다.

writeLog() 함수는 callRPC() 함수 안에서 호출됩니다. input parameter는 아래와 같습니다.

writeLog(logFileName, klayMethod, host, data, resultText, testingStatus="", testingUnit="", saveFile=False)

logFileName은 callRPC의 input으로 넘긴 logfile과 같습다.

klayMethod는 Blockchain의 API method입니다.

host는 callRPC에서 nodeHost와 rpcPort를 조합한 host 입니다.

data는 API 호출의 body 데이터 입니다. callRPC에서 payload 변수로 만들어집니다.

resultText는 response.text를 그대로 넘겨줍니다.

testingStatus는 로그 파일을 만들 때 Testcase 별로 구분해 주기위해 사용됩니다. "Start"또는 "Finish"로 넘겨주면 구분선이 기록됩니다.

testingUnit 는 Testcase 이름이나 테스트 영역에 대한 구분을 넣으면 됩니다. 타입은 String입니다.

saveFile은 callRPC()의 saveResult를 그대로 넘겨 줍니다.


BlockchainSample 클래스는 위 함수들을 이용해 unittest를 실행한 것입니다.

설명은 생략하겠습니다.


위 스크립트를 실행하면 아래와 같이 Terminal에 출력됩니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
$ python callRPC.py
Running tests...
----------------------------------------------------------------------
Test Start
------------ Testing Start (Account_RPC) -------------
[2018-12-20 11:31:43.436163] [klay_accounts]
curl -H "Content-Type: application/json" --data '{"jsonrpc": "2.0", "method": "klay_accounts", "params": [], "id": 51}' http://10.10.1.168:8551
{
"id": 51,
"jsonrpc": "2.0",
"result": [
"0x859ba4df391ffb81a67a050097527a2c02aef596",
"0x2259cfdae62f9853f84298aaf20c999391b1c6a3",
"0x9789e98e28cbc7f6ff2d6a0f71586e1c10cf829a",
"0xc346de6bd0f3f75d852f481f9f35e3a6452a977e",
"0xabd8ec30f8f896844f01ecb9e7ddb418354d90e5"
]
}
Waiting for 0 seconds until writing in block a transaction.
[2018-12-20 11:31:49.467644] [klay_blockNumber]
curl -H "Content-Type: application/json" --data '{"jsonrpc": "2.0", "method": "klay_blockNumber", "params": [], "id": 1}' http://10.10.1.168:8551
{
"id": 1,
"jsonrpc": "2.0",
"result": "0x67d6bd"
}
------------ Testing Finish (Account_RPC) ------------
Test Finish
test_Accounts_RPC (__main__.BlockchainSample) ... OK (6.053954)s
----------------------------------------------------------------------
Ran 1 test in 6.054s
OK
Generating HTML reports...
Template is not specified, load default template instead.
Reports generated: /Users/tongchunkim/Documents/Test_Python/used_functions/reports/20181220113143/klayTestReport.html
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX

실행 후 reports 폴더와 하위에 날짜와 시간으로 구성된 폴더(20181220113143)가 생성됩니다.

안에 보면 HTMLTestRunner에 의해 생성된 klayTestReport.html 파일과 writeLog() 함수로 작성된 klaytn.log 파일이 생성되어 있습니다.


klayTestReport.html 파일은 아래와 같이 unittest의 결과가 출력됩니다.

HTMLTestRunner에 대한 소개는 아래 링크에서 확인할 수 있습니다.

http://dejavuqa.tistory.com/286


klaytn.log 파일은 아래와 같이 작성되어 있습니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
------------ Testing Start (Account_RPC) -------------
[2018-12-20 11:31:43.436163] [klay_accounts]
curl -H "Content-Type: application/json" --data '{"jsonrpc": "2.0", "method": "klay_accounts", "params": [], "id": 51}' http://10.10.1.168:8551
{
"id": 51,
"jsonrpc": "2.0",
"result": [
"0x859ba4df391ffb81a67a050097527a2c02aef596",
"0x2259cfdae62f9853f84298aaf20c999391b1c6a3",
"0x9789e98e28cbc7f6ff2d6a0f71586e1c10cf829a",
"0xc346de6bd0f3f75d852f481f9f35e3a6452a977e",
"0xabd8ec30f8f896844f01ecb9e7ddb418354d90e5"
]
}
[2018-12-20 11:31:49.467644] [klay_blockNumber]
curl -H "Content-Type: application/json" --data '{"jsonrpc": "2.0", "method": "klay_blockNumber", "params": [], "id": 1}' http://10.10.1.168:8551
{
"id": 1,
"jsonrpc": "2.0",
"result": "0x67d6bd"
}
------------ Testing Finish (Account_RPC) ------------
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX


여기까지 callRPC() 함수와 unittest를 이용한 테스트 설명이었습니다.