@digitalnodecom/node-red-contrib-analyzer
Version:
A Node-RED global service that monitors function nodes for debugging artifacts and performance issues. Features real-time quality metrics, Vue.js dashboard, and comprehensive code analysis.
418 lines (318 loc) • 14.8 kB
JavaScript
const { detectDebuggingTraits } = require('../../lib/detection/detector');
const sampleCode = require('../fixtures/sample-code');
describe('Detector - detectDebuggingTraits', () => {
describe('Level 1 Detection - Critical Issues', () => {
test('should detect simple top-level return statement', () => {
// Arrange
const code = sampleCode.topLevelReturn.simple;
const expectedIssueType = 'top-level-return';
// Act
const issues = detectDebuggingTraits(code, 1);
// Assert
expect(issues).toHaveLength(1);
expect(issues[0].type).toBe(expectedIssueType);
expect(issues[0].message).toContain('top-level return');
expect(issues[0].line).toBe(2);
});
test('should detect top-level return with semicolon', () => {
// Arrange
const code = sampleCode.topLevelReturn.withSemicolon;
const expectedIssueType = 'top-level-return';
// Act
const issues = detectDebuggingTraits(code, 1);
// Assert
expect(issues).toHaveLength(1);
expect(issues[0].type).toBe(expectedIssueType);
});
test('should detect top-level return with extra spaces', () => {
// Arrange
const code = sampleCode.topLevelReturn.withSpaces;
const expectedIssueType = 'top-level-return';
// Act
const issues = detectDebuggingTraits(code, 1);
// Assert
expect(issues).toHaveLength(1);
expect(issues[0].type).toBe(expectedIssueType);
});
test('should NOT detect valid return statement with value', () => {
// Arrange
const code = sampleCode.topLevelReturn.validReturn;
// Act
const issues = detectDebuggingTraits(code, 1);
// Assert
expect(issues).toHaveLength(0);
});
test('should NOT detect return statement inside nested block', () => {
// Arrange
const code = sampleCode.topLevelReturn.nestedReturn;
// Act
const issues = detectDebuggingTraits(code, 1);
// Assert
expect(issues).toHaveLength(0);
});
});
describe('Level 2 Detection - Important Issues', () => {
test('should detect simple node.warn statement', () => {
// Arrange
const code = sampleCode.nodeWarn.simple;
const expectedIssueType = 'node-warn';
// Act
const issues = detectDebuggingTraits(code, 2);
// Assert
expect(issues).toHaveLength(1);
expect(issues[0].type).toBe(expectedIssueType);
expect(issues[0].message).toContain('node.warn()');
expect(issues[0].line).toBe(1);
});
test('should detect node.warn with variable', () => {
// Arrange
const code = sampleCode.nodeWarn.withVariable;
const expectedIssueType = 'node-warn';
// Act
const issues = detectDebuggingTraits(code, 2);
// Assert
expect(issues).toHaveLength(1);
expect(issues[0].type).toBe(expectedIssueType);
});
test('should detect multiple node.warn statements', () => {
// Arrange
const code = sampleCode.nodeWarn.multipleWarns;
const expectedIssueType = 'node-warn';
// Act
const issues = detectDebuggingTraits(code, 2);
// Assert
expect(issues).toHaveLength(2);
expect(issues[0].type).toBe(expectedIssueType);
expect(issues[1].type).toBe(expectedIssueType);
});
test('should NOT detect valid node.log statement', () => {
// Arrange
const code = sampleCode.nodeWarn.validLog;
// Act
const issues = detectDebuggingTraits(code, 2);
// Assert
expect(issues).toHaveLength(0);
});
test('should detect TODO comment (uppercase)', () => {
// Arrange
const code = sampleCode.todoComments.todoUppercase;
const expectedIssueType = 'todo-comment';
// Act
const issues = detectDebuggingTraits(code, 2);
// Assert
expect(issues).toHaveLength(1);
expect(issues[0].type).toBe(expectedIssueType);
expect(issues[0].message).toContain('TODO');
});
test('should detect FIXME comment (lowercase)', () => {
// Arrange
const code = sampleCode.todoComments.fixmeLowercase;
const expectedIssueType = 'todo-comment';
// Act
const issues = detectDebuggingTraits(code, 2);
// Assert
expect(issues).toHaveLength(1);
expect(issues[0].type).toBe(expectedIssueType);
expect(issues[0].message).toContain('FIXME');
});
test('should NOT detect regular comment', () => {
// Arrange
const code = sampleCode.todoComments.validComment;
// Act
const issues = detectDebuggingTraits(code, 2);
// Assert
expect(issues).toHaveLength(0);
});
});
describe('Level 3 Detection - Minor Issues', () => {
test('should detect multiple consecutive empty lines in middle of code', () => {
// Arrange
const code = 'var x = 1;\n\n\n\nvar y = 2;\nconsole.log(x, y);';
// Act
const issues = detectDebuggingTraits(code, 3);
const emptyLineIssues = issues.filter(issue => issue.type === 'multiple-empty-lines');
// Assert
expect(emptyLineIssues).toHaveLength(1);
expect(emptyLineIssues[0].type).toBe('multiple-empty-lines');
expect(emptyLineIssues[0].message).toContain('3 consecutive empty lines');
});
test('should detect multiple consecutive empty lines at end of code', () => {
// Arrange
const code = 'var x = 1;\nconsole.log(x);\n\n\n\n';
// Act
const issues = detectDebuggingTraits(code, 3);
const emptyLineIssues = issues.filter(issue => issue.type === 'multiple-empty-lines');
// Assert
expect(emptyLineIssues).toHaveLength(1);
expect(emptyLineIssues[0].type).toBe('multiple-empty-lines');
expect(emptyLineIssues[0].message).toContain('4 consecutive empty lines');
});
test('should detect hardcoded test string', () => {
// Arrange
const code = sampleCode.hardcodedValues.testString;
const expectedIssueType = 'hardcoded-test';
// Act
const issues = detectDebuggingTraits(code, 3);
const hardcodedIssues = issues.filter(issue => issue.type.startsWith('hardcoded-'));
// Assert
expect(hardcodedIssues).toHaveLength(1);
expect(hardcodedIssues[0].type).toBe(expectedIssueType);
expect(hardcodedIssues[0].message).toContain('hardcoded test');
});
test('should detect hardcoded debug string', () => {
// Arrange
const code = sampleCode.hardcodedValues.debugString;
const expectedIssueType = 'hardcoded-debug';
// Act
const issues = detectDebuggingTraits(code, 3);
const hardcodedIssues = issues.filter(issue => issue.type.startsWith('hardcoded-'));
// Assert
expect(hardcodedIssues).toHaveLength(1);
expect(hardcodedIssues[0].type).toBe(expectedIssueType);
});
test('should detect hardcoded temp string', () => {
// Arrange
const code = sampleCode.hardcodedValues.tempString;
const expectedIssueType = 'hardcoded-temp';
// Act
const issues = detectDebuggingTraits(code, 3);
const hardcodedIssues = issues.filter(issue => issue.type.startsWith('hardcoded-'));
// Assert
expect(hardcodedIssues).toHaveLength(1);
expect(hardcodedIssues[0].type).toBe(expectedIssueType);
});
test('should detect hardcoded test number', () => {
// Arrange
const code = sampleCode.hardcodedValues.testNumber;
const expectedIssueType = 'hardcoded-number';
// Act
const issues = detectDebuggingTraits(code, 3);
const hardcodedIssues = issues.filter(issue => issue.type.startsWith('hardcoded-'));
// Assert
expect(hardcodedIssues).toHaveLength(1);
expect(hardcodedIssues[0].type).toBe(expectedIssueType);
});
test('should NOT detect valid production values', () => {
// Arrange
const code = sampleCode.hardcodedValues.validValues;
// Act
const issues = detectDebuggingTraits(code, 3);
const hardcodedIssues = issues.filter(issue => issue.type.startsWith('hardcoded-'));
// Assert
expect(hardcodedIssues).toHaveLength(0);
});
});
describe('Detection Level Filtering', () => {
test('should only detect level 1 issues when level=1', () => {
// Arrange
const code = sampleCode.multipleIssues;
const level = 1;
// Act
const issues = detectDebuggingTraits(code, level);
// Assert
expect(issues).toHaveLength(1);
expect(issues[0].type).toBe('top-level-return');
});
test('should detect level 1 and 2 issues when level=2', () => {
// Arrange
const code = sampleCode.multipleIssues;
const level = 2;
// Act
const issues = detectDebuggingTraits(code, level);
// Assert
expect(issues.length).toBeGreaterThan(1);
const issueTypes = issues.map(issue => issue.type);
expect(issueTypes).toContain('top-level-return');
expect(issueTypes).toContain('node-warn');
expect(issueTypes).toContain('todo-comment');
});
test('should detect all issues when level=3', () => {
// Arrange
const code = sampleCode.multipleIssues;
const level = 3;
// Act
const issues = detectDebuggingTraits(code, level);
// Assert
expect(issues.length).toBeGreaterThan(2);
const issueTypes = issues.map(issue => issue.type);
expect(issueTypes).toContain('top-level-return');
expect(issueTypes).toContain('node-warn');
expect(issueTypes).toContain('todo-comment');
expect(issueTypes).toContain('hardcoded-test');
});
});
describe('Edge Cases', () => {
test('should handle empty function', () => {
// Arrange
const code = sampleCode.edgeCases.emptyFunction;
// Act
const issues = detectDebuggingTraits(code, 3);
// Assert
expect(issues).toHaveLength(0);
});
test('should handle only comments', () => {
// Arrange
const code = sampleCode.edgeCases.onlyComments;
// Act
const issues = detectDebuggingTraits(code, 3);
// Assert
expect(issues).toHaveLength(0);
});
test('should handle null/undefined input', () => {
// Arrange
const code = null;
// Act & Assert
expect(() => detectDebuggingTraits(code, 1)).not.toThrow();
});
test('should handle empty string input', () => {
// Arrange
const code = '';
// Act
const issues = detectDebuggingTraits(code, 3);
// Assert
expect(issues).toHaveLength(0);
});
test('should return empty array for clean code', () => {
// Arrange
const code = sampleCode.cleanCode;
// Act
const issues = detectDebuggingTraits(code, 3);
// Assert
expect(issues).toHaveLength(0);
});
});
describe('Issue Properties', () => {
test('should return issues with correct structure', () => {
// Arrange
const code = sampleCode.nodeWarn.simple;
// Act
const issues = detectDebuggingTraits(code, 2);
const issue = issues[0];
// Assert
expect(issue).toHaveProperty('type');
expect(issue).toHaveProperty('message');
expect(issue).toHaveProperty('line');
expect(issue).toHaveProperty('column');
expect(issue).toHaveProperty('severity');
expect(typeof issue.type).toBe('string');
expect(typeof issue.message).toBe('string');
expect(typeof issue.line).toBe('number');
expect(typeof issue.column).toBe('number');
expect(typeof issue.severity).toBe('string');
});
test('should return correct line numbers', () => {
// Arrange
const code = `const x = 1;
const y = 2;
node.warn("debug");
const z = 4;
console.log(x, y, z);`;
// Act
const issues = detectDebuggingTraits(code, 2);
const nodeWarnIssues = issues.filter(issue => issue.type === 'node-warn');
// Assert
expect(nodeWarnIssues).toHaveLength(1);
expect(nodeWarnIssues[0].line).toBe(3);
});
});
});