fix(backups): подтверждать операции в текущем контексте

This commit is contained in:
Denozordec committed 2026-10-01 16:11:26 +07:00
1 parent 714af7e872
commit 4c5604bf87
9 files changed
+174 -49

No files matched your search

+10 -1
View File
@@ -1,6 +1,6 @@
import assert from "node:assert/strict"
import { test } from "node:test"
import { listBackups, createBackupsAsync, getBackupJob, deleteBackup, restoreBackup, getBackupScheduleSettings, putBackupScheduleSettings, getBackupStorageSettings, putBackupStorageSettings, testBackupStorage, syncBackupsFromStorage } from "./backups"
import { listBackups, downloadBackup, createBackupsAsync, getBackupJob, deleteBackup, restoreBackup, getBackupScheduleSettings, putBackupScheduleSettings, getBackupStorageSettings, putBackupStorageSettings, testBackupStorage, syncBackupsFromStorage } from "./backups"
const base = "https://api.example.test"
const item = { id: "backup-1", serverId: 1, serverName: "router", filename: "config.rsc", sizeBytes: 0, createdAt: "2026-10-01T00:00:00Z", kind: "manual", storage: "local" }
@@ -96,3 +96,12 @@ test("backup request ID is sent and a different acknowledgment is rejected", asy
globalThis.fetch = async (_url, init) => { body = JSON.parse(String(init?.body)); return json({ jobId: "other", serverIds: ["1"], status: "queued", total: 1, completed: 0 }, 202) }
try { await assert.rejects(() => createBackupsAsync(base, { requestId, serverIds: ["1"] }), /другому запросу/); assert.equal(body.requestId, requestId) } finally { globalThis.fetch = original }
})
test("download encodes identity and refuses HTML/JSON success bodies", async () => {
const original = globalThis.fetch; let url = ""
try {
for (const type of ["text/html", "application/json", ""]) { globalThis.fetch = async input => { url = String(input); return new Response("wrong body", { headers: { "Content-Type": type } }) }; await assert.rejects(() => downloadBackup(base, "a/b"), /не подтверждён/) }
assert.ok(url.endsWith("a%2Fb/download"))
globalThis.fetch = async () => new Response("config", { headers: { "Content-Type": "text/plain; charset=utf-8" } }); assert.equal(await (await downloadBackup(base, "backup-1")).text(), "config")
} finally { globalThis.fetch = original }
})
+7
View File
@@ -77,3 +77,10 @@ export async function testBackupStorage(baseUrl: string, options?: RequestInit)
export async function syncBackupsFromStorage(baseUrl: string, options?: RequestInit) {
return syncBackupsResponseSchema.parse(await backupJson(baseUrl, "/api/backups/storage/sync", { ...options, method: "POST" }))
}
export async function downloadBackup(baseUrl: string, id: string, options?: RequestInit) {
const response = await requestBlob(baseUrl, "/api/backups/" + encodeURIComponent(id) + "/download", backupOptions({ ...options, method: "GET" }))
const type = response.headers.get("Content-Type")?.split(";")[0].trim().toLowerCase()
if (type !== "text/plain" && type !== "application/octet-stream") throw new Error("Файл резервной копии не подтверждён API")
return response.blob()
}