test: 整併測試至 app/test 並解決 AI 審查 findings #8
+2
-2
@@ -68,7 +68,7 @@ function isValidRepoSegment(part) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 驗證 repository 為 owner/repo 格式(恰兩段,且每段皆為合法區段)
|
// 驗證 repository 為 owner/repo 格式(恰兩段,且每段皆為合法區段)
|
||||||
function assertRepository(name, value) {
|
function assertGiteaRepositoryFormat(name, value) {
|
||||||
const parts = value.split('/');
|
const parts = value.split('/');
|
||||||
const isRepoFormatValid = parts.length === 2 && parts.every(isValidRepoSegment);
|
const isRepoFormatValid = parts.length === 2 && parts.every(isValidRepoSegment);
|
||||||
if (!isRepoFormatValid) {
|
if (!isRepoFormatValid) {
|
||||||
@@ -91,7 +91,7 @@ function loadConfig(env = process.env) {
|
|||||||
const serverUrl = requireEnv('GITEA_SERVER_URL', env.GITEA_SERVER_URL);
|
const serverUrl = requireEnv('GITEA_SERVER_URL', env.GITEA_SERVER_URL);
|
||||||
assertHttpUrl('GITEA_SERVER_URL', serverUrl);
|
assertHttpUrl('GITEA_SERVER_URL', serverUrl);
|
||||||
const repository = requireEnv('GITEA_REPOSITORY', env.GITEA_REPOSITORY);
|
const repository = requireEnv('GITEA_REPOSITORY', env.GITEA_REPOSITORY);
|
||||||
assertRepository('GITEA_REPOSITORY', repository);
|
assertGiteaRepositoryFormat('GITEA_REPOSITORY', repository);
|
||||||
const token = isUnset(env.GITEA_TOKEN) ? null : env.GITEA_TOKEN;
|
const token = isUnset(env.GITEA_TOKEN) ? null : env.GITEA_TOKEN;
|
||||||
const isBeta = normalizeBetaFlag(env.IS_BETA);
|
const isBeta = normalizeBetaFlag(env.IS_BETA);
|
||||||
|
|
||||||
|
|||||||
+9
-5
@@ -6,6 +6,13 @@ const RELEASES_PER_PAGE = 10;
|
|||||||
// JSON 解析失敗時,附在錯誤訊息中的回應內容片段最大字元數
|
// JSON 解析失敗時,附在錯誤訊息中的回應內容片段最大字元數
|
||||||
const API_ERROR_SNIPPET_LENGTH = 200;
|
const API_ERROR_SNIPPET_LENGTH = 200;
|
||||||
|
|
||||||
|
// 以字元(而非 UTF-16 碼元)安全截斷字串至 limit 個字元,避免拆分多位元組字元造成亂碼;
|
||||||
|
// 先以長度上限粗略截掉過長輸入,避免將可能極大的字串整個陣列化
|
||||||
|
function truncateString(text, limit) {
|
||||||
|
const bounded = text.slice(0, limit * 2);
|
||||||
|
return Array.from(bounded).slice(0, limit).join('');
|
||||||
|
}
|
||||||
|
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* 以分頁方式取得指定 Gitea repo 的所有 release,並回傳合併後的陣列。
|
* 以分頁方式取得指定 Gitea repo 的所有 release,並回傳合併後的陣列。
|
||||||
*
|
*
|
||||||
@@ -66,10 +73,7 @@ async function fetchReleases(baseUrl, options = {}) {
|
|||||||
try {
|
try {
|
||||||
pageJson = JSON.parse(text);
|
pageJson = JSON.parse(text);
|
||||||
} catch {
|
} catch {
|
||||||
// 先以 UTF-16 長度粗略上限截斷(避免將可能極大的回應整個陣列化),
|
const contentSnippet = truncateString(text, API_ERROR_SNIPPET_LENGTH);
|
||||||
// 再以字元(而非 UTF-16 碼元)精準截斷,避免拆分多位元組字元造成亂碼
|
|
||||||
const boundedText = text.slice(0, API_ERROR_SNIPPET_LENGTH * 2);
|
|
||||||
const contentSnippet = Array.from(boundedText).slice(0, API_ERROR_SNIPPET_LENGTH).join('');
|
|
||||||
throw new Error(`release API 回傳資料無法解析 (page=${page}),回應內容片段:「${contentSnippet}」`);
|
throw new Error(`release API 回傳資料無法解析 (page=${page}),回應內容片段:「${contentSnippet}」`);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -96,4 +100,4 @@ async function fetchReleases(baseUrl, options = {}) {
|
|||||||
return combined;
|
return combined;
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = { RELEASES_PER_PAGE, fetchReleases };
|
module.exports = { RELEASES_PER_PAGE, truncateString, fetchReleases };
|
||||||
|
|||||||
Reference in New Issue
Block a user
嚴重等級:🟡 警告
審查員:Leo
問題:
truncateString使用Array.from(bounded).slice(0, limit).join('')的方式處理字串截斷,雖然能避免拆分代理對(surrogate pairs),但在處理極大字串(例如API_ERROR_SNIPPET_LENGTH很大時)會因為Array.from產生巨大的陣列而導致記憶體使用量激增。考慮到這是在解析失敗時處理的錯誤訊息,這種設計可能讓原本就已經吃緊的記憶體狀況雪上加霜。建議:若不需要嚴格支援所有 Unicode 字元組合,考慮改用更節省記憶體的方式(如
Intl.Segmenter或調整截斷邏輯),或者明確說明此處對記憶體的使用考量。若目的是為了錯誤記錄,或許直接截斷原始字串的長度後確保不要在最後一個字元產生半個代理對即可。