From b00dee4f103aaaf48b940140bb3d0a8de2d04908 Mon Sep 17 00:00:00 2001 From: Pier-Paolo Mammi Date: Mon, 29 Jun 2026 10:47:51 +0200 Subject: [PATCH] fix authorization headers generation add unit tests --- scripts/generate-http-docs.js | 101 ++++++++++++++++++---------- scripts/generate-http-docs.test.mjs | 34 ++++++++++ 2 files changed, 100 insertions(+), 35 deletions(-) create mode 100644 scripts/generate-http-docs.test.mjs diff --git a/scripts/generate-http-docs.js b/scripts/generate-http-docs.js index 321157a..617040b 100644 --- a/scripts/generate-http-docs.js +++ b/scripts/generate-http-docs.js @@ -197,38 +197,7 @@ function formatVariableValue(value) { return JSON.stringify(value); } -function mergeRequestConfig(base, updates) { - if (!updates || typeof updates !== 'object') { - return base; - } - - const merged = { ...(base || {}) }; - if (updates.auth) { - if (updates.auth === 'inherit' && merged.auth && typeof merged.auth === 'object') { - merged.auth = merged.auth; - } else if (typeof updates.auth === 'object') { - merged.auth = updates.auth; - } - } - if (Array.isArray(updates.variables)) { - const variables = [...(Array.isArray(base.variables) ? base.variables : [])]; - const byName = new Map(); - for (const variable of variables) { - if (variable && variable.name) { - byName.set(String(variable.name), variable); - } - } - for (const variable of updates.variables) { - if (variable && variable.name) { - byName.set(String(variable.name), variable); - } - } - merged.variables = [...byName.values()]; - } - return merged; -} - -function getRequestConfigForFile(yamlFile, sourceDir) { +export function getRequestConfigForFile(yamlFile, sourceDir) { const resolved = []; const seenFiles = new Set(); @@ -243,16 +212,35 @@ function getRequestConfigForFile(yamlFile, sourceDir) { try { const parsed = parseYaml(filePath); + let requestConfig = null; + if (parsed && parsed.request && typeof parsed.request === 'object') { - resolved.push(parsed.request); + requestConfig = parsed.request; + } else if (parsed && typeof parsed === 'object') { + const config = {}; + if (Object.prototype.hasOwnProperty.call(parsed, 'auth')) { + config.auth = parsed.auth; + } else if (parsed.http && typeof parsed.http === 'object' && Object.prototype.hasOwnProperty.call(parsed.http, 'auth')) { + config.auth = parsed.http.auth; + } + if (Array.isArray(parsed.variables)) { + config.variables = parsed.variables; + } + if (Object.keys(config).length > 0) { + requestConfig = config; + } + } + + if (requestConfig !== null) { + resolved.push(requestConfig); + } else if (path.resolve(filePath) === path.resolve(yamlFile)) { + resolved.push({}); } } catch (error) { // Ignore files that cannot be parsed as YAML for request inheritance. } }; - addFile(yamlFile); - const dirChain = []; let currentDir = path.dirname(yamlFile); while (true) { @@ -272,9 +260,52 @@ function getRequestConfigForFile(yamlFile, sourceDir) { addFile(path.join(dir, 'folder.yml')); } + addFile(yamlFile); + return resolved.reduce((result, config) => mergeRequestConfig(result, config), {}); } +export function mergeRequestConfig(base, updates) { + if (!updates || typeof updates !== 'object') { + return base; + } + + const merged = { ...(base || {}) }; + if (Object.prototype.hasOwnProperty.call(updates, 'auth')) { + if (updates.auth === 'inherit') { + if (merged.auth && typeof merged.auth === 'object') { + merged.auth = merged.auth; + } else { + delete merged.auth; + } + } else if (typeof updates.auth === 'object' && updates.auth !== null) { + merged.auth = updates.auth; + } else { + delete merged.auth; + } + } else { + delete merged.auth; + } + + if (Array.isArray(updates.variables)) { + const variables = [...(Array.isArray(base.variables) ? base.variables : [])]; + const byName = new Map(); + for (const variable of variables) { + if (variable && variable.name) { + byName.set(String(variable.name), variable); + } + } + for (const variable of updates.variables) { + if (variable && variable.name) { + byName.set(String(variable.name), variable); + } + } + merged.variables = [...byName.values()]; + } + + return merged; +} + function buildRequestContent(request, requestName, requestConfig = {}, dotenvVariables = new Set()) { const lines = []; const variableDefinitions = []; diff --git a/scripts/generate-http-docs.test.mjs b/scripts/generate-http-docs.test.mjs new file mode 100644 index 0000000..4b86757 --- /dev/null +++ b/scripts/generate-http-docs.test.mjs @@ -0,0 +1,34 @@ +import test from 'node:test'; +import assert from 'node:assert/strict'; +import fs from 'node:fs'; +import os from 'node:os'; +import path from 'node:path'; +import { getRequestConfigForFile, mergeRequestConfig } from './generate-http-docs.js'; + +test('does not inherit parent auth when a child config has no auth override', () => { + const parentAuth = { type: 'bearer', token: 'parent-token' }; + const merged = mergeRequestConfig({ auth: parentAuth }, {}); + + assert.equal(merged.auth, undefined); +}); + +test('uses an explicit child auth object instead of inheriting the parent auth', () => { + const parentAuth = { type: 'bearer', token: 'parent-token' }; + const childAuth = { type: 'basic', username: 'user', password: 'pass' }; + const merged = mergeRequestConfig({ auth: parentAuth }, { auth: childAuth }); + + assert.deepEqual(merged.auth, childAuth); +}); + +test('reads auth inherit from a Bruno-style http block', () => { + const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), 'bruno-auth-')); + const childDir = path.join(tempRoot, 'nested'); + fs.mkdirSync(childDir, { recursive: true }); + fs.writeFileSync(path.join(tempRoot, 'opencollection.yml'), `request:\n auth:\n type: bearer\n token: "parent-token"\n`); + fs.writeFileSync(path.join(tempRoot, 'folder.yml'), 'auth: inherit\n'); + fs.writeFileSync(path.join(childDir, 'request.yml'), 'http:\n auth: inherit\n'); + + const config = getRequestConfigForFile(path.join(childDir, 'request.yml'), tempRoot); + + assert.deepEqual(config.auth, { type: 'bearer', token: 'parent-token' }); +});