Coverage for tests/conftest.py: 96%

213 statements  

« prev     ^ index     » next       coverage.py v7.4.4, created at 2024-04-27 23:45 -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' 

17 

18 

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

20def actual_index(entitymgr): 

21 def _actual_index(tb, which): 

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

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

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

25 

26 return _actual_index 

27 

28 

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

30def actual_rollover(entitymgr): 

31 def _actual_rollover(tb): 

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

33 return entitymgr(tb).last 

34 if tb.plan.rollover_alias: 

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

36 return entitymgr(tb).alias.name 

37 return '' # implied else 

38 

39 return _actual_rollover 

40 

41 

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

43def actual_write_index(actual_rollover): 

44 def _actual_write_index(tb): 

45 name = actual_rollover(tb) 

46 if not name: 

47 return name 

48 func = get_write_index 

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

50 func = get_ds_current 

51 return func(tb.client, name) 

52 

53 return _actual_write_index 

54 

55 

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

57def client(): 

58 """Return an Elasticsearch client""" 

59 host = environ.get('TEST_ES_SERVER', 'http://127.0.0.1:9200') 

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

61 if file: 61 ↛ 64line 61 didn't jump to line 64, because the condition on line 61 was never false

62 kwargs = {'configfile': file} 

63 else: 

64 kwargs = {'configdict': {'elasticsearch': {'client': {'hosts': host}}}} 

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

66 builder = Builder(**kwargs) 

67 builder.connect() 

68 return builder.client 

69 

70 

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

72def cold(): 

73 """Return the prefix for cold indices""" 

74 return 'restored-' 

75 

76 

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

78def components(namecore): 

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

80 components = [] 

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

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

83 return components 

84 

85 

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

87def entity_count(defaults): 

88 def _entity_count(kind): 

89 if kind == 'data_stream': 

90 return 1 

91 return defaults()['entity_count'] 

92 

93 return _entity_count 

94 

95 

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

97def defaults() -> t.Dict: 

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

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

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

101 retval['searchable'] = sstier 

102 return retval 

103 

104 return _defaults 

105 

106 

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

108def entitymgr(): 

109 def _entitymgr(tb): 

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

111 return tb.data_streammgr 

112 return tb.indexmgr # implied else 

113 

114 return _entitymgr 

115 

116 

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

118def first(): 

119 return 0 

120 

121 

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

123def frozen(): 

124 """Return the prefix for frozen indices""" 

125 return 'partial-' 

126 

127 

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

129def get_template(template): 

130 def _get_template(client): 

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

132 

133 return _get_template 

134 

135 

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

137def idxmain(namecore, ymd): 

138 def _idxmain(kind): 

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

140 if kind == 'data_stream': 

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

142 return result 

143 

144 return _idxmain 

145 

146 

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

148def idxss(first, ssprefix, rollable): 

149 def _idxss(tier, which, plan): 

150 if which != first: 

151 if rollable(plan): 

152 return '' # No searchable prefix 

153 return ssprefix(tier) 

154 

155 return _idxss 

156 

157 

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

159def idxtail(first, last): 

160 def _idxtail(which): 

161 if which == first: 

162 return '-000001' 

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

164 return '-000003' 

165 return '-000002' # implied else 

166 

167 return _idxtail 

168 

169 

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

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

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

173 prefix = idxss(tier, which, plan) 

174 main = idxmain(plan.type) 

175 suffix = idxtail(which) 

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

177 

178 return _index_name 

179 

180 

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

182def last(): 

183 return -1 

184 

185 

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

187def namecore(prefix, uniq): 

188 def _namecore(kind): 

189 if kind == 'indices': 

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

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

192 

193 return _namecore 

194 

195 

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

197def prefix(): 

198 """Return a random prefix""" 

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

200 

201 

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

203 """Generate a random string""" 

204 letters = string.ascii_uppercase 

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

206 letters = string.ascii_lowercase 

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

208 

209 

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

211def repo(client): 

212 """Return the elasticsearch repository""" 

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

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

215 return False 

216 try: 

217 client.snapshot.get_repository(name=name) 

218 except NotFoundError: 

219 return False 

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

221 

222 

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

224def rollable(): 

225 def _rollable(plan): 

226 if plan.type == 'data_stream': 

227 return True 

228 if plan.rollover_alias: 

229 return True 

230 return False 

231 

232 return _rollable 

233 

234 

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

236def rollovername(namecore, rollable): 

237 def _rollovername(plan): 

238 if rollable(plan): 

239 if plan.type == 'data_stream': 

240 return namecore(plan.type) 

241 return namecore('index') 

242 return '' 

243 

244 return _rollovername 

245 

246 

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

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

249 def _settings( 

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

251 rollover_alias: bool = False, 

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

253 sstier: str = 'hot', 

254 ): 

255 return { 

256 'type': plan_type, 

257 'prefix': prefix, 

258 'rollover_alias': rollover_alias, 

259 'repository': repo, 

260 'uniq': uniq, 

261 'ilm': ilm, 

262 'defaults': defaults(sstier), 

263 } 

264 

265 return _settings 

266 

267 

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

269def skip_no_repo(repo) -> None: 

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

271 if skip_it: 

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

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

274 

275 return _skip_no_repo 

276 

277 

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

279def ssprefix(cold, frozen): 

280 def _ssprefix(tier): 

281 retval = '' # hot or warm 

282 if tier == 'cold': 

283 retval = cold 

284 if tier == 'frozen': 

285 retval = frozen 

286 return retval 

287 

288 return _ssprefix 

289 

290 

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

292def template(namecore): 

293 """Return the name of the index template""" 

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

295 

296 

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

298def uniq(): 

299 """Return a random uniq value""" 

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

301 

302 

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

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

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

306 if not rollable(plan): 

307 return '' 

308 prefix = idxss(tier, which, plan) 

309 main = idxmain(plan.type) 

310 suffix = idxtail(which) 

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

312 

313 return _write_index_name 

314 

315 

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

317def ymd(): 

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