Deploy contracts-flask / validate (push) Canceled after 0s
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
167 lines
4.9 KiB
JavaScript
167 lines
4.9 KiB
JavaScript
import assert from 'node:assert/strict';
|
|
import { putToVm } from './upload/put_to_vm.js';
|
|
import { uploadViaVM } from './upload/upload_via_vm.js';
|
|
|
|
if (typeof globalThis.File === 'undefined') {
|
|
globalThis.File = class File extends Blob {
|
|
constructor(parts, name, options = {}) {
|
|
super(parts, options);
|
|
this.name = name;
|
|
this.lastModified = options.lastModified || Date.now();
|
|
}
|
|
};
|
|
}
|
|
globalThis.crypto = globalThis.crypto || { randomUUID: () => 'test-upload-id' };
|
|
|
|
let lastXHR;
|
|
class FakeXHR {
|
|
constructor() {
|
|
this.upload = {};
|
|
this.status = 0;
|
|
this.statusText = '';
|
|
this.timeout = 0;
|
|
lastXHR = this;
|
|
}
|
|
open(method, url) {
|
|
this.method = method;
|
|
this.url = url;
|
|
}
|
|
send(body) {
|
|
this.body = body;
|
|
}
|
|
complete(status = 200) {
|
|
this.status = status;
|
|
this.onload();
|
|
}
|
|
}
|
|
globalThis.XMLHttpRequest = FakeXHR;
|
|
|
|
let fetchCalls = [];
|
|
let fetchResponder;
|
|
globalThis.fetch = async (url, options) => {
|
|
fetchCalls.push({ url, options });
|
|
return fetchResponder(fetchCalls.length);
|
|
};
|
|
|
|
const tick = () => new Promise((resolve) => setTimeout(resolve, 0));
|
|
async function waitForNextXHR(previous) {
|
|
for (let attempt = 0; attempt < 20 && lastXHR === previous; attempt += 1) await tick();
|
|
assert.notEqual(lastXHR, previous, 'next PUT request was not started');
|
|
return lastXHR;
|
|
}
|
|
|
|
async function testPutToVmSuccess() {
|
|
const file = new File(['payload'], 'a.pdf');
|
|
const request = putToVm(file, 'https://vm.example/a');
|
|
assert.equal(lastXHR.method, 'PUT');
|
|
assert.equal(lastXHR.body, file);
|
|
lastXHR.complete();
|
|
await request;
|
|
}
|
|
|
|
async function testUploadIncludesPerFilePayloadAndResult() {
|
|
fetchCalls = [];
|
|
fetchResponder = async (callNumber) => ({
|
|
ok: true,
|
|
status: 200,
|
|
statusText: 'OK',
|
|
json: async () => ({
|
|
ok: true,
|
|
results: [{ ok: true, doc_id: `doc-${callNumber}`, parsed: { status: 'parsed' } }],
|
|
}),
|
|
});
|
|
const files = [
|
|
{ id: 'one', name: 'one.pdf', path: 'one.pdf', file: new File(['1'], 'one.pdf') },
|
|
{ id: 'two', name: 'two.pdf', path: 'archive.zip/folder/two.pdf', file: new File(['2'], 'two.pdf') },
|
|
];
|
|
const completed = [];
|
|
const previous = lastXHR;
|
|
const request = uploadViaVM(files, {
|
|
vmUploadUrl: 'https://vm.example/contracts-upload/',
|
|
backendUploadUrl: '/api/upload_refs',
|
|
getExtraPayload: (entry) => ({
|
|
batch_id: 'batch-1',
|
|
contract_id: 'contract-1',
|
|
zip_source: entry.id === 'two' ? 'archive.zip' : null,
|
|
}),
|
|
onFileComplete: (event) => completed.push(event),
|
|
});
|
|
|
|
const firstXHR = await waitForNextXHR(previous);
|
|
firstXHR.complete();
|
|
const secondXHR = await waitForNextXHR(firstXHR);
|
|
secondXHR.complete();
|
|
const result = await request;
|
|
|
|
assert.equal(result.ok, true);
|
|
assert.equal(fetchCalls.length, 2);
|
|
const firstBody = JSON.parse(fetchCalls[0].options.body);
|
|
const secondBody = JSON.parse(fetchCalls[1].options.body);
|
|
assert.equal(firstBody.batch_id, 'batch-1');
|
|
assert.equal(firstBody.contract_id, 'contract-1');
|
|
assert.equal(firstBody.files.length, 1);
|
|
assert.equal(secondBody.zip_source, 'archive.zip');
|
|
assert.equal(completed.length, 2);
|
|
assert.equal(completed[1].result.doc_id, 'doc-2');
|
|
assert.equal(completed[1].source.id, 'two');
|
|
}
|
|
|
|
async function testUploadReportsPutFailure() {
|
|
fetchCalls = [];
|
|
const previous = lastXHR;
|
|
const request = uploadViaVM([new File(['x'], 'x.pdf')], {
|
|
vmUploadUrl: 'https://vm.example/',
|
|
});
|
|
const xhr = await waitForNextXHR(previous);
|
|
xhr.onerror();
|
|
const result = await request;
|
|
assert.equal(result.ok, false);
|
|
assert.equal(fetchCalls.length, 0);
|
|
assert.match(result.error, /Ошибка отправки файла/);
|
|
}
|
|
|
|
async function testUploadReportsBackendFailure() {
|
|
fetchCalls = [];
|
|
fetchResponder = async () => ({
|
|
ok: false,
|
|
status: 502,
|
|
statusText: 'Bad Gateway',
|
|
json: async () => ({ ok: false, error: 'failed' }),
|
|
});
|
|
const previous = lastXHR;
|
|
const request = uploadViaVM([new File(['x'], 'x.pdf')], {
|
|
vmUploadUrl: 'https://vm.example/',
|
|
backendUploadUrl: '/api/upload_refs',
|
|
});
|
|
const xhr = await waitForNextXHR(previous);
|
|
xhr.complete();
|
|
const result = await request;
|
|
assert.equal(result.ok, false);
|
|
assert.match(result.error, /HTTP 502/);
|
|
}
|
|
|
|
async function testAbortBeforeUpload() {
|
|
const controller = new AbortController();
|
|
controller.abort();
|
|
const previous = lastXHR;
|
|
const result = await uploadViaVM([new File(['x'], 'x.pdf')], {
|
|
vmUploadUrl: 'https://vm.example/',
|
|
signal: controller.signal,
|
|
});
|
|
assert.equal(result.aborted, true);
|
|
assert.equal(lastXHR, previous);
|
|
}
|
|
|
|
const tests = [
|
|
['PUT via WebDAV', testPutToVmSuccess],
|
|
['per-file extra payload and sink result', testUploadIncludesPerFilePayloadAndResult],
|
|
['PUT failure', testUploadReportsPutFailure],
|
|
['backend failure', testUploadReportsBackendFailure],
|
|
['abort before upload', testAbortBeforeUpload],
|
|
];
|
|
|
|
for (const [name, test] of tests) {
|
|
await test();
|
|
console.log(`PASS ${name}`);
|
|
}
|