Andrey Antukh 636bc22cc4
Add Node.js E2E API tests for backend (#10787)
Add end-to-end HTTP tests under backend/test/e2e/ using Node.js built-in
test runner (node:test) and native fetch. Tests run through the devenv
nginx proxy on port 3450.

Test suites (19 tests total):
- auth-flow: demo profile creation, login, session cookies, access tokens
- export-binfile: file creation, export to asset URL via SSE
- asset-download: download with cookie/token auth, 401 without auth,
  S3 redirect behavior, full export-to-download flow

Key findings documented in tests:
- nginx @handle_redirect intercepts backend 307 and proxies to S3 directly,
  stripping the client Authorization header (bug does not reproduce in devenv)
- SSE end event uses ~#uri tagged format for URLs
- Unauthenticated RPC returns uuid/zero profile (not null)

AI-assisted-by: mimo-v2.5-pro

Signed-off-by: Andrey Antukh <niwi@niwi.nz>
2026-08-05 10:15:41 +02:00

87 lines
2.0 KiB
JavaScript

import config from "../config.mjs";
async function parseResponse(response) {
const contentType = response.headers.get("content-type") || "";
const setCookie = response.headers.get("set-cookie") || null;
let body;
if (contentType.includes("application/json")) {
body = await response.json();
} else {
body = await response.text();
}
return {
status: response.status,
headers: response.headers,
body,
setCookie,
};
}
export function extractCookie(setCookieHeader, name = "auth-token") {
if (!setCookieHeader) return null;
const match = setCookieHeader.match(new RegExp(`${name}=([^;]+)`));
return match ? match[1] : null;
}
export async function rpcPost(method, body = {}, { cookieToken, accessToken } = {}) {
const headers = {
"Content-Type": "application/json",
Accept: "application/json",
};
if (cookieToken) {
headers.Cookie = `auth-token=${cookieToken}`;
}
if (accessToken) {
headers.Authorization = `Token ${accessToken}`;
}
const response = await fetch(`${config.baseUrl}/api/main/methods/${method}`, {
method: "POST",
headers,
body: JSON.stringify(body),
});
return parseResponse(response);
}
export async function multipartPost(method, formData, { cookieToken } = {}) {
const headers = {
Accept: "application/json",
};
if (cookieToken) {
headers.Cookie = `auth-token=${cookieToken}`;
}
const response = await fetch(`${config.baseUrl}/api/main/methods/${method}`, {
method: "POST",
headers,
body: formData,
});
return parseResponse(response);
}
export async function getAsset(
id,
{ cookieToken, accessToken, redirect = "manual" } = {}
) {
const headers = { Accept: "application/json" };
if (cookieToken) {
headers.Cookie = `auth-token=${cookieToken}`;
}
if (accessToken) {
headers.Authorization = `Token ${accessToken}`;
}
const response = await fetch(`${config.baseUrl}/assets/by-id/${id}`, {
method: "GET",
headers,
redirect,
});
return parseResponse(response);
}