Skip to content

Commit f99b314

Browse files
committed
분석 대상 파일이 문자열 길이 제한을 초과해 잘린 경우에만 생략 문구 표시
downloadFileEntries가 isContentTruncated 값을 함께 반환하고, 해당 값을 분석 댓글 렌더링에 사용하도록 변경한다. MAX_FILE_SIZE는 파일의 바이트 단위 크기로 오해할 수 있어 문자열 길이 제한임이 드러나도록 MAX_FILE_CONTENT_LENGTH로 변경한다. 생략 문구 표시에 대한 경계값 테스트를 추가한다.
1 parent 31ac045 commit f99b314

2 files changed

Lines changed: 55 additions & 10 deletions

File tree

handlers/tag-patterns.js

Lines changed: 17 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ const COMMENT_MARKER = "<!-- dalestudy-pattern-tag -->";
2323
// 레거시 단독 복잡도 issue comment 식별용. 새 합본 댓글에는 박지 않는다.
2424
const LEGACY_COMPLEXITY_MARKER = "<!-- dalestudy-complexity-analysis -->";
2525
const SOLUTION_PATH_REGEX = /^[^/]+\/[^/]+\.[^.]+$/;
26-
const MAX_FILE_SIZE = 20000; // 20K 문자 제한 (OpenAI 토큰 안전장치)
26+
const MAX_FILE_CONTENT_LENGTH = 20000; // OpenAI 입력 크기 안전장치
2727

2828
/**
2929
* PR의 솔루션 파일들에 알고리즘 패턴 태그 달기
@@ -139,7 +139,7 @@ export async function tagPatterns(
139139
/**
140140
* 단일 파일 분석 + 코멘트 작성
141141
*
142-
* @param {{file: object, problemName: string, content: string}} fileEntry
142+
* @param {{file: object, problemName: string, content: string, isContentTruncated: boolean}} fileEntry
143143
* @param {Promise<Array>} complexityPromise - 모든 파일의 복잡도 분석 결과 (병렬 진행)
144144
*/
145145
async function tagSingleFile(
@@ -152,7 +152,12 @@ async function tagSingleFile(
152152
appToken,
153153
openaiApiKey
154154
) {
155-
const { file, problemName, content: fileContent } = fileEntry;
155+
const {
156+
file,
157+
problemName,
158+
content: fileContent,
159+
isContentTruncated,
160+
} = fileEntry;
156161

157162
// OpenAI 패턴 분석
158163
const analysis = await generatePatternAnalysis(
@@ -167,7 +172,7 @@ async function tagSingleFile(
167172
let body = `${COMMENT_MARKER}
168173
### 🏷️ 알고리즘 패턴 분석
169174
170-
${renderAnalyzedSource(file.filename, fileContent)}
175+
${renderAnalyzedSource(file.filename, fileContent, isContentTruncated)}
171176
172177
- **패턴**: ${patternsText}
173178
- **설명**: ${analysis.description || "(설명 없음)"}`;
@@ -209,15 +214,15 @@ ${renderAnalyzedSource(file.filename, fileContent)}
209214
return { patterns: analysis.patterns };
210215
}
211216

212-
function renderAnalyzedSource(filename, content) {
217+
function renderAnalyzedSource(filename, content, isContentTruncated) {
213218
const language = filename.includes(".") ? filename.split(".").pop() : "";
214-
const truncated = content.length >= MAX_FILE_SIZE ? "\n... (이하 생략)" : "";
219+
const truncationNotice = isContentTruncated ? "\n... (이하 생략)" : "";
215220

216221
return `<details>
217222
<summary>${filename}</summary>
218223
219224
\`\`\`${language}
220-
${content}${truncated}
225+
${content}${truncationNotice}
221226
\`\`\`
222227
223228
</details>`;
@@ -237,16 +242,18 @@ async function downloadFileEntries(solutionFiles) {
237242
);
238243
}
239244
let content = await res.text();
240-
if (content.length > MAX_FILE_SIZE) {
241-
content = content.slice(0, MAX_FILE_SIZE);
245+
const isContentTruncated = content.length > MAX_FILE_CONTENT_LENGTH;
246+
if (isContentTruncated) {
247+
content = content.slice(0, MAX_FILE_CONTENT_LENGTH);
242248
console.log(
243-
`[tagPatterns] Truncated ${file.filename} to ${MAX_FILE_SIZE} chars`
249+
`[tagPatterns] Truncated ${file.filename} to ${MAX_FILE_CONTENT_LENGTH} chars`
244250
);
245251
}
246252
return {
247253
file,
248254
problemName: file.filename.split("/")[0],
249255
content,
256+
isContentTruncated,
250257
};
251258
})
252259
);

tests/tag-patterns.test.js

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ const OPENAI_KEY = "fake-openai-key";
1919

2020
const PATTERN_MARKER = "<!-- dalestudy-pattern-tag -->";
2121
const LEGACY_COMPLEXITY_MARKER = "<!-- dalestudy-complexity-analysis -->";
22+
const MAX_FILE_CONTENT_LENGTH = 20000;
2223

2324
const PLAIN_SOURCE = "function solution() { return 0; }";
2425

@@ -248,6 +249,43 @@ ${PLAIN_SOURCE}
248249
</details>`);
249250
});
250251

252+
it.each([MAX_FILE_CONTENT_LENGTH - 1, MAX_FILE_CONTENT_LENGTH])(
253+
"파일 내용 길이가 제한값 이하인 경우(%i자) 생략 문구를 표시하지 않는다",
254+
async (contentLength) => {
255+
const posts = [];
256+
globalThis.fetch = makeFetchMock({
257+
solutionFiles: [makeSolutionFile("two-sum")],
258+
rawContent: "a".repeat(contentLength),
259+
postCapture: posts,
260+
});
261+
262+
await tagPatterns(
263+
REPO_OWNER, REPO_NAME, PR_NUMBER, HEAD_SHA,
264+
makePrData(),
265+
APP_TOKEN, OPENAI_KEY
266+
);
267+
268+
expect(posts[0].body).not.toContain("... (이하 생략)");
269+
}
270+
);
271+
272+
it("파일 내용 길이가 제한값을 초과하면 생략 문구를 표시한다", async () => {
273+
const posts = [];
274+
globalThis.fetch = makeFetchMock({
275+
solutionFiles: [makeSolutionFile("two-sum")],
276+
rawContent: "a".repeat(MAX_FILE_CONTENT_LENGTH + 1),
277+
postCapture: posts,
278+
});
279+
280+
await tagPatterns(
281+
REPO_OWNER, REPO_NAME, PR_NUMBER, HEAD_SHA,
282+
makePrData(),
283+
APP_TOKEN, OPENAI_KEY
284+
);
285+
286+
expect(posts[0].body).toContain("... (이하 생략)");
287+
});
288+
251289
it("복잡도 OpenAI 가 실패해도 패턴 댓글은 정상 작성된다", async () => {
252290
const posts = [];
253291
globalThis.fetch = makeFetchMock({

0 commit comments

Comments
 (0)