Skip to content

Commit fc7c7ae

Browse files
committed
generator of test_private_requests added
1 parent c5296c7 commit fc7c7ae

File tree

1 file changed

+209
-0
lines changed

1 file changed

+209
-0
lines changed

build/generate_private_tests.js

Lines changed: 209 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,209 @@
1+
const { match } = require('assert');
2+
const fs = require('fs');
3+
4+
const SOURSE_FILE_NAME = './kucoin/client.py'
5+
const TARGET_FILE_NAME = './tests/test_private_requests_generated.py'
6+
const METHOD_DEFINITION_MATCH = /def\s(\w+)/
7+
const METHOD_CALL_MATCH = /self\.(\w+)\(/
8+
const REQUEST_METHODS = ['_get', '_post', '_put', '_delete']
9+
const METHOD_NAME_MATCH = /(\w+)\(/
10+
const ARGUMENTS_MATCH = /\((.*)\)/
11+
const REST_API_URL = 'https://api.kucoin.com'
12+
const REST_FUTURES_API_URL = 'https://api-futures.kucoin.com'
13+
const API_VERSIONS = {
14+
'API_VERSION': 'v1',
15+
'API_VERSION2': 'v2',
16+
'API_VERSION3': 'v3'
17+
}
18+
19+
const MANDATORY_ARGS = [ // could be a dictionary with specific values
20+
'sub_user_id',
21+
'include_base_ammount',
22+
'sub_name',
23+
'passphrase',
24+
'remark',
25+
'api_key',
26+
'account_id',
27+
'account_type',
28+
'currency',
29+
'type',
30+
'client_oid',
31+
'amount',
32+
'from_account_type',
33+
'to_account_type',
34+
'pay_account_type',
35+
'withdrawal_id',
36+
'symbols',
37+
'symbol',
38+
'side',
39+
'order_list',
40+
'order_id',
41+
'cancel_size',
42+
'timeout',
43+
'stop_price',
44+
'size',
45+
'price',
46+
'limit_price',
47+
'orders_data',
48+
'trade_type',
49+
'interest_rate',
50+
'purchase_order_no'
51+
]
52+
53+
function getPrivateMethodsArgumentsAndRequests (data) {
54+
const lines = data.split ('\n')
55+
lines.push('\n')
56+
const methods = {}
57+
let metodName = ''
58+
let methodArgs = []
59+
let lastMethodDefinition = ''
60+
for (let i = 0; i < lines.length; i++) {
61+
let line = lines[i]
62+
const methodDefinition = line.match (METHOD_DEFINITION_MATCH)
63+
const methodCall = line.match (METHOD_CALL_MATCH)
64+
if (methodDefinition) { // if line is method definition
65+
while (!line.includes (':')) {
66+
i++
67+
line += lines[i].trim()
68+
}
69+
lastMethodDefinition = line.replace ('def ', '')
70+
.replaceAll (' ', '')
71+
.replaceAll (',)', ')')
72+
.replace (':', '')
73+
continue // we need to check if this is private method
74+
} else if (methodCall) {
75+
let name = methodCall[1]
76+
if (!REQUEST_METHODS.includes (name)) { // if this is not request method just skip
77+
continue
78+
}
79+
if (line.endsWith ('(')) { // if request method is called not in one line
80+
i++
81+
let nextLine = lines[i].trim()
82+
while (nextLine !== ')') {
83+
line += nextLine
84+
i++
85+
nextLine = lines[i].trim()
86+
}
87+
line += nextLine
88+
}
89+
if (line.match (/[^,]+, True/)) { // if request method is called with second argument True
90+
if (line.indexOf ('path') !== -1) {
91+
continue // skip methods with path argument
92+
}
93+
[ metodName, methodArgs ] = getNameAndArgs (lastMethodDefinition)
94+
methods[metodName] = {
95+
args: methodArgs,
96+
request: getParamsFromRequestCall (line.trim ().replace ('return self._', '').replaceAll ("'", '"'))
97+
}
98+
}
99+
}
100+
}
101+
return methods
102+
}
103+
104+
function getNameAndArgs (line) {
105+
let name = line.match (METHOD_NAME_MATCH)[1]
106+
let args = line.match (ARGUMENTS_MATCH)[1].trim().split (',').filter (arg => (arg !== 'self' && arg !== '**params'))
107+
return [ name, args ]
108+
}
109+
110+
function getParamsFromRequestCall (line) {
111+
const matchMethodAndEndpointPattern = /(\w+)\("([^"]*)"/
112+
const matchFuturePattern = /is_futures=(\w+)/
113+
const matchVersionPattern = /version=self.(\w+)/
114+
const matchFormatPattern = /.format\((\w+)\)/
115+
const matchMethodAndEndpoint = line.match (matchMethodAndEndpointPattern)
116+
const matchFuture = line.match (matchFuturePattern)
117+
const matchVersion = line.match (matchVersionPattern)
118+
const isFutures = (matchFuture && matchFuture[1] === 'True') ? true : false
119+
const baseUrl = isFutures ? REST_FUTURES_API_URL : REST_API_URL
120+
const version = matchVersion ? API_VERSIONS[matchVersion[1]] : API_VERSIONS.API_VERSION
121+
let endpoint = matchMethodAndEndpoint[2]
122+
const matchFormat = line.match (matchFormatPattern)
123+
if (matchFormat) {
124+
endpoint = endpoint.replace (matchFormat[0], '').replace ('{}', '{' + matchFormat[1] + '}')
125+
}
126+
const url = baseUrl + '/api/' + version + '/' + endpoint
127+
return {
128+
full: line,
129+
url: url,
130+
method: matchMethodAndEndpoint[1],
131+
endpoint: endpoint,
132+
isFutures: isFutures,
133+
}
134+
}
135+
136+
function generateTests (methods) {
137+
const tests = [
138+
'import requests_mock',
139+
'import pytest',
140+
'from aioresponses import aioresponses'
141+
]
142+
const methodNames = Object.keys (methods)
143+
for (let methodName of methodNames) {
144+
const method = methods[methodName]
145+
let mandatoryArgs = generateMandatoryArgs (method)
146+
let functionArgs = ''
147+
for (let arg of mandatoryArgs) {
148+
functionArgs += '"' + arg + '", '
149+
}
150+
functionArgs = functionArgs.slice (0, -2)
151+
const request = method.request
152+
let url = request.url
153+
const paramInParth = url.match (/{(\w+)}/)
154+
if (paramInParth) {
155+
const param = paramInParth[1]
156+
mandatoryArgs = mandatoryArgs.filter (arg => arg !== param)
157+
url = url.replace (paramInParth[0], param)
158+
}
159+
160+
const test = [
161+
'\n',
162+
'def test_' + methodName + '(client):',
163+
' with requests_mock.mock() as m:',
164+
' url = "' + url + '"',
165+
' m.' + request.method + '(url, json={}, status_code=200)',
166+
' client.' + methodName + '('+ functionArgs + ')',
167+
]
168+
if (mandatoryArgs.length > 0) {
169+
if (request.method !== 'post' && request.method !== 'put') { // for methods without body
170+
for (let arg of mandatoryArgs) {
171+
test.push (' url = m.last_request._request.url')
172+
test.push (' assert "' + arg + '" in url')
173+
}
174+
} else {
175+
test.push (' body = m.last_request._request.body')
176+
for (let arg of mandatoryArgs) {
177+
test.push (' assert "' + arg + '" in body')
178+
}
179+
}
180+
}
181+
tests.push (...test)
182+
}
183+
return tests.join ('\n')
184+
}
185+
186+
function generateMandatoryArgs (method) {
187+
const args = method.args
188+
return args.filter (arg => arg.indexOf ('=') === -1)
189+
}
190+
191+
function main () {
192+
fs.readFile (SOURSE_FILE_NAME, 'utf8', (err, data) => {
193+
if (err) {
194+
console.error (err)
195+
return
196+
}
197+
const privateMethodsArgumentsAndRequests = getPrivateMethodsArgumentsAndRequests (data)
198+
const tests = generateTests (privateMethodsArgumentsAndRequests)
199+
fs.writeFile (TARGET_FILE_NAME, tests, (err) => {
200+
if (err) {
201+
console.error (err)
202+
return
203+
}
204+
console.log (TARGET_FILE_NAME + ' file is generated')
205+
})
206+
})
207+
}
208+
209+
main ()

0 commit comments

Comments
 (0)