From 4a02f7f9aa259f8c24097a3b2038b630fda16ac3 Mon Sep 17 00:00:00 2001 From: mike12345567 Date: Wed, 27 Apr 2022 15:58:55 +0100 Subject: [PATCH 1/8] Fix for issue being able to delete apps. --- packages/backend-core/src/db/index.js | 3 ++- packages/server/yarn.lock | 18 +++++++++--------- packages/worker/yarn.lock | 18 +++++++++--------- 3 files changed, 20 insertions(+), 19 deletions(-) diff --git a/packages/backend-core/src/db/index.js b/packages/backend-core/src/db/index.js index 1a29c8c2b0..7d54b881b1 100644 --- a/packages/backend-core/src/db/index.js +++ b/packages/backend-core/src/db/index.js @@ -41,7 +41,8 @@ exports.closeDB = async db => { return } try { - return db.close() + // specifically await so that if there is an error, it can be ignored + return await db.close() } catch (err) { // ignore error, already closed } diff --git a/packages/server/yarn.lock b/packages/server/yarn.lock index 64f7be12e9..239b342eff 100644 --- a/packages/server/yarn.lock +++ b/packages/server/yarn.lock @@ -1014,10 +1014,10 @@ resolved "https://registry.yarnpkg.com/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz#75a2e8b51cb758a7553d6804a5932d7aace75c39" integrity sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw== -"@budibase/backend-core@1.0.124-alpha.0": - version "1.0.124-alpha.0" - resolved "https://registry.yarnpkg.com/@budibase/backend-core/-/backend-core-1.0.124-alpha.0.tgz#33a9408206088da49154710910dafc8088d864d2" - integrity sha512-0ZUkDeqaoXS9qyK91SjwokYEA1wUPhi48nFE0+UwBloF8i7zVDFp2kOX7VNUrUer4gLuND9BoihEdpqsdQDvAg== +"@budibase/backend-core@1.0.127": + version "1.0.127" + resolved "https://registry.yarnpkg.com/@budibase/backend-core/-/backend-core-1.0.127.tgz#5d1f4b18b31436ddb770dc1ddf16f201ec95dda7" + integrity sha512-3INFkAIxL0Q8Sa65ELRGQqPs+4baykKyb1z/XuO1MyuDPnbFKXGOjl1V61EMy622gsmLk90IJL6aQfh3Grwhvw== dependencies: "@techpass/passport-openidconnect" "^0.3.0" aws-sdk "^2.901.0" @@ -1091,12 +1091,12 @@ svelte-flatpickr "^3.2.3" svelte-portal "^1.0.0" -"@budibase/pro@1.0.124-alpha.0": - version "1.0.124-alpha.0" - resolved "https://registry.yarnpkg.com/@budibase/pro/-/pro-1.0.124-alpha.0.tgz#6287a51fa7c19754e44374c209c4aa3480fc3ac9" - integrity sha512-EgMuh+XSd/9tb3Ej9EZa4Y8hgiS6fHG+tuUwUcTuP6zvHbTijQGPb9075yImUbSc10bS3o41AP2qa2/ZdZKV2w== +"@budibase/pro@1.0.127": + version "1.0.127" + resolved "https://registry.yarnpkg.com/@budibase/pro/-/pro-1.0.127.tgz#a8bcffb8ccc6afde64370b3a3dc22e2d0dd04f46" + integrity sha512-dj0SFTmO8JuMQ97/Ik6jVPQsh9AW7U5Wkgpa4yeNfwWw3DvSoktCxpeZ9mND6BR/DWTaeZljFKsQ6uk6nimzdQ== dependencies: - "@budibase/backend-core" "1.0.124-alpha.0" + "@budibase/backend-core" "1.0.127" node-fetch "^2.6.1" "@budibase/standard-components@^0.9.139": diff --git a/packages/worker/yarn.lock b/packages/worker/yarn.lock index 405845b896..dfdcc129d9 100644 --- a/packages/worker/yarn.lock +++ b/packages/worker/yarn.lock @@ -293,10 +293,10 @@ resolved "https://registry.yarnpkg.com/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz#75a2e8b51cb758a7553d6804a5932d7aace75c39" integrity sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw== -"@budibase/backend-core@1.0.124-alpha.0": - version "1.0.124-alpha.0" - resolved "https://registry.yarnpkg.com/@budibase/backend-core/-/backend-core-1.0.124-alpha.0.tgz#33a9408206088da49154710910dafc8088d864d2" - integrity sha512-0ZUkDeqaoXS9qyK91SjwokYEA1wUPhi48nFE0+UwBloF8i7zVDFp2kOX7VNUrUer4gLuND9BoihEdpqsdQDvAg== +"@budibase/backend-core@1.0.127": + version "1.0.127" + resolved "https://registry.yarnpkg.com/@budibase/backend-core/-/backend-core-1.0.127.tgz#5d1f4b18b31436ddb770dc1ddf16f201ec95dda7" + integrity sha512-3INFkAIxL0Q8Sa65ELRGQqPs+4baykKyb1z/XuO1MyuDPnbFKXGOjl1V61EMy622gsmLk90IJL6aQfh3Grwhvw== dependencies: "@techpass/passport-openidconnect" "^0.3.0" aws-sdk "^2.901.0" @@ -321,12 +321,12 @@ uuid "^8.3.2" zlib "^1.0.5" -"@budibase/pro@1.0.124-alpha.0": - version "1.0.124-alpha.0" - resolved "https://registry.yarnpkg.com/@budibase/pro/-/pro-1.0.124-alpha.0.tgz#6287a51fa7c19754e44374c209c4aa3480fc3ac9" - integrity sha512-EgMuh+XSd/9tb3Ej9EZa4Y8hgiS6fHG+tuUwUcTuP6zvHbTijQGPb9075yImUbSc10bS3o41AP2qa2/ZdZKV2w== +"@budibase/pro@1.0.127": + version "1.0.127" + resolved "https://registry.yarnpkg.com/@budibase/pro/-/pro-1.0.127.tgz#a8bcffb8ccc6afde64370b3a3dc22e2d0dd04f46" + integrity sha512-dj0SFTmO8JuMQ97/Ik6jVPQsh9AW7U5Wkgpa4yeNfwWw3DvSoktCxpeZ9mND6BR/DWTaeZljFKsQ6uk6nimzdQ== dependencies: - "@budibase/backend-core" "1.0.124-alpha.0" + "@budibase/backend-core" "1.0.127" node-fetch "^2.6.1" "@cspotcode/source-map-consumer@0.8.0": From 2773c049535a17c2ec862a15887aee44d52063a8 Mon Sep 17 00:00:00 2001 From: mike12345567 Date: Wed, 27 Apr 2022 16:05:27 +0100 Subject: [PATCH 2/8] Fix for #5522 - making sure that app roles aren't removed when an app is unpublished. --- packages/server/src/api/controllers/application.ts | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/packages/server/src/api/controllers/application.ts b/packages/server/src/api/controllers/application.ts index 4e6d37765f..aa76dd403c 100644 --- a/packages/server/src/api/controllers/application.ts +++ b/packages/server/src/api/controllers/application.ts @@ -406,11 +406,14 @@ const destroyApp = async (ctx: any) => { if (!env.isTest() && !isUnpublish) { await deleteApp(appId) } + // automations only in production if (isUnpublish) { await cleanupAutomations(appId) } - // make sure the app/role doesn't stick around after the app has been deleted - await removeAppFromUserRoles(ctx, appId) + // remove app role when the dev app is deleted (no trace of app anymore) + else { + await removeAppFromUserRoles(ctx, appId) + } await appCache.invalidateAppMetadata(appId) return result } From 056d9defe49c519ab3e0b54fba99851d5e371d3a Mon Sep 17 00:00:00 2001 From: mike12345567 Date: Wed, 27 Apr 2022 16:29:03 +0100 Subject: [PATCH 3/8] Fix for #5219 - default all string types to basic text format, allow user to switch string types to longform if they desire. --- packages/builder/src/constants/backend/index.js | 6 +++++- packages/server/src/constants/index.js | 6 +++++- packages/server/src/integrations/utils.ts | 6 +++--- 3 files changed, 13 insertions(+), 5 deletions(-) diff --git a/packages/builder/src/constants/backend/index.js b/packages/builder/src/constants/backend/index.js index 2f5f26e476..922955591a 100644 --- a/packages/builder/src/constants/backend/index.js +++ b/packages/builder/src/constants/backend/index.js @@ -144,7 +144,11 @@ export const RelationshipTypes = { MANY_TO_ONE: "many-to-one", } -export const ALLOWABLE_STRING_OPTIONS = [FIELDS.STRING, FIELDS.OPTIONS] +export const ALLOWABLE_STRING_OPTIONS = [ + FIELDS.STRING, + FIELDS.OPTIONS, + FIELDS.LONGFORM, +] export const ALLOWABLE_STRING_TYPES = ALLOWABLE_STRING_OPTIONS.map( opt => opt.type ) diff --git a/packages/server/src/constants/index.js b/packages/server/src/constants/index.js index 0d52eecb5b..60f3c981d6 100644 --- a/packages/server/src/constants/index.js +++ b/packages/server/src/constants/index.js @@ -47,7 +47,11 @@ exports.FieldTypes = { exports.CanSwitchTypes = [ [exports.FieldTypes.JSON, exports.FieldTypes.ARRAY], - [exports.FieldTypes.STRING, exports.FieldTypes.OPTIONS], + [ + exports.FieldTypes.STRING, + exports.FieldTypes.OPTIONS, + exports.FieldTypes.LONGFORM, + ], [exports.FieldTypes.BOOLEAN, exports.FieldTypes.NUMBER], ] diff --git a/packages/server/src/integrations/utils.ts b/packages/server/src/integrations/utils.ts index f7f18c6fb9..d166523f1d 100644 --- a/packages/server/src/integrations/utils.ts +++ b/packages/server/src/integrations/utils.ts @@ -42,9 +42,9 @@ const SQL_STRING_TYPE_MAP = { nvarchar: FieldTypes.STRING, ntext: FieldTypes.STRING, enum: FieldTypes.STRING, - blob: FieldTypes.LONGFORM, - long: FieldTypes.LONGFORM, - text: FieldTypes.LONGFORM, + blob: FieldTypes.STRING, + long: FieldTypes.STRING, + text: FieldTypes.STRING, } const SQL_BOOLEAN_TYPE_MAP = { From 00c67289b0d7eed413f4e2477b390297a136d156 Mon Sep 17 00:00:00 2001 From: mike12345567 Date: Wed, 27 Apr 2022 16:56:30 +0100 Subject: [PATCH 4/8] Fixing an issue with cookie auth. --- packages/backend-core/src/db/pouch.js | 63 ++++++++----------- packages/backend-core/src/db/utils.js | 14 +++-- .../src/api/controllers/row/internalSearch.js | 15 +++-- 3 files changed, 44 insertions(+), 48 deletions(-) diff --git a/packages/backend-core/src/db/pouch.js b/packages/backend-core/src/db/pouch.js index 722913aa67..a04fd60a02 100644 --- a/packages/backend-core/src/db/pouch.js +++ b/packages/backend-core/src/db/pouch.js @@ -1,35 +1,36 @@ const PouchDB = require("pouchdb") const env = require("../environment") -exports.getCouchUrl = () => { - if (!env.COUCH_DB_URL) return - - // username and password already exist in URL - if (env.COUCH_DB_URL.includes("@")) { - return env.COUCH_DB_URL +exports.getCouchInfo = () => { + let url = "http://localhost:4005" + if (env.COUCH_DB_URL && env.COUCH_DB_URL.includes("@")) { + url = env.COUCH_DB_URL + } else if (env.COUCH_DB_URL) { + const [protocol, ...rest] = env.COUCH_DB_URL.split("://") + url = `${protocol}://${env.COUCH_DB_USERNAME}:${env.COUCH_DB_PASSWORD}@${rest}` + if (!env.COUCH_DB_USERNAME || !env.COUCH_DB_PASSWORD) { + throw new Error( + "CouchDB configuration invalid. You must provide a fully qualified CouchDB url, or the COUCH_DB_USER and COUCH_DB_PASSWORD environment variables." + ) + } } - - const [protocol, ...rest] = env.COUCH_DB_URL.split("://") - - if (!env.COUCH_DB_USERNAME || !env.COUCH_DB_PASSWORD) { - throw new Error( - "CouchDB configuration invalid. You must provide a fully qualified CouchDB url, or the COUCH_DB_USER and COUCH_DB_PASSWORD environment variables." - ) - } - - return `${protocol}://${env.COUCH_DB_USERNAME}:${env.COUCH_DB_PASSWORD}@${rest}` -} - -exports.splitCouchUrl = url => { const [protocol, rest] = url.split("://") const [auth, host] = rest.split("@") - const [username, password] = auth.split(":") + let [username, password] = auth.split(":") + if (!username && env.COUCH_DB_USERNAME) { + username = env.COUCH_DB_USERNAME + } + if (!password && env.COUCH_DB_PASSWORD) { + password = env.COUCH_DB_PASSWORD + } + const authCookie = Buffer.from(`${username}:${password}`).toString("base64") return { url: `${protocol}://${host}`, auth: { - username, - password, + username: username, + password: password, }, + cookie: `Basic ${authCookie}`, } } @@ -39,26 +40,12 @@ exports.splitCouchUrl = url => { * Exposed for exceptional cases such as in-memory views. */ exports.getPouch = (opts = {}) => { - let auth = { - username: env.COUCH_DB_USERNAME, - password: env.COUCH_DB_PASSWORD, - } - let url = exports.getCouchUrl() || "http://localhost:4005" - // need to update security settings - if (!auth.username || !auth.password || url.includes("@")) { - const split = exports.splitCouchUrl(url) - url = split.url - auth = split.auth - } - - const authCookie = Buffer.from(`${auth.username}:${auth.password}`).toString( - "base64" - ) + let { url, cookie } = exports.getCouchInfo() let POUCH_DB_DEFAULTS = { prefix: url, fetch: (url, opts) => { // use a specific authorization cookie - be very explicit about how we authenticate - opts.headers.set("Authorization", `Basic ${authCookie}`) + opts.headers.set("Authorization", cookie) return PouchDB.fetch(url, opts) }, } diff --git a/packages/backend-core/src/db/utils.js b/packages/backend-core/src/db/utils.js index 9e2a06d065..1e2ba846de 100644 --- a/packages/backend-core/src/db/utils.js +++ b/packages/backend-core/src/db/utils.js @@ -12,7 +12,7 @@ const { const { getTenantId, getGlobalDBName } = require("../tenancy") const fetch = require("node-fetch") const { doWithDB, allDbs } = require("./index") -const { getCouchUrl } = require("./pouch") +const { getCouchInfo } = require("./pouch") const { getAppMetadata } = require("../cache/appMetadata") const { checkSlashesInUrl } = require("../helpers") const { @@ -169,8 +169,14 @@ exports.getAllDbs = async (opts = { efficient: false }) => { return allDbs() } let dbs = [] - async function addDbs(url) { - const response = await fetch(checkSlashesInUrl(encodeURI(url))) + let { url, cookie } = getCouchInfo() + async function addDbs(couchUrl) { + const response = await fetch(checkSlashesInUrl(encodeURI(couchUrl)), { + method: "GET", + headers: { + Authorization: cookie, + }, + }) if (response.status === 200) { let json = await response.json() dbs = dbs.concat(json) @@ -178,7 +184,7 @@ exports.getAllDbs = async (opts = { efficient: false }) => { throw "Cannot connect to CouchDB instance" } } - let couchUrl = `${getCouchUrl()}/_all_dbs` + let couchUrl = `${url}/_all_dbs` let tenantId = getTenantId() if (!env.MULTI_TENANCY || (!efficient && tenantId === DEFAULT_TENANT_ID)) { // just get all DBs when: diff --git a/packages/server/src/api/controllers/row/internalSearch.js b/packages/server/src/api/controllers/row/internalSearch.js index ad95a25fc5..5f1dc25faa 100644 --- a/packages/server/src/api/controllers/row/internalSearch.js +++ b/packages/server/src/api/controllers/row/internalSearch.js @@ -1,6 +1,6 @@ const { SearchIndexes } = require("../../../db/utils") const fetch = require("node-fetch") -const { getCouchUrl } = require("@budibase/backend-core/db") +const { getCouchInfo } = require("@budibase/backend-core/db") const { getAppId } = require("@budibase/backend-core/context") /** @@ -242,11 +242,10 @@ class QueryBuilder { async run() { const appId = getAppId() - const url = `${getCouchUrl()}/${appId}/_design/database/_search/${ - SearchIndexes.ROWS - }` + const { url, cookie } = getCouchInfo() + const fullPath = `${url}/${appId}/_design/database/_search/${SearchIndexes.ROWS}` const body = this.buildSearchBody() - return await runQuery(url, body) + return await runQuery(fullPath, body, cookie) } } @@ -254,12 +253,16 @@ class QueryBuilder { * Executes a lucene search query. * @param url The query URL * @param body The request body defining search criteria + * @param cookie The auth cookie for CouchDB * @returns {Promise<{rows: []}>} */ -const runQuery = async (url, body) => { +const runQuery = async (url, body, cookie) => { const response = await fetch(url, { body: JSON.stringify(body), method: "POST", + headers: { + Authorization: cookie, + }, }) const json = await response.json() From ad26d29de57b9d45beeac2414a82b5773aafeaf8 Mon Sep 17 00:00:00 2001 From: mike12345567 Date: Wed, 27 Apr 2022 16:58:29 +0100 Subject: [PATCH 5/8] Making it so that the filter table button isn't disabled when the filter/search didn't return any data. --- .../builder/src/components/backend/DataTable/DataTable.svelte | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/builder/src/components/backend/DataTable/DataTable.svelte b/packages/builder/src/components/backend/DataTable/DataTable.svelte index 6a1befd7e4..1f461ebad3 100644 --- a/packages/builder/src/components/backend/DataTable/DataTable.svelte +++ b/packages/builder/src/components/backend/DataTable/DataTable.svelte @@ -165,7 +165,7 @@ {/key} From 8d5931f94c11df9127b52e1cd704735c3a9de240 Mon Sep 17 00:00:00 2001 From: mike12345567 Date: Wed, 27 Apr 2022 17:11:57 +0100 Subject: [PATCH 6/8] Fixing some issues with types being lost when fetching SQL tables, after making changes to tables within Budibase. --- packages/server/src/integrations/utils.ts | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/packages/server/src/integrations/utils.ts b/packages/server/src/integrations/utils.ts index d166523f1d..326b213bc7 100644 --- a/packages/server/src/integrations/utils.ts +++ b/packages/server/src/integrations/utils.ts @@ -207,11 +207,20 @@ function shouldCopySpecialColumn( column: { type: string }, fetchedColumn: { type: string } | undefined ) { + const specialTypes = [ + FieldTypes.OPTIONS, + FieldTypes.LONGFORM, + FieldTypes.ARRAY, + FieldTypes.FORMULA, + ] + if (column && !fetchedColumn) { + return true + } + const fetchedIsNumber = + !fetchedColumn || fetchedColumn.type === FieldTypes.NUMBER return ( - column.type === FieldTypes.OPTIONS || - column.type === FieldTypes.ARRAY || - ((!fetchedColumn || fetchedColumn.type === FieldTypes.NUMBER) && - column.type === FieldTypes.BOOLEAN) + specialTypes.indexOf(column.type) !== -1 || + (fetchedIsNumber && column.type === FieldTypes.BOOLEAN) ) } From 84afa832c5192b0ffa45e3b3bc233b076e2feb00 Mon Sep 17 00:00:00 2001 From: mike12345567 Date: Wed, 27 Apr 2022 22:36:45 +0100 Subject: [PATCH 7/8] Review comments. --- packages/backend-core/src/db/pouch.js | 60 ++++++++++++++++-------- packages/backend-core/src/environment.js | 2 +- 2 files changed, 42 insertions(+), 20 deletions(-) diff --git a/packages/backend-core/src/db/pouch.js b/packages/backend-core/src/db/pouch.js index a04fd60a02..2a6c27b6db 100644 --- a/packages/backend-core/src/db/pouch.js +++ b/packages/backend-core/src/db/pouch.js @@ -1,31 +1,53 @@ const PouchDB = require("pouchdb") const env = require("../environment") -exports.getCouchInfo = () => { - let url = "http://localhost:4005" - if (env.COUCH_DB_URL && env.COUCH_DB_URL.includes("@")) { - url = env.COUCH_DB_URL - } else if (env.COUCH_DB_URL) { - const [protocol, ...rest] = env.COUCH_DB_URL.split("://") - url = `${protocol}://${env.COUCH_DB_USERNAME}:${env.COUCH_DB_PASSWORD}@${rest}` - if (!env.COUCH_DB_USERNAME || !env.COUCH_DB_PASSWORD) { - throw new Error( - "CouchDB configuration invalid. You must provide a fully qualified CouchDB url, or the COUCH_DB_USER and COUCH_DB_PASSWORD environment variables." - ) - } - } +function getUrlInfo() { + let url = env.COUCH_DB_URL + let username, password, host const [protocol, rest] = url.split("://") - const [auth, host] = rest.split("@") - let [username, password] = auth.split(":") - if (!username && env.COUCH_DB_USERNAME) { - username = env.COUCH_DB_USERNAME + if (url.includes("@")) { + const hostParts = rest.split("@") + host = hostParts[1] + const authParts = hostParts[0].split(":") + username = authParts[0] + password = authParts[1] + } else { + host = rest } - if (!password && env.COUCH_DB_PASSWORD) { + return { + url: `${protocol}://${host}`, + auth: { + username, + password, + }, + } +} + +exports.getCouchInfo = () => { + const urlInfo = getUrlInfo() + let username + let password + if (env.COUCH_DB_USERNAME) { + // set from env + username = env.COUCH_DB_USERNAME + } else if (urlInfo.auth.username) { + // set from url + username = urlInfo.auth.username + } else { + throw new Error("CouchDB username not set") + } + if (env.COUCH_DB_PASSWORD) { + // set from env password = env.COUCH_DB_PASSWORD + } else if (urlInfo.auth.password) { + // set from url + password = urlInfo.auth.password + } else { + throw new Error("CouchDB password not set") } const authCookie = Buffer.from(`${username}:${password}`).toString("base64") return { - url: `${protocol}://${host}`, + url: urlInfo.url, auth: { username: username, password: password, diff --git a/packages/backend-core/src/environment.js b/packages/backend-core/src/environment.js index 8a92e39469..26e0d486f7 100644 --- a/packages/backend-core/src/environment.js +++ b/packages/backend-core/src/environment.js @@ -8,7 +8,7 @@ function isTest() { module.exports = { JWT_SECRET: process.env.JWT_SECRET, - COUCH_DB_URL: process.env.COUCH_DB_URL, + COUCH_DB_URL: process.env.COUCH_DB_URL || "http://localhost:4005", COUCH_DB_USERNAME: process.env.COUCH_DB_USER, COUCH_DB_PASSWORD: process.env.COUCH_DB_PASSWORD, GOOGLE_CLIENT_ID: process.env.GOOGLE_CLIENT_ID, From 28584a01db4d0828e179a1b241bfba3d4d6dab8a Mon Sep 17 00:00:00 2001 From: mike12345567 Date: Wed, 27 Apr 2022 22:57:03 +0100 Subject: [PATCH 8/8] Fixing test case. --- packages/backend-core/src/db/pouch.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/backend-core/src/db/pouch.js b/packages/backend-core/src/db/pouch.js index 2a6c27b6db..9c1ada8d76 100644 --- a/packages/backend-core/src/db/pouch.js +++ b/packages/backend-core/src/db/pouch.js @@ -33,7 +33,7 @@ exports.getCouchInfo = () => { } else if (urlInfo.auth.username) { // set from url username = urlInfo.auth.username - } else { + } else if (!env.isTest()) { throw new Error("CouchDB username not set") } if (env.COUCH_DB_PASSWORD) { @@ -42,7 +42,7 @@ exports.getCouchInfo = () => { } else if (urlInfo.auth.password) { // set from url password = urlInfo.auth.password - } else { + } else if (!env.isTest()) { throw new Error("CouchDB password not set") } const authCookie = Buffer.from(`${username}:${password}`).toString("base64")