-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathtests.py
359 lines (305 loc) · 17.2 KB
/
tests.py
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
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
import os.path
from os import remove
from aiounittest import AsyncTestCase
from unittest import TestCase, main, skipIf
from pathlib import Path
from parser import Parser
from main import Main
from db import SQLite
from settings import SHOPS, BRANDS_URLS, CSV_FILE, JSON_FILE, XML_FILE
SKIP = False # set False to check all tests
# more cases relevant only in oct- nov 2021
@skipIf(SKIP, 'skip parse_main')
class TestAsyncParseMain(AsyncTestCase):
async def test_parse_main_correct_data(self):
cases = (['http://www.kant.ru/brand/brooks/products/'], [BRANDS_URLS[-1]])
solution_str = 'https://www.kant.ru/catalog/product' # any solution item must be starts with this str
for i in cases:
with self.subTest(case=i):
response = await Parser.parse_main(i, 1)
self.assertEqual(response[0][:35], solution_str)
async def test_parse_main_counts_and_values(self):
case = ['https://www.kant.ru/catalog/shoes/running-shoes/brand-asics/']
small_page_count = 1 # min pag pars
approx_min_values = 24 # an usual, 24 items in one main parsing page and less, if it is last main page
response = await Parser.parse_main(case, small_page_count)
self.assertLessEqual(len(response), approx_min_values)
async def test_parse_main_max_pagination(self):
case = ['https://www.kant.ru/catalog/shoes/running-shoes/brand-asics/']
big_page_num = 30 # max pagination parsing
approx_count = 100 # from Asics many deep models line, if parsed all pages
solution = 'https://www.kant.ru/catalog/product/3052137/' # actual for jul 2021
response = await Parser.parse_main(case, big_page_num)
self.assertIn(solution, response)
self.assertGreater(len(response), approx_count)
async def test_parse_main_uncorrect_url(self):
with self.assertRaises(ValueError):
await Parser.parse_main(['kant.ru/catalog/shoes/running-shoes/brand-asics/'], 1)
async def test_parse_main_bad_kant_ru_url(self):
response = await Parser.parse_main(['http://www.kant.ru/brand/NOEXISTBRAND/products/'], 1)
self.assertEqual(response, list())
async def test_parse_main_uncorrect_type(self):
case = 'http://www.kant.ru/catalog/shoes/running-shoes/brand-asics/'
with self.assertRaises(TypeError):
await Parser.parse_main(case, 1) # need list of strs, send str
with self.assertRaises(TypeError):
await Parser.parse_main([case], 1.0) # need int value to pagination
async def test_parse_main_uncorrect_value(self):
case = ['http://www.kant.ru/catalog/shoes/running-shoes/brand-asics/']
with self.assertRaises(ValueError):
await Parser.parse_main(case, 0)
with self.assertRaises(ValueError):
await Parser.parse_main(case, 40)
@skipIf(SKIP, 'skip parse_details')
class TestAsyncParseDetails(AsyncTestCase):
async def test_parse_details_correct_data(self):
cases = ['https://www.kant.ru/catalog/product/1693473/', 'https://www.kant.ru/catalog/product/3052137/',
'https://www.kant.ru/catalog/product/2906145/'
]
solution_count_positions = 14
for i in cases:
with self.subTest(case=i):
response = await Parser.parse_details([i])
solution = response[0]
self.assertEqual(len(solution), solution_count_positions)
self.assertEqual((type(solution[0]), type(solution[1]), type(solution[2]), type(solution[3]),
type(solution[4]), type(solution[5]), type(solution[6]), type(solution[7]),
type(solution[8]), type(solution[9]), type(solution[10]), type(solution[11]),
type(solution[12]), type(solution[13])
),
(int, str, str, str, str, str, str, int, str, str, str, str, int, str)
)
async def test_parse_details_correct_year(self):
cases = ['https://www.kant.ru/catalog/product/1693473/', 'https://www.kant.ru/catalog/product/2783417/']
for i in cases:
with self.subTest(case=i):
response = await Parser.parse_details([i])
solution = response[0]
self.assertIsNot(solution[7], 0)
self.assertGreater(solution[7], 2015)
self.assertLess(solution[7], 2050)
async def test_parse_details_bad_url(self):
case = 'https://www.kant.ru/catalog/product/7777777/'
response = await Parser.parse_details([case])
self.assertEqual(response, list())
async def test_parse_details_uncorrect_type(self):
case_without_list = 'https://www.kant.ru/catalog/product/3052137/'
with self.assertRaises(TypeError):
await Parser.parse_details(case_without_list) # need list of strs, send str
async def test_parse_details_uncorrect_url_format(self):
case = ['catalog/product/2780445/'] # raw data from kant.ru without program preset
with self.assertRaises(ValueError):
await Parser.parse_details(case)
@skipIf(SKIP, 'skip parse_price')
class TestAsyncParsePrice(AsyncTestCase):
async def test_parse_price_correct_data(self):
cases = [(1626114, 'https://www.kant.ru/catalog/product/2906145/'),
(1599205, 'https://www.kant.ru/catalog/product/2780445/')
]
for i, (code, url) in enumerate(cases):
with self.subTest(case=(code, url)):
response = await Parser.parse_price([(code, url)])
self.assertEqual(response[0][0], code)
self.assertGreater(response[0][1], 1000) # min price
self.assertLess(response[0][1], 30000) # max price
async def test_parse_price_bad_data_type(self):
case_reversed_code_url_values = [('https://www.kant.ru/catalog/product/2906145/', 1626114)]
case_without_list = (1626114, 'https://www.kant.ru/catalog/product/2906145/')
with self.assertRaises(TypeError):
await Parser.parse_price(case_reversed_code_url_values)
with self.assertRaises(TypeError):
await Parser.parse_price(case_without_list)
async def test_parse_bad_url(self):
case = [(1626114, 'catalog/product/2906145/')]
with self.assertRaises(ValueError):
await Parser.parse_price(case)
@skipIf(SKIP, 'skip parse_available')
class TestAsyncParseAvailable(AsyncTestCase):
async def test_parse_available_correct_data(self):
cases = [(1663184, 3094643), (1648135, 3006103), (1653643, 3052219)]
for (code, url_code) in cases:
with self.subTest(case=(code, url_code)):
response = await Parser.parse_available([(code, url_code)])
response_code = response[0][0]
response_instock = response[0][1]
is_shops = [i for i in response_instock.keys() if i in SHOPS]
size_value = response_instock[is_shops[0]][0]
self.assertEqual(response_code, code) # первый параметр-- код, тот же, что и на входе
self.assertEqual(type(response_instock), dict) # вывод в dict(), магазины и размерный ряд в них
self.assertTrue(is_shops) # хотя бы в в 1 магазине есть хотя бы 1 товар
self.assertEqual(type(size_value), tuple) # в dict магазинов действительно находятся пары: size, count
self.assertEqual(type(size_value[0]), float) # графа 'размер' содержит float значение
self.assertEqual(type(size_value[1]), int) # значение количества товара определенного размера в int
async def test_parse_available_reversed_codes(self):
case = [(3006103, 1648135)]
response = await Parser.parse_available(case)
self.assertEqual(response[0][1], dict()) # пустой dict, если перепутали коды местами
async def test_parse_available_without_url_code(self):
case = [(1648135)] # code must be with code_url in tuple
with self.assertRaises(TypeError):
await Parser.parse_available(case)
async def test_parse_available_bad_type_including_codes(self):
case = [1648135] # real code without his code url pair
with self.assertRaises(TypeError):
await Parser.parse_available(case)
async def test_parse_available_bad_type_value(self):
case = [('1648135', '3006103')] # support only int, not str type
with self.assertRaises(TypeError):
await Parser.parse_available(case)
# True start main parsing class.
# Run after filling 'products' table from test_update_products() and test_update_prices() or uncomment these cases
@skipIf(SKIP, 'skip main page parsing')
class TestMain(TestCase):
def test_update_products(self):
main_page = Main('On')
solution = main_page.update_products_table()
self.assertTrue(solution)
def test_update_prices(self):
main_page = Main('Brooks')
# uncomment the lines below if 'products' table is empty (if this brand does not exist)
# solution = main_page.update_prices_table()
# self.assertTrue(solution)
solution = main_page.update_prices_table()
self.assertTrue(solution)
def test_update_instock(self):
main_page = Main('Saucony')
# uncomment the lines below if 'products' table is empty (if this brand does not exist)
# solution = main_page.update_instock_table()
# self.assertTrue(solution)
solution = main_page.update_instock_table()
self.assertTrue(solution)
@skipIf(SKIP, 'skip test db')
class TestDb(TestCase):
"""
Test django database by ordered by logger/models.py
model Product mapping to 'products' table in database
model Prices mapping to 'prices' table
model InstockNagornaya mapping to 'instock_nagornaya' table
Use after filling all working tables. See README.txt or main.py
"""
def __init__(self, *args, **kwargs):
super(TestDb, self).__init__(*args, **kwargs)
self.db = SQLite()
def test_connect(self):
self.assertIsNotNone(self.db.conn)
def test_products_contains(self):
solution = self.db.test_products()
self.assertEqual(len(solution), 14) # count of fields
# Products 'products' fields: code, brand, model, url, img, age, gender, year, use, pronation, article,
# season, timestamp, rating
self.assertEqual((type(solution[0]), type(solution[1]), type(solution[2]), type(solution[3]),
type(solution[4]), type(solution[5]), type(solution[6]), type(solution[7]),
type(solution[8]), type(solution[9]), type(solution[10]), type(solution[11]),
type(solution[12]), type(solution[13])
),
(int, str, str, str, str, str, str, int, str, str, str, str, str, int)
) # true ordering
self.assertGreater(solution[0], 1_000_000) # check correct code value
self.assertLess(solution[0], 2_000_000) # correct code: 1_000_000 < code < 2_000_000
def test_prices_contains(self):
solution = self.db.test_prices()
self.assertEqual(len(solution), 4) # count of fields
# Prices 'prices' fields: code_id, price, timestamp, rating
self.assertEqual((type(solution[0]), type(solution[1]), type(solution[2]), type(solution[3])),
(int, int, str, int)
) # true ordering
def test_instock_nagornaya(self): # others instock_... tables work similarly
solution = self.db.test_instock_nagornaya()
self.assertEqual(len(solution), 5) # count of fields
# InstockNagornaya 'instock_nagornaya' fields: code_id, size, count, timestamp, rating
self.assertEqual((type(solution[0]), type(solution[2]), type(solution[3]), type(solution[4])),
(int, int, str, int)
) # true ordering
size_is_digit = (type(solution[1]) == float) or (type(solution[1]) == int)
self.assertTrue(size_is_digit)
def test_export_card_and_price(self):
solution = self.db.export_card_and_price()
self.assertEqual(type(solution), list)
self.assertEqual(len(solution[0]), 13)
# Products 'products' fields: code, model, brand, price, url, img, age, gender, year, use, pronation, article,
# season
self.assertEqual((type(solution[0][0]), type(solution[0][1]), type(solution[0][2]), type(solution[0][3]),
type(solution[0][4]), type(solution[0][5]), type(solution[0][6]), type(solution[0][7]),
type(solution[0][8]), type(solution[0][9]), type(solution[0][10]), type(solution[0][11]),
type(solution[0][12])
),
(int, str, str, int, str, str, str, str, int, str, str, str, str)
) # true ordering
def test_export_card_and_price_by_code(self):
solution = self.db.export_card_and_price(1646099)
self.assertEqual(type(solution), list)
self.assertEqual(len(solution[0]), 13)
# Products 'products' fields: code, model, brand, price, url, img, age, gender, year, use, pronation, article,
# season
self.assertEqual((type(solution[0][0]), type(solution[0][1]), type(solution[0][2]), type(solution[0][3]),
type(solution[0][4]), type(solution[0][5]), type(solution[0][6]), type(solution[0][7]),
type(solution[0][8]), type(solution[0][9]), type(solution[0][10]), type(solution[0][11]),
type(solution[0][12])
),
(int, str, str, int, str, str, str, str, int, str, str, str, str)
) # true ordering
def test_export_availability(self):
solution = self.db.export_available(1646099)
self.assertTrue(type(solution), dict) # real card description if ok or code is incorrect if Test Error happen
self.assertGreater(len(solution.keys()), 1)
for i in solution.items():
with self.subTest(case=i):
self.assertTrue(i[0] == '_comment' or i[0] in SHOPS)
value = i[1]
if type(value) == str:
self.assertTrue(value.startswith('format')) # comment finded
else: # instock list finded
self.assertEqual(type(value), dict)
size = next(iter(value.items()))[0]
self.assertTrue(1 < size < 15)
count = next(iter(value.items()))[1]
self.assertTrue(0 < count < 20)
@skipIf(SKIP, 'skip test db')
class TestExport(TestCase):
"""
Test export card description to popular formats. Use after filling all working tables.
See README.txt or main.py
"""
def __init__(self, *args, **kwargs):
super(TestExport, self).__init__(*args, **kwargs)
self.page = Main()
self.parent_dir = Path(__file__).resolve().parent
def test_export_csv(self):
# without 'to' parameter
self.csv_file = os.path.join(self.parent_dir, CSV_FILE) # path + file with any OS
if os.path.isfile(self.csv_file):
remove(self.csv_file)
self.assertTrue(not os.path.isfile(self.csv_file)) # absolutely not on the disk
self.page.export()
self.assertTrue(os.path.isfile(self.csv_file))
# with 'to' parameter
if os.path.isfile(self.csv_file):
remove(self.csv_file)
self.assertTrue(not os.path.isfile(self.csv_file)) # absolutely not on the disk
self.page.export(to='csv')
self.assertTrue(os.path.isfile(self.csv_file))
if os.path.isfile(self.csv_file):
remove(self.csv_file)
self.assertTrue(not os.path.isfile(self.csv_file)) # absolutely not on the disk
def test_export_json(self):
self.json_file = os.path.join(self.parent_dir, JSON_FILE)
if os.path.isfile(self.json_file):
remove(self.json_file)
self.assertTrue(not os.path.isfile(self.json_file)) # absolutely not on the disk
self.page.export(to='json')
self.assertTrue(os.path.isfile(self.json_file))
if os.path.isfile(self.json_file):
remove(self.json_file)
self.assertTrue(not os.path.isfile(self.json_file)) # absolutely not on the disk
def test_export_xml(self):
self.xml_file = os.path.join(self.parent_dir, XML_FILE)
if os.path.isfile(self.xml_file):
remove(self.xml_file)
self.assertTrue(not os.path.isfile(self.xml_file)) # absolutely not on the disk
self.page.export(to='xml')
self.assertTrue(os.path.isfile(self.xml_file))
if os.path.isfile(self.xml_file):
remove(self.xml_file)
self.assertTrue(not os.path.isfile(self.xml_file)) # absolutely not on the disk
if __name__ == "__main__":
main()