testes/.agent/agents/DataIntegrityAgent.js

251 lines
7.9 KiB
JavaScript

/**
* 🤖 AGENTE DE INTEGRIDADE DE DADOS
*
* Implementação programática do agente de validação de integridade de dados
*/
export class DataIntegrityAgent {
constructor() {
this.name = 'DataIntegrityAgent';
this.description = 'Valida integridade e mapeamento de dados front-back';
// Personalidade e Background
this.personality = {
name: 'Dr. Data "The Mapper"',
traits: [
'Analítico e preciso',
'Obsessivo com detalhes de dados',
'Sistemático e organizado',
'Cético até provar o contrário',
'Valoriza documentação e tipos'
],
quirks: [
'Sempre verifica null/undefined antes de confiar',
'Tem uma memória fotográfica de estruturas de dados',
'Fica incomodado quando vê dados sem tipagem',
'Prefere JSDoc detalhado a código sem documentação'
],
catchphrases: [
'E se esse campo vier null?',
'Onde está a tipagem disso?',
'Isso está mapeado corretamente?',
'Precisamos validar esse dado antes de usar'
]
};
this.background = {
origin: 'Ex-backend developer que migrou para frontend e viu o caos de dados não tipados',
motivation: 'Garantir que nenhum dado seja perdido ou corrompido na jornada front-back',
experience: '8 anos trabalhando com APIs e mapeamento de dados',
turningPoint: 'Perdeu um fim de semana inteiro debugando um bug causado por um campo null não tratado',
philosophy: 'Dados são sagrados - devem ser tratados com respeito e precisão',
relationships: {
withBrowserValidation: 'Trabalham em parceria - ele valida o fluxo, eu valido os dados',
withSecurity: 'Compartilha a preocupação com integridade, mas foca em dados, não em segurança',
withDocumentation: 'Aprecia muito a documentação que ele mantém'
}
};
}
/**
* Valida um componente
* @param {string|Object} component - Componente ou caminho
* @param {Object} context - Contexto
* @returns {Promise<Object>}
*/
async validate(component, context = {}) {
const errors = [];
const warnings = [];
const metadata = {};
const componentPath = typeof component === 'string' ? component : component.path;
const componentCode = context.code || await this.readComponent(componentPath);
if (!componentCode) {
return {
passed: false,
errors: ['Não foi possível ler o componente'],
warnings: [],
metadata: {}
};
}
// 1. Verificar null-safety
const hasNullSafety = this.hasNullSafetyChecks(componentCode);
if (!hasNullSafety && this.usesApiData(componentCode)) {
warnings.push('Componente pode não estar tratando valores null/undefined da API');
}
// 2. Verificar formatação de dados
const needsFormatting = this.needsDataFormatting(componentCode);
if (needsFormatting && !this.hasFormatting(componentCode)) {
warnings.push('Dados podem precisar de formatação (datas, moedas, CPF/CNPJ)');
}
// 3. Verificar uso de Service para mapeamento
const usesDirectApi = this.usesDirectApi(componentCode);
if (usesDirectApi) {
warnings.push('Comunicação com API deve ser feita via Services para melhor mapeamento de dados');
}
// 4. Verificar tratamento de valores falsy
const hasFalsyIssues = this.hasFalsyValueIssues(componentCode);
if (hasFalsyIssues) {
warnings.push('Pode haver problemas com valores 0 ou false sendo ocultados erroneamente');
}
// 5. Verificar sincronização de estado
const hasStateSync = this.hasStateSynchronization(componentCode);
if (!hasStateSync && this.hasDataUpdates(componentCode)) {
warnings.push('Atualizações de dados podem não estar sincronizando com a interface');
}
// 6. Verificar JSDoc/types
const hasTypeDocumentation = this.hasTypeDocumentation(componentCode);
if (!hasTypeDocumentation && this.usesComplexData(componentCode)) {
warnings.push('Componente pode se beneficiar de documentação de tipos (JSDoc)');
}
const passed = errors.length === 0;
return {
passed,
errors,
warnings,
metadata: {
hasNullSafety,
hasFormatting,
hasTypeDocumentation,
...metadata
}
};
}
/**
* Verifica se tem checks de null-safety
* @param {string} code - Código
* @returns {boolean}
*/
hasNullSafetyChecks(code) {
const nullSafetyPatterns = [
'?.', '??', 'optional chaining', 'nullish',
'||', 'if (', '&&', '?.map', '?.filter'
];
return nullSafetyPatterns.some(pattern => code.includes(pattern));
}
/**
* Verifica se usa dados de API
* @param {string} code - Código
* @returns {boolean}
*/
usesApiData(code) {
return code.includes('data') || code.includes('response') ||
code.includes('api') || code.includes('fetch') || code.includes('axios');
}
/**
* Verifica se precisa de formatação
* @param {string} code - Código
* @returns {boolean}
*/
needsDataFormatting(code) {
return code.includes('date') || code.includes('Date') ||
code.includes('price') || code.includes('value') ||
code.includes('cpf') || code.includes('cnpj') || code.includes('CPF') || code.includes('CNPJ');
}
/**
* Verifica se tem formatação
* @param {string} code - Código
* @returns {boolean}
*/
hasFormatting(code) {
return code.includes('format') || code.includes('Format') ||
code.includes('toLocaleString') || code.includes('toFixed') ||
code.includes('Intl') || code.includes('mask');
}
/**
* Verifica se usa API diretamente
* @param {string} code - Código
* @returns {boolean}
*/
usesDirectApi(code) {
return (code.includes('axios') || code.includes('fetch')) &&
!code.includes('Service') && !code.includes('service');
}
/**
* Verifica problemas com valores falsy
* @param {string} code - Código
* @returns {boolean}
*/
hasFalsyValueIssues(code) {
// Padrões problemáticos: && data.value (oculta 0), !data (oculta false)
const problematicPatterns = [
'&& data.', '&& item.', '&& value.',
'!data &&', '!item &&', '!value &&'
];
return problematicPatterns.some(pattern => code.includes(pattern));
}
/**
* Verifica sincronização de estado
* @param {string} code - Código
* @returns {boolean}
*/
hasStateSynchronization(code) {
return code.includes('useState') || code.includes('useEffect') ||
code.includes('refetch') || code.includes('invalidate') ||
code.includes('setState') || code.includes('update');
}
/**
* Verifica se tem atualizações de dados
* @param {string} code - Código
* @returns {boolean}
*/
hasDataUpdates(code) {
return code.includes('update') || code.includes('edit') ||
code.includes('save') || code.includes('submit') ||
code.includes('create') || code.includes('delete');
}
/**
* Verifica se usa dados complexos
* @param {string} code - Código
* @returns {boolean}
*/
usesComplexData(code) {
return code.includes('object') || code.includes('array') ||
code.includes('interface') || code.includes('type ') ||
code.includes('{') && code.includes('}');
}
/**
* Verifica se tem documentação de tipos
* @param {string} code - Código
* @returns {boolean}
*/
hasTypeDocumentation(code) {
return code.includes('/**') || code.includes('@typedef') ||
code.includes('@param') || code.includes('@property') ||
code.includes('interface') || code.includes('type ');
}
/**
* Lê o conteúdo do componente
* @param {string} path - Caminho do arquivo
* @returns {Promise<string|null>}
*/
async readComponent(path) {
try {
const fs = await import('fs/promises');
return await fs.readFile(path, 'utf-8');
} catch (error) {
return null;
}
}
}