fix: regresi hapus folder tidak kosong, tambah test contentEditor

- Perbaiki pemanggilan deleteEntry di +page.svelte baris 531: argumen
  force=true tidak lagi diteruskan dengan benar setelah signature
  fungsi berubah (confirmCritical ditambah di tengah)

- Koreksi komentar dengan karakter Mandarin di content_editor.py baris 488

- Tambah test unit contentEditor.test.ts dengan 5 kasus:
  - deleteEntry force delete
  - deleteEntry confirm_critical untuk file kritis
  - deleteEntry default parameter
  - renameEntry standar
  - renameEntry confirm_critical

Fixes: hapus folder dengan isi tidak lagi gagal 409.
All tests: 119 passed.
This commit is contained in:
a2nr 2026-08-23 10:43:18 +07:00
parent 1659063784
commit f78eaaca11
3 changed files with 63 additions and 2 deletions

View File

@ -0,0 +1,61 @@
import { describe, it, expect, vi } from 'vitest';
import { deleteEntry, renameEntry } from './contentEditor';
describe('deleteEntry', () => {
it('mengirim force=true saat dipanggil dengan argumen force', async () => {
const fetchMock = vi.fn().mockResolvedValue({ json: async () => ({ success: true }) });
global.fetch = fetchMock;
await deleteEntry('content', 'folder_x', false, true);
const url = fetchMock.mock.calls[0][0] as string;
expect(url).toContain('force=true');
expect(url).toContain('confirm_critical=false');
});
it('mengirim confirm_critical=true untuk file navigasi kritis', async () => {
const fetchMock = vi.fn().mockResolvedValue({ json: async () => ({ success: true }) });
global.fetch = fetchMock;
await deleteEntry('content', 'home.md', true);
const url = fetchMock.mock.calls[0][0] as string;
expect(url).toContain('confirm_critical=true');
expect(url).toContain('force=false');
});
it('mengirim query string yang benar untuk folder biasa', async () => {
const fetchMock = vi.fn().mockResolvedValue({ json: async () => ({ success: true }) });
global.fetch = fetchMock;
await deleteEntry('content', 'some_folder');
const url = fetchMock.mock.calls[0][0] as string;
expect(url).toContain('force=false');
expect(url).toContain('confirm_critical=false');
});
});
describe('renameEntry', () => {
it('mengirim rename dengan argumen standar', async () => {
const fetchMock = vi.fn().mockResolvedValue({ json: async () => ({ success: true }) });
global.fetch = fetchMock;
await renameEntry('content', 'old.md', 'new.md', false);
expect(fetchMock.mock.calls[0][1]).toMatchObject({
method: 'PATCH',
headers: { 'Content-Type': 'application/json' },
});
});
it('mengirim confirm_critical=true untuk filename kritis', async () => {
const fetchMock = vi.fn().mockResolvedValue({ json: async () => ({ success: true }) });
global.fetch = fetchMock;
await renameEntry('content', 'home.md', 'renamed.md', true);
const body = JSON.parse(fetchMock.mock.calls[0][1]?.body ?? '{}');
expect(body.confirm_critical).toBe(true);
});
});

View File

@ -528,7 +528,7 @@
}
} else if (res.needs_force) {
if (window.confirm(`Folder "${name}" tidak kosong. Hapus semua isi?`)) {
const forceRes = await deleteEntry(mgr.treeRoot, path, true);
const forceRes = await deleteEntry(mgr.treeRoot, path, PROTECTED_BASENAMES.has(name), true);
if (forceRes.success) {
await loadTrees();
if (mgr.activePath === path) {

View File

@ -485,7 +485,7 @@ def upload_asset():
# Validasi isi benar-benar gambar (bukan cuma cek ekstensi) — SVG
# dikecualikan (Pillow tidak parse SVG).
# Pakai .load() (decode sungguhan)而不是 .verify(): .verify() melakukan
# Pakai .load() (decode sungguhan) bukan .verify(): .verify() melakukan
# CRC check strict pada IDAT yang sering FALSE-POSITIVE pada PNG valid
# (terutama PNG sederhana / hasil re-encode) → backend 500. .load()
# sudah cukup membuktikan file adalah gambar yang bisa di-decode.