From 1d4698a241032e33e268b116b6e2a56cd7bd635d Mon Sep 17 00:00:00 2001 From: Nikhil Mittal Date: Tue, 14 Jul 2026 12:58:33 +0530 Subject: [PATCH] feat(apexguru): mark all rules as DevPreview so engine does not run by default Add DEV_PREVIEW_TAG constant and apply it as the first tag on all 23 ApexGuru rules. For the 7 rules that previously had RECOMMENDED, the Recommended tag is removed entirely. This follows the same pattern used by SFGE, ensuring the default rule selector (which targets Recommended) never auto-selects ApexGuru rules while explicit selectors still work. --- .../src/apexguru-rules.ts | 48 +++---- .../test/ApexGuruEngine.test.ts | 48 ++++++- .../test/apexguru-rules.test.ts | 129 ++++++++++++++++++ 3 files changed, 201 insertions(+), 24 deletions(-) create mode 100644 packages/code-analyzer-apexguru-engine/test/apexguru-rules.test.ts diff --git a/packages/code-analyzer-apexguru-engine/src/apexguru-rules.ts b/packages/code-analyzer-apexguru-engine/src/apexguru-rules.ts index 0b1a15bf..91aadff9 100644 --- a/packages/code-analyzer-apexguru-engine/src/apexguru-rules.ts +++ b/packages/code-analyzer-apexguru-engine/src/apexguru-rules.ts @@ -2,6 +2,8 @@ import { RuleDescription, SeverityLevel, COMMON_TAGS } from '@salesforce/code-analyzer-engine-api'; +export const DEV_PREVIEW_TAG: string = 'DevPreview'; + /** * Known ApexGuru rules with descriptions and metadata. * @@ -16,7 +18,7 @@ export const APEXGURU_RULES: RuleDescription[] = [ { name: 'SoqlInALoop', severityLevel: SeverityLevel.High, - tags: [COMMON_TAGS.RECOMMENDED, COMMON_TAGS.CATEGORIES.PERFORMANCE, COMMON_TAGS.LANGUAGES.APEX], + tags: [DEV_PREVIEW_TAG, COMMON_TAGS.CATEGORIES.PERFORMANCE, COMMON_TAGS.LANGUAGES.APEX], description: 'SOQL query inside a loop causes performance issues and can hit governor limits', resourceUrls: ['https://help.salesforce.com/s/articleView?id=xcloud.apexguru_antipattern_soql_in_loop.htm&type=5'] }, @@ -24,7 +26,7 @@ export const APEXGURU_RULES: RuleDescription[] = [ { name: 'DmlInALoop', severityLevel: SeverityLevel.High, - tags: [COMMON_TAGS.RECOMMENDED, COMMON_TAGS.CATEGORIES.PERFORMANCE, COMMON_TAGS.LANGUAGES.APEX], + tags: [DEV_PREVIEW_TAG, COMMON_TAGS.CATEGORIES.PERFORMANCE, COMMON_TAGS.LANGUAGES.APEX], description: 'DML statement inside a loop causes performance issues and can hit governor limits', resourceUrls: ['https://help.salesforce.com/s/articleView?id=xcloud.apexguru_antipattern_dml_in_loop.htm&type=5'] }, @@ -36,7 +38,7 @@ export const APEXGURU_RULES: RuleDescription[] = [ { name: 'SoqlInALoopOneHop', severityLevel: SeverityLevel.High, - tags: [COMMON_TAGS.CATEGORIES.PERFORMANCE, COMMON_TAGS.LANGUAGES.APEX], + tags: [DEV_PREVIEW_TAG, COMMON_TAGS.CATEGORIES.PERFORMANCE, COMMON_TAGS.LANGUAGES.APEX], description: 'SOQL query reached one method-hop away inside a loop causes performance issues and can hit governor limits', resourceUrls: ['https://help.salesforce.com/s/articleView?id=xcloud.apexguru_antipattern_soql_in_loop_one_hop.htm&type=5'] }, @@ -44,7 +46,7 @@ export const APEXGURU_RULES: RuleDescription[] = [ { name: 'ExpensiveMethods', severityLevel: SeverityLevel.High, - tags: [COMMON_TAGS.CATEGORIES.PERFORMANCE, COMMON_TAGS.LANGUAGES.APEX], + tags: [DEV_PREVIEW_TAG, COMMON_TAGS.CATEGORIES.PERFORMANCE, COMMON_TAGS.LANGUAGES.APEX], description: 'Method accounts for a large share of observed Apex CPU time and is a hotspot for performance work', resourceUrls: ['https://help.salesforce.com/s/articleView?id=xcloud.apexguru_antipattern_expensive_methods.htm&type=5'] }, @@ -56,7 +58,7 @@ export const APEXGURU_RULES: RuleDescription[] = [ { name: 'SoqlWithoutAWhereClauseOrLimitStatement', severityLevel: SeverityLevel.Moderate, - tags: [COMMON_TAGS.RECOMMENDED, COMMON_TAGS.CATEGORIES.PERFORMANCE, COMMON_TAGS.LANGUAGES.APEX], + tags: [DEV_PREVIEW_TAG, COMMON_TAGS.CATEGORIES.PERFORMANCE, COMMON_TAGS.LANGUAGES.APEX], description: 'SOQL query without WHERE clause or LIMIT statement can cause performance issues and heap size exceptions', resourceUrls: ['https://help.salesforce.com/s/articleView?id=xcloud.apexguru_antipattern_soql_without_where_clause_or_limit_statement.htm&type=5'] }, @@ -64,7 +66,7 @@ export const APEXGURU_RULES: RuleDescription[] = [ { name: 'SoqlWithWildcardFilter', severityLevel: SeverityLevel.Moderate, - tags: [COMMON_TAGS.RECOMMENDED, COMMON_TAGS.CATEGORIES.PERFORMANCE, COMMON_TAGS.LANGUAGES.APEX], + tags: [DEV_PREVIEW_TAG, COMMON_TAGS.CATEGORIES.PERFORMANCE, COMMON_TAGS.LANGUAGES.APEX], description: 'SOQL query using LIKE with leading wildcard is inefficient and cannot use indexes', resourceUrls: ['https://help.salesforce.com/s/articleView?id=xcloud.apexguru_antipattern_soql_with_wildcard_filter.htm&type=5'] }, @@ -72,7 +74,7 @@ export const APEXGURU_RULES: RuleDescription[] = [ { name: 'SchemaGetGlobalDescribeNotEfficient', severityLevel: SeverityLevel.Moderate, - tags: [COMMON_TAGS.RECOMMENDED, COMMON_TAGS.CATEGORIES.PERFORMANCE, COMMON_TAGS.LANGUAGES.APEX], + tags: [DEV_PREVIEW_TAG, COMMON_TAGS.CATEGORIES.PERFORMANCE, COMMON_TAGS.LANGUAGES.APEX], description: 'Using Schema.getGlobalDescribe() causes unnecessary overhead and decreases performance', resourceUrls: ['https://help.salesforce.com/s/articleView?id=xcloud.apexguru_antipattern_schema_getglobaldescribe_not_efficient.htm&type=5'] }, @@ -84,7 +86,7 @@ export const APEXGURU_RULES: RuleDescription[] = [ { name: 'Soql Aggregation', severityLevel: SeverityLevel.Moderate, - tags: [COMMON_TAGS.CATEGORIES.PERFORMANCE, COMMON_TAGS.LANGUAGES.APEX], + tags: [DEV_PREVIEW_TAG, COMMON_TAGS.CATEGORIES.PERFORMANCE, COMMON_TAGS.LANGUAGES.APEX], description: 'Manual aggregation in Apex instead of using SOQL aggregate functions causes performance issues', resourceUrls: ['https://help.salesforce.com/s/articleView?id=xcloud.apexguru_antipattern_aggregating_in_apex.htm&type=5'] }, @@ -92,7 +94,7 @@ export const APEXGURU_RULES: RuleDescription[] = [ { name: 'SoqlWithApexFilter', severityLevel: SeverityLevel.Moderate, - tags: [COMMON_TAGS.CATEGORIES.PERFORMANCE, COMMON_TAGS.LANGUAGES.APEX], + tags: [DEV_PREVIEW_TAG, COMMON_TAGS.CATEGORIES.PERFORMANCE, COMMON_TAGS.LANGUAGES.APEX], description: 'Filtering records in Apex instead of using SOQL WHERE clause causes performance issues', resourceUrls: ['https://help.salesforce.com/s/articleView?id=xcloud.apexguru_antipattern_soql_with_apex_filter.htm&type=5'] }, @@ -100,7 +102,7 @@ export const APEXGURU_RULES: RuleDescription[] = [ { name: 'CopyingListOrSetElementsUsingAForLoop', severityLevel: SeverityLevel.Moderate, - tags: [COMMON_TAGS.CATEGORIES.PERFORMANCE, COMMON_TAGS.LANGUAGES.APEX], + tags: [DEV_PREVIEW_TAG, COMMON_TAGS.CATEGORIES.PERFORMANCE, COMMON_TAGS.LANGUAGES.APEX], description: 'Copying list or set elements using a for loop is inefficient - use addAll() instead', resourceUrls: ['https://help.salesforce.com/s/articleView?id=xcloud.apexguru_antipattern_copying_elements_with_for_loop.htm&type=5'] }, @@ -108,7 +110,7 @@ export const APEXGURU_RULES: RuleDescription[] = [ { name: 'Redundant Soql', severityLevel: SeverityLevel.Moderate, - tags: [COMMON_TAGS.CATEGORIES.PERFORMANCE, COMMON_TAGS.LANGUAGES.APEX], + tags: [DEV_PREVIEW_TAG, COMMON_TAGS.CATEGORIES.PERFORMANCE, COMMON_TAGS.LANGUAGES.APEX], description: 'Multiple identical SOQL queries cause unnecessary database round trips', resourceUrls: ['https://help.salesforce.com/s/articleView?id=xcloud.apexguru_antipattern_redundant_soql.htm&type=5'] }, @@ -116,7 +118,7 @@ export const APEXGURU_RULES: RuleDescription[] = [ { name: 'SoqlWithNegativeExpressions', severityLevel: SeverityLevel.Moderate, - tags: [COMMON_TAGS.CATEGORIES.PERFORMANCE, COMMON_TAGS.LANGUAGES.APEX], + tags: [DEV_PREVIEW_TAG, COMMON_TAGS.CATEGORIES.PERFORMANCE, COMMON_TAGS.LANGUAGES.APEX], description: 'SOQL queries using negative expressions (NOT IN, !=) don\'t use indexes and cause full table scans', resourceUrls: ['https://help.salesforce.com/s/articleView?id=xcloud.apexguru_antipattern_soql_with_negative_expressions.htm&type=5'] }, @@ -124,7 +126,7 @@ export const APEXGURU_RULES: RuleDescription[] = [ { name: 'SObjectMapInAForLoop', severityLevel: SeverityLevel.Moderate, - tags: [COMMON_TAGS.CATEGORIES.PERFORMANCE, COMMON_TAGS.LANGUAGES.APEX], + tags: [DEV_PREVIEW_TAG, COMMON_TAGS.CATEGORIES.PERFORMANCE, COMMON_TAGS.LANGUAGES.APEX], description: 'Building Map using .put() in a for loop is inefficient - use map constructor or putAll()', resourceUrls: ['https://help.salesforce.com/s/articleView?id=xcloud.apexguru_antipattern_sobject_map_in_for_loop.htm&type=5'] }, @@ -132,7 +134,7 @@ export const APEXGURU_RULES: RuleDescription[] = [ { name: 'SoqlWithoutPlatformCache', severityLevel: SeverityLevel.Moderate, - tags: [COMMON_TAGS.CATEGORIES.PERFORMANCE, COMMON_TAGS.LANGUAGES.APEX], + tags: [DEV_PREVIEW_TAG, COMMON_TAGS.CATEGORIES.PERFORMANCE, COMMON_TAGS.LANGUAGES.APEX], description: 'Frequently executed SOQL query whose results could be served from Platform Cache to reduce database load', resourceUrls: ['https://help.salesforce.com/s/articleView?id=xcloud.apexguru_antipattern_soql_without_platform_cache.htm&type=5'] }, @@ -144,7 +146,7 @@ export const APEXGURU_RULES: RuleDescription[] = [ { name: 'LimitsGetHeapsizeMethods', severityLevel: SeverityLevel.Low, - tags: [COMMON_TAGS.CATEGORIES.PERFORMANCE, COMMON_TAGS.LANGUAGES.APEX], + tags: [DEV_PREVIEW_TAG, COMMON_TAGS.CATEGORIES.PERFORMANCE, COMMON_TAGS.LANGUAGES.APEX], description: 'Frequent Limits.getHeapSize() calls add runtime overhead', resourceUrls: ['https://help.salesforce.com/s/articleView?id=xcloud.apexguru_antipattern_limits_getheapsize_methods.htm&type=5'] }, @@ -152,7 +154,7 @@ export const APEXGURU_RULES: RuleDescription[] = [ { name: 'ExpensiveStringComparison', severityLevel: SeverityLevel.Low, - tags: [COMMON_TAGS.CATEGORIES.PERFORMANCE, COMMON_TAGS.LANGUAGES.APEX], + tags: [DEV_PREVIEW_TAG, COMMON_TAGS.CATEGORIES.PERFORMANCE, COMMON_TAGS.LANGUAGES.APEX], description: 'Inefficient string comparison wastes CPU time', resourceUrls: ['https://help.salesforce.com/s/articleView?id=xcloud.apexguru_antipattern_expensive_string_comparison.htm&type=5'] }, @@ -160,7 +162,7 @@ export const APEXGURU_RULES: RuleDescription[] = [ { name: 'ExpensiveDebugStatements', severityLevel: SeverityLevel.Low, - tags: [COMMON_TAGS.CATEGORIES.PERFORMANCE, COMMON_TAGS.LANGUAGES.APEX], + tags: [DEV_PREVIEW_TAG, COMMON_TAGS.CATEGORIES.PERFORMANCE, COMMON_TAGS.LANGUAGES.APEX], description: 'Expensive System.debug() statements add runtime overhead', resourceUrls: ['https://help.salesforce.com/s/articleView?id=xcloud.apexguru_antipattern_expensive_debug_statements.htm&type=5'] }, @@ -172,7 +174,7 @@ export const APEXGURU_RULES: RuleDescription[] = [ { name: 'UsingTheTestMethodKeyword', severityLevel: SeverityLevel.Low, - tags: [COMMON_TAGS.RECOMMENDED, COMMON_TAGS.CATEGORIES.BEST_PRACTICES, COMMON_TAGS.LANGUAGES.APEX], + tags: [DEV_PREVIEW_TAG, COMMON_TAGS.CATEGORIES.BEST_PRACTICES, COMMON_TAGS.LANGUAGES.APEX], description: 'The testMethod keyword is deprecated - use @isTest annotation instead', resourceUrls: ['https://help.salesforce.com/s/articleView?id=xcloud.apexguru_test_case_antipattern_using_testmethod.htm&type=5'] }, @@ -184,7 +186,7 @@ export const APEXGURU_RULES: RuleDescription[] = [ { name: 'SortingInApex', severityLevel: SeverityLevel.Low, - tags: [COMMON_TAGS.CATEGORIES.BEST_PRACTICES, COMMON_TAGS.LANGUAGES.APEX], + tags: [DEV_PREVIEW_TAG, COMMON_TAGS.CATEGORIES.BEST_PRACTICES, COMMON_TAGS.LANGUAGES.APEX], description: 'Sorting records in Apex wastes CPU time and can exceed governor limits - use ORDER BY in SOQL', resourceUrls: ['https://help.salesforce.com/s/articleView?id=xcloud.apexguru_antipattern_sorting_in_apex.htm&type=5'] }, @@ -192,7 +194,7 @@ export const APEXGURU_RULES: RuleDescription[] = [ { name: 'BusyLoopDelay', severityLevel: SeverityLevel.Low, - tags: [COMMON_TAGS.CATEGORIES.BEST_PRACTICES, COMMON_TAGS.LANGUAGES.APEX], + tags: [DEV_PREVIEW_TAG, COMMON_TAGS.CATEGORIES.BEST_PRACTICES, COMMON_TAGS.LANGUAGES.APEX], description: 'Using empty loops to delay execution wastes CPU time - use System.enqueueJob with delay parameter', resourceUrls: ['https://help.salesforce.com/s/articleView?id=xcloud.apexguru_antipattern_busy_loop_delay.htm&type=5'] }, @@ -200,7 +202,7 @@ export const APEXGURU_RULES: RuleDescription[] = [ { name: 'SoqlWithUnusedFields', severityLevel: SeverityLevel.Low, - tags: [COMMON_TAGS.CATEGORIES.BEST_PRACTICES, COMMON_TAGS.LANGUAGES.APEX], + tags: [DEV_PREVIEW_TAG, COMMON_TAGS.CATEGORIES.BEST_PRACTICES, COMMON_TAGS.LANGUAGES.APEX], description: 'SOQL query selecting unused fields increases resource consumption unnecessarily', resourceUrls: ['https://help.salesforce.com/s/articleView?id=xcloud.apexguru_antipattern_soql_with_unused_fields.htm&type=5'] }, @@ -208,7 +210,7 @@ export const APEXGURU_RULES: RuleDescription[] = [ { name: 'WritingFillerStatements', severityLevel: SeverityLevel.Low, - tags: [COMMON_TAGS.CATEGORIES.BEST_PRACTICES, COMMON_TAGS.LANGUAGES.APEX], + tags: [DEV_PREVIEW_TAG, COMMON_TAGS.CATEGORIES.BEST_PRACTICES, COMMON_TAGS.LANGUAGES.APEX], description: 'Filler statements written to inflate code coverage instead of testing real behavior', resourceUrls: ['https://help.salesforce.com/s/articleView?id=xcloud.apexguru_test_case_antipattern_filler_statements.htm&type=5'] }, @@ -220,7 +222,7 @@ export const APEXGURU_RULES: RuleDescription[] = [ { name: 'apexguru-other', severityLevel: SeverityLevel.Moderate, - tags: [COMMON_TAGS.RECOMMENDED, COMMON_TAGS.CATEGORIES.BEST_PRACTICES, COMMON_TAGS.LANGUAGES.APEX], + tags: [DEV_PREVIEW_TAG, COMMON_TAGS.CATEGORIES.BEST_PRACTICES, COMMON_TAGS.LANGUAGES.APEX], description: 'Other ApexGuru rules - covers new rules added by Salesforce that are not yet explicitly declared', resourceUrls: ['https://help.salesforce.com/s/articleView?id=xcloud.apexguru.htm'] } diff --git a/packages/code-analyzer-apexguru-engine/test/ApexGuruEngine.test.ts b/packages/code-analyzer-apexguru-engine/test/ApexGuruEngine.test.ts index 5269938c..60fa6c9b 100644 --- a/packages/code-analyzer-apexguru-engine/test/ApexGuruEngine.test.ts +++ b/packages/code-analyzer-apexguru-engine/test/ApexGuruEngine.test.ts @@ -1,6 +1,6 @@ import { ApexGuruEngine } from '../src/engine'; import { ApexGuruService } from '../src/services/ApexGuruService'; -import { LogLevel, RunOptions, Workspace } from '@salesforce/code-analyzer-engine-api'; +import { LogLevel, RunOptions, Workspace, COMMON_TAGS } from '@salesforce/code-analyzer-engine-api'; import * as fs from 'node:fs/promises'; import * as fsSync from 'node:fs'; import * as path from 'node:path'; @@ -602,4 +602,50 @@ describe('ApexGuruEngine', () => { expect(results.violations[0].codeLocations[0].file).toBe(realFilePath); }); }); + + describe('DevPreview behavior', () => { + it('rules list should show DevPreview tag on all rules and no Recommended tag', async () => { + const rules = await engine.describeRules({ + logFolder: '/tmp/logs', + workingFolder: '/tmp/working' + }); + + expect(rules.length).toBeGreaterThan(0); + for (const rule of rules) { + expect(rule.tags).toContain('DevPreview'); + expect(rule.tags).not.toContain('Recommended'); + } + }); + + it('should describe rules that can be selected by engine name apexguru', async () => { + const rules = await engine.describeRules({ + logFolder: '/tmp/logs', + workingFolder: '/tmp/working' + }); + + expect(rules.length).toBeGreaterThan(0); + expect(engine.getName()).toBe('apexguru'); + }); + + it('should describe rules selectable by DevPreview tag', async () => { + const rules = await engine.describeRules({ + logFolder: '/tmp/logs', + workingFolder: '/tmp/working' + }); + + const devPreviewRules = rules.filter(r => r.tags.includes('DevPreview')); + expect(devPreviewRules).toHaveLength(rules.length); + }); + + it('should describe individual rules by name for explicit selection', async () => { + const rules = await engine.describeRules({ + logFolder: '/tmp/logs', + workingFolder: '/tmp/working' + }); + + expect(rules.find(r => r.name === 'SoqlInALoop')).toBeDefined(); + expect(rules.find(r => r.name === 'DmlInALoop')).toBeDefined(); + expect(rules.find(r => r.name === 'ExpensiveMethods')).toBeDefined(); + }); + }); }); diff --git a/packages/code-analyzer-apexguru-engine/test/apexguru-rules.test.ts b/packages/code-analyzer-apexguru-engine/test/apexguru-rules.test.ts new file mode 100644 index 00000000..ed958a28 --- /dev/null +++ b/packages/code-analyzer-apexguru-engine/test/apexguru-rules.test.ts @@ -0,0 +1,129 @@ +import { APEXGURU_RULES, DEV_PREVIEW_TAG, isKnownRule, FALLBACK_RULE_NAME } from '../src/apexguru-rules'; +import { COMMON_TAGS } from '@salesforce/code-analyzer-engine-api'; + +describe('apexguru-rules', () => { + + describe('DEV_PREVIEW_TAG constant', () => { + it('should equal DevPreview', () => { + expect(DEV_PREVIEW_TAG).toBe('DevPreview'); + }); + }); + + describe('7 rules that previously had RECOMMENDED tag', () => { + const rulesWithFormerRecommended = [ + 'SoqlInALoop', + 'DmlInALoop', + 'SoqlWithoutAWhereClauseOrLimitStatement', + 'SoqlWithWildcardFilter', + 'SchemaGetGlobalDescribeNotEfficient', + 'UsingTheTestMethodKeyword', + 'apexguru-other' + ]; + + it.each(rulesWithFormerRecommended)('%s should have DevPreview as first tag', (ruleName) => { + const rule = APEXGURU_RULES.find(r => r.name === ruleName); + expect(rule).toBeDefined(); + expect(rule!.tags[0]).toBe('DevPreview'); + }); + + it.each(rulesWithFormerRecommended)('%s should NOT contain Recommended tag', (ruleName) => { + const rule = APEXGURU_RULES.find(r => r.name === ruleName); + expect(rule).toBeDefined(); + expect(rule!.tags).not.toContain('Recommended'); + }); + + it('should have zero rules with Recommended tag across all APEXGURU_RULES', () => { + const rulesWithRecommended = APEXGURU_RULES.filter(r => r.tags.includes('Recommended')); + expect(rulesWithRecommended).toHaveLength(0); + }); + }); + + describe('16 rules without former RECOMMENDED tag', () => { + const rulesWithoutFormerRecommended = [ + 'SoqlInALoopOneHop', + 'ExpensiveMethods', + 'Soql Aggregation', + 'SoqlWithApexFilter', + 'CopyingListOrSetElementsUsingAForLoop', + 'Redundant Soql', + 'SoqlWithNegativeExpressions', + 'SObjectMapInAForLoop', + 'SoqlWithoutPlatformCache', + 'LimitsGetHeapsizeMethods', + 'ExpensiveStringComparison', + 'ExpensiveDebugStatements', + 'SortingInApex', + 'BusyLoopDelay', + 'SoqlWithUnusedFields', + 'WritingFillerStatements' + ]; + + it.each(rulesWithoutFormerRecommended)('%s should have DevPreview as first tag', (ruleName) => { + const rule = APEXGURU_RULES.find(r => r.name === ruleName); + expect(rule).toBeDefined(); + expect(rule!.tags[0]).toBe('DevPreview'); + }); + + it.each(rulesWithoutFormerRecommended)('%s should retain its category tag', (ruleName) => { + const rule = APEXGURU_RULES.find(r => r.name === ruleName); + expect(rule).toBeDefined(); + const hasCategoryTag = rule!.tags.includes(COMMON_TAGS.CATEGORIES.PERFORMANCE) || + rule!.tags.includes(COMMON_TAGS.CATEGORIES.BEST_PRACTICES); + expect(hasCategoryTag).toBe(true); + }); + + it.each(rulesWithoutFormerRecommended)('%s should NOT contain Recommended tag', (ruleName) => { + const rule = APEXGURU_RULES.find(r => r.name === ruleName); + expect(rule).toBeDefined(); + expect(rule!.tags).not.toContain('Recommended'); + }); + }); + + describe('aggregate invariants across all 23 rules', () => { + it('should have exactly 23 rules', () => { + expect(APEXGURU_RULES).toHaveLength(23); + }); + + it('every rule should have DevPreview in its tags', () => { + for (const rule of APEXGURU_RULES) { + expect(rule.tags).toContain('DevPreview'); + } + }); + + it('no rule should have Recommended in its tags', () => { + for (const rule of APEXGURU_RULES) { + expect(rule.tags).not.toContain('Recommended'); + } + }); + + it('every rule should have a category tag (Performance or BestPractices)', () => { + for (const rule of APEXGURU_RULES) { + const hasCategoryTag = rule.tags.includes(COMMON_TAGS.CATEGORIES.PERFORMANCE) || + rule.tags.includes(COMMON_TAGS.CATEGORIES.BEST_PRACTICES); + expect(hasCategoryTag).toBe(true); + } + }); + + it('every rule should have Apex language tag', () => { + for (const rule of APEXGURU_RULES) { + expect(rule.tags).toContain(COMMON_TAGS.LANGUAGES.APEX); + } + }); + }); + + describe('isKnownRule', () => { + it('should return true for known rules', () => { + expect(isKnownRule('SoqlInALoop')).toBe(true); + }); + + it('should return false for unknown rules', () => { + expect(isKnownRule('NonExistentRule')).toBe(false); + }); + }); + + describe('FALLBACK_RULE_NAME', () => { + it('should equal apexguru-other', () => { + expect(FALLBACK_RULE_NAME).toBe('apexguru-other'); + }); + }); +});