@ai-dev-tools/csharp-copilot-core 0.0.33 → 0.0.35

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (69) hide show
  1. package/out/batch/generateCodeTests.js +22 -6
  2. package/out/batch/generateCodeTests.js.map +1 -1
  3. package/out/changedFilesProcessor/processChangedFiles.d.ts +9 -0
  4. package/out/changedFilesProcessor/processChangedFiles.js +156 -0
  5. package/out/changedFilesProcessor/processChangedFiles.js.map +1 -0
  6. package/out/codebk/prompts/buildAfGuidelines_20251226.liquid +15 -0
  7. package/out/codebk/prompts/general/generalUtGuidelines_20251226.liquid +16 -0
  8. package/out/codebk/prompts/xap/xapUtGuideline_20251225.liquid +37 -0
  9. package/out/codebk/prompts/xap/xapUtGuideline_20251226.liquid +49 -0
  10. package/out/command/index.js +32 -0
  11. package/out/command/index.js.map +1 -1
  12. package/out/command/utGenWrapper.js +85 -4
  13. package/out/command/utGenWrapper.js.map +1 -1
  14. package/out/gen/autoFix.d.ts +2 -2
  15. package/out/gen/autoFix.js +88 -31
  16. package/out/gen/autoFix.js.map +1 -1
  17. package/out/gen/csharpUtGen.d.ts +1 -1
  18. package/out/gen/csharpUtGen.js +16 -13
  19. package/out/gen/csharpUtGen.js.map +1 -1
  20. package/out/gen/ensureValidLLMResponse.d.ts +5 -1
  21. package/out/gen/ensureValidLLMResponse.js +15 -3
  22. package/out/gen/ensureValidLLMResponse.js.map +1 -1
  23. package/out/gen/postGen/postGenMoreUTProcess.d.ts +25 -0
  24. package/out/gen/postGen/postGenMoreUTProcess.js +502 -0
  25. package/out/gen/postGen/postGenMoreUTProcess.js.map +1 -0
  26. package/out/gen/postGen/postGenProcess.d.ts +1 -1
  27. package/out/gen/postGen/postGenProcess.js +7 -7
  28. package/out/gen/postGen/postGenProcess.js.map +1 -1
  29. package/out/gen/postGen/repairRequiredNameSpaces.d.ts +17 -0
  30. package/out/gen/postGen/repairRequiredNameSpaces.js +87 -20
  31. package/out/gen/postGen/repairRequiredNameSpaces.js.map +1 -1
  32. package/out/llm/preparePrompt.d.ts +2 -1
  33. package/out/llm/preparePrompt.js +38 -4
  34. package/out/llm/preparePrompt.js.map +1 -1
  35. package/out/llm/prompt/buildAfGuidelines.liquid +8 -8
  36. package/out/llm/prompt/general/generalUtGuidelines.liquid +3 -0
  37. package/out/llm/prompt/moreUT/generateMoreUTSourceCode.liquid +4 -0
  38. package/out/llm/prompt/moreUT/generateMoreUtAutoFix.liquid +45 -0
  39. package/out/llm/prompt/moreUT/generateMoreUtTemplate.liquid +115 -0
  40. package/out/llm/prompt/moreUT/generateMoreUtTestCode.liquid +5 -0
  41. package/out/llm/prompt/moreUT/utGenerationGuidelines.liquid +15 -0
  42. package/out/llm/prompt/xap/xapUtGuideline.liquid +66 -15
  43. package/out/types/changedFilesResult.d.ts +37 -0
  44. package/out/types/changedFilesResult.js +3 -0
  45. package/out/types/changedFilesResult.js.map +1 -0
  46. package/out/types/constants.d.ts +1 -0
  47. package/out/types/constants.js +2 -1
  48. package/out/types/constants.js.map +1 -1
  49. package/out/types/genResult.d.ts +1 -1
  50. package/out/types/genResult.js.map +1 -1
  51. package/out/utils/checkXapCode.d.ts +1 -1
  52. package/out/utils/checkXapCode.js +14 -2
  53. package/out/utils/checkXapCode.js.map +1 -1
  54. package/out/utils/fileUtils.d.ts +1 -0
  55. package/out/utils/fileUtils.js +13 -0
  56. package/out/utils/fileUtils.js.map +1 -1
  57. package/out/utils/getCodeStructurePath.d.ts +4 -0
  58. package/out/utils/getCodeStructurePath.js +30 -0
  59. package/out/utils/getCodeStructurePath.js.map +1 -1
  60. package/out/utils/getTestFile.d.ts +18 -8
  61. package/out/utils/getTestFile.js +735 -62
  62. package/out/utils/getTestFile.js.map +1 -1
  63. package/out/utils/removeFailedTestMethods.d.ts +8 -0
  64. package/out/utils/removeFailedTestMethods.js +363 -0
  65. package/out/utils/removeFailedTestMethods.js.map +1 -1
  66. package/out/utils/verifyGeneratedCode.d.ts +2 -0
  67. package/out/utils/verifyGeneratedCode.js +17 -0
  68. package/out/utils/verifyGeneratedCode.js.map +1 -0
  69. package/package.json +4 -3
@@ -2,15 +2,27 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.ensureValidLLMResponse = ensureValidLLMResponse;
4
4
  const postGenProcess_1 = require("./postGen/postGenProcess");
5
+ const postGenMoreUTProcess_1 = require("./postGen/postGenMoreUTProcess");
5
6
  const sendRequestToLLM_1 = require("../llm/sendRequestToLLM");
6
- async function ensureValidLLMResponse(prompt, testFramework, isXapCode, retryTimes = 3) {
7
+ async function ensureValidLLMResponse(prompt, testFramework, isXapCode, testFileExist, testFilePath, isAutoFix = false, retryTimes = 3, sourceCode) {
7
8
  for (let i = 0; i < retryTimes; i++) {
8
9
  // call LLM service to gen code
9
10
  const { content: generatedCode } = await (0, sendRequestToLLM_1.sendRequestToLLM)(prompt);
10
11
  // extract valid code from LLM response
11
- const testCode = (0, postGenProcess_1.postGenProcess)(generatedCode, testFramework, isXapCode);
12
+ let testCode;
13
+ let insertedRanges = undefined;
14
+ if (testFileExist && !isAutoFix) {
15
+ const result = (0, postGenMoreUTProcess_1.postGenMoreUTProcess)(generatedCode, testFilePath, isXapCode);
16
+ if (result) {
17
+ testCode = result.testCode;
18
+ insertedRanges = result.insertedRanges;
19
+ }
20
+ }
21
+ else {
22
+ testCode = (0, postGenProcess_1.postGenProcess)(generatedCode, testFramework, isXapCode, sourceCode);
23
+ }
12
24
  if (!!testCode) {
13
- return testCode;
25
+ return { testCode, insertedRanges };
14
26
  }
15
27
  console.log(`Failed to generate valid test code, retrying... (${i + 1}/${retryTimes})`);
16
28
  }
@@ -1 +1 @@
1
- {"version":3,"file":"ensureValidLLMResponse.js","sourceRoot":"","sources":["../../src/gen/ensureValidLLMResponse.ts"],"names":[],"mappings":";;AAGA,wDAcC;AAjBD,6DAA0D;AAC1D,8DAA2D;AAEpD,KAAK,UAAU,sBAAsB,CAAC,MAAa,EAAE,aAAqB,EAAE,SAAkB,EAAE,aAAqB,CAAC;IACzH,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,UAAU,EAAE,CAAC,EAAE,EAAE,CAAC;QAClC,+BAA+B;QAC/B,MAAM,EAAE,OAAO,EAAE,aAAa,EAAE,GAAG,MAAM,IAAA,mCAAgB,EAAC,MAAM,CAAC,CAAC;QAElE,uCAAuC;QACvC,MAAM,QAAQ,GAAG,IAAA,+BAAc,EAAC,aAAa,EAAE,aAAa,EAAE,SAAS,CAAC,CAAC;QACzE,IAAI,CAAC,CAAC,QAAQ,EAAE,CAAC;YACb,OAAO,QAAQ,CAAC;QACpB,CAAC;QACD,OAAO,CAAC,GAAG,CAAC,oDAAoD,CAAC,GAAG,CAAC,IAAI,UAAU,GAAG,CAAC,CAAC;IAC5F,CAAC;IAED,OAAO,SAAS,CAAC;AACrB,CAAC","sourcesContent":["import { postGenProcess } from \"./postGen/postGenProcess\";\r\nimport { sendRequestToLLM } from \"../llm/sendRequestToLLM\";\r\n\r\nexport async function ensureValidLLMResponse(prompt: any[], testFramework: string, isXapCode: boolean, retryTimes: number = 3): Promise<string> {\r\n for (let i = 0; i < retryTimes; i++) {\r\n // call LLM service to gen code\r\n const { content: generatedCode } = await sendRequestToLLM(prompt);\r\n \r\n // extract valid code from LLM response\r\n const testCode = postGenProcess(generatedCode, testFramework, isXapCode);\r\n if (!!testCode) {\r\n return testCode;\r\n }\r\n console.log(`Failed to generate valid test code, retrying... (${i + 1}/${retryTimes})`);\r\n }\r\n\r\n return undefined;\r\n}"]}
1
+ {"version":3,"file":"ensureValidLLMResponse.js","sourceRoot":"","sources":["../../src/gen/ensureValidLLMResponse.ts"],"names":[],"mappings":";;AASA,wDAwBC;AAjCD,6DAA0D;AAC1D,yEAAsE;AACtE,8DAA2D;AAOpD,KAAK,UAAU,sBAAsB,CAAC,MAAa,EAAE,aAAqB,EAAE,SAAkB,EAAE,aAAsB,EAAE,YAAoB,EAAE,YAAqB,KAAK,EAAE,aAAqB,CAAC,EAAE,UAAmB;IACxN,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,UAAU,EAAE,CAAC,EAAE,EAAE,CAAC;QAClC,+BAA+B;QAC/B,MAAM,EAAE,OAAO,EAAE,aAAa,EAAE,GAAG,MAAM,IAAA,mCAAgB,EAAC,MAAM,CAAC,CAAC;QAElE,uCAAuC;QACvC,IAAI,QAAgB,CAAC;QACrB,IAAI,cAAc,GAAuB,SAAS,CAAC;QACnD,IAAI,aAAa,IAAI,CAAC,SAAS,EAAE,CAAC;YAC9B,MAAM,MAAM,GAAG,IAAA,2CAAoB,EAAC,aAAa,EAAE,YAAY,EAAE,SAAS,CAAC,CAAC;YAC5E,IAAI,MAAM,EAAE,CAAC;gBACT,QAAQ,GAAG,MAAM,CAAC,QAAQ,CAAC;gBAC3B,cAAc,GAAG,MAAM,CAAC,cAAc,CAAC;YAC3C,CAAC;QACL,CAAC;aAAM,CAAC;YACJ,QAAQ,GAAG,IAAA,+BAAc,EAAC,aAAa,EAAE,aAAa,EAAE,SAAS,EAAE,UAAU,CAAC,CAAC;QACnF,CAAC;QACD,IAAI,CAAC,CAAC,QAAQ,EAAE,CAAC;YACb,OAAO,EAAE,QAAQ,EAAE,cAAc,EAAE,CAAC;QACxC,CAAC;QACD,OAAO,CAAC,GAAG,CAAC,oDAAoD,CAAC,GAAG,CAAC,IAAI,UAAU,GAAG,CAAC,CAAC;IAC5F,CAAC;IAED,OAAO,SAAS,CAAC;AACrB,CAAC","sourcesContent":["import { postGenProcess } from \"./postGen/postGenProcess\";\r\nimport { postGenMoreUTProcess } from \"./postGen/postGenMoreUTProcess\";\r\nimport { sendRequestToLLM } from \"../llm/sendRequestToLLM\";\r\n\r\nexport interface LLMResponseResult {\r\n testCode: string;\r\n insertedRanges?: [number, number][];\r\n}\r\n\r\nexport async function ensureValidLLMResponse(prompt: any[], testFramework: string, isXapCode: boolean, testFileExist: boolean, testFilePath: string, isAutoFix: boolean = false, retryTimes: number = 3, sourceCode?: string): Promise<LLMResponseResult> {\r\n for (let i = 0; i < retryTimes; i++) {\r\n // call LLM service to gen code\r\n const { content: generatedCode } = await sendRequestToLLM(prompt);\r\n \r\n // extract valid code from LLM response\r\n let testCode: string;\r\n let insertedRanges: [number, number][] = undefined;\r\n if (testFileExist && !isAutoFix) {\r\n const result = postGenMoreUTProcess(generatedCode, testFilePath, isXapCode);\r\n if (result) {\r\n testCode = result.testCode;\r\n insertedRanges = result.insertedRanges;\r\n }\r\n } else {\r\n testCode = postGenProcess(generatedCode, testFramework, isXapCode, sourceCode);\r\n }\r\n if (!!testCode) {\r\n return { testCode, insertedRanges };\r\n }\r\n console.log(`Failed to generate valid test code, retrying... (${i + 1}/${retryTimes})`);\r\n }\r\n\r\n return undefined;\r\n}"]}
@@ -0,0 +1,25 @@
1
+ /** Result of the post-generation process for additional unit tests */
2
+ interface MoreUTResult {
3
+ testCode: string;
4
+ insertedRanges: [number, number][];
5
+ }
6
+ /**
7
+ * Post-process LLM-generated content for the "more UT" scenario.
8
+ *
9
+ * Unlike `postGenProcess` which handles brand-new test files, this function
10
+ * merges newly generated test methods, using statements and helper code into
11
+ * an **existing** test file. It:
12
+ * 1. Parses the structured LLM response (USING_STATEMENTS / TEST_METHODS / HELPER_CODE sections).
13
+ * 2. De-duplicates and inserts new `using` directives at the correct position.
14
+ * 3. Inserts new test methods right after the last existing test method.
15
+ * 4. Appends helper code immediately after the newly inserted test methods.
16
+ * 5. Preserves the original line-ending style (CRLF / LF).
17
+ *
18
+ * @param generatedContent Raw LLM response containing fenced code sections.
19
+ * @param testFilePath Absolute path to the existing test file on disk.
20
+ * @param isXapCode Whether Xap-specific using directives should be injected.
21
+ * @returns An object with the merged test code and the line ranges that were inserted,
22
+ * or `undefined` if the generated content could not be parsed.
23
+ */
24
+ export declare function postGenMoreUTProcess(generatedContent: string, testFilePath: string, isXapCode: boolean): MoreUTResult | undefined;
25
+ export {};
@@ -0,0 +1,502 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.postGenMoreUTProcess = postGenMoreUTProcess;
37
+ const fs = __importStar(require("fs"));
38
+ const repairRequiredNameSpaces_1 = require("./repairRequiredNameSpaces");
39
+ const constants_1 = require("../../types/constants");
40
+ /**
41
+ * Post-process LLM-generated content for the "more UT" scenario.
42
+ *
43
+ * Unlike `postGenProcess` which handles brand-new test files, this function
44
+ * merges newly generated test methods, using statements and helper code into
45
+ * an **existing** test file. It:
46
+ * 1. Parses the structured LLM response (USING_STATEMENTS / TEST_METHODS / HELPER_CODE sections).
47
+ * 2. De-duplicates and inserts new `using` directives at the correct position.
48
+ * 3. Inserts new test methods right after the last existing test method.
49
+ * 4. Appends helper code immediately after the newly inserted test methods.
50
+ * 5. Preserves the original line-ending style (CRLF / LF).
51
+ *
52
+ * @param generatedContent Raw LLM response containing fenced code sections.
53
+ * @param testFilePath Absolute path to the existing test file on disk.
54
+ * @param isXapCode Whether Xap-specific using directives should be injected.
55
+ * @returns An object with the merged test code and the line ranges that were inserted,
56
+ * or `undefined` if the generated content could not be parsed.
57
+ */
58
+ function postGenMoreUTProcess(generatedContent, testFilePath, isXapCode) {
59
+ // Step 1: Parse the structured LLM response
60
+ const parsed = parseGeneratedMoreUtContent(generatedContent);
61
+ if (!parsed) {
62
+ return undefined;
63
+ }
64
+ const { usingStatements, testMethods, helperCode } = parsed;
65
+ // Step 2: Read and normalise the original test file
66
+ const originalTestCode = fs.readFileSync(testFilePath, 'utf-8');
67
+ const lineEnding = detectLineEnding(originalTestCode);
68
+ const normalizedOriginal = originalTestCode.replace(/\r\n/g, '\n');
69
+ const originalLines = normalizedOriginal.split('\n');
70
+ // Step 3: Detect the indentation convention used in the file
71
+ const baseIndent = detectTestMethodIndent(originalLines);
72
+ const insertedRanges = [];
73
+ let resultLines = [...originalLines];
74
+ let offset = 0;
75
+ // Step 4: Insert using statements (if any)
76
+ const usingResult = insertUsingStatements(resultLines, usingStatements, isXapCode, testMethods, helperCode, offset);
77
+ resultLines = usingResult.lines;
78
+ if (usingResult.range) {
79
+ insertedRanges.push(usingResult.range);
80
+ }
81
+ offset += usingResult.linesInserted;
82
+ // Step 5: Insert new test methods
83
+ let testMethodInsertEnd = 0;
84
+ const testMethodResult = insertTestMethods(resultLines, testMethods, baseIndent, originalLines, offset);
85
+ resultLines = testMethodResult.lines;
86
+ if (testMethodResult.range) {
87
+ insertedRanges.push(testMethodResult.range);
88
+ testMethodInsertEnd = testMethodResult.insertEnd;
89
+ }
90
+ offset += testMethodResult.linesInserted;
91
+ // Step 6: Insert helper code right after the test methods
92
+ const helperResult = insertHelperCode(resultLines, helperCode, baseIndent, originalLines, offset, testMethodInsertEnd);
93
+ resultLines = helperResult.lines;
94
+ if (helperResult.range) {
95
+ insertedRanges.push(helperResult.range);
96
+ }
97
+ // Step 7: Restore the original line-ending style
98
+ const testCode = resultLines.join('\n').replace(/\n/g, lineEnding);
99
+ console.log(`Post generation (more UT) process completed, code length: ${testCode.length}`);
100
+ return { testCode, insertedRanges };
101
+ }
102
+ // ---------------------------------------------------------------------------
103
+ // Section inserters – each returns the mutated line array, the inserted range
104
+ // and the number of lines added so callers can maintain an offset.
105
+ // ---------------------------------------------------------------------------
106
+ /**
107
+ * Insert new `using` directives that don't already exist in the file.
108
+ * Also injects Xap-specific usings when `isXapCode` is true.
109
+ */
110
+ function insertUsingStatements(resultLines, usingStatements, isXapCode, testMethods, helperCode, offset) {
111
+ if (!usingStatements?.trim() && !isXapCode) {
112
+ return { lines: resultLines, range: null, linesInserted: 0 };
113
+ }
114
+ // Collect all using statements (user-provided + Xap-specific)
115
+ let allUsingStatements = usingStatements ? usingStatements.replace(/\r\n/g, '\n') : '';
116
+ if (isXapCode) {
117
+ const xapUsings = [...repairRequiredNameSpaces_1.XAP_REQUIRED_USINGS];
118
+ // Only add XapBondRelatedNamespace if generated code references XapBlob
119
+ if ((testMethods && testMethods.includes('XapBlob')) || (helperCode && helperCode.includes('XapBlob'))) {
120
+ xapUsings.push(constants_1.XapBondRelatedNamespace);
121
+ }
122
+ const xapUsingsStr = xapUsings.join('\n');
123
+ allUsingStatements = allUsingStatements ? `${allUsingStatements}\n${xapUsingsStr}` : xapUsingsStr;
124
+ }
125
+ // De-duplicate against the original file
126
+ const existingUsings = extractExistingUsingStatements(resultLines);
127
+ const newUsingLines = allUsingStatements
128
+ .split('\n')
129
+ .filter(line => line.trim() !== '')
130
+ .filter(line => !existingUsings.has(normalizeUsingStatement(line)));
131
+ if (newUsingLines.length === 0) {
132
+ console.log('All using statements already exist in the original file, skipping insertion.');
133
+ return { lines: resultLines, range: null, linesInserted: 0 };
134
+ }
135
+ // Determine where and with what indentation to insert
136
+ const { insertIndex: insertUsingAt, indent: usingIndent } = (0, repairRequiredNameSpaces_1.detectUsingInsertPosition)(resultLines);
137
+ const indentedUsing = applyIndentation(newUsingLines.join('\n'), usingIndent);
138
+ const usingLines = indentedUsing.split('\n').filter(line => line.trim() !== '');
139
+ const insertIndex = insertUsingAt + offset;
140
+ resultLines.splice(insertIndex, 0, ...usingLines);
141
+ const usingStartLine = insertIndex + 1;
142
+ const usingEndLine = insertIndex + usingLines.length;
143
+ console.log(`Inserted ${usingLines.length} using lines at index ${insertIndex}, range: [${usingStartLine}, ${usingEndLine}]`);
144
+ return { lines: resultLines, range: [usingStartLine, usingEndLine], linesInserted: usingLines.length };
145
+ }
146
+ /**
147
+ * Insert new test methods after the last existing test method in the file.
148
+ * Falls back to inserting after the test-class opening brace when no methods exist.
149
+ */
150
+ function insertTestMethods(resultLines, testMethods, baseIndent, originalLines, offset) {
151
+ if (!testMethods?.trim()) {
152
+ return { lines: resultLines, range: null, linesInserted: 0, insertEnd: 0 };
153
+ }
154
+ const normalizedTestMethods = testMethods.replace(/\r\n/g, '\n');
155
+ const indentedTestMethods = applyIndentation(normalizedTestMethods, baseIndent);
156
+ const testMethodLines = indentedTestMethods.split('\n');
157
+ // Find the insertion point in the *original* file (before any offset)
158
+ let calculatedInsertLine = findLastTestMethodEndLine(originalLines);
159
+ if (calculatedInsertLine === 0) {
160
+ calculatedInsertLine = findTestClassInsertPosition(originalLines);
161
+ if (calculatedInsertLine === 0) {
162
+ console.error('Failed to find insert position for new test methods');
163
+ return { lines: resultLines, range: null, linesInserted: 0, insertEnd: 0 };
164
+ }
165
+ console.log('No existing test methods found, inserting after test class opening brace');
166
+ }
167
+ const insertMethodAt = calculatedInsertLine - 1 + offset;
168
+ // Prepend an empty line for readability
169
+ resultLines.splice(insertMethodAt, 0, '', ...testMethodLines);
170
+ const linesInserted = testMethodLines.length + 1; // +1 for empty line
171
+ const methodStartLine = insertMethodAt + 1;
172
+ const methodEndLine = insertMethodAt + linesInserted;
173
+ const insertEnd = insertMethodAt + linesInserted;
174
+ console.log(`Inserted ${linesInserted} lines (including empty line) at index ${insertMethodAt}, range: [${methodStartLine}, ${methodEndLine}]`);
175
+ return { lines: resultLines, range: [methodStartLine, methodEndLine], linesInserted, insertEnd };
176
+ }
177
+ /**
178
+ * Insert helper / utility code immediately after the newly inserted test methods.
179
+ * Falls back to appending after the last test method if no new methods were inserted.
180
+ */
181
+ function insertHelperCode(resultLines, helperCode, baseIndent, originalLines, offset, testMethodInsertEnd) {
182
+ if (!helperCode?.trim()) {
183
+ return { lines: resultLines, range: null, linesInserted: 0 };
184
+ }
185
+ const normalizedHelperCode = helperCode.replace(/\r\n/g, '\n');
186
+ const indentedHelperCode = applyIndentation(normalizedHelperCode, baseIndent);
187
+ const helperLines = indentedHelperCode.split('\n');
188
+ // Determine insertion point: right after test methods, or fallback
189
+ let insertHelperAt;
190
+ if (testMethodInsertEnd > 0) {
191
+ insertHelperAt = testMethodInsertEnd;
192
+ }
193
+ else {
194
+ let helperInsertLine = findLastTestMethodEndLine(originalLines);
195
+ if (helperInsertLine === 0) {
196
+ helperInsertLine = findTestClassInsertPosition(originalLines);
197
+ }
198
+ insertHelperAt = helperInsertLine - 1 + offset;
199
+ }
200
+ resultLines.splice(insertHelperAt, 0, ...helperLines);
201
+ const helperStartLine = insertHelperAt + 1;
202
+ const helperEndLine = insertHelperAt + helperLines.length;
203
+ console.log(`Inserted ${helperLines.length} helper code lines at index ${insertHelperAt}, range: [${helperStartLine}, ${helperEndLine}]`);
204
+ return { lines: resultLines, range: [helperStartLine, helperEndLine], linesInserted: helperLines.length };
205
+ }
206
+ // ---------------------------------------------------------------------------
207
+ // Parsing & detection helpers
208
+ // ---------------------------------------------------------------------------
209
+ /**
210
+ * Parse the structured LLM response into three fenced-code sections:
211
+ * `USING_STATEMENTS`, `TEST_METHODS` and `HELPER_CODE`.
212
+ *
213
+ * Only `TEST_METHODS` is mandatory – the others may be empty.
214
+ */
215
+ function parseGeneratedMoreUtContent(content) {
216
+ if (!content || content.trim() === '') {
217
+ return undefined;
218
+ }
219
+ const usingMatch = content.match(/```USING_STATEMENTS\s*([\s\S]*?)```/);
220
+ const usingStatements = usingMatch ? usingMatch[1].trim() : '';
221
+ const testMethodsMatch = content.match(/```TEST_METHODS\s*([\s\S]*?)```/);
222
+ const testMethods = testMethodsMatch ? testMethodsMatch[1].trim() : '';
223
+ const helperCodeMatch = content.match(/```HELPER_CODE\s*([\s\S]*?)```/);
224
+ const helperCode = helperCodeMatch ? helperCodeMatch[1].trim() : '';
225
+ if (!testMethods) {
226
+ console.error('Failed to parse generated content: missing TEST_METHODS');
227
+ return undefined;
228
+ }
229
+ return { usingStatements, testMethods, helperCode };
230
+ }
231
+ /**
232
+ * Detect the line-ending convention used in the file.
233
+ * @returns `'\r\n'` for Windows (CRLF) or `'\n'` for Unix (LF).
234
+ */
235
+ function detectLineEnding(content) {
236
+ return content.includes('\r\n') ? '\r\n' : '\n';
237
+ }
238
+ /**
239
+ * Collect all `using` directives already present in the file.
240
+ * @returns A `Set` of normalised using strings for O(1) de-duplication.
241
+ */
242
+ function extractExistingUsingStatements(lines) {
243
+ const existingUsings = new Set();
244
+ for (const line of lines) {
245
+ const normalized = normalizeUsingStatement(line);
246
+ if (normalized) {
247
+ existingUsings.add(normalized);
248
+ }
249
+ }
250
+ return existingUsings;
251
+ }
252
+ /**
253
+ * Normalise a `using` directive for comparison.
254
+ * Strips whitespace and trailing semicolons so that
255
+ * `" using System.Linq ; "` and `"using System.Linq"` match.
256
+ *
257
+ * @returns `null` when the line is not a using directive.
258
+ */
259
+ function normalizeUsingStatement(line) {
260
+ const trimmed = line.trim();
261
+ if (!trimmed.startsWith('using ') || trimmed.includes('(')) {
262
+ return null;
263
+ }
264
+ return trimmed.replace(/;?\s*$/, '').replace(/\s+/g, ' ');
265
+ }
266
+ /**
267
+ * Detect the indentation level of test methods in the file
268
+ * by looking for `[TestMethod]`, `[Test]`, `[Fact]` or `[Theory]` attributes.
269
+ *
270
+ * Falls back to the first non-empty line inside a class body,
271
+ * or 8 spaces if nothing useful is found.
272
+ */
273
+ function detectTestMethodIndent(lines) {
274
+ const testMethodPatterns = [
275
+ /\[TestMethod\]/i,
276
+ /\[Test\]/i,
277
+ /\[Fact\]/i,
278
+ /\[Theory\]/i
279
+ ];
280
+ // Try to match an existing test-method attribute
281
+ for (let i = 0; i < lines.length; i++) {
282
+ const line = lines[i];
283
+ for (const pattern of testMethodPatterns) {
284
+ if (pattern.test(line)) {
285
+ const match = line.match(/^(\s*)/);
286
+ return match ? match[1] : '';
287
+ }
288
+ }
289
+ }
290
+ // Fallback: use the first non-empty line inside a class body
291
+ const classPattern = /^\s*(public\s+)?(partial\s+)?class\s+/i;
292
+ for (let i = 0; i < lines.length; i++) {
293
+ if (classPattern.test(lines[i])) {
294
+ for (let j = i; j < lines.length; j++) {
295
+ if (lines[j].includes('{')) {
296
+ for (let k = j + 1; k < lines.length; k++) {
297
+ const nextLine = lines[k];
298
+ if (nextLine.trim() !== '' && !nextLine.trim().startsWith('}')) {
299
+ const match = nextLine.match(/^(\s*)/);
300
+ if (match && match[1].length > 0) {
301
+ return match[1];
302
+ }
303
+ }
304
+ }
305
+ break;
306
+ }
307
+ }
308
+ break;
309
+ }
310
+ }
311
+ return ' '; // default: 8 spaces
312
+ }
313
+ /**
314
+ * Re-indent a block of code so its minimum indentation matches `baseIndent`,
315
+ * while preserving the *relative* indentation of nested lines.
316
+ */
317
+ function applyIndentation(code, baseIndent) {
318
+ const lines = code.split('\n');
319
+ const result = [];
320
+ // Detect existing minimum indentation
321
+ let minIndent = Infinity;
322
+ for (const line of lines) {
323
+ if (line.trim() === '')
324
+ continue;
325
+ const match = line.match(/^(\s*)/);
326
+ const indent = match ? match[1].length : 0;
327
+ if (indent < minIndent) {
328
+ minIndent = indent;
329
+ }
330
+ }
331
+ if (minIndent === Infinity)
332
+ minIndent = 0;
333
+ for (const line of lines) {
334
+ if (line.trim() === '') {
335
+ result.push('');
336
+ }
337
+ else {
338
+ const match = line.match(/^(\s*)/);
339
+ const currentIndent = match ? match[1].length : 0;
340
+ const relativeIndent = currentIndent - minIndent;
341
+ const newIndent = baseIndent + ' '.repeat(relativeIndent);
342
+ result.push(newIndent + line.trim());
343
+ }
344
+ }
345
+ return result.join('\n');
346
+ }
347
+ /**
348
+ * Find the 1-based line number immediately after the last test method's
349
+ * closing brace. Returns 0 when no test method is found.
350
+ */
351
+ function findLastTestMethodEndLine(lines) {
352
+ const testMethodPatterns = [
353
+ /\[TestMethod\]/i,
354
+ /\[Test\]/i,
355
+ /\[Fact\]/i,
356
+ /\[Theory\]/i
357
+ ];
358
+ let lastTestMethodStartIndex = -1;
359
+ // Scan backwards for the last test-method attribute
360
+ for (let i = lines.length - 1; i >= 0; i--) {
361
+ const line = lines[i].trim();
362
+ for (const pattern of testMethodPatterns) {
363
+ if (pattern.test(line)) {
364
+ lastTestMethodStartIndex = i;
365
+ break;
366
+ }
367
+ }
368
+ if (lastTestMethodStartIndex !== -1)
369
+ break;
370
+ }
371
+ if (lastTestMethodStartIndex === -1) {
372
+ return 0;
373
+ }
374
+ // Walk forward tracking brace depth to locate the matching closing brace
375
+ // Skips braces inside strings, verbatim strings, char literals, and comments
376
+ let braceCount = 0;
377
+ let foundOpeningBrace = false;
378
+ let inMultiLineComment = false;
379
+ for (let i = lastTestMethodStartIndex; i < lines.length; i++) {
380
+ const line = lines[i];
381
+ let inString = false;
382
+ let inVerbatimString = false;
383
+ let inCharLiteral = false;
384
+ for (let j = 0; j < line.length; j++) {
385
+ const ch = line[j];
386
+ const next = j + 1 < line.length ? line[j + 1] : '';
387
+ // Inside multi-line comment: look for */
388
+ if (inMultiLineComment) {
389
+ if (ch === '*' && next === '/') {
390
+ inMultiLineComment = false;
391
+ j++;
392
+ }
393
+ continue;
394
+ }
395
+ // Inside verbatim string: only "" (escaped quote) or closing "
396
+ if (inVerbatimString) {
397
+ if (ch === '"') {
398
+ if (next === '"') {
399
+ j++;
400
+ } // escaped quote
401
+ else {
402
+ inVerbatimString = false;
403
+ }
404
+ }
405
+ continue;
406
+ }
407
+ // Inside regular string: handle \" escape
408
+ if (inString) {
409
+ if (ch === '\\') {
410
+ j++;
411
+ }
412
+ else if (ch === '"') {
413
+ inString = false;
414
+ }
415
+ continue;
416
+ }
417
+ // Inside char literal: handle \' escape
418
+ if (inCharLiteral) {
419
+ if (ch === '\\') {
420
+ j++;
421
+ }
422
+ else if (ch === '\'') {
423
+ inCharLiteral = false;
424
+ }
425
+ continue;
426
+ }
427
+ // Detect start of comments, strings, char literals
428
+ if (ch === '/' && next === '/') {
429
+ break;
430
+ } // single-line comment: skip rest of line
431
+ if (ch === '/' && next === '*') {
432
+ inMultiLineComment = true;
433
+ j++;
434
+ continue;
435
+ }
436
+ if (ch === '@' && next === '"') {
437
+ inVerbatimString = true;
438
+ j++;
439
+ continue;
440
+ }
441
+ if (ch === '"') {
442
+ inString = true;
443
+ continue;
444
+ }
445
+ if (ch === '\'') {
446
+ inCharLiteral = true;
447
+ continue;
448
+ }
449
+ // Count braces in actual code
450
+ if (ch === '{') {
451
+ braceCount++;
452
+ foundOpeningBrace = true;
453
+ }
454
+ else if (ch === '}') {
455
+ braceCount--;
456
+ if (foundOpeningBrace && braceCount === 0) {
457
+ return i + 2; // i is 0-based → +1 for 1-based, +1 for *next* line
458
+ }
459
+ }
460
+ }
461
+ }
462
+ return 0;
463
+ }
464
+ /**
465
+ * Find the 1-based line number right after the test class's opening brace.
466
+ * Used as a fallback when there are no existing test methods.
467
+ */
468
+ function findTestClassInsertPosition(lines) {
469
+ const testClassPatterns = [/\[TestClass\]/i, /\[TestFixture\]/i];
470
+ const classPattern = /^\s*(public\s+)?(partial\s+)?class\s+\w*Test\w*/i;
471
+ let testClassAttributeIndex = -1;
472
+ for (let i = 0; i < lines.length; i++) {
473
+ const line = lines[i].trim();
474
+ for (const pattern of testClassPatterns) {
475
+ if (pattern.test(line)) {
476
+ testClassAttributeIndex = i;
477
+ break;
478
+ }
479
+ }
480
+ if (testClassAttributeIndex !== -1)
481
+ break;
482
+ }
483
+ const searchStartIndex = testClassAttributeIndex !== -1 ? testClassAttributeIndex : 0;
484
+ let classDeclarationIndex = -1;
485
+ for (let i = searchStartIndex; i < lines.length; i++) {
486
+ const line = lines[i];
487
+ if (classPattern.test(line) || (testClassAttributeIndex !== -1 && /^\s*(public\s+)?(partial\s+)?class\s+/i.test(line))) {
488
+ classDeclarationIndex = i;
489
+ break;
490
+ }
491
+ }
492
+ if (classDeclarationIndex === -1) {
493
+ return 0;
494
+ }
495
+ for (let i = classDeclarationIndex; i < lines.length; i++) {
496
+ if (lines[i].indexOf('{') !== -1) {
497
+ return i + 2; // 0-based → +1 for 1-based, +1 for *next* line
498
+ }
499
+ }
500
+ return 0;
501
+ }
502
+ //# sourceMappingURL=postGenMoreUTProcess.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"postGenMoreUTProcess.js","sourceRoot":"","sources":["../../../src/gen/postGen/postGenMoreUTProcess.ts"],"names":[],"mappings":"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","sourcesContent":["import * as fs from 'fs';\r\n\r\nimport { detectUsingInsertPosition, repairXapRequiredNameSpaces, XAP_REQUIRED_USINGS } from \"./repairRequiredNameSpaces\";\r\nimport { XapBondRelatedNamespace } from \"../../types/constants\";\r\n\r\n/** Result of the post-generation process for additional unit tests */\r\ninterface MoreUTResult {\r\n testCode: string;\r\n insertedRanges: [number, number][];\r\n}\r\n\r\n/** Parsed sections from the LLM-generated content */\r\ninterface ParsedMoreUtContent {\r\n usingStatements: string;\r\n testMethods: string;\r\n helperCode: string;\r\n}\r\n\r\n/**\r\n * Post-process LLM-generated content for the \"more UT\" scenario.\r\n *\r\n * Unlike `postGenProcess` which handles brand-new test files, this function\r\n * merges newly generated test methods, using statements and helper code into\r\n * an **existing** test file. It:\r\n * 1. Parses the structured LLM response (USING_STATEMENTS / TEST_METHODS / HELPER_CODE sections).\r\n * 2. De-duplicates and inserts new `using` directives at the correct position.\r\n * 3. Inserts new test methods right after the last existing test method.\r\n * 4. Appends helper code immediately after the newly inserted test methods.\r\n * 5. Preserves the original line-ending style (CRLF / LF).\r\n *\r\n * @param generatedContent Raw LLM response containing fenced code sections.\r\n * @param testFilePath Absolute path to the existing test file on disk.\r\n * @param isXapCode Whether Xap-specific using directives should be injected.\r\n * @returns An object with the merged test code and the line ranges that were inserted,\r\n * or `undefined` if the generated content could not be parsed.\r\n */\r\nexport function postGenMoreUTProcess(\r\n generatedContent: string,\r\n testFilePath: string,\r\n isXapCode: boolean\r\n): MoreUTResult | undefined {\r\n // Step 1: Parse the structured LLM response\r\n const parsed = parseGeneratedMoreUtContent(generatedContent);\r\n if (!parsed) {\r\n return undefined;\r\n }\r\n\r\n const { usingStatements, testMethods, helperCode } = parsed;\r\n\r\n // Step 2: Read and normalise the original test file\r\n const originalTestCode = fs.readFileSync(testFilePath, 'utf-8');\r\n const lineEnding = detectLineEnding(originalTestCode);\r\n const normalizedOriginal = originalTestCode.replace(/\\r\\n/g, '\\n');\r\n const originalLines = normalizedOriginal.split('\\n');\r\n\r\n // Step 3: Detect the indentation convention used in the file\r\n const baseIndent = detectTestMethodIndent(originalLines);\r\n\r\n const insertedRanges: [number, number][] = [];\r\n let resultLines = [...originalLines];\r\n let offset = 0;\r\n\r\n // Step 4: Insert using statements (if any)\r\n const usingResult = insertUsingStatements(\r\n resultLines, usingStatements, isXapCode, testMethods, helperCode, offset\r\n );\r\n resultLines = usingResult.lines;\r\n if (usingResult.range) {\r\n insertedRanges.push(usingResult.range);\r\n }\r\n offset += usingResult.linesInserted;\r\n\r\n // Step 5: Insert new test methods\r\n let testMethodInsertEnd = 0;\r\n const testMethodResult = insertTestMethods(resultLines, testMethods, baseIndent, originalLines, offset);\r\n resultLines = testMethodResult.lines;\r\n if (testMethodResult.range) {\r\n insertedRanges.push(testMethodResult.range);\r\n testMethodInsertEnd = testMethodResult.insertEnd;\r\n }\r\n offset += testMethodResult.linesInserted;\r\n\r\n // Step 6: Insert helper code right after the test methods\r\n const helperResult = insertHelperCode(\r\n resultLines, helperCode, baseIndent, originalLines, offset, testMethodInsertEnd\r\n );\r\n resultLines = helperResult.lines;\r\n if (helperResult.range) {\r\n insertedRanges.push(helperResult.range);\r\n }\r\n\r\n // Step 7: Restore the original line-ending style\r\n const testCode = resultLines.join('\\n').replace(/\\n/g, lineEnding);\r\n console.log(`Post generation (more UT) process completed, code length: ${testCode.length}`);\r\n\r\n return { testCode, insertedRanges };\r\n}\r\n\r\n// ---------------------------------------------------------------------------\r\n// Section inserters – each returns the mutated line array, the inserted range\r\n// and the number of lines added so callers can maintain an offset.\r\n// ---------------------------------------------------------------------------\r\n\r\n/**\r\n * Insert new `using` directives that don't already exist in the file.\r\n * Also injects Xap-specific usings when `isXapCode` is true.\r\n */\r\nfunction insertUsingStatements(\r\n resultLines: string[],\r\n usingStatements: string | undefined,\r\n isXapCode: boolean,\r\n testMethods: string | undefined,\r\n helperCode: string | undefined,\r\n offset: number\r\n): { lines: string[]; range: [number, number] | null; linesInserted: number } {\r\n if (!usingStatements?.trim() && !isXapCode) {\r\n return { lines: resultLines, range: null, linesInserted: 0 };\r\n }\r\n\r\n // Collect all using statements (user-provided + Xap-specific)\r\n let allUsingStatements = usingStatements ? usingStatements.replace(/\\r\\n/g, '\\n') : '';\r\n\r\n if (isXapCode) {\r\n const xapUsings = [...XAP_REQUIRED_USINGS];\r\n // Only add XapBondRelatedNamespace if generated code references XapBlob\r\n if ((testMethods && testMethods.includes('XapBlob')) || (helperCode && helperCode.includes('XapBlob'))) {\r\n xapUsings.push(XapBondRelatedNamespace);\r\n }\r\n const xapUsingsStr = xapUsings.join('\\n');\r\n allUsingStatements = allUsingStatements ? `${allUsingStatements}\\n${xapUsingsStr}` : xapUsingsStr;\r\n }\r\n\r\n // De-duplicate against the original file\r\n const existingUsings = extractExistingUsingStatements(resultLines);\r\n const newUsingLines = allUsingStatements\r\n .split('\\n')\r\n .filter(line => line.trim() !== '')\r\n .filter(line => !existingUsings.has(normalizeUsingStatement(line)));\r\n\r\n if (newUsingLines.length === 0) {\r\n console.log('All using statements already exist in the original file, skipping insertion.');\r\n return { lines: resultLines, range: null, linesInserted: 0 };\r\n }\r\n\r\n // Determine where and with what indentation to insert\r\n const { insertIndex: insertUsingAt, indent: usingIndent } = detectUsingInsertPosition(resultLines);\r\n const indentedUsing = applyIndentation(newUsingLines.join('\\n'), usingIndent);\r\n const usingLines = indentedUsing.split('\\n').filter(line => line.trim() !== '');\r\n\r\n const insertIndex = insertUsingAt + offset;\r\n resultLines.splice(insertIndex, 0, ...usingLines);\r\n\r\n const usingStartLine = insertIndex + 1;\r\n const usingEndLine = insertIndex + usingLines.length;\r\n console.log(`Inserted ${usingLines.length} using lines at index ${insertIndex}, range: [${usingStartLine}, ${usingEndLine}]`);\r\n\r\n return { lines: resultLines, range: [usingStartLine, usingEndLine], linesInserted: usingLines.length };\r\n}\r\n\r\n/**\r\n * Insert new test methods after the last existing test method in the file.\r\n * Falls back to inserting after the test-class opening brace when no methods exist.\r\n */\r\nfunction insertTestMethods(\r\n resultLines: string[],\r\n testMethods: string | undefined,\r\n baseIndent: string,\r\n originalLines: string[],\r\n offset: number\r\n): { lines: string[]; range: [number, number] | null; linesInserted: number; insertEnd: number } {\r\n if (!testMethods?.trim()) {\r\n return { lines: resultLines, range: null, linesInserted: 0, insertEnd: 0 };\r\n }\r\n\r\n const normalizedTestMethods = testMethods.replace(/\\r\\n/g, '\\n');\r\n const indentedTestMethods = applyIndentation(normalizedTestMethods, baseIndent);\r\n const testMethodLines = indentedTestMethods.split('\\n');\r\n\r\n // Find the insertion point in the *original* file (before any offset)\r\n let calculatedInsertLine = findLastTestMethodEndLine(originalLines);\r\n if (calculatedInsertLine === 0) {\r\n calculatedInsertLine = findTestClassInsertPosition(originalLines);\r\n if (calculatedInsertLine === 0) {\r\n console.error('Failed to find insert position for new test methods');\r\n return { lines: resultLines, range: null, linesInserted: 0, insertEnd: 0 };\r\n }\r\n console.log('No existing test methods found, inserting after test class opening brace');\r\n }\r\n\r\n const insertMethodAt = calculatedInsertLine - 1 + offset;\r\n\r\n // Prepend an empty line for readability\r\n resultLines.splice(insertMethodAt, 0, '', ...testMethodLines);\r\n\r\n const linesInserted = testMethodLines.length + 1; // +1 for empty line\r\n const methodStartLine = insertMethodAt + 1;\r\n const methodEndLine = insertMethodAt + linesInserted;\r\n const insertEnd = insertMethodAt + linesInserted;\r\n console.log(`Inserted ${linesInserted} lines (including empty line) at index ${insertMethodAt}, range: [${methodStartLine}, ${methodEndLine}]`);\r\n\r\n return { lines: resultLines, range: [methodStartLine, methodEndLine], linesInserted, insertEnd };\r\n}\r\n\r\n/**\r\n * Insert helper / utility code immediately after the newly inserted test methods.\r\n * Falls back to appending after the last test method if no new methods were inserted.\r\n */\r\nfunction insertHelperCode(\r\n resultLines: string[],\r\n helperCode: string | undefined,\r\n baseIndent: string,\r\n originalLines: string[],\r\n offset: number,\r\n testMethodInsertEnd: number\r\n): { lines: string[]; range: [number, number] | null; linesInserted: number } {\r\n if (!helperCode?.trim()) {\r\n return { lines: resultLines, range: null, linesInserted: 0 };\r\n }\r\n\r\n const normalizedHelperCode = helperCode.replace(/\\r\\n/g, '\\n');\r\n const indentedHelperCode = applyIndentation(normalizedHelperCode, baseIndent);\r\n const helperLines = indentedHelperCode.split('\\n');\r\n\r\n // Determine insertion point: right after test methods, or fallback\r\n let insertHelperAt: number;\r\n if (testMethodInsertEnd > 0) {\r\n insertHelperAt = testMethodInsertEnd;\r\n } else {\r\n let helperInsertLine = findLastTestMethodEndLine(originalLines);\r\n if (helperInsertLine === 0) {\r\n helperInsertLine = findTestClassInsertPosition(originalLines);\r\n }\r\n insertHelperAt = helperInsertLine - 1 + offset;\r\n }\r\n\r\n resultLines.splice(insertHelperAt, 0, ...helperLines);\r\n\r\n const helperStartLine = insertHelperAt + 1;\r\n const helperEndLine = insertHelperAt + helperLines.length;\r\n console.log(`Inserted ${helperLines.length} helper code lines at index ${insertHelperAt}, range: [${helperStartLine}, ${helperEndLine}]`);\r\n\r\n return { lines: resultLines, range: [helperStartLine, helperEndLine], linesInserted: helperLines.length };\r\n}\r\n\r\n// ---------------------------------------------------------------------------\r\n// Parsing & detection helpers\r\n// ---------------------------------------------------------------------------\r\n\r\n/**\r\n * Parse the structured LLM response into three fenced-code sections:\r\n * `USING_STATEMENTS`, `TEST_METHODS` and `HELPER_CODE`.\r\n *\r\n * Only `TEST_METHODS` is mandatory – the others may be empty.\r\n */\r\nfunction parseGeneratedMoreUtContent(content: string): ParsedMoreUtContent | undefined {\r\n if (!content || content.trim() === '') {\r\n return undefined;\r\n }\r\n\r\n const usingMatch = content.match(/```USING_STATEMENTS\\s*([\\s\\S]*?)```/);\r\n const usingStatements = usingMatch ? usingMatch[1].trim() : '';\r\n\r\n const testMethodsMatch = content.match(/```TEST_METHODS\\s*([\\s\\S]*?)```/);\r\n const testMethods = testMethodsMatch ? testMethodsMatch[1].trim() : '';\r\n\r\n const helperCodeMatch = content.match(/```HELPER_CODE\\s*([\\s\\S]*?)```/);\r\n const helperCode = helperCodeMatch ? helperCodeMatch[1].trim() : '';\r\n\r\n if (!testMethods) {\r\n console.error('Failed to parse generated content: missing TEST_METHODS');\r\n return undefined;\r\n }\r\n\r\n return { usingStatements, testMethods, helperCode };\r\n}\r\n\r\n/**\r\n * Detect the line-ending convention used in the file.\r\n * @returns `'\\r\\n'` for Windows (CRLF) or `'\\n'` for Unix (LF).\r\n */\r\nfunction detectLineEnding(content: string): string {\r\n return content.includes('\\r\\n') ? '\\r\\n' : '\\n';\r\n}\r\n\r\n/**\r\n * Collect all `using` directives already present in the file.\r\n * @returns A `Set` of normalised using strings for O(1) de-duplication.\r\n */\r\nfunction extractExistingUsingStatements(lines: string[]): Set<string> {\r\n const existingUsings = new Set<string>();\r\n for (const line of lines) {\r\n const normalized = normalizeUsingStatement(line);\r\n if (normalized) {\r\n existingUsings.add(normalized);\r\n }\r\n }\r\n return existingUsings;\r\n}\r\n\r\n/**\r\n * Normalise a `using` directive for comparison.\r\n * Strips whitespace and trailing semicolons so that\r\n * `\" using System.Linq ; \"` and `\"using System.Linq\"` match.\r\n *\r\n * @returns `null` when the line is not a using directive.\r\n */\r\nfunction normalizeUsingStatement(line: string): string | null {\r\n const trimmed = line.trim();\r\n if (!trimmed.startsWith('using ') || trimmed.includes('(')) {\r\n return null;\r\n }\r\n return trimmed.replace(/;?\\s*$/, '').replace(/\\s+/g, ' ');\r\n}\r\n\r\n/**\r\n * Detect the indentation level of test methods in the file\r\n * by looking for `[TestMethod]`, `[Test]`, `[Fact]` or `[Theory]` attributes.\r\n *\r\n * Falls back to the first non-empty line inside a class body,\r\n * or 8 spaces if nothing useful is found.\r\n */\r\nfunction detectTestMethodIndent(lines: string[]): string {\r\n const testMethodPatterns = [\r\n /\\[TestMethod\\]/i,\r\n /\\[Test\\]/i,\r\n /\\[Fact\\]/i,\r\n /\\[Theory\\]/i\r\n ];\r\n\r\n // Try to match an existing test-method attribute\r\n for (let i = 0; i < lines.length; i++) {\r\n const line = lines[i];\r\n for (const pattern of testMethodPatterns) {\r\n if (pattern.test(line)) {\r\n const match = line.match(/^(\\s*)/);\r\n return match ? match[1] : '';\r\n }\r\n }\r\n }\r\n\r\n // Fallback: use the first non-empty line inside a class body\r\n const classPattern = /^\\s*(public\\s+)?(partial\\s+)?class\\s+/i;\r\n for (let i = 0; i < lines.length; i++) {\r\n if (classPattern.test(lines[i])) {\r\n for (let j = i; j < lines.length; j++) {\r\n if (lines[j].includes('{')) {\r\n for (let k = j + 1; k < lines.length; k++) {\r\n const nextLine = lines[k];\r\n if (nextLine.trim() !== '' && !nextLine.trim().startsWith('}')) {\r\n const match = nextLine.match(/^(\\s*)/);\r\n if (match && match[1].length > 0) {\r\n return match[1];\r\n }\r\n }\r\n }\r\n break;\r\n }\r\n }\r\n break;\r\n }\r\n }\r\n\r\n return ' '; // default: 8 spaces\r\n}\r\n\r\n/**\r\n * Re-indent a block of code so its minimum indentation matches `baseIndent`,\r\n * while preserving the *relative* indentation of nested lines.\r\n */\r\nfunction applyIndentation(code: string, baseIndent: string): string {\r\n const lines = code.split('\\n');\r\n const result: string[] = [];\r\n\r\n // Detect existing minimum indentation\r\n let minIndent = Infinity;\r\n for (const line of lines) {\r\n if (line.trim() === '') continue;\r\n const match = line.match(/^(\\s*)/);\r\n const indent = match ? match[1].length : 0;\r\n if (indent < minIndent) {\r\n minIndent = indent;\r\n }\r\n }\r\n if (minIndent === Infinity) minIndent = 0;\r\n\r\n for (const line of lines) {\r\n if (line.trim() === '') {\r\n result.push('');\r\n } else {\r\n const match = line.match(/^(\\s*)/);\r\n const currentIndent = match ? match[1].length : 0;\r\n const relativeIndent = currentIndent - minIndent;\r\n const newIndent = baseIndent + ' '.repeat(relativeIndent);\r\n result.push(newIndent + line.trim());\r\n }\r\n }\r\n\r\n return result.join('\\n');\r\n}\r\n\r\n/**\r\n * Find the 1-based line number immediately after the last test method's\r\n * closing brace. Returns 0 when no test method is found.\r\n */\r\nfunction findLastTestMethodEndLine(lines: string[]): number {\r\n const testMethodPatterns = [\r\n /\\[TestMethod\\]/i,\r\n /\\[Test\\]/i,\r\n /\\[Fact\\]/i,\r\n /\\[Theory\\]/i\r\n ];\r\n\r\n let lastTestMethodStartIndex = -1;\r\n\r\n // Scan backwards for the last test-method attribute\r\n for (let i = lines.length - 1; i >= 0; i--) {\r\n const line = lines[i].trim();\r\n for (const pattern of testMethodPatterns) {\r\n if (pattern.test(line)) {\r\n lastTestMethodStartIndex = i;\r\n break;\r\n }\r\n }\r\n if (lastTestMethodStartIndex !== -1) break;\r\n }\r\n\r\n if (lastTestMethodStartIndex === -1) {\r\n return 0;\r\n }\r\n\r\n // Walk forward tracking brace depth to locate the matching closing brace\r\n // Skips braces inside strings, verbatim strings, char literals, and comments\r\n let braceCount = 0;\r\n let foundOpeningBrace = false;\r\n let inMultiLineComment = false;\r\n\r\n for (let i = lastTestMethodStartIndex; i < lines.length; i++) {\r\n const line = lines[i];\r\n let inString = false;\r\n let inVerbatimString = false;\r\n let inCharLiteral = false;\r\n\r\n for (let j = 0; j < line.length; j++) {\r\n const ch = line[j];\r\n const next = j + 1 < line.length ? line[j + 1] : '';\r\n\r\n // Inside multi-line comment: look for */\r\n if (inMultiLineComment) {\r\n if (ch === '*' && next === '/') {\r\n inMultiLineComment = false;\r\n j++;\r\n }\r\n continue;\r\n }\r\n\r\n // Inside verbatim string: only \"\" (escaped quote) or closing \"\r\n if (inVerbatimString) {\r\n if (ch === '\"') {\r\n if (next === '\"') { j++; } // escaped quote\r\n else { inVerbatimString = false; }\r\n }\r\n continue;\r\n }\r\n\r\n // Inside regular string: handle \\\" escape\r\n if (inString) {\r\n if (ch === '\\\\') { j++; }\r\n else if (ch === '\"') { inString = false; }\r\n continue;\r\n }\r\n\r\n // Inside char literal: handle \\' escape\r\n if (inCharLiteral) {\r\n if (ch === '\\\\') { j++; }\r\n else if (ch === '\\'') { inCharLiteral = false; }\r\n continue;\r\n }\r\n\r\n // Detect start of comments, strings, char literals\r\n if (ch === '/' && next === '/') { break; } // single-line comment: skip rest of line\r\n if (ch === '/' && next === '*') { inMultiLineComment = true; j++; continue; }\r\n if (ch === '@' && next === '\"') { inVerbatimString = true; j++; continue; }\r\n if (ch === '\"') { inString = true; continue; }\r\n if (ch === '\\'') { inCharLiteral = true; continue; }\r\n\r\n // Count braces in actual code\r\n if (ch === '{') {\r\n braceCount++;\r\n foundOpeningBrace = true;\r\n } else if (ch === '}') {\r\n braceCount--;\r\n if (foundOpeningBrace && braceCount === 0) {\r\n return i + 2; // i is 0-based → +1 for 1-based, +1 for *next* line\r\n }\r\n }\r\n }\r\n }\r\n\r\n return 0;\r\n}\r\n\r\n/**\r\n * Find the 1-based line number right after the test class's opening brace.\r\n * Used as a fallback when there are no existing test methods.\r\n */\r\nfunction findTestClassInsertPosition(lines: string[]): number {\r\n const testClassPatterns = [/\\[TestClass\\]/i, /\\[TestFixture\\]/i];\r\n const classPattern = /^\\s*(public\\s+)?(partial\\s+)?class\\s+\\w*Test\\w*/i;\r\n\r\n let testClassAttributeIndex = -1;\r\n for (let i = 0; i < lines.length; i++) {\r\n const line = lines[i].trim();\r\n for (const pattern of testClassPatterns) {\r\n if (pattern.test(line)) {\r\n testClassAttributeIndex = i;\r\n break;\r\n }\r\n }\r\n if (testClassAttributeIndex !== -1) break;\r\n }\r\n\r\n const searchStartIndex = testClassAttributeIndex !== -1 ? testClassAttributeIndex : 0;\r\n let classDeclarationIndex = -1;\r\n\r\n for (let i = searchStartIndex; i < lines.length; i++) {\r\n const line = lines[i];\r\n if (classPattern.test(line) || (testClassAttributeIndex !== -1 && /^\\s*(public\\s+)?(partial\\s+)?class\\s+/i.test(line))) {\r\n classDeclarationIndex = i;\r\n break;\r\n }\r\n }\r\n\r\n if (classDeclarationIndex === -1) {\r\n return 0;\r\n }\r\n\r\n for (let i = classDeclarationIndex; i < lines.length; i++) {\r\n if (lines[i].indexOf('{') !== -1) {\r\n return i + 2; // 0-based → +1 for 1-based, +1 for *next* line\r\n }\r\n }\r\n\r\n return 0;\r\n}\r\n"]}
@@ -1 +1 @@
1
- export declare function postGenProcess(generatedCode: string, testFramework: string, isXapCode: boolean): string;
1
+ export declare function postGenProcess(generatedCode: string, testFramework: string, isXapCode: boolean, sourceCode?: string): string;