import child_process from "child_process"; import kill from "tree-kill"; let etapiAuthToken: string | undefined; const getEtapiAuthorizationHeader = (): string => "Basic " + Buffer.from(`etapi:${etapiAuthToken}`).toString("base64"); const PORT: string = "9999"; const HOST: string = "http://localhost:" + PORT; type SpecDefinitionsFunc = () => void; function describeEtapi( description: string, specDefinitions: SpecDefinitionsFunc ): void { describe(description, () => { let appProcess: ReturnType; beforeAll(async () => { }); afterAll(() => { }); specDefinitions(); }); } async function getEtapiResponse(url: string): Promise { return await fetch(`${HOST}/etapi/${url}`, { method: "GET", headers: { Authorization: getEtapiAuthorizationHeader(), }, }); } async function getEtapi(url: string): Promise { const response = await getEtapiResponse(url); return await processEtapiResponse(response); } async function getEtapiContent(url: string): Promise { const response = await fetch(`${HOST}/etapi/${url}`, { method: "GET", headers: { Authorization: getEtapiAuthorizationHeader(), }, }); checkStatus(response); return response; } async function postEtapi( url: string, data: Record = {} ): Promise { const response = await fetch(`${HOST}/etapi/${url}`, { method: "POST", headers: { "Content-Type": "application/json", Authorization: getEtapiAuthorizationHeader(), }, body: JSON.stringify(data), }); return await processEtapiResponse(response); } async function postEtapiContent( url: string, data: BodyInit ): Promise { const response = await fetch(`${HOST}/etapi/${url}`, { method: "POST", headers: { "Content-Type": "application/octet-stream", Authorization: getEtapiAuthorizationHeader(), }, body: data, }); checkStatus(response); return response; } async function putEtapi( url: string, data: Record = {} ): Promise { const response = await fetch(`${HOST}/etapi/${url}`, { method: "PUT", headers: { "Content-Type": "application/json", Authorization: getEtapiAuthorizationHeader(), }, body: JSON.stringify(data), }); return await processEtapiResponse(response); } async function putEtapiContent( url: string, data?: BodyInit ): Promise { const response = await fetch(`${HOST}/etapi/${url}`, { method: "PUT", headers: { "Content-Type": "application/octet-stream", Authorization: getEtapiAuthorizationHeader(), }, body: data, }); checkStatus(response); return response; } async function patchEtapi( url: string, data: Record = {} ): Promise { const response = await fetch(`${HOST}/etapi/${url}`, { method: "PATCH", headers: { "Content-Type": "application/json", Authorization: getEtapiAuthorizationHeader(), }, body: JSON.stringify(data), }); return await processEtapiResponse(response); } async function deleteEtapi(url: string): Promise { const response = await fetch(`${HOST}/etapi/${url}`, { method: "DELETE", headers: { Authorization: getEtapiAuthorizationHeader(), }, }); return await processEtapiResponse(response); } async function processEtapiResponse(response: Response): Promise { const text = await response.text(); if (response.status < 200 || response.status >= 300) { throw new Error(`ETAPI error ${response.status}: ${text}`); } return text?.trim() ? JSON.parse(text) : null; } function checkStatus(response: Response): void { if (response.status < 200 || response.status >= 300) { throw new Error(`ETAPI error ${response.status}`); } } export default { describeEtapi, getEtapi, getEtapiResponse, getEtapiContent, postEtapi, postEtapiContent, putEtapi, putEtapiContent, patchEtapi, deleteEtapi, };