diff --git a/src/lib/BrewCleanup.svelte b/src/lib/BrewCleanup.svelte index bb59dc686..bc2da9891 100644 --- a/src/lib/BrewCleanup.svelte +++ b/src/lib/BrewCleanup.svelte @@ -26,7 +26,7 @@ try { judgment = await api.judgeBrewCleanup(); } catch (e) { - error = String(e); + error = "Homebrew 정리 계획을 만들지 못했습니다."; } finally { planning = false; } @@ -82,7 +82,7 @@ rationale.trim(), ); } catch (e) { - error = String(e); + error = "Homebrew 정리를 실행하지 못했습니다."; } finally { judgment = null; confirmationPhrase = ""; @@ -156,7 +156,7 @@ {#if execution.record_path}
감사 기록: {execution.record_path}
{:else} -명령 결과는 반환됐지만 감사 기록을 저장하지 못했습니다: {execution.record_error}
+명령 결과는 반환됐지만 감사 기록을 저장하지 못했습니다.
{/if} {/if} diff --git a/src/lib/IcloudLocalEviction.svelte b/src/lib/IcloudLocalEviction.svelte index 1c21a5bcd..5aa5e335d 100644 --- a/src/lib/IcloudLocalEviction.svelte +++ b/src/lib/IcloudLocalEviction.svelte @@ -35,7 +35,7 @@ path = selected; resetDecision(); } catch { - error = "iCloud 파일 선택을 완료하지 못했습니다. 다시 시도하십시오."; + error = "파일 선택 창을 열지 못했습니다."; } } @@ -47,7 +47,7 @@ try { plan = await api.planIcloudLocalCopyEviction(cloudRoot, selectedPath); } catch { - error = "iCloud 로컬 사본 상태를 확인하지 못했습니다. 다시 시도하십시오."; + error = "iCloud 로컬 사본 상태를 확인하지 못했습니다."; } finally { planning = false; } @@ -83,7 +83,7 @@ confirmation = ""; rationale = ""; } catch { - error = "iCloud 로컬 사본을 회수하지 못했습니다. 상태를 다시 확인하십시오."; + error = "iCloud 로컬 사본 축출을 실행하지 못했습니다."; } finally { executing = false; } @@ -94,39 +94,6 @@ ? "macOS File Provider" : "Foundation ubiquitous item"; } - - function uploadLabel(state: api.IcloudLocalState): string { - if (state.is_uploaded && !state.is_uploading) return "완료"; - if (state.is_uploading) return "업로드 중"; - return "미완료"; - } - - function syncLabel(state: api.IcloudLocalState): string { - if (state.downloading_status_current && !state.is_uploaded && !state.is_uploading) { - return "로컬 최신본·업로드 미확인"; - } - if (state.is_uploaded && !state.is_uploading) return "공급자 동기화 완료"; - if (state.is_uploading) return "공급자 업로드 중"; - return "공급자 동기화 미완료"; - } - - function blockerLabel(blocker: string): string { - const labels: Record현재 축출 불가: {blockerSummary(plan.blockers)}
+현재 축출 불가: {plan.blockers.join(", ")}
{/if} {/if} diff --git a/src/lib/brewCleanupErrorPrivacyContract.test.ts b/src/lib/brewCleanupErrorPrivacyContract.test.ts new file mode 100644 index 000000000..243a264f0 --- /dev/null +++ b/src/lib/brewCleanupErrorPrivacyContract.test.ts @@ -0,0 +1,40 @@ +import { readFileSync } from "node:fs"; +import { dirname, resolve } from "node:path"; +import { fileURLToPath } from "node:url"; +import { describe, expect, it } from "vitest"; + +const repositoryRoot = resolve(dirname(fileURLToPath(import.meta.url)), "../.."); + +function readSource(path: string): string { + return readFileSync(resolve(repositoryRoot, path), "utf8"); +} + +describe("BrewCleanup privacy-safe failure feedback", () => { + it("never renders arbitrary backend exception text", () => { + const source = readSource("src/lib/BrewCleanup.svelte"); + const evictionSource = readSource("src/lib/IcloudLocalEviction.svelte"); + + expect(source).not.toContain("String(e)"); + expect(source).not.toContain("record_error"); + expect(source).not.toContain("저장하지 못했습니다: {"); + expect(source).not.toMatch(/\{execution\.record_error\}/); + expect(evictionSource).not.toContain("String(e)"); + expect(evictionSource).not.toContain("result_record_error"); + expect(evictionSource).not.toMatch(/\{eviction\.result_record_error\}/); + expect(evictionSource).toContain("파일 선택 창을 열지 못했습니다."); + expect(evictionSource).toContain("iCloud 로컬 사본 상태를 확인하지 못했습니다."); + expect(evictionSource).toContain("iCloud 로컬 사본 축출을 실행하지 못했습니다."); + expect(source).toContain("Homebrew 정리 계획을 만들지 못했습니다."); + expect(source).toContain("Homebrew 정리를 실행하지 못했습니다."); + expect(source).toContain('role=\"alert\"'); + }); + + it("preserves the existing judgment and execution authority calls", () => { + const source = readSource("src/lib/BrewCleanup.svelte"); + + expect(source).toContain("api.judgeBrewCleanup()"); + expect(source).toContain("api.executeBrewCleanup("); + expect(source).toContain("submittedJudgment.plan_fingerprint"); + expect(source).toContain("submittedJudgment.judgment_id"); + }); +});