update semantics and terminology from 'brand rules' to 'rules profiles'
This commit is contained in:
@@ -1102,31 +1102,31 @@ describe('rules command', () => {
|
||||
mockExit = jest.spyOn(process, 'exit').mockImplementation(() => {});
|
||||
});
|
||||
|
||||
test('should handle rules add <brand> command', async () => {
|
||||
test('should handle rules add <profile> command', async () => {
|
||||
// Simulate: task-master rules add roo
|
||||
await program.parseAsync(['rules', 'add', 'roo'], { from: 'user' });
|
||||
// Expect some log output indicating success
|
||||
expect(mockConsoleLog).toHaveBeenCalledWith(
|
||||
expect.stringMatching(/adding rules for brand: roo/i)
|
||||
expect.stringMatching(/adding rules for profile: roo/i)
|
||||
);
|
||||
expect(mockConsoleLog).toHaveBeenCalledWith(
|
||||
expect.stringMatching(/completed adding rules for brand: roo/i)
|
||||
expect.stringMatching(/completed adding rules for profile: roo/i)
|
||||
);
|
||||
// Should not exit with error
|
||||
expect(mockExit).not.toHaveBeenCalledWith(1);
|
||||
});
|
||||
|
||||
test('should handle rules remove <brand> command', async () => {
|
||||
test('should handle rules remove <profile> command', async () => {
|
||||
// Simulate: task-master rules remove roo --force
|
||||
await program.parseAsync(['rules', 'remove', 'roo', '--force'], {
|
||||
from: 'user'
|
||||
});
|
||||
// Expect some log output indicating removal
|
||||
expect(mockConsoleLog).toHaveBeenCalledWith(
|
||||
expect.stringMatching(/removing rules for brand: roo/i)
|
||||
expect.stringMatching(/removing rules for profile: roo/i)
|
||||
);
|
||||
expect(mockConsoleLog).toHaveBeenCalledWith(
|
||||
expect.stringMatching(/completed removal for brand: roo/i)
|
||||
expect.stringMatching(/completed removal for profile: roo/i)
|
||||
);
|
||||
// Should not exit with error
|
||||
expect(mockExit).not.toHaveBeenCalledWith(1);
|
||||
|
||||
201
tests/unit/mcp-config-validation.test.js
Normal file
201
tests/unit/mcp-config-validation.test.js
Normal file
@@ -0,0 +1,201 @@
|
||||
import { RULES_PROFILES } from '../../src/constants/profiles.js';
|
||||
import { getRulesProfile } from '../../src/utils/rule-transformer.js';
|
||||
import path from 'path';
|
||||
|
||||
describe('MCP Configuration Validation', () => {
|
||||
describe('Profile MCP Configuration Properties', () => {
|
||||
const expectedMcpConfigurations = {
|
||||
cursor: {
|
||||
shouldHaveMcp: true,
|
||||
expectedDir: '.cursor',
|
||||
expectedConfigName: 'mcp.json',
|
||||
expectedPath: '.cursor/mcp.json'
|
||||
},
|
||||
windsurf: {
|
||||
shouldHaveMcp: true,
|
||||
expectedDir: '.windsurf',
|
||||
expectedConfigName: 'mcp.json',
|
||||
expectedPath: '.windsurf/mcp.json'
|
||||
},
|
||||
roo: {
|
||||
shouldHaveMcp: true,
|
||||
expectedDir: '.roo',
|
||||
expectedConfigName: 'mcp.json',
|
||||
expectedPath: '.roo/mcp.json'
|
||||
},
|
||||
cline: {
|
||||
shouldHaveMcp: false,
|
||||
expectedDir: '.clinerules',
|
||||
expectedConfigName: 'cline_mcp_settings.json',
|
||||
expectedPath: '.clinerules/cline_mcp_settings.json'
|
||||
}
|
||||
};
|
||||
|
||||
Object.entries(expectedMcpConfigurations).forEach(([profileName, expected]) => {
|
||||
test(`should have correct MCP configuration for ${profileName} profile`, () => {
|
||||
const profile = getRulesProfile(profileName);
|
||||
expect(profile).toBeDefined();
|
||||
expect(profile.mcpConfig).toBe(expected.shouldHaveMcp);
|
||||
expect(profile.profileDir).toBe(expected.expectedDir);
|
||||
expect(profile.mcpConfigName).toBe(expected.expectedConfigName);
|
||||
expect(profile.mcpConfigPath).toBe(expected.expectedPath);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('MCP Configuration Path Consistency', () => {
|
||||
test('should ensure all profiles have consistent mcpConfigPath construction', () => {
|
||||
RULES_PROFILES.forEach(profileName => {
|
||||
const profile = getRulesProfile(profileName);
|
||||
if (profile.mcpConfig !== false) {
|
||||
const expectedPath = path.join(profile.profileDir, profile.mcpConfigName);
|
||||
expect(profile.mcpConfigPath).toBe(expectedPath);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
test('should ensure no two profiles have the same MCP config path', () => {
|
||||
const mcpPaths = new Set();
|
||||
RULES_PROFILES.forEach(profileName => {
|
||||
const profile = getRulesProfile(profileName);
|
||||
if (profile.mcpConfig !== false) {
|
||||
expect(mcpPaths.has(profile.mcpConfigPath)).toBe(false);
|
||||
mcpPaths.add(profile.mcpConfigPath);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
test('should ensure all MCP-enabled profiles use proper directory structure', () => {
|
||||
RULES_PROFILES.forEach(profileName => {
|
||||
const profile = getRulesProfile(profileName);
|
||||
if (profile.mcpConfig !== false) {
|
||||
expect(profile.mcpConfigPath).toMatch(/^\.[\w-]+\/[\w_.]+$/);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
test('should ensure all profiles have required MCP properties', () => {
|
||||
RULES_PROFILES.forEach(profileName => {
|
||||
const profile = getRulesProfile(profileName);
|
||||
expect(profile).toHaveProperty('mcpConfig');
|
||||
expect(profile).toHaveProperty('profileDir');
|
||||
expect(profile).toHaveProperty('mcpConfigName');
|
||||
expect(profile).toHaveProperty('mcpConfigPath');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('MCP Configuration File Names', () => {
|
||||
test('should use standard mcp.json for MCP-enabled profiles', () => {
|
||||
const standardMcpProfiles = ['cursor', 'windsurf', 'roo'];
|
||||
standardMcpProfiles.forEach(profileName => {
|
||||
const profile = getRulesProfile(profileName);
|
||||
expect(profile.mcpConfigName).toBe('mcp.json');
|
||||
});
|
||||
});
|
||||
|
||||
test('should use profile-specific config name for non-MCP profiles', () => {
|
||||
const profile = getRulesProfile('cline');
|
||||
expect(profile.mcpConfigName).toBe('cline_mcp_settings.json');
|
||||
});
|
||||
});
|
||||
|
||||
describe('Profile Directory Structure', () => {
|
||||
test('should ensure each profile has a unique directory', () => {
|
||||
const profileDirs = new Set();
|
||||
RULES_PROFILES.forEach(profileName => {
|
||||
const profile = getRulesProfile(profileName);
|
||||
expect(profileDirs.has(profile.profileDir)).toBe(false);
|
||||
profileDirs.add(profile.profileDir);
|
||||
});
|
||||
});
|
||||
|
||||
test('should ensure profile directories follow expected naming convention', () => {
|
||||
RULES_PROFILES.forEach(profileName => {
|
||||
const profile = getRulesProfile(profileName);
|
||||
expect(profile.profileDir).toMatch(/^\.[\w-]+$/);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('MCP Configuration Creation Logic', () => {
|
||||
test('should indicate which profiles require MCP configuration creation', () => {
|
||||
const mcpEnabledProfiles = RULES_PROFILES.filter(profileName => {
|
||||
const profile = getRulesProfile(profileName);
|
||||
return profile.mcpConfig !== false;
|
||||
});
|
||||
|
||||
expect(mcpEnabledProfiles).toContain('cursor');
|
||||
expect(mcpEnabledProfiles).toContain('windsurf');
|
||||
expect(mcpEnabledProfiles).toContain('roo');
|
||||
expect(mcpEnabledProfiles).not.toContain('cline');
|
||||
});
|
||||
|
||||
test('should provide all necessary information for MCP config creation', () => {
|
||||
RULES_PROFILES.forEach(profileName => {
|
||||
const profile = getRulesProfile(profileName);
|
||||
if (profile.mcpConfig !== false) {
|
||||
expect(profile.mcpConfigPath).toBeDefined();
|
||||
expect(typeof profile.mcpConfigPath).toBe('string');
|
||||
expect(profile.mcpConfigPath.length).toBeGreaterThan(0);
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('MCP Configuration Path Usage Verification', () => {
|
||||
test('should verify that rule transformer functions use mcpConfigPath correctly', () => {
|
||||
// This test verifies that the mcpConfigPath property exists and is properly formatted
|
||||
// for use with the setupMCPConfiguration function
|
||||
RULES_PROFILES.forEach(profileName => {
|
||||
const profile = getRulesProfile(profileName);
|
||||
if (profile.mcpConfig !== false) {
|
||||
// Verify the path is properly formatted for path.join usage
|
||||
expect(profile.mcpConfigPath.startsWith('/')).toBe(false);
|
||||
expect(profile.mcpConfigPath).toContain('/');
|
||||
|
||||
// Verify it matches the expected pattern: profileDir/configName
|
||||
const expectedPath = `${profile.profileDir}/${profile.mcpConfigName}`;
|
||||
expect(profile.mcpConfigPath).toBe(expectedPath);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
test('should verify that mcpConfigPath is properly constructed for path.join usage', () => {
|
||||
RULES_PROFILES.forEach(profileName => {
|
||||
const profile = getRulesProfile(profileName);
|
||||
if (profile.mcpConfig !== false) {
|
||||
// Test that path.join works correctly with the mcpConfigPath
|
||||
const testProjectRoot = '/test/project';
|
||||
const fullPath = path.join(testProjectRoot, profile.mcpConfigPath);
|
||||
|
||||
// Should result in a proper absolute path
|
||||
expect(fullPath).toBe(`${testProjectRoot}/${profile.mcpConfigPath}`);
|
||||
expect(fullPath).toContain(profile.profileDir);
|
||||
expect(fullPath).toContain(profile.mcpConfigName);
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('MCP Configuration Function Integration', () => {
|
||||
test('should verify that setupMCPConfiguration receives the correct mcpConfigPath parameter', () => {
|
||||
// This test verifies the integration between rule transformer and mcp-utils
|
||||
RULES_PROFILES.forEach(profileName => {
|
||||
const profile = getRulesProfile(profileName);
|
||||
if (profile.mcpConfig !== false) {
|
||||
// Verify that the mcpConfigPath can be used directly with setupMCPConfiguration
|
||||
// The function signature is: setupMCPConfiguration(projectDir, mcpConfigPath)
|
||||
expect(profile.mcpConfigPath).toBeDefined();
|
||||
expect(typeof profile.mcpConfigPath).toBe('string');
|
||||
|
||||
// Verify the path structure is correct for the new function signature
|
||||
const parts = profile.mcpConfigPath.split('/');
|
||||
expect(parts).toHaveLength(2); // Should be profileDir/configName
|
||||
expect(parts[0]).toBe(profile.profileDir);
|
||||
expect(parts[1]).toBe(profile.mcpConfigName);
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -3,8 +3,9 @@ import path from 'path';
|
||||
import { fileURLToPath } from 'url';
|
||||
import { dirname } from 'path';
|
||||
import {
|
||||
convertRuleToBrandRule,
|
||||
convertAllRulesToBrandRules
|
||||
convertAllRulesToProfileRules,
|
||||
convertRuleToProfileRule,
|
||||
getRulesProfile
|
||||
} from '../../src/utils/rule-transformer.js';
|
||||
import * as cursorProfile from '../../scripts/profiles/cursor.js';
|
||||
|
||||
@@ -44,7 +45,7 @@ Also has references to .mdc files.`;
|
||||
|
||||
// Convert it
|
||||
const testCursorOut = path.join(testDir, 'basic-terms.mdc');
|
||||
convertRuleToBrandRule(testCursorRule, testCursorOut, cursorProfile);
|
||||
convertRuleToProfileRule(testCursorRule, testCursorOut, cursorProfile);
|
||||
|
||||
// Read the converted file
|
||||
const convertedContent = fs.readFileSync(testCursorOut, 'utf8');
|
||||
@@ -75,7 +76,7 @@ alwaysApply: true
|
||||
|
||||
// Convert it
|
||||
const testCursorOut = path.join(testDir, 'tool-refs.mdc');
|
||||
convertRuleToBrandRule(testCursorRule, testCursorOut, cursorProfile);
|
||||
convertRuleToProfileRule(testCursorRule, testCursorOut, cursorProfile);
|
||||
|
||||
// Read the converted file
|
||||
const convertedContent = fs.readFileSync(testCursorOut, 'utf8');
|
||||
@@ -105,7 +106,7 @@ This references [dev_workflow.mdc](mdc:.cursor/rules/dev_workflow.mdc) and
|
||||
|
||||
// Convert it
|
||||
const testCursorOut = path.join(testDir, 'file-refs.mdc');
|
||||
convertRuleToBrandRule(testCursorRule, testCursorOut, cursorProfile);
|
||||
convertRuleToProfileRule(testCursorRule, testCursorOut, cursorProfile);
|
||||
|
||||
// Read the converted file
|
||||
const convertedContent = fs.readFileSync(testCursorOut, 'utf8');
|
||||
|
||||
@@ -3,8 +3,9 @@ import path from 'path';
|
||||
import { fileURLToPath } from 'url';
|
||||
import { dirname } from 'path';
|
||||
import {
|
||||
convertRuleToBrandRule,
|
||||
convertAllRulesToBrandRules
|
||||
convertAllRulesToProfileRules,
|
||||
convertRuleToProfileRule,
|
||||
getRulesProfile
|
||||
} from '../../src/utils/rule-transformer.js';
|
||||
import * as rooProfile from '../../scripts/profiles/roo.js';
|
||||
|
||||
@@ -44,7 +45,7 @@ Also has references to .mdc files.`;
|
||||
|
||||
// Convert it
|
||||
const testRooRule = path.join(testDir, 'basic-terms.md');
|
||||
convertRuleToBrandRule(testCursorRule, testRooRule, rooProfile);
|
||||
convertRuleToProfileRule(testCursorRule, testRooRule, rooProfile);
|
||||
|
||||
// Read the converted file
|
||||
const convertedContent = fs.readFileSync(testRooRule, 'utf8');
|
||||
@@ -75,7 +76,7 @@ alwaysApply: true
|
||||
|
||||
// Convert it
|
||||
const testRooRule = path.join(testDir, 'tool-refs.md');
|
||||
convertRuleToBrandRule(testCursorRule, testRooRule, rooProfile);
|
||||
convertRuleToProfileRule(testCursorRule, testRooRule, rooProfile);
|
||||
|
||||
// Read the converted file
|
||||
const convertedContent = fs.readFileSync(testRooRule, 'utf8');
|
||||
@@ -103,7 +104,7 @@ This references [dev_workflow.mdc](mdc:.cursor/rules/dev_workflow.mdc) and
|
||||
|
||||
// Convert it
|
||||
const testRooRule = path.join(testDir, 'file-refs.md');
|
||||
convertRuleToBrandRule(testCursorRule, testRooRule, rooProfile);
|
||||
convertRuleToProfileRule(testCursorRule, testRooRule, rooProfile);
|
||||
|
||||
// Read the converted file
|
||||
const convertedContent = fs.readFileSync(testRooRule, 'utf8');
|
||||
@@ -121,7 +122,7 @@ This references [dev_workflow.mdc](mdc:.cursor/rules/dev_workflow.mdc) and
|
||||
const assetRule = path.join(assetsRulesDir, 'dev_workflow.mdc');
|
||||
fs.writeFileSync(assetRule, 'dummy');
|
||||
// Should create .roo/rules and call post-processing
|
||||
convertAllRulesToBrandRules(testDir, rooProfile);
|
||||
convertAllRulesToProfileRules(testDir, rooProfile);
|
||||
// Check for post-processing artifacts, e.g., rules-* folders or extra files
|
||||
const rooDir = path.join(testDir, '.roo');
|
||||
const found = fs.readdirSync(rooDir).some((f) => f.startsWith('rules-'));
|
||||
|
||||
@@ -2,7 +2,7 @@ import fs from 'fs';
|
||||
import path from 'path';
|
||||
import { fileURLToPath } from 'url';
|
||||
import { dirname } from 'path';
|
||||
import { convertRuleToBrandRule } from '../../src/utils/rule-transformer.js';
|
||||
import { convertRuleToProfileRule } from '../../src/utils/rule-transformer.js';
|
||||
import * as windsurfProfile from '../../scripts/profiles/windsurf.js';
|
||||
|
||||
const __filename = fileURLToPath(import.meta.url);
|
||||
@@ -41,7 +41,7 @@ Also has references to .mdc files.`;
|
||||
|
||||
// Convert it
|
||||
const testWindsurfRule = path.join(testDir, 'basic-terms.md');
|
||||
convertRuleToBrandRule(testCursorRule, testWindsurfRule, windsurfProfile);
|
||||
convertRuleToProfileRule(testCursorRule, testWindsurfRule, windsurfProfile);
|
||||
|
||||
// Read the converted file
|
||||
const convertedContent = fs.readFileSync(testWindsurfRule, 'utf8');
|
||||
@@ -72,7 +72,7 @@ alwaysApply: true
|
||||
|
||||
// Convert it
|
||||
const testWindsurfRule = path.join(testDir, 'tool-refs.md');
|
||||
convertRuleToBrandRule(testCursorRule, testWindsurfRule, windsurfProfile);
|
||||
convertRuleToProfileRule(testCursorRule, testWindsurfRule, windsurfProfile);
|
||||
|
||||
// Read the converted file
|
||||
const convertedContent = fs.readFileSync(testWindsurfRule, 'utf8');
|
||||
@@ -100,7 +100,7 @@ This references [dev_workflow.mdc](mdc:.cursor/rules/dev_workflow.mdc) and
|
||||
|
||||
// Convert it
|
||||
const testWindsurfRule = path.join(testDir, 'file-refs.md');
|
||||
convertRuleToBrandRule(testCursorRule, testWindsurfRule, windsurfProfile);
|
||||
convertRuleToProfileRule(testCursorRule, testWindsurfRule, windsurfProfile);
|
||||
|
||||
// Read the converted file
|
||||
const convertedContent = fs.readFileSync(testWindsurfRule, 'utf8');
|
||||
|
||||
@@ -1,67 +1,204 @@
|
||||
import {
|
||||
BRAND_PROFILES,
|
||||
BRAND_NAMES,
|
||||
isValidBrand,
|
||||
getBrandProfile
|
||||
isValidProfile,
|
||||
getRulesProfile
|
||||
} from '../../src/utils/rule-transformer.js';
|
||||
import { BRAND_RULE_OPTIONS } from '../../src/constants/rules.js';
|
||||
import { RULES_PROFILES } from '../../src/constants/profiles.js';
|
||||
|
||||
describe('Rule Transformer - General', () => {
|
||||
describe('Brand Configuration Validation', () => {
|
||||
it('should have BRAND_PROFILES that match BRAND_RULE_OPTIONS', () => {
|
||||
// Ensure BRAND_PROFILES keys match the authoritative list from constants/rules.js
|
||||
const profileKeys = Object.keys(BRAND_PROFILES).sort();
|
||||
const ruleOptions = [...BRAND_RULE_OPTIONS].sort();
|
||||
describe('Profile Configuration Validation', () => {
|
||||
it('should use RULES_PROFILES as the single source of truth', () => {
|
||||
// Ensure RULES_PROFILES is properly defined and contains expected profiles
|
||||
expect(Array.isArray(RULES_PROFILES)).toBe(true);
|
||||
expect(RULES_PROFILES.length).toBeGreaterThan(0);
|
||||
|
||||
expect(profileKeys).toEqual(ruleOptions);
|
||||
// Verify expected profiles are present
|
||||
const expectedProfiles = ['cline', 'cursor', 'roo', 'windsurf'];
|
||||
expectedProfiles.forEach(profile => {
|
||||
expect(RULES_PROFILES).toContain(profile);
|
||||
});
|
||||
});
|
||||
|
||||
it('should have BRAND_NAMES derived from BRAND_PROFILES', () => {
|
||||
const expectedNames = Object.keys(BRAND_PROFILES);
|
||||
expect(BRAND_NAMES).toEqual(expectedNames);
|
||||
});
|
||||
|
||||
it('should validate brands correctly with isValidBrand', () => {
|
||||
// Test valid brands
|
||||
BRAND_RULE_OPTIONS.forEach(brand => {
|
||||
expect(isValidBrand(brand)).toBe(true);
|
||||
it('should validate profiles correctly with isValidProfile', () => {
|
||||
// Test valid profiles
|
||||
RULES_PROFILES.forEach((profile) => {
|
||||
expect(isValidProfile(profile)).toBe(true);
|
||||
});
|
||||
|
||||
// Test invalid brands
|
||||
expect(isValidBrand('invalid')).toBe(false);
|
||||
expect(isValidBrand('vscode')).toBe(false);
|
||||
expect(isValidBrand('')).toBe(false);
|
||||
expect(isValidBrand(null)).toBe(false);
|
||||
expect(isValidBrand(undefined)).toBe(false);
|
||||
// Test invalid profiles
|
||||
expect(isValidProfile('invalid')).toBe(false);
|
||||
expect(isValidProfile('')).toBe(false);
|
||||
expect(isValidProfile(null)).toBe(false);
|
||||
expect(isValidProfile(undefined)).toBe(false);
|
||||
});
|
||||
|
||||
it('should return correct brand profiles with getBrandProfile', () => {
|
||||
BRAND_RULE_OPTIONS.forEach(brand => {
|
||||
const profile = getBrandProfile(brand);
|
||||
expect(profile).toBeDefined();
|
||||
expect(profile.brandName.toLowerCase()).toBe(brand);
|
||||
it('should return correct rules profile with getRulesProfile', () => {
|
||||
// Test valid profiles
|
||||
RULES_PROFILES.forEach((profile) => {
|
||||
const profileConfig = getRulesProfile(profile);
|
||||
expect(profileConfig).toBeDefined();
|
||||
expect(profileConfig.profileName.toLowerCase()).toBe(profile);
|
||||
});
|
||||
|
||||
// Test invalid brand
|
||||
expect(getBrandProfile('invalid')).toBeUndefined();
|
||||
// Test invalid profile - should return null
|
||||
expect(getRulesProfile('invalid')).toBeNull();
|
||||
});
|
||||
});
|
||||
|
||||
describe('Brand Profile Structure', () => {
|
||||
it('should have all required properties for each brand profile', () => {
|
||||
BRAND_RULE_OPTIONS.forEach(brand => {
|
||||
const profile = BRAND_PROFILES[brand];
|
||||
|
||||
describe('Profile Structure', () => {
|
||||
it('should have all required properties for each profile', () => {
|
||||
RULES_PROFILES.forEach((profile) => {
|
||||
const profileConfig = getRulesProfile(profile);
|
||||
|
||||
// Check required properties
|
||||
expect(profile).toHaveProperty('brandName');
|
||||
expect(profile).toHaveProperty('conversionConfig');
|
||||
expect(profile).toHaveProperty('fileMap');
|
||||
expect(profile).toHaveProperty('rulesDir');
|
||||
expect(profile).toHaveProperty('brandDir');
|
||||
|
||||
// Verify brand name matches (brandName is capitalized in profiles)
|
||||
expect(profile.brandName.toLowerCase()).toBe(brand);
|
||||
expect(profileConfig).toHaveProperty('profileName');
|
||||
expect(profileConfig).toHaveProperty('conversionConfig');
|
||||
expect(profileConfig).toHaveProperty('fileMap');
|
||||
expect(profileConfig).toHaveProperty('rulesDir');
|
||||
expect(profileConfig).toHaveProperty('profileDir');
|
||||
|
||||
// Check that conversionConfig has required structure
|
||||
expect(profileConfig.conversionConfig).toHaveProperty('profileTerms');
|
||||
expect(profileConfig.conversionConfig).toHaveProperty('toolNames');
|
||||
expect(profileConfig.conversionConfig).toHaveProperty('toolContexts');
|
||||
expect(profileConfig.conversionConfig).toHaveProperty('toolGroups');
|
||||
expect(profileConfig.conversionConfig).toHaveProperty('docUrls');
|
||||
expect(profileConfig.conversionConfig).toHaveProperty('fileReferences');
|
||||
|
||||
// Verify arrays are actually arrays
|
||||
expect(Array.isArray(profileConfig.conversionConfig.profileTerms)).toBe(
|
||||
true
|
||||
);
|
||||
expect(typeof profileConfig.conversionConfig.toolNames).toBe('object');
|
||||
expect(Array.isArray(profileConfig.conversionConfig.toolContexts)).toBe(
|
||||
true
|
||||
);
|
||||
expect(Array.isArray(profileConfig.conversionConfig.toolGroups)).toBe(
|
||||
true
|
||||
);
|
||||
expect(Array.isArray(profileConfig.conversionConfig.docUrls)).toBe(
|
||||
true
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
it('should have valid fileMap with required files for each profile', () => {
|
||||
const expectedFiles = ['cursor_rules.mdc', 'dev_workflow.mdc', 'self_improve.mdc', 'taskmaster.mdc'];
|
||||
|
||||
RULES_PROFILES.forEach((profile) => {
|
||||
const profileConfig = getRulesProfile(profile);
|
||||
|
||||
// Check that fileMap exists and is an object
|
||||
expect(profileConfig.fileMap).toBeDefined();
|
||||
expect(typeof profileConfig.fileMap).toBe('object');
|
||||
expect(profileConfig.fileMap).not.toBeNull();
|
||||
|
||||
// Check that fileMap is not empty
|
||||
const fileMapKeys = Object.keys(profileConfig.fileMap);
|
||||
expect(fileMapKeys.length).toBeGreaterThan(0);
|
||||
|
||||
// Check that all expected source files are defined in fileMap
|
||||
expectedFiles.forEach(expectedFile => {
|
||||
expect(fileMapKeys).toContain(expectedFile);
|
||||
expect(typeof profileConfig.fileMap[expectedFile]).toBe('string');
|
||||
expect(profileConfig.fileMap[expectedFile].length).toBeGreaterThan(0);
|
||||
});
|
||||
|
||||
// Verify fileMap has exactly the expected files
|
||||
expect(fileMapKeys.sort()).toEqual(expectedFiles.sort());
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('MCP Configuration Properties', () => {
|
||||
it('should have all required MCP properties for each profile', () => {
|
||||
RULES_PROFILES.forEach((profile) => {
|
||||
const profileConfig = getRulesProfile(profile);
|
||||
|
||||
// Check MCP-related properties exist
|
||||
expect(profileConfig).toHaveProperty('mcpConfig');
|
||||
expect(profileConfig).toHaveProperty('mcpConfigName');
|
||||
expect(profileConfig).toHaveProperty('mcpConfigPath');
|
||||
|
||||
// Check types
|
||||
expect(typeof profileConfig.mcpConfig).toBe('boolean');
|
||||
expect(typeof profileConfig.mcpConfigName).toBe('string');
|
||||
expect(typeof profileConfig.mcpConfigPath).toBe('string');
|
||||
|
||||
// Check that mcpConfigPath is properly constructed
|
||||
expect(profileConfig.mcpConfigPath).toBe(
|
||||
`${profileConfig.profileDir}/${profileConfig.mcpConfigName}`
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
it('should have correct MCP configuration for each profile', () => {
|
||||
const expectedConfigs = {
|
||||
cursor: {
|
||||
mcpConfig: true,
|
||||
mcpConfigName: 'mcp.json',
|
||||
expectedPath: '.cursor/mcp.json'
|
||||
},
|
||||
windsurf: {
|
||||
mcpConfig: true,
|
||||
mcpConfigName: 'mcp.json',
|
||||
expectedPath: '.windsurf/mcp.json'
|
||||
},
|
||||
roo: {
|
||||
mcpConfig: true,
|
||||
mcpConfigName: 'mcp.json',
|
||||
expectedPath: '.roo/mcp.json'
|
||||
},
|
||||
cline: {
|
||||
mcpConfig: false,
|
||||
mcpConfigName: 'cline_mcp_settings.json',
|
||||
expectedPath: '.clinerules/cline_mcp_settings.json'
|
||||
}
|
||||
};
|
||||
|
||||
RULES_PROFILES.forEach((profile) => {
|
||||
const profileConfig = getRulesProfile(profile);
|
||||
const expected = expectedConfigs[profile];
|
||||
|
||||
expect(profileConfig.mcpConfig).toBe(expected.mcpConfig);
|
||||
expect(profileConfig.mcpConfigName).toBe(expected.mcpConfigName);
|
||||
expect(profileConfig.mcpConfigPath).toBe(expected.expectedPath);
|
||||
});
|
||||
});
|
||||
|
||||
it('should have consistent profileDir and mcpConfigPath relationship', () => {
|
||||
RULES_PROFILES.forEach((profile) => {
|
||||
const profileConfig = getRulesProfile(profile);
|
||||
|
||||
// The mcpConfigPath should start with the profileDir
|
||||
expect(profileConfig.mcpConfigPath).toMatch(
|
||||
new RegExp(`^${profileConfig.profileDir.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}/`)
|
||||
);
|
||||
|
||||
// The mcpConfigPath should end with the mcpConfigName
|
||||
expect(profileConfig.mcpConfigPath).toMatch(
|
||||
new RegExp(`${profileConfig.mcpConfigName.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}$`)
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
it('should have unique profile directories', () => {
|
||||
const profileDirs = RULES_PROFILES.map((profile) => {
|
||||
const profileConfig = getRulesProfile(profile);
|
||||
return profileConfig.profileDir;
|
||||
});
|
||||
|
||||
const uniqueProfileDirs = [...new Set(profileDirs)];
|
||||
expect(uniqueProfileDirs).toHaveLength(profileDirs.length);
|
||||
});
|
||||
|
||||
it('should have unique MCP config paths', () => {
|
||||
const mcpConfigPaths = RULES_PROFILES.map((profile) => {
|
||||
const profileConfig = getRulesProfile(profile);
|
||||
return profileConfig.mcpConfigPath;
|
||||
});
|
||||
|
||||
const uniqueMcpConfigPaths = [...new Set(mcpConfigPaths)];
|
||||
expect(uniqueMcpConfigPaths).toHaveLength(mcpConfigPaths.length);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user