Coverage for tests/conftest.py: 96%

236 statements  

« prev     ^ index     » next       coverage.py v7.4.4, created at 2024-05-02 08:55 -0600

1"""Top-level conftest.py""" 

2 

3# pylint: disable=missing-function-docstring,redefined-outer-name,R0913 

4import typing as t 

5from os import environ 

6from datetime import datetime, timezone 

7import random 

8import string 

9import pytest 

10from elasticsearch8.exceptions import NotFoundError 

11from es_client import Builder 

12from es_client.helpers.logging import set_logging 

13from es_testbed.defaults import NAMEMAPPER 

14from es_testbed.helpers.es_api import get_ds_current, get_write_index 

15 

16LOGLEVEL = 'DEBUG' 

17LOCALREPO = 'testing' 

18 

19 

20@pytest.fixture(scope='class') 

21def actual_index(entitymgr): 

22 def _actual_index(tb, which): 

23 if tb.plan.type == 'data_stream': 

24 return entitymgr(tb).ds.backing_indices[which] 

25 return entitymgr(tb).entity_list[which].name # implied else 

26 

27 return _actual_index 

28 

29 

30@pytest.fixture(scope='class') 

31def actual_rollover(entitymgr): 

32 def _actual_rollover(tb): 

33 if tb.plan.type == 'data_stream': 

34 return entitymgr(tb).last 

35 if tb.plan.rollover_alias: 

36 if entitymgr(tb).alias.name is not None: 36 ↛ 38line 36 didn't jump to line 38, because the condition on line 36 was never false

37 return entitymgr(tb).alias.name 

38 return '' # implied else 

39 

40 return _actual_rollover 

41 

42 

43@pytest.fixture(scope='class') 

44def actual_write_index(actual_rollover): 

45 def _actual_write_index(tb): 

46 name = actual_rollover(tb) 

47 if not name: 

48 return name 

49 func = get_write_index 

50 if tb.plan.type == 'data_stream': 

51 func = get_ds_current 

52 return func(tb.client, name) 

53 

54 return _actual_write_index 

55 

56 

57@pytest.fixture(scope='session') 

58def client(): 

59 """Return an Elasticsearch client""" 

60 host = environ.get('TEST_ES_SERVER') 

61 user = environ.get('TEST_USER') 

62 pswd = environ.get('TEST_PASS') 

63 cacrt = environ.get('CA_CRT') 

64 file = environ.get('ES_CLIENT_FILE', None) # Path to es_client YAML config 

65 repo = environ.get('TEST_ES_REPO', 'found-snapshots') 

66 if file: 66 ↛ 67line 66 didn't jump to line 67, because the condition on line 66 was never true

67 kwargs = {'configfile': file} 

68 else: 

69 kwargs = { 

70 'configdict': { 

71 'elasticsearch': { 

72 'client': {'hosts': host, 'ca_certs': cacrt}, 

73 'other_settings': {'username': user, 'password': pswd}, 

74 } 

75 } 

76 } 

77 set_logging({'loglevel': LOGLEVEL, 'blacklist': ['elastic_transport', 'urllib3']}) 

78 builder = Builder(**kwargs) 

79 builder.connect() 

80 if builder.client.license.get_trial_status()['eligible_to_start_trial']: 80 ↛ 83line 80 didn't jump to line 83, because the condition on line 80 was never false

81 builder.client.license.post_start_trial(acknowledge=True) 

82 # This is a contradiction that cannot exist... 

83 if repo == 'found-snapshots' and host == 'https://127.0.0.1:9200' and not file: 83 ↛ 87line 83 didn't jump to line 87, because the condition on line 83 was never false

84 # We'll make our own and set the ENV var 

85 create_repository(builder.client, LOCALREPO) 

86 environ['TEST_ES_REPO'] = LOCALREPO 

87 return builder.client 

88 

89 

90@pytest.fixture(scope='class') 

91def cold(): 

92 """Return the prefix for cold indices""" 

93 return 'restored-' 

94 

95 

96@pytest.fixture(scope='class') 

97def components(namecore): 

98 """Return the component names in a list""" 

99 components = [] 

100 components.append(f'{namecore("component")}-000001') 

101 components.append(f'{namecore("component")}-000002') 

102 return components 

103 

104 

105def create_repository(client, name: str) -> None: 

106 """ 

107 PUT _snapshot/REPO_NAME 

108 { 

109 "type": "fs", 

110 "settings": { 

111 "location": "RELATIVE_PATH" 

112 } 

113 } 

114 """ 

115 repobody = {'type': 'fs', 'settings': {'location': '/media'}} 

116 client.snapshot.create_repository(name=name, repository=repobody, verify=False) 

117 

118 

119@pytest.fixture(scope='class') 

120def entity_count(defaults): 

121 def _entity_count(kind): 

122 if kind == 'data_stream': 

123 return 1 

124 return defaults()['entity_count'] 

125 

126 return _entity_count 

127 

128 

129@pytest.fixture(scope='class') 

130def defaults() -> t.Dict: 

131 def _defaults(sstier: str = 'hot') -> t.Dict: 

132 retval = {'entity_count': 3, 'docs': 10, 'match': True, 'searchable': None} 

133 if sstier in ['cold', 'frozen']: 

134 retval['searchable'] = sstier 

135 return retval 

136 

137 return _defaults 

138 

139 

140@pytest.fixture(scope='class') 

141def entitymgr(): 

142 def _entitymgr(tb): 

143 if tb.plan.type == 'data_stream': 

144 return tb.data_streammgr 

145 return tb.indexmgr # implied else 

146 

147 return _entitymgr 

148 

149 

150@pytest.fixture(scope='class') 

151def first(): 

152 return 0 

153 

154 

155@pytest.fixture(scope='class') 

156def frozen(): 

157 """Return the prefix for frozen indices""" 

158 return 'partial-' 

159 

160 

161@pytest.fixture(scope='class') 

162def get_template(template): 

163 def _get_template(client): 

164 return client.indices.get_index_template(name=template)['index_templates'] 

165 

166 return _get_template 

167 

168 

169@pytest.fixture(scope='class') 

170def idxmain(namecore, ymd): 

171 def _idxmain(kind): 

172 result = f'{namecore(kind)}' 

173 if kind == 'data_stream': 

174 return f'.ds-{result}-{ymd}' 

175 return result 

176 

177 return _idxmain 

178 

179 

180@pytest.fixture(scope='class') 

181def idxss(first, ssprefix, rollable): 

182 def _idxss(tier, which, plan): 

183 if which != first: 

184 if rollable(plan): 

185 return '' # No searchable prefix 

186 return ssprefix(tier) 

187 

188 return _idxss 

189 

190 

191@pytest.fixture(scope='class') 

192def idxtail(first, last): 

193 def _idxtail(which): 

194 if which == first: 

195 return '-000001' 

196 if which == last: 196 ↛ 198line 196 didn't jump to line 198, because the condition on line 196 was never false

197 return '-000003' 

198 return '-000002' # implied else 

199 

200 return _idxtail 

201 

202 

203@pytest.fixture(scope='class') 

204def index_name(first, idxmain, idxss, idxtail): 

205 def _index_name(which=first, plan=None, tier: str = 'hot'): 

206 prefix = idxss(tier, which, plan) 

207 main = idxmain(plan.type) 

208 suffix = idxtail(which) 

209 return f'{prefix}{main}{suffix}' 

210 

211 return _index_name 

212 

213 

214@pytest.fixture(scope='class') 

215def last(): 

216 return -1 

217 

218 

219@pytest.fixture(scope='class') 

220def namecore(prefix, uniq): 

221 def _namecore(kind): 

222 if kind == 'indices': 

223 return f'{prefix}-{NAMEMAPPER["index"]}-{uniq}' 

224 return f'{prefix}-{NAMEMAPPER[kind]}-{uniq}' 

225 

226 return _namecore 

227 

228 

229@pytest.fixture(scope='class') 

230def prefix(): 

231 """Return a random prefix""" 

232 return randomstr(length=8, lowercase=True) 

233 

234 

235def randomstr(length: int = 16, lowercase: bool = False): 

236 """Generate a random string""" 

237 letters = string.ascii_uppercase 

238 if lowercase: 238 ↛ 240line 238 didn't jump to line 240, because the condition on line 238 was never false

239 letters = string.ascii_lowercase 

240 return str(''.join(random.choices(letters + string.digits, k=length))) 

241 

242 

243@pytest.fixture(scope='class') 

244def repo(client): 

245 """Return the elasticsearch repository""" 

246 name = environ.get('TEST_ES_REPO', 'found-snapshots') # Going with Cloud default 

247 if not repo: 247 ↛ 248line 247 didn't jump to line 248, because the condition on line 247 was never true

248 return False 

249 try: 

250 client.snapshot.get_repository(name=name) 

251 except NotFoundError: 

252 return False 

253 return name # Return the repo name if it's online 

254 

255 

256@pytest.fixture(scope='class') 

257def rollable(): 

258 def _rollable(plan): 

259 if plan.type == 'data_stream': 

260 return True 

261 if plan.rollover_alias: 

262 return True 

263 return False 

264 

265 return _rollable 

266 

267 

268@pytest.fixture(scope='class') 

269def rollovername(namecore, rollable): 

270 def _rollovername(plan): 

271 if rollable(plan): 

272 if plan.type == 'data_stream': 

273 return namecore(plan.type) 

274 return namecore('index') 

275 return '' 

276 

277 return _rollovername 

278 

279 

280@pytest.fixture(scope='class') 

281def settings(defaults, prefix, repo, uniq): 

282 def _settings( 

283 plan_type: t.Literal['data_stream', 'index'] = 'data_stream', 

284 rollover_alias: bool = False, 

285 ilm: t.Union[t.Dict, False] = False, 

286 sstier: str = 'hot', 

287 ): 

288 return { 

289 'type': plan_type, 

290 'prefix': prefix, 

291 'rollover_alias': rollover_alias, 

292 'repository': repo, 

293 'uniq': uniq, 

294 'ilm': ilm, 

295 'defaults': defaults(sstier), 

296 } 

297 

298 return _settings 

299 

300 

301@pytest.fixture(scope='class') 

302def skip_no_repo(repo) -> None: 

303 def _skip_no_repo(skip_it: bool) -> None: 

304 if skip_it: 

305 if not repo: 305 ↛ 306line 305 didn't jump to line 306, because the condition on line 305 was never true

306 pytest.skip('No snapshot repository', allow_module_level=True) 

307 

308 return _skip_no_repo 

309 

310 

311@pytest.fixture(scope='class') 

312def skip_localhost() -> None: 

313 def _skip_localhost(skip_it: bool) -> None: 

314 if skip_it: 

315 host = environ.get('TEST_ES_SERVER') 

316 file = environ.get('ES_CLIENT_FILE', None) # Path to es_client YAML config 

317 repo = environ.get('TEST_ES_REPO') 

318 if repo == LOCALREPO and host == 'https://127.0.0.1:9200' and not file: 318 ↛ exitline 318 didn't return from function '_skip_localhost', because the condition on line 318 was never false

319 pytest.skip( 

320 'Local Docker test does not work with this test', 

321 allow_module_level=False, 

322 ) 

323 

324 return _skip_localhost 

325 

326 

327@pytest.fixture(scope='class') 

328def ssprefix(cold, frozen): 

329 def _ssprefix(tier): 

330 retval = '' # hot or warm 

331 if tier == 'cold': 

332 retval = cold 

333 if tier == 'frozen': 

334 retval = frozen 

335 return retval 

336 

337 return _ssprefix 

338 

339 

340@pytest.fixture(scope='class') 

341def template(namecore): 

342 """Return the name of the index template""" 

343 return f'{namecore("template")}-000001' 

344 

345 

346@pytest.fixture(scope='class') 

347def uniq(): 

348 """Return a random uniq value""" 

349 return randomstr(length=8, lowercase=True) 

350 

351 

352@pytest.fixture(scope='class') 

353def write_index_name(last, idxmain, idxss, idxtail, rollable): 

354 def _write_index_name(which=last, plan=None, tier: str = 'hot'): 

355 if not rollable(plan): 

356 return '' 

357 prefix = idxss(tier, which, plan) 

358 main = idxmain(plan.type) 

359 suffix = idxtail(which) 

360 return f'{prefix}{main}{suffix}' 

361 

362 return _write_index_name 

363 

364 

365@pytest.fixture(scope='class') 

366def ymd(): 

367 return datetime.now(timezone.utc).strftime('%Y.%m.%d')