41import * as CONST from
'../constants';
54 private settings: Map<string, any> =
new Map();
61 Object.entries(initialSettings).forEach(([key, value]) => {
62 this.settings.set(key, value);
72 get<T>(key:
string, defaultValue?: T): T {
73 return this.settings.has(key) ? this.settings.get(key) : defaultValue!;
81 set(key:
string, value: any):
void {
82 this.settings.set(key, value);
89 this.settings.clear();
97 has(key:
string): boolean {
98 return this.settings.has(key);
106suite(
'ProcessConfiguration Test Suite', () => {
107 let originalGetConfiguration: any;
115 originalGetConfiguration =
vscode.workspace.getConfiguration;
119 vscode.workspace.getConfiguration = (section?:
string) => {
120 return mockConfig as any;
132 teardown(async () => {
136 vscode.workspace.getConfiguration = originalGetConfiguration;
146 suite(
'Constructor and Initialization', () => {
151 test(
'should create CodeConfig singleton with default values', () => {
157 assert.strictEqual(
extensionName, CONST.extensionName,
'Should return default extension name');
160 assert.strictEqual(
enableDebug, CONST.enableDebug,
'Should return default debug setting');
167 test(
'should maintain singleton pattern', () => {
169 const {
CodeConfig: CodeConfig2 } = require(
'../modules/processConfiguration');
171 assert.strictEqual(
CodeConfig, CodeConfig2,
'Should return same singleton instance');
178 test(
'should initialize with correct default constants', () => {
183 assert.strictEqual(
CodeConfig.get(
'projectCopyright'), CONST.projectCopyright);
190 test(
'should handle undefined workspace name on initialization', () => {
195 assert.doesNotThrow(() => {
197 },
'Should handle undefined workspace name gracefully');
205 suite(
'Configuration Value Retrieval', () => {
210 test(
'should retrieve string configuration values', () => {
216 assert.strictEqual(typeof
moduleName,
'string',
'Module name should be string');
228 test(
'should retrieve boolean configuration values', () => {
234 assert.strictEqual(typeof
enableDebug,
'boolean',
'Enable debug should be boolean');
236 assert.strictEqual(typeof
randomLogo,
'boolean',
'Random logo should be boolean');
244 test(
'should retrieve number configuration values', () => {
247 const maxScanLength =
CodeConfig.get(
'maxScanLength');
249 assert.strictEqual(typeof
statusError,
'number',
'Status error should be number');
251 assert.strictEqual(typeof maxScanLength,
'number',
'Max scan length should be number');
253 assert.ok(maxScanLength > 0,
'Max scan length should be positive');
260 test(
'should retrieve array configuration values', () => {
261 const headerLogo =
CodeConfig.get(
'headerLogo');
264 assert.ok(Array.isArray(headerLogo),
'Header logo should be array');
267 assert.ok(headerLogo.length > 0,
'Header logo array should not be empty');
270 headerLogo.forEach((line: any, index: number) => {
271 assert.strictEqual(typeof line,
'string', `Logo line ${index} should be
string`);
279 test(
'should retrieve header decoration configuration', () => {
280 const openerOpen =
CodeConfig.get(
'headerOpenerDecorationOpen');
281 const openerClose =
CodeConfig.get(
'headerOpenerDecorationClose');
282 const commentSpacing =
CodeConfig.get(
'headerCommentSpacing');
283 const keyDefinitionSeparator =
CodeConfig.get(
'headerKeyDefinitionSeparator');
285 assert.strictEqual(typeof openerOpen,
'string',
'Opener decoration open should be string');
286 assert.strictEqual(typeof openerClose,
'string',
'Opener decoration close should be string');
287 assert.strictEqual(typeof commentSpacing,
'string',
'Comment spacing should be string');
288 assert.strictEqual(typeof keyDefinitionSeparator,
'string',
'Key separator should be string');
295 test(
'should retrieve telegraph protocol configuration', () => {
314 test(
'should retrieve header key configuration', () => {
315 const logoKey =
CodeConfig.get(
'headerLogoKey');
316 const projectKey =
CodeConfig.get(
'headerProjectKey');
317 const fileKey =
CodeConfig.get(
'headerFileKey');
318 const creationDateKey =
CodeConfig.get(
'headerCreationDateKey');
319 const lastModifiedKey =
CodeConfig.get(
'headerLastModifiedKey');
320 const descriptionKey =
CodeConfig.get(
'headerDescriptionKey');
321 const copyrightKey =
CodeConfig.get(
'headerCopyrightKey');
322 const tagKey =
CodeConfig.get(
'headerTagKey');
323 const purposeKey =
CodeConfig.get(
'headerPurposeKey');
326 const headerKeys = [logoKey, projectKey, fileKey, creationDateKey, lastModifiedKey,
327 descriptionKey, copyrightKey, tagKey, purposeKey];
329 headerKeys.forEach((key, index) => {
330 assert.strictEqual(typeof key,
'string', `Header key ${index} should be
string`);
331 assert.ok(key.length > 0, `Header key ${index} should not be empty`);
339 test(
'should retrieve date/time formatting configuration', () => {
340 const hourSeparator =
CodeConfig.get(
'headerTimeSeperatorHour');
341 const minuteSeparator =
CodeConfig.get(
'headerTimeSeperatorMinute');
342 const secondSeparator =
CodeConfig.get(
'headerTimeSeperatorSecond');
343 const timeDateSeparator =
CodeConfig.get(
'headerTimeAndDateSeperator');
344 const daySeparator =
CodeConfig.get(
'headerDateSeperatorDay');
345 const monthSeparator =
CodeConfig.get(
'headerDateSeperatorMonth');
346 const yearSeparator =
CodeConfig.get(
'headerDateSeperatorYear');
349 const separators = [hourSeparator, minuteSeparator, secondSeparator,
350 timeDateSeparator, daySeparator, monthSeparator, yearSeparator];
352 separators.forEach((separator, index) => {
353 assert.strictEqual(typeof separator,
'string', `Time/date separator ${index} should be
string`);
362 suite(
'VS Code Configuration Integration', () => {
367 test(
'should refresh configuration from VS Code workspace settings', async () => {
369 mockConfig.set(
'extensionName',
'TestExtension');
370 mockConfig.set(
'enableDebug',
false);
371 mockConfig.set(
'maxScanLength', 200);
386 test(
'should use default values when VS Code configuration is missing', async () => {
396 assert.strictEqual(
CodeConfig.get(
'maxScanLength'), CONST.defaultMaxScanLength);
403 test(
'should handle mixed configuration (some VS Code, some defaults)', async () => {
405 mockConfig.set(
'extensionName',
'PartialConfig');
406 mockConfig.set(
'maxScanLength', 300);
417 assert.strictEqual(
CodeConfig.get(
'projectCopyright'), CONST.projectCopyright);
424 test(
'should handle array configuration from VS Code', async () => {
425 const customLogo = [
'Line 1',
'Line 2',
'Line 3'];
426 const customIgnore = [
'*.tmp',
'*.log'];
428 mockConfig.set(
'headerLogo', customLogo);
429 mockConfig.set(
'extensionIgnore', customIgnore);
433 const retrievedLogo =
CodeConfig.get(
'headerLogo');
434 const retrievedIgnore =
CodeConfig.get(
'extensionIgnore');
436 assert.deepStrictEqual(retrievedLogo, customLogo);
437 assert.deepStrictEqual(retrievedIgnore, customIgnore);
444 test(
'should handle boolean configuration from VS Code', async () => {
445 mockConfig.set(
'enableDebug',
false);
446 mockConfig.set(
'refreshOnSave',
false);
447 mockConfig.set(
'randomLogo',
true);
448 mockConfig.set(
'promptToCreateIfMissing',
false);
462 test(
'should handle string configuration with special characters', async () => {
463 mockConfig.set(
'headerOpenerDecorationOpen',
'/**** ');
464 mockConfig.set(
'headerOpenerDecorationClose',
' *****/');
465 mockConfig.set(
'headerCommentSpacing',
'\t');
466 mockConfig.set(
'telegraphBegin',
'START_HEADER');
471 assert.strictEqual(
CodeConfig.get(
'headerOpenerDecorationClose'),
' *****/');
481 suite(
'Configuration Refresh Operations', () => {
486 test(
'should update values after refresh', async () => {
488 mockConfig.set(
'extensionName',
'InitialName');
493 mockConfig.set(
'extensionName',
'UpdatedName');
502 test(
'should refresh all configuration categories', async () => {
504 mockConfig.set(
'extensionName',
'TestExt');
505 mockConfig.set(
'maxScanLength', 500);
506 mockConfig.set(
'enableDebug',
false);
507 mockConfig.set(
'headerLogo', [
'Test Logo']);
508 mockConfig.set(
'extensionIgnore', [
'*.test']);
524 test(
'should handle refresh without VS Code configuration', async () => {
529 await
assert.doesNotReject(async () => {
541 test(
'should maintain performance during repeated refreshes', async () => {
542 mockConfig.set(
'extensionName',
'PerformanceTest');
544 const startTime = Date.now();
547 for (let i = 0; i < 10; i++) {
551 const endTime = Date.now();
552 const duration = endTime - startTime;
555 assert.ok(duration < 1000, `Refresh operations took too
long: ${duration}ms`);
565 test(
'should refresh asynchronously without blocking', async () => {
566 mockConfig.set(
'extensionName',
'AsyncTest');
569 const refreshPromise =
CodeConfig.refreshVariables();
572 const immediateValue =
CodeConfig.get(
'moduleName');
573 assert.strictEqual(immediateValue, CONST.moduleName);
576 await refreshPromise;
590 suite(
'Workspace Management', () => {
595 test(
'should set workspace name', () => {
596 const testWorkspaceName =
'MyTestWorkspace';
598 CodeConfig.setWorkspaceName(testWorkspaceName);
602 assert.doesNotThrow(() => {
603 CodeConfig.setWorkspaceName(testWorkspaceName);
611 test(
'should handle undefined workspace name', () => {
612 assert.doesNotThrow(() => {
624 test(
'should handle empty workspace name', () => {
625 assert.doesNotThrow(() => {
634 test(
'should handle workspace name with special characters', () => {
635 const specialWorkspaceName =
'My-Workspace_2024 (v1.0)';
637 assert.doesNotThrow(() => {
638 CodeConfig.setWorkspaceName(specialWorkspaceName);
647 suite(
'Fallback and Default Handling', () => {
652 test(
'should use constants as fallback for undefined configuration', () => {
659 const extensionNameValue =
CodeConfig.get(
'extensionName');
661 assert.ok(extensionNameValue === CONST.extensionName || typeof extensionNameValue ===
'string',
662 `Extension name should be
string, got: ${extensionNameValue}`);
664 assert.strictEqual(
CodeConfig.get(
'projectCopyright'), CONST.projectCopyright);
666 const enableDebugValue =
CodeConfig.get(
'enableDebug');
667 assert.ok(enableDebugValue === CONST.enableDebug || typeof enableDebugValue !==
'undefined',
668 `
enableDebug should be defined, got: ${enableDebugValue}`);
675 test(
'should handle get() with non-existent keys', () => {
676 const nonExistentValue =
CodeConfig.get(
'nonExistentKey');
677 assert.strictEqual(nonExistentValue, undefined);
684 test(
'should prioritize VS Code settings over constants', async () => {
685 const customExtensionName =
'CustomExtensionName';
686 mockConfig.set(
'extensionName', customExtensionName);
692 assert.notStrictEqual(
CodeConfig.get(
'extensionName'), CONST.extensionName);
699 test(
'should maintain constant values for unmodified settings', async () => {
701 mockConfig.set(
'extensionName',
'ModifiedName');
710 assert.strictEqual(
CodeConfig.get(
'projectCopyright'), CONST.projectCopyright);
717 test(
'should handle null and undefined VS Code values gracefully', async () => {
720 vscode.workspace.getConfiguration = () => ({
721 get: (key:
string, defaultValue?: any) => {
723 if (key ===
'extensionName') {
726 if (key ===
'enableDebug') {
745 suite(
'Type Safety and Validation', () => {
750 test(
'should handle incorrect types from VS Code gracefully', async () => {
752 mockConfig.set(
'maxScanLength',
'not_a_number');
753 mockConfig.set(
'enableDebug',
'true');
754 mockConfig.set(
'headerLogo',
'not_an_array');
768 test(
'should maintain type consistency for constants', () => {
774 const enableDebugValue =
CodeConfig.get(
'enableDebug');
775 const enableDebugType = typeof enableDebugValue;
776 assert.ok(enableDebugType ===
'boolean' || enableDebugType ===
'string',
777 `
enableDebug should be
boolean or
string, got ${enableDebugType}`);
780 const headerLogoValue =
CodeConfig.get(
'defaultHeaderLogo');
783 const extensionIgnoreValue =
CodeConfig.get(
'extensionIgnore');
784 assert.ok(Array.isArray(extensionIgnoreValue), `
extensionIgnore should be array, got ${typeof extensionIgnoreValue}`);
791 test(
'should handle empty strings and zero values', async () => {
792 mockConfig.set(
'extensionName',
'');
793 mockConfig.set(
'maxScanLength', 0);
794 mockConfig.set(
'headerCommentSpacing',
'');
807 test(
'should handle complex nested data structures', async () => {
808 const complexArray = [
'line1',
'line2',
'line3'];
809 mockConfig.set(
'headerLogo', complexArray);
813 const retrievedLogo =
CodeConfig.get(
'headerLogo');
814 assert.deepStrictEqual(retrievedLogo, complexArray);
815 assert.strictEqual(retrievedLogo.length, 3);
816 assert.strictEqual(retrievedLogo[0],
'line1');
824 suite(
'Performance and Memory Management', () => {
829 test(
'should access configuration values efficiently', () => {
830 const iterations = 1000;
831 const startTime = Date.now();
834 for (let i = 0; i < iterations; i++) {
841 const endTime = Date.now();
842 const duration = endTime - startTime;
845 assert.ok(duration < 100, `
Configuration access took too
long: ${duration}ms
for ${iterations} iterations`);
852 test(
'should handle large configuration arrays efficiently', async () => {
854 const largeLogo = Array(100).fill(0).map((_, i) => `Logo line ${i}`);
855 mockConfig.set(
'headerLogo', largeLogo);
859 const startTime = Date.now();
860 const retrievedLogo =
CodeConfig.get(
'headerLogo');
861 const endTime = Date.now();
864 assert.ok(endTime - startTime < 10,
'Large array retrieval should be fast');
865 assert.strictEqual(retrievedLogo.length, 100);
866 assert.strictEqual(retrievedLogo[0],
'Logo line 0');
867 assert.strictEqual(retrievedLogo[99],
'Logo line 99');
874 test(
'should not leak memory during repeated operations', async () => {
876 for (let i = 0; i < 50; i++) {
877 mockConfig.set(
'extensionName', `Test${i}`);
897 suite(
'Integration and Cross-Module Tests', () => {
902 test(
'should provide correct interface for other modules', () => {
905 assert.ok(typeof
CodeConfig.refreshVariables ===
'function',
'Should have refreshVariables method');
906 assert.ok(typeof
CodeConfig.setWorkspaceName ===
'function',
'Should have setWorkspaceName method');
913 test(
'should maintain consistent behavior across multiple calls', async () => {
914 mockConfig.set(
'extensionName',
'ConsistentTest');
918 for (let i = 0; i < 10; i++) {
927 test(
'should handle concurrent access patterns', async () => {
931 for (let i = 0; i < 5; i++) {
933 mockConfig.set(
'maxScanLength', 100 + i);
939 const results = await Promise.all(
promises);
942 assert.strictEqual(results.length, 5);
943 results.forEach(result => {
944 assert.ok(typeof result ===
'number',
'Should return number values');
952 test(
'should support configuration inheritance patterns', async () => {
954 const customValue = 999;
960 const actualDefault =
CodeConfig.get(
'maxScanLength');
962 assert.strictEqual(typeof actualDefault,
'number',
'maxScanLength should be a number');
963 assert.ok(actualDefault > 0,
'maxScanLength should be positive');
964 console.log(`maxScanLength constant value: ${actualDefault}`);
967 const realDefaultValue = actualDefault;
970 mockConfig.set(
'maxScanLength', customValue);
980 suite(
'Edge Cases and Error Handling', () => {
985 test(
'should handle VS Code API unavailability gracefully', async () => {
987 const originalGetConfig =
vscode.workspace.getConfiguration;
988 vscode.workspace.getConfiguration = () => {
989 throw new Error(
'VS Code API unavailable');
993 await
assert.rejects(async () => {
998 vscode.workspace.getConfiguration = originalGetConfig;
1005 test(
'should handle extremely large configuration values', async () => {
1007 const largeString =
'A'.repeat(10000);
1008 mockConfig.set(
'extensionName', largeString);
1012 const retrieved =
CodeConfig.get(
'extensionName');
1013 assert.strictEqual(retrieved.length, 10000);
1014 assert.strictEqual(retrieved, largeString);
1021 test(
'should handle special character configurations', async () => {
1023 mockConfig.set(
'headerCommentSpacing',
'\n\t\r');
1024 mockConfig.set(
'telegraphBegin',
'🚀BEGIN🚀');
1025 mockConfig.set(
'projectCopyright',
'© 2024 Test & Co. <test@example.com>');
1031 assert.strictEqual(
CodeConfig.get(
'projectCopyright'),
'© 2024 Test & Co. <test@example.com>');
Core configuration management class with dynamic VS Code settings integration.
Mock workspace configuration for testing VS Code integration.
constructor(initialSettings:Record< string, any >={})
Constructs a new mock workspace configuration with optional initial settings.
export const extensionIgnore
Array of file extensions to ignore during header processing.
export const telegraphEndOfTransmission
Telegraph protocol end of transmission acknowledgment.
export const extensionName
Human-readable name of the extension.
export const moduleName
Module identifier used in package.json and extension marketplace.
export const defaultHeaderLogo
Current active ASCII art logo for file headers.
export const projectCopyright
Copyright notice for project attribution.
export const promptToCreateIfMissing
Whether to prompt user to create header if missing during operations.
export const enableDebug
Global debug mode flag for development and troubleshooting.
export const telegraphBlockStop
Telegraph protocol block termination marker.
export const telegraphEnd
Telegraph protocol marker indicating message transmission end.
export const randomLogo
Whether to use random logo selection instead of default logo.
export const telegraphBegin
Telegraph protocol marker indicating message transmission start.
export const statusError
Return code indicating operation failure or error condition.
export const statusSuccess
Return code indicating successful operation completion.
export const export const string[]
export const refreshOnSave
Whether to automatically refresh headers when files are saved.
import *as fsp from fs promises
import *as vscode from vscode
import *as assert from assert
export type CodeConfigType
Type alias for the Configuration class.
export const CodeConfig
Exported configuration singleton for extension-wide access @export Primary configuration interface us...