param-manager 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,80 @@
1
+ Metadata-Version: 2.1
2
+ Name: param-manager
3
+ Version: 0.1.0
4
+ Summary: Biblioteca para gerenciamento de parâmetros com padrão Singleton,cache e armazenamento local usando TinyDB.
5
+ Author-email: MatheusLPolidoro <mattpolidoro4@gmail.com>
6
+ Requires-Python: >=3.8
7
+ Description-Content-Type: text/markdown
8
+ Requires-Dist: tinydb
9
+ Requires-Dist: requests
10
+
11
+ # Biblioteca ParamManager
12
+
13
+ ## Descrição
14
+ Biblioteca Python orientada a objetos que implementa o padrão Singleton para interagir com a API de parâmetros. A biblioteca oferece funcionalidades de cache, armazenamento local com TinyDB e fallback automático em caso de indisponibilidade da API.
15
+
16
+ ## Funcionalidades
17
+
18
+ - **Padrão Singleton**: Garante que exista apenas uma instância da classe de acesso à API
19
+ - **Cache**: Armazena resultados em memória por até 1 hora para reduzir chamadas à API
20
+ - **Armazenamento Local**: Usa TinyDB para persistir dados localmente
21
+ - **Fallback Automático**: Utiliza dados locais quando a API está indisponível
22
+ - **Recuperação de Parâmetros**: Permite buscar todos os parâmetros de um app ou um parâmetro específico
23
+
24
+ ## Instalação
25
+
26
+ ```bash
27
+ pip install param-manager
28
+ ```
29
+
30
+ ## Uso Básico
31
+
32
+ ```python
33
+ from param_manager import ParamManager
34
+
35
+ # Obter a instância do gerenciador
36
+ param_manager = ParamManager.get_instance()
37
+
38
+ # Recuperar todos os parâmetros de um app
39
+ params = param_manager.get_all_params('nome_do_app')
40
+
41
+ # Recuperar um parâmetro específico
42
+ param = param_manager.get_param('nome_do_app', 'NOME_PARAMETRO')
43
+
44
+ # Limpar o cache para um app específico
45
+ param_manager.clear_cache('nome_do_app')
46
+
47
+ # Obter informações sobre o cache atual
48
+ cache_info = param_manager.get_cache_info()
49
+ ```
50
+
51
+ ## Configuração Avançada
52
+
53
+ ```python
54
+ # Configurar com URL de API personalizada, duração de cache e timeout
55
+ param_manager = ParamManager.get_instance(
56
+ api_url="http://minha-api.exemplo.com",
57
+ cache_duration=1800, # 30 minutos
58
+ timeout=10 # 10 segundos
59
+ )
60
+ ```
61
+
62
+ ## Comportamento de Fallback
63
+
64
+ Quando a API está indisponível, a biblioteca automaticamente:
65
+ 1. Tenta acessar a API
66
+ 2. Em caso de falha, busca dados do armazenamento local
67
+ 3. Retorna os dados mais recentes disponíveis localmente
68
+
69
+ ## Estrutura de Arquivos
70
+
71
+ - `param_manager.py`: Implementação principal da biblioteca
72
+ - `test_param_manager.py`: Testes unitários para validar o funcionamento
73
+ - `README.md`: Documentação da biblioteca
74
+ - `requirements.txt`: Dependências necessárias
75
+
76
+ ## Dependências
77
+
78
+ - Python 3.6+
79
+ - requests
80
+ - tinydb
@@ -0,0 +1,70 @@
1
+ # Biblioteca ParamManager
2
+
3
+ ## Descrição
4
+ Biblioteca Python orientada a objetos que implementa o padrão Singleton para interagir com a API de parâmetros. A biblioteca oferece funcionalidades de cache, armazenamento local com TinyDB e fallback automático em caso de indisponibilidade da API.
5
+
6
+ ## Funcionalidades
7
+
8
+ - **Padrão Singleton**: Garante que exista apenas uma instância da classe de acesso à API
9
+ - **Cache**: Armazena resultados em memória por até 1 hora para reduzir chamadas à API
10
+ - **Armazenamento Local**: Usa TinyDB para persistir dados localmente
11
+ - **Fallback Automático**: Utiliza dados locais quando a API está indisponível
12
+ - **Recuperação de Parâmetros**: Permite buscar todos os parâmetros de um app ou um parâmetro específico
13
+
14
+ ## Instalação
15
+
16
+ ```bash
17
+ pip install param-manager
18
+ ```
19
+
20
+ ## Uso Básico
21
+
22
+ ```python
23
+ from param_manager import ParamManager
24
+
25
+ # Obter a instância do gerenciador
26
+ param_manager = ParamManager.get_instance()
27
+
28
+ # Recuperar todos os parâmetros de um app
29
+ params = param_manager.get_all_params('nome_do_app')
30
+
31
+ # Recuperar um parâmetro específico
32
+ param = param_manager.get_param('nome_do_app', 'NOME_PARAMETRO')
33
+
34
+ # Limpar o cache para um app específico
35
+ param_manager.clear_cache('nome_do_app')
36
+
37
+ # Obter informações sobre o cache atual
38
+ cache_info = param_manager.get_cache_info()
39
+ ```
40
+
41
+ ## Configuração Avançada
42
+
43
+ ```python
44
+ # Configurar com URL de API personalizada, duração de cache e timeout
45
+ param_manager = ParamManager.get_instance(
46
+ api_url="http://minha-api.exemplo.com",
47
+ cache_duration=1800, # 30 minutos
48
+ timeout=10 # 10 segundos
49
+ )
50
+ ```
51
+
52
+ ## Comportamento de Fallback
53
+
54
+ Quando a API está indisponível, a biblioteca automaticamente:
55
+ 1. Tenta acessar a API
56
+ 2. Em caso de falha, busca dados do armazenamento local
57
+ 3. Retorna os dados mais recentes disponíveis localmente
58
+
59
+ ## Estrutura de Arquivos
60
+
61
+ - `param_manager.py`: Implementação principal da biblioteca
62
+ - `test_param_manager.py`: Testes unitários para validar o funcionamento
63
+ - `README.md`: Documentação da biblioteca
64
+ - `requirements.txt`: Dependências necessárias
65
+
66
+ ## Dependências
67
+
68
+ - Python 3.6+
69
+ - requests
70
+ - tinydb
@@ -0,0 +1,29 @@
1
+ # ██████╗ █████╗ ██████╗ █████╗ ███╗ ███╗
2
+ # ██╔══██╗██╔══██╗██╔══██╗██╔══██╗████╗ ████║
3
+ # ██████╔╝███████║██████╔╝███████║██╔████╔██║
4
+ # ██╔═══╝ ██╔══██║██╔══██╗██╔══██║██║╚██╔╝██║
5
+ # ██║ ██║ ██║██║ ██║██║ ██║██║ ╚═╝ ██║
6
+ # ╚═╝ ╚═╝ ╚═╝╚═╝ ╚═╝╚═╝ ╚═╝╚═╝ ╚═╝
7
+ # ███╗ ███╗ █████╗ ███╗ ██╗ █████╗ ██████╗ ███████╗██████╗
8
+ # ████╗ ████║██╔══██╗████╗ ██║██╔══██╗██╔════╝ ██╔════╝██╔══██╗
9
+ # ██╔████╔██║███████║██╔██╗ ██║███████║██║ ███╗█████╗ ██████╔╝
10
+ # ██║╚██╔╝██║██╔══██║██║╚██╗██║██╔══██║██║ ██║██╔══╝ ██╔══██╗
11
+ # ██║ ╚═╝ ██║██║ ██║██║ ╚████║██║ ██║╚██████╔╝███████╗██║ ██║
12
+ # ╚═╝ ╚═╝╚═╝ ╚═╝╚═╝ ╚═══╝╚═╝ ╚═╝ ╚═════╝ ╚══════╝╚═╝ ╚═╝
13
+ """
14
+ ParamManager
15
+ ~~~~~~~~~~~~~~~
16
+ Biblioteca para gerenciamento de parâmetros.
17
+
18
+ """
19
+
20
+ import pathlib
21
+ import sys
22
+
23
+ sys.path.append(str(pathlib.Path(__file__).parent))
24
+
25
+ from .manager import ParamManager
26
+
27
+ __author__ = 'MatheusLPolidoro'
28
+ __version__ = '0.1.0'
29
+ __all__ = ['ParamManager', 'TinyDB']
@@ -0,0 +1,505 @@
1
+ import logging
2
+ import os
3
+ import time
4
+ from datetime import datetime, timedelta
5
+ from typing import Any, Dict, Optional
6
+
7
+ import requests
8
+ from tinydb import TinyDB
9
+
10
+ # Configuração de logging
11
+ logging.basicConfig(
12
+ level=logging.INFO,
13
+ format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
14
+ )
15
+ logger = logging.getLogger('ParamManager')
16
+
17
+
18
+ class ParamManager:
19
+ """
20
+ Gerenciador de parâmetros que implementa o padrão Singleton.
21
+
22
+ Esta classe permite recuperar parâmetros de uma API, com sistema de cache
23
+ e fallback para armazenamento local usando TinyDB quando a API está indisponível.
24
+ """
25
+
26
+ # Atributo de classe para armazenar a instância única (padrão Singleton)
27
+ __instance = None
28
+
29
+ def __new__(cls, *args, **kwargs):
30
+ """
31
+ Implementa o padrão Singleton, garantindo uma única instância da classe.
32
+ """
33
+ if cls.__instance is None:
34
+ cls.__instance = super(ParamManager, cls).__new__(cls)
35
+ logger.info('Nova instância do ParamManager criada')
36
+ return cls.__instance
37
+
38
+ def __init__(
39
+ self, api_url: str = None, cache_duration: int = 3600, timeout: int = 5
40
+ ):
41
+ """
42
+ Inicializa a instância com configurações.
43
+
44
+ Args:
45
+ api_url: URL base da API de parâmetros. Se None, usa o valor padrão.
46
+ cache_duration: Duração do cache em segundos (padrão: 1 hora).
47
+ timeout: Tempo limite para requisições à API em segundos.
48
+ """
49
+ # Evita reinicialização se já foi inicializado
50
+ if hasattr(self, '_initialized') and self._initialized:
51
+ return
52
+
53
+ self._api_base_url = api_url or 'http://10.111.188.61:8084'
54
+ self._cache_duration = (
55
+ cache_duration # em segundos (1 hora por padrão)
56
+ )
57
+ self._timeout = timeout # em segundos
58
+
59
+ # Dicionário para armazenar resultados em cache
60
+ self._cache = {} # formato: {app_name: {params}}
61
+
62
+ # Dicionário para armazenar timestamps de cada cache
63
+ self._cache_timestamp = {} # formato: {app_name: timestamp}
64
+
65
+ # Cache específico para parâmetros individuais
66
+ self._param_cache = {} # formato: {app_name:param_name: param_value}
67
+ self._param_cache_timestamp = {} # formato: {app_name:param_name: timestamp}
68
+
69
+ # Diretório para o banco de dados local
70
+ db_dir = os.path.join(os.path.expanduser('~'), '.param_manager')
71
+ os.makedirs(db_dir, exist_ok=True)
72
+
73
+ # Instância do TinyDB para armazenamento local
74
+ self._db_path = os.path.join(db_dir, 'params_db.json')
75
+ self._db = TinyDB(self._db_path)
76
+
77
+ self._initialized = True
78
+ logger.info(f'ParamManager inicializado com API: {self._api_base_url}')
79
+
80
+ @staticmethod
81
+ def get_instance(
82
+ api_url: str = None, cache_duration: int = 3600, timeout: int = 5
83
+ ) -> 'ParamManager':
84
+ """
85
+ Método estático para obter a instância única.
86
+
87
+ Args:
88
+ api_url: URL base da API de parâmetros.
89
+ cache_duration: Duração do cache em segundos.
90
+ timeout: Tempo limite para requisições à API em segundos.
91
+
92
+ Returns:
93
+ A instância única de ParamManager.
94
+ """
95
+ if ParamManager.__instance is None:
96
+ ParamManager(api_url, cache_duration, timeout)
97
+ return ParamManager.__instance
98
+
99
+ def get_all_params(self, app_name: str) -> Dict[str, Any]:
100
+ """
101
+ Recupera todos os parâmetros de um app específico.
102
+
103
+ Args:
104
+ app_name: Nome do aplicativo.
105
+
106
+ Returns:
107
+ Dicionário com todos os parâmetros do app.
108
+ """
109
+ logger.info(f'Solicitando todos os parâmetros para o app: {app_name}')
110
+
111
+ # Verifica se há cache válido
112
+ if self._is_cache_valid(app_name):
113
+ logger.info(f'Usando cache para o app: {app_name}')
114
+ return self._cache[app_name]
115
+
116
+ # Se não houver cache válido, tenta buscar da API
117
+ try:
118
+ params = self._fetch_from_api(app_name)
119
+ return params
120
+ except Exception as e:
121
+ logger.error(f'Erro ao buscar parâmetros da API: {str(e)}')
122
+ return self._handle_api_error(app_name, None, e)
123
+
124
+ def get_param(self, app_name: str, param_name: str) -> Any:
125
+ """
126
+ Recupera um parâmetro específico de um app.
127
+
128
+ Args:
129
+ app_name: Nome do aplicativo.
130
+ param_name: Nome do parâmetro.
131
+
132
+ Returns:
133
+ Valor do parâmetro ou None se não encontrado.
134
+ """
135
+ logger.info(
136
+ f'Solicitando parâmetro {param_name} para o app: {app_name}'
137
+ )
138
+
139
+ # Chave para o cache específico do parâmetro
140
+ param_cache_key = f'{app_name}:{param_name}'
141
+
142
+ # Verifica se há cache específico válido para o parâmetro
143
+ if self._is_param_cache_valid(app_name, param_name):
144
+ logger.info(
145
+ f'Usando cache específico para o parâmetro: {param_name} do app: {app_name}'
146
+ )
147
+ return self._param_cache[param_cache_key]['value']
148
+
149
+ # Se não houver cache específico válido, verifica o cache global do app
150
+ if self._is_cache_valid(app_name):
151
+ logger.info(
152
+ f'Usando cache global do app para o parâmetro: {param_name}'
153
+ )
154
+ params = self._cache[app_name]
155
+ param_value = params.get(param_name)
156
+
157
+ # Atualiza o cache específico do parâmetro
158
+ if param_value is not None:
159
+ self._param_cache[param_cache_key] = param_value
160
+ self._param_cache_timestamp[param_cache_key] = time.time()
161
+
162
+ return param_value.get('value')
163
+
164
+ # Se não houver cache válido, tenta buscar da API
165
+ try:
166
+ # Busca o parâmetro específico da API
167
+ param_value = self._fetch_param_from_api(app_name, param_name)
168
+ if not isinstance(param_value, dict):
169
+ param_value = dict()
170
+ return param_value.get('value')
171
+ except Exception as e:
172
+ logger.error(f'Erro ao buscar parâmetro da API: {str(e)}')
173
+
174
+ # Tenta recuperar do banco local
175
+ params = self._handle_api_error(app_name, param_name, e)
176
+ return (
177
+ params.get(param_name, dict()).get('value') if params else None
178
+ )
179
+
180
+ def _fetch_from_api(
181
+ self, app_name: str, param_name: Optional[str] = None
182
+ ) -> Dict[str, Any]:
183
+ """
184
+ Faz requisição à API para buscar todos os parâmetros de um app.
185
+
186
+ Args:
187
+ app_name: Nome do aplicativo.
188
+ param_name: Nome do parâmetro específico (opcional, mantido para compatibilidade).
189
+
190
+ Returns:
191
+ Dicionário com os parâmetros.
192
+
193
+ Raises:
194
+ Exception: Se ocorrer erro na requisição.
195
+ """
196
+ # Constrói URL apropriada para todos os parâmetros
197
+ url = f'{self._api_base_url}/parameters/apps/{app_name}/params'
198
+
199
+ logger.info(f'Buscando todos os parâmetros da API: {url}')
200
+
201
+ # Faz requisição HTTP
202
+ response = requests.get(url, timeout=self._timeout, verify=False)
203
+
204
+ # Verifica se a requisição foi bem-sucedida
205
+ if response.status_code != 200:
206
+ raise Exception(f'API retornou status code {response.status_code}')
207
+
208
+ # Processa resposta
209
+ data = response.json()
210
+
211
+ # Extrai parâmetros da resposta
212
+ params = data.get('params', {})
213
+
214
+ # Atualiza cache e timestamp
215
+ self._cache[app_name] = params
216
+ self._cache_timestamp[app_name] = time.time()
217
+
218
+ # Salva dados localmente
219
+ self._save_to_local_db(app_name, params)
220
+
221
+ return params
222
+
223
+ def _fetch_param_from_api(self, app_name: str, param_name: str) -> Any:
224
+ """
225
+ Faz requisição à API para buscar um parâmetro específico.
226
+
227
+ Args:
228
+ app_name: Nome do aplicativo.
229
+ param_name: Nome do parâmetro específico.
230
+
231
+ Returns:
232
+ Valor do parâmetro ou None se não encontrado.
233
+
234
+ Raises:
235
+ Exception: Se ocorrer erro na requisição.
236
+ """
237
+ # Constrói URL apropriada para o parâmetro específico
238
+ url = f'{self._api_base_url}/parameters/apps/{app_name}/params/{param_name}'
239
+
240
+ logger.info(f'Buscando parâmetro específico da API: {url}')
241
+
242
+ # Faz requisição HTTP
243
+ response = requests.get(url, timeout=self._timeout, verify=False)
244
+
245
+ # Verifica se a requisição foi bem-sucedida
246
+ if response.status_code != 200:
247
+ raise Exception(f'API retornou status code {response.status_code}')
248
+
249
+ # Processa resposta
250
+ data = response.json()
251
+
252
+ # Extrai parâmetro da resposta
253
+ param_value = data.get('param')
254
+
255
+ # Chave para o cache específico do parâmetro
256
+ param_cache_key = f'{app_name}:{param_name}'
257
+
258
+ # Atualiza o cache específico do parâmetro
259
+ self._param_cache[param_cache_key] = param_value
260
+ self._param_cache_timestamp[param_cache_key] = time.time()
261
+
262
+ # Também atualiza o cache global se existir
263
+ if app_name in self._cache:
264
+ self._cache[app_name][param_name] = param_value
265
+ self._cache_timestamp[app_name] = time.time()
266
+
267
+ # Salva dados localmente
268
+ self._save_to_local_db(app_name, self._cache[app_name])
269
+
270
+ return param_value
271
+
272
+ def _is_cache_valid(self, app_name: str) -> bool:
273
+ """
274
+ Verifica se o cache global para um app é válido.
275
+
276
+ Args:
277
+ app_name: Nome do aplicativo.
278
+
279
+ Returns:
280
+ True se o cache for válido, False caso contrário.
281
+ """
282
+ # Verifica se existe cache para o app
283
+ if (
284
+ app_name not in self._cache
285
+ or app_name not in self._cache_timestamp
286
+ ):
287
+ return False
288
+
289
+ # Verifica se o timestamp é recente (menos de cache_duration segundos)
290
+ current_time = time.time()
291
+ cache_time = self._cache_timestamp[app_name]
292
+
293
+ return (current_time - cache_time) < self._cache_duration
294
+
295
+ def _is_param_cache_valid(self, app_name: str, param_name: str) -> bool:
296
+ """
297
+ Verifica se o cache específico para um parâmetro é válido.
298
+
299
+ Args:
300
+ app_name: Nome do aplicativo.
301
+ param_name: Nome do parâmetro.
302
+
303
+ Returns:
304
+ True se o cache for válido, False caso contrário.
305
+ """
306
+ # Chave para o cache específico do parâmetro
307
+ param_cache_key = f'{app_name}:{param_name}'
308
+
309
+ # Verifica se existe cache específico para o parâmetro
310
+ if (
311
+ param_cache_key not in self._param_cache
312
+ or param_cache_key not in self._param_cache_timestamp
313
+ ):
314
+ return False
315
+
316
+ # Verifica se o timestamp é recente (menos de cache_duration segundos)
317
+ current_time = time.time()
318
+ cache_time = self._param_cache_timestamp[param_cache_key]
319
+
320
+ return (current_time - cache_time) < self._cache_duration
321
+
322
+ def _save_to_local_db(self, app_name: str, params: Dict[str, Any]) -> None:
323
+ """
324
+ Salva parâmetros no banco local.
325
+
326
+ Args:
327
+ app_name: Nome do aplicativo.
328
+ params: Dicionário com os parâmetros.
329
+ """
330
+ logger.info(f'Salvando parâmetros localmente para o app: {app_name}')
331
+
332
+ # Define a tabela para o app
333
+ table = self._db.table(app_name)
334
+
335
+ # Limpa a tabela antes de inserir novos dados
336
+ table.truncate()
337
+
338
+ # Insere os parâmetros
339
+ table.insert({'timestamp': time.time(), 'params': params})
340
+
341
+ def _get_from_local_db(
342
+ self, app_name: str, param_name: Optional[str] = None
343
+ ) -> Dict[str, Any]:
344
+ """
345
+ Recupera dados do banco local.
346
+
347
+ Args:
348
+ app_name: Nome do aplicativo.
349
+ param_name: Nome do parâmetro específico (opcional).
350
+
351
+ Returns:
352
+ Dicionário com os parâmetros ou vazio se não encontrado.
353
+ """
354
+ logger.info(f'Buscando parâmetros localmente para o app: {app_name}')
355
+
356
+ # Define a tabela para o app
357
+ table = self._db.table(app_name)
358
+
359
+ # Busca o registro mais recente
360
+ records = table.all()
361
+
362
+ if not records:
363
+ logger.warning(
364
+ f'Nenhum registro local encontrado para o app: {app_name}'
365
+ )
366
+ return {}
367
+
368
+ # Ordena por timestamp (mais recente primeiro)
369
+ records.sort(key=lambda x: x.get('timestamp', 0), reverse=True)
370
+
371
+ # Obtém os parâmetros do registro mais recente
372
+ params = records[0].get('params', {})
373
+
374
+ # Filtra por param_name se especificado
375
+ if param_name:
376
+ if param_name in params:
377
+ return {param_name: params[param_name]}
378
+ return {}
379
+
380
+ return params
381
+
382
+ def _handle_api_error(
383
+ self, app_name: str, param_name: Optional[str], error: Exception
384
+ ) -> Dict[str, Any]:
385
+ """
386
+ Trata erros de API.
387
+
388
+ Args:
389
+ app_name: Nome do aplicativo.
390
+ param_name: Nome do parâmetro específico (opcional).
391
+ error: Exceção ocorrida.
392
+
393
+ Returns:
394
+ Dados locais se disponíveis ou dicionário vazio.
395
+ """
396
+ logger.error(f'Erro ao acessar API para {app_name}: {str(error)}')
397
+ logger.info(f'Tentando usar dados locais para {app_name}')
398
+
399
+ # Busca dados locais
400
+ return self._get_from_local_db(app_name, param_name)
401
+
402
+ def clear_cache(
403
+ self, app_name: Optional[str] = None, param_name: Optional[str] = None
404
+ ) -> None:
405
+ """
406
+ Limpa o cache para um app específico, um parâmetro específico ou para todos os apps.
407
+
408
+ Args:
409
+ app_name: Nome do aplicativo (opcional).
410
+ param_name: Nome do parâmetro (opcional).
411
+ """
412
+ if app_name and param_name:
413
+ # Limpa o cache específico do parâmetro
414
+ param_cache_key = f'{app_name}:{param_name}'
415
+ if param_cache_key in self._param_cache:
416
+ del self._param_cache[param_cache_key]
417
+ if param_cache_key in self._param_cache_timestamp:
418
+ del self._param_cache_timestamp[param_cache_key]
419
+ logger.info(
420
+ f'Cache limpo para o parâmetro {param_name} do app: {app_name}'
421
+ )
422
+ elif app_name:
423
+ # Limpa o cache do app
424
+ if app_name in self._cache:
425
+ del self._cache[app_name]
426
+ if app_name in self._cache_timestamp:
427
+ del self._cache_timestamp[app_name]
428
+
429
+ # Limpa também todos os caches específicos relacionados ao app
430
+ param_cache_keys = [
431
+ k
432
+ for k in self._param_cache.keys()
433
+ if k.startswith(f'{app_name}:')
434
+ ]
435
+ for key in param_cache_keys:
436
+ if key in self._param_cache:
437
+ del self._param_cache[key]
438
+ if key in self._param_cache_timestamp:
439
+ del self._param_cache_timestamp[key]
440
+
441
+ logger.info(f'Cache limpo para o app: {app_name}')
442
+ else:
443
+ # Limpa todos os caches
444
+ self._cache = {}
445
+ self._cache_timestamp = {}
446
+ self._param_cache = {}
447
+ self._param_cache_timestamp = {}
448
+ logger.info('Cache limpo para todos os apps e parâmetros')
449
+
450
+ def get_cache_info(self) -> Dict[str, Any]:
451
+ """
452
+ Retorna informações sobre o cache atual.
453
+
454
+ Returns:
455
+ Dicionário com informações do cache.
456
+ """
457
+ info = {
458
+ 'apps_cached': list(self._cache.keys()),
459
+ 'cache_timestamps': {},
460
+ 'cache_valid': {},
461
+ 'params_cached': [],
462
+ 'param_cache_timestamps': {},
463
+ 'param_cache_valid': {},
464
+ }
465
+
466
+ # Informações sobre o cache global
467
+ for app_name, timestamp in self._cache_timestamp.items():
468
+ dt = datetime.fromtimestamp(timestamp)
469
+ expires_at = dt + timedelta(seconds=self._cache_duration)
470
+ is_valid = self._is_cache_valid(app_name)
471
+
472
+ info['cache_timestamps'][app_name] = {
473
+ 'cached_at': dt.isoformat(),
474
+ 'expires_at': expires_at.isoformat(),
475
+ 'seconds_remaining': int(
476
+ timestamp + self._cache_duration - time.time()
477
+ )
478
+ if is_valid
479
+ else 0,
480
+ }
481
+ info['cache_valid'][app_name] = is_valid
482
+
483
+ # Informações sobre o cache específico de parâmetros
484
+ for param_key, timestamp in self._param_cache_timestamp.items():
485
+ info['params_cached'].append(param_key)
486
+
487
+ dt = datetime.fromtimestamp(timestamp)
488
+ expires_at = dt + timedelta(seconds=self._cache_duration)
489
+
490
+ # Extrai app_name e param_name da chave
491
+ app_name, param_name = param_key.split(':', 1)
492
+ is_valid = self._is_param_cache_valid(app_name, param_name)
493
+
494
+ info['param_cache_timestamps'][param_key] = {
495
+ 'cached_at': dt.isoformat(),
496
+ 'expires_at': expires_at.isoformat(),
497
+ 'seconds_remaining': int(
498
+ timestamp + self._cache_duration - time.time()
499
+ )
500
+ if is_valid
501
+ else 0,
502
+ }
503
+ info['param_cache_valid'][param_key] = is_valid
504
+
505
+ return info
@@ -0,0 +1,80 @@
1
+ Metadata-Version: 2.1
2
+ Name: param-manager
3
+ Version: 0.1.0
4
+ Summary: Biblioteca para gerenciamento de parâmetros com padrão Singleton,cache e armazenamento local usando TinyDB.
5
+ Author-email: MatheusLPolidoro <mattpolidoro4@gmail.com>
6
+ Requires-Python: >=3.8
7
+ Description-Content-Type: text/markdown
8
+ Requires-Dist: tinydb
9
+ Requires-Dist: requests
10
+
11
+ # Biblioteca ParamManager
12
+
13
+ ## Descrição
14
+ Biblioteca Python orientada a objetos que implementa o padrão Singleton para interagir com a API de parâmetros. A biblioteca oferece funcionalidades de cache, armazenamento local com TinyDB e fallback automático em caso de indisponibilidade da API.
15
+
16
+ ## Funcionalidades
17
+
18
+ - **Padrão Singleton**: Garante que exista apenas uma instância da classe de acesso à API
19
+ - **Cache**: Armazena resultados em memória por até 1 hora para reduzir chamadas à API
20
+ - **Armazenamento Local**: Usa TinyDB para persistir dados localmente
21
+ - **Fallback Automático**: Utiliza dados locais quando a API está indisponível
22
+ - **Recuperação de Parâmetros**: Permite buscar todos os parâmetros de um app ou um parâmetro específico
23
+
24
+ ## Instalação
25
+
26
+ ```bash
27
+ pip install param-manager
28
+ ```
29
+
30
+ ## Uso Básico
31
+
32
+ ```python
33
+ from param_manager import ParamManager
34
+
35
+ # Obter a instância do gerenciador
36
+ param_manager = ParamManager.get_instance()
37
+
38
+ # Recuperar todos os parâmetros de um app
39
+ params = param_manager.get_all_params('nome_do_app')
40
+
41
+ # Recuperar um parâmetro específico
42
+ param = param_manager.get_param('nome_do_app', 'NOME_PARAMETRO')
43
+
44
+ # Limpar o cache para um app específico
45
+ param_manager.clear_cache('nome_do_app')
46
+
47
+ # Obter informações sobre o cache atual
48
+ cache_info = param_manager.get_cache_info()
49
+ ```
50
+
51
+ ## Configuração Avançada
52
+
53
+ ```python
54
+ # Configurar com URL de API personalizada, duração de cache e timeout
55
+ param_manager = ParamManager.get_instance(
56
+ api_url="http://minha-api.exemplo.com",
57
+ cache_duration=1800, # 30 minutos
58
+ timeout=10 # 10 segundos
59
+ )
60
+ ```
61
+
62
+ ## Comportamento de Fallback
63
+
64
+ Quando a API está indisponível, a biblioteca automaticamente:
65
+ 1. Tenta acessar a API
66
+ 2. Em caso de falha, busca dados do armazenamento local
67
+ 3. Retorna os dados mais recentes disponíveis localmente
68
+
69
+ ## Estrutura de Arquivos
70
+
71
+ - `param_manager.py`: Implementação principal da biblioteca
72
+ - `test_param_manager.py`: Testes unitários para validar o funcionamento
73
+ - `README.md`: Documentação da biblioteca
74
+ - `requirements.txt`: Dependências necessárias
75
+
76
+ ## Dependências
77
+
78
+ - Python 3.6+
79
+ - requests
80
+ - tinydb
@@ -0,0 +1,12 @@
1
+ README.md
2
+ pyproject.toml
3
+ param_manager/__init__.py
4
+ param_manager/manager.py
5
+ param_manager.egg-info/PKG-INFO
6
+ param_manager.egg-info/SOURCES.txt
7
+ param_manager.egg-info/dependency_links.txt
8
+ param_manager.egg-info/requires.txt
9
+ param_manager.egg-info/top_level.txt
10
+ tests/__init__.py
11
+ tests/conftest.py
12
+ tests/test_param_manager.py
@@ -0,0 +1,2 @@
1
+ tinydb
2
+ requests
@@ -0,0 +1,4 @@
1
+ dist
2
+ docs
3
+ param_manager
4
+ tests
@@ -0,0 +1,95 @@
1
+ [project]
2
+ name = "param-manager"
3
+ version = "0.1.0"
4
+ description = "Biblioteca para gerenciamento de parâmetros com padrão Singleton,cache e armazenamento local usando TinyDB."
5
+ authors = [{name = "MatheusLPolidoro", email = "mattpolidoro4@gmail.com"}]
6
+ readme = "README.md"
7
+ requires-python = ">=3.8"
8
+
9
+ dependencies = [
10
+ "tinydb",
11
+ "requests"
12
+ ]
13
+
14
+ [tool.setuptools.packages.find]
15
+ where = ["."]
16
+
17
+ [build-system]
18
+ requires = ["setuptools>=61.0,<70"]
19
+ build-backend = "setuptools.build_meta"
20
+
21
+ [tool.ruff]
22
+ line-length = 79
23
+
24
+ [tool.ruff.lint]
25
+ preview = true
26
+ select = ['I', 'F', 'E', 'W', 'PL', 'PT']
27
+
28
+ [tool.ruff.format]
29
+ preview = true
30
+ quote-style = 'single'
31
+
32
+ [tool.taskipy.tasks]
33
+ lint = "ruff check . --diff"
34
+ format = "ruff format ."
35
+ test = "pytest . -s -x --cov=param_manager -vv"
36
+ post_test = "python -m coverage html"
37
+
38
+ [tool.towncrier]
39
+ package = "param_manager"
40
+ package_dir = "."
41
+ filename = "CHANGELOG.md"
42
+ directory = "chanlogs.d"
43
+ underlines = ["=", "-", "~"]
44
+ title_format = "Versão {version} ({project_date})"
45
+ issue_format = "[#{issue}](https://github.com/seu_usuario/seu_repo/issues/{issue})"
46
+
47
+ [[tool.towncrier.type]]
48
+ directory = "breaking"
49
+ name = ":warning: MUDANÇAS QUEBRADAS"
50
+ showcontent = true
51
+
52
+ [[tool.towncrier.type]]
53
+ directory = "feature"
54
+ name = ":zap: NOVAS FUNCIONALIDADES"
55
+ showcontent = true
56
+
57
+ [[tool.towncrier.type]]
58
+ directory = "enhancement"
59
+ name = ":rocket: OTIMIZAÇÕES"
60
+ showcontent = true
61
+
62
+ [[tool.towncrier.type]]
63
+ directory = "fix"
64
+ name = ":bug: CORREÇÕES"
65
+ showcontent = true
66
+
67
+ [[tool.towncrier.type]]
68
+ directory = "deprecation"
69
+ name = ":wastebasket: OBSOLETOS"
70
+ showcontent = true
71
+
72
+ [[tool.towncrier.type]]
73
+ directory = "security"
74
+ name = ":shield: SEGURANÇA"
75
+ showcontent = true
76
+
77
+ [[tool.towncrier.type]]
78
+ directory = "docs"
79
+ name = ":open_file_folder: DOCUMENTAÇÃO"
80
+ showcontent = true
81
+
82
+ [[tool.towncrier.type]]
83
+ directory = "tests"
84
+ name = ":test_tube: TESTES"
85
+ showcontent = true
86
+
87
+ [[tool.towncrier.type]]
88
+ directory = "infra"
89
+ name = ":classical_building: INFRAESTRUTURA"
90
+ showcontent = true
91
+
92
+ [[tool.towncrier.type]]
93
+ directory = "chore"
94
+ name = ":broom: LIMPEZA"
95
+ showcontent = true
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
File without changes
@@ -0,0 +1,45 @@
1
+ import os
2
+ import pytest
3
+ from param_manager.manager import ParamManager
4
+ from unittest.mock import patch, MagicMock
5
+
6
+
7
+ @pytest.fixture
8
+ def setup_param_manager():
9
+ """Fixture para configurar o ambiente de teste para o ParamManager."""
10
+ # Limpa a instância singleton entre os testes
11
+ ParamManager._ParamManager__instance = None
12
+
13
+ # Cria um diretório temporário para o banco de dados de teste
14
+ test_db_dir = os.path.join(os.path.expanduser('~'), '.param_manager_test')
15
+ os.makedirs(test_db_dir, exist_ok=True)
16
+ test_db_path = os.path.join(test_db_dir, 'test_params_db.json')
17
+
18
+ # Mock para o TinyDB
19
+ with patch('param_manager.manager.TinyDB') as mock_db:
20
+ mock_db_instance = MagicMock()
21
+ mock_db.return_value = mock_db_instance
22
+
23
+ # Mock para a tabela do TinyDB
24
+ mock_table = MagicMock()
25
+ mock_db_instance.table.return_value = mock_table
26
+
27
+ # Cria a instância do ParamManager com URL de API de teste
28
+ param_manager = ParamManager.get_instance(
29
+ api_url='http://test-api.example.com',
30
+ cache_duration=60, # 1 minuto para facilitar os testes
31
+ timeout=2,
32
+ )
33
+
34
+ # Retorna os objetos necessários para os testes
35
+ yield param_manager, mock_table
36
+
37
+ # Limpeza após o teste
38
+ if os.path.exists(test_db_path):
39
+ os.remove(test_db_path)
40
+
41
+ # Remove o diretório de teste se estiver vazio
42
+ try:
43
+ os.rmdir(test_db_dir)
44
+ except OSError:
45
+ pass
@@ -0,0 +1,332 @@
1
+ import time
2
+ import pytest
3
+ from unittest.mock import patch, MagicMock
4
+
5
+ import requests
6
+
7
+
8
+ @pytest.mark.parametrize(
9
+ 'mock_response_data', [{'param': {'value': 'test_value'}}]
10
+ )
11
+ def test_get_param_from_api_with_individual_cache(
12
+ setup_param_manager, mock_response_data
13
+ ):
14
+ """Testa a recuperação de um parâmetro específico com cache individual."""
15
+ param_manager, _ = setup_param_manager
16
+
17
+ # Configura o mock para requests.get
18
+ with patch('param_manager.manager.requests.get') as mock_get:
19
+ mock_response = MagicMock()
20
+ mock_response.status_code = 200
21
+ mock_response.json.return_value = mock_response_data
22
+ mock_get.return_value = mock_response
23
+
24
+ # Primeira chamada - deve acessar a API
25
+ param1 = param_manager.get_param('test_app', 'PARAM1')
26
+
27
+ # Verifica se a API foi chamada corretamente
28
+ mock_get.assert_called_once_with(
29
+ 'http://test-api.example.com/parameters/apps/test_app/params/PARAM1',
30
+ timeout=2,
31
+ verify=False,
32
+ )
33
+
34
+ # Verifica se o parâmetro foi retornado corretamente
35
+ assert param1 == 'test_value'
36
+
37
+ # Verifica se o cache específico foi atualizado
38
+ assert 'test_app:PARAM1' in param_manager._param_cache
39
+ assert 'test_app:PARAM1' in param_manager._param_cache_timestamp
40
+
41
+ # Reseta o mock para a próxima chamada
42
+ mock_get.reset_mock()
43
+
44
+ # Segunda chamada - deve usar o cache específico
45
+ param2 = param_manager.get_param('test_app', 'PARAM1')
46
+
47
+ # Verifica se a API não foi chamada novamente
48
+ mock_get.assert_not_called()
49
+
50
+ # Verifica se o parâmetro foi retornado corretamente do cache
51
+ assert param2 == 'test_value'
52
+
53
+
54
+ @pytest.mark.parametrize(
55
+ 'mock_response_data',
56
+ [
57
+ {
58
+ 'params': {
59
+ 'PARAM1': {'value': 'value1'},
60
+ 'PARAM2': {'value': 'value2'},
61
+ }
62
+ }
63
+ ],
64
+ )
65
+ def test_get_param_from_global_cache(setup_param_manager, mock_response_data):
66
+ """Testa a recuperação de um parâmetro do cache global quando não há cache específico."""
67
+ param_manager, _ = setup_param_manager
68
+
69
+ # Configura o mock para requests.get para a chamada de todos os parâmetros
70
+ with patch('param_manager.manager.requests.get') as mock_get:
71
+ mock_response_all = MagicMock()
72
+ mock_response_all.status_code = 200
73
+ mock_response_all.json.return_value = mock_response_data
74
+ mock_get.return_value = mock_response_all
75
+
76
+ # Primeiro, busca todos os parâmetros para preencher o cache global
77
+ params = param_manager.get_all_params('test_app')
78
+
79
+ # Verifica se a API foi chamada corretamente
80
+ mock_get.assert_called_once_with(
81
+ 'http://test-api.example.com/parameters/apps/test_app/params',
82
+ timeout=2,
83
+ verify=False,
84
+ )
85
+
86
+ # Reseta o mock para a próxima chamada
87
+ mock_get.reset_mock()
88
+
89
+ # Agora, busca um parâmetro específico - deve usar o cache global
90
+ param = param_manager.get_param('test_app', 'PARAM1')
91
+
92
+ # Verifica se a API não foi chamada novamente
93
+ mock_get.assert_not_called()
94
+
95
+ # Verifica se o parâmetro foi retornado corretamente do cache global
96
+ assert param == 'value1'
97
+
98
+ # Agora, busca de todos os parametros - deve usar o cache global
99
+ params = param_manager.get_all_params('test_app')
100
+
101
+ # Verifica se o cache específico foi atualizado
102
+ assert 'test_app:PARAM1' in param_manager._param_cache
103
+ assert 'test_app:PARAM1' in param_manager._param_cache_timestamp
104
+
105
+
106
+ @pytest.mark.parametrize('mock_response_data', [{'param': 'test_value'}])
107
+ def test_cache_expiration_for_individual_param(
108
+ setup_param_manager, mock_response_data
109
+ ):
110
+ """Testa a expiração do cache para um parâmetro individual."""
111
+ param_manager, _ = setup_param_manager
112
+
113
+ # Configura o mock para requests.get
114
+ with patch('param_manager.manager.requests.get') as mock_get:
115
+ mock_response = MagicMock()
116
+ mock_response.status_code = 200
117
+ mock_response.json.return_value = mock_response_data
118
+ mock_get.return_value = mock_response
119
+
120
+ # Define uma duração de cache muito curta para o teste
121
+ param_manager._cache_duration = 0.1 # 100ms
122
+
123
+ # Primeira chamada - deve acessar a API
124
+ param1 = param_manager.get_param('test_app', 'PARAM1')
125
+
126
+ # Verifica se a API foi chamada
127
+ assert mock_get.call_count == 1
128
+
129
+ # Espera o cache expirar
130
+ time.sleep(0.2)
131
+
132
+ # Reseta o mock para a próxima chamada
133
+ mock_get.reset_mock()
134
+
135
+ # Segunda chamada - deve acessar a API novamente
136
+ param2 = param_manager.get_param('test_app', 'PARAM1')
137
+
138
+ # Verifica se a API foi chamada novamente
139
+ assert mock_get.call_count == 1
140
+
141
+
142
+ @pytest.mark.parametrize('mock_response_data', [{'param': 'test_value'}])
143
+ def test_clear_cache_for_individual_param(
144
+ setup_param_manager, mock_response_data
145
+ ):
146
+ """Testa a limpeza do cache para um parâmetro individual."""
147
+ param_manager, _ = setup_param_manager
148
+
149
+ # Configura o mock para requests.get
150
+ with patch('param_manager.manager.requests.get') as mock_get:
151
+ mock_response = MagicMock()
152
+ mock_response.status_code = 200
153
+ mock_response.json.return_value = mock_response_data
154
+ mock_get.return_value = mock_response
155
+
156
+ # Primeira chamada - deve acessar a API
157
+ param1 = param_manager.get_param('test_app', 'PARAM1')
158
+
159
+ # Verifica se a API foi chamada
160
+ assert mock_get.call_count == 1
161
+
162
+ # Limpa o cache específico
163
+ param_manager.clear_cache('test_app', 'PARAM1')
164
+
165
+ # Reseta o mock para a próxima chamada
166
+ mock_get.reset_mock()
167
+
168
+ # Segunda chamada - deve acessar a API novamente
169
+ param2 = param_manager.get_param('test_app', 'PARAM1')
170
+
171
+ # Verifica se a API foi chamada novamente
172
+ assert mock_get.call_count == 1
173
+
174
+
175
+ @pytest.mark.parametrize('mock_response_data', [{'param': 'test_value'}])
176
+ def test_clear_cache_for_app_clears_all_related_params(
177
+ setup_param_manager, mock_response_data
178
+ ):
179
+ """Testa se a limpeza do cache de um app limpa todos os parâmetros relacionados."""
180
+ param_manager, _ = setup_param_manager
181
+
182
+ # Configura o mock para requests.get
183
+ with patch('param_manager.manager.requests.get') as mock_get:
184
+ mock_response = MagicMock()
185
+ mock_response.status_code = 200
186
+ mock_response.json.return_value = mock_response_data
187
+ mock_get.return_value = mock_response
188
+
189
+ # Busca dois parâmetros diferentes para preencher o cache
190
+ param1 = param_manager.get_param('test_app', 'PARAM1')
191
+ param2 = param_manager.get_param('test_app', 'PARAM2')
192
+
193
+ # Verifica se a API foi chamada duas vezes
194
+ assert mock_get.call_count == 2
195
+
196
+ # Verifica se os caches específicos foram criados
197
+ assert 'test_app:PARAM1' in param_manager._param_cache
198
+ assert 'test_app:PARAM2' in param_manager._param_cache
199
+
200
+ # Limpa o cache do app
201
+ param_manager.clear_cache('test_app')
202
+
203
+ # Verifica se os caches específicos foram limpos
204
+ assert 'test_app:PARAM1' not in param_manager._param_cache
205
+ assert 'test_app:PARAM2' not in param_manager._param_cache
206
+
207
+
208
+ def test_api_error_fallback_for_individual_param(setup_param_manager):
209
+ """Testa o fallback para dados locais quando a API falha para um parâmetro individual."""
210
+ param_manager, mock_table = setup_param_manager
211
+
212
+ # Configura o mock para requests.get para lançar uma exceção
213
+ with patch('param_manager.manager.requests.get') as mock_get:
214
+ mock_get.side_effect = requests.exceptions.RequestException(
215
+ 'API indisponível'
216
+ )
217
+
218
+ # Configura o mock para retornar dados locais
219
+ mock_table.all.return_value = [
220
+ {
221
+ 'timestamp': time.time(),
222
+ 'params': {'PARAM1': {'value': 'local_value'}},
223
+ }
224
+ ]
225
+
226
+ # Chama o método para buscar um parâmetro específico
227
+ param = param_manager.get_param('test_app', 'PARAM1')
228
+
229
+ # Verifica se a API foi chamada
230
+ mock_get.assert_called_once()
231
+
232
+ # Verifica se os dados locais foram buscados
233
+ mock_table.all.assert_called_once()
234
+
235
+ # Verifica se o parâmetro local foi retornado
236
+ assert param == 'local_value'
237
+
238
+
239
+ def test_api_error_fallback_for_all_params(setup_param_manager):
240
+ """Testa o fallback para dados locais quando a API falha para um parâmetro individual."""
241
+ param_manager, mock_table = setup_param_manager
242
+
243
+ # Configura o mock para requests.get para lançar uma exceção
244
+ with patch('param_manager.manager.requests.get') as mock_get:
245
+ mock_get.side_effect = requests.exceptions.RequestException(
246
+ 'API indisponível'
247
+ )
248
+
249
+ result = {'PARAM1': {'value': 'value1'}, 'PARAM2': {'value': 'value2'}}
250
+ # Configura o mock para retornar dados locais
251
+ mock_table.all.return_value = [
252
+ {
253
+ 'timestamp': time.time(),
254
+ 'params': result,
255
+ }
256
+ ]
257
+
258
+ # Chama o método para buscar um parâmetro específico
259
+ params = param_manager.get_all_params('test_app')
260
+
261
+ # Verifica se a API foi chamada
262
+ mock_get.assert_called_once()
263
+
264
+ # Verifica se os dados locais foram buscados
265
+ mock_table.all.assert_called_once()
266
+
267
+ # Verifica se o parâmetro local foi retornado
268
+ assert params == result
269
+
270
+
271
+ @pytest.mark.parametrize(
272
+ 'mock_response_data', [{'param': {'value': 'test_value'}}]
273
+ )
274
+ def test_api_error_status_code(setup_param_manager, mock_response_data):
275
+ """Testa se o a chamada da API retorna algo diferente de 200"""
276
+ param_manager, _ = setup_param_manager
277
+
278
+ # Define uma duração de cache muito curta para o teste
279
+ param_manager._cache_duration = 0 # 100ms
280
+
281
+ # Configura o mock para requests.get
282
+ with patch('param_manager.manager.requests.get') as mock_get:
283
+ mock_response = MagicMock()
284
+ mock_response.status_code = 200
285
+ mock_response.json.return_value = mock_response_data
286
+ mock_get.return_value = mock_response
287
+
288
+ # Busca um parâmetro para preencher o cache
289
+ param = param_manager.get_param('test_app', 'PARAM1')
290
+
291
+ # Verifica se o parâmetro local foi retornado
292
+ assert param == 'test_value'
293
+
294
+ mock_response.status_code = 500
295
+
296
+ # Busca um parâmetro para preencher o cache
297
+ param = param_manager.get_param('test_app', 'PARAM1')
298
+
299
+ assert param == None
300
+
301
+
302
+ @pytest.mark.parametrize(
303
+ 'mock_response_data', [{'param': {'value': 'test_value'}}]
304
+ )
305
+ def test_get_cache_info_includes_param_cache(
306
+ setup_param_manager, mock_response_data
307
+ ):
308
+ """Testa se o método get_cache_info inclui informações sobre o cache de parâmetros individuais."""
309
+ param_manager, _ = setup_param_manager
310
+
311
+ # Configura o mock para requests.get
312
+ with patch('param_manager.manager.requests.get') as mock_get:
313
+ mock_response = MagicMock()
314
+ mock_response.status_code = 200
315
+ mock_response.json.return_value = mock_response_data
316
+ mock_get.return_value = mock_response
317
+
318
+ # Busca um parâmetro para preencher o cache
319
+ param = param_manager.get_param('test_app', 'PARAM1')
320
+
321
+ # Obtém informações do cache
322
+ cache_info = param_manager.get_cache_info()
323
+
324
+ # Verifica se as informações sobre o cache de parâmetros estão presentes
325
+ assert 'params_cached' in cache_info
326
+ assert 'param_cache_timestamps' in cache_info
327
+ assert 'param_cache_valid' in cache_info
328
+
329
+ # Verifica se o parâmetro específico está nas informações
330
+ assert 'test_app:PARAM1' in cache_info['params_cached']
331
+ assert 'test_app:PARAM1' in cache_info['param_cache_timestamps']
332
+ assert 'test_app:PARAM1' in cache_info['param_cache_valid']