Updating testing system across the board after playing around with it, having the worker tests run when top level test is ran, fixing environment in worker when testing, removing the use of redis (replacing with ioredis-mock) when in test.

This commit is contained in:
mike12345567 2021-05-05 17:49:34 +01:00
parent 48236b2c2f
commit 2ee4fd21e4
22 changed files with 353 additions and 34 deletions

View File

@ -15,5 +15,8 @@
"passport-jwt": "^4.0.0", "passport-jwt": "^4.0.0",
"passport-local": "^1.0.0", "passport-local": "^1.0.0",
"uuid": "^8.3.2" "uuid": "^8.3.2"
},
"devDependencies": {
"ioredis-mock": "^5.5.5"
} }
} }

View File

@ -1,7 +1,16 @@
function isTest() {
return (
process.env.NODE_ENV === "jest" ||
process.env.NODE_ENV === "cypress" ||
process.env.JEST_WORKER_ID != null
)
}
module.exports = { module.exports = {
JWT_SECRET: process.env.JWT_SECRET, JWT_SECRET: process.env.JWT_SECRET,
COUCH_DB_URL: process.env.COUCH_DB_URL, COUCH_DB_URL: process.env.COUCH_DB_URL,
SALT_ROUNDS: process.env.SALT_ROUNDS, SALT_ROUNDS: process.env.SALT_ROUNDS,
REDIS_URL: process.env.REDIS_URL, REDIS_URL: process.env.REDIS_URL,
REDIS_PASSWORD: process.env.REDIS_PASSWORD, REDIS_PASSWORD: process.env.REDIS_PASSWORD,
isTest,
} }

View File

@ -1,9 +1,12 @@
const Redis = require("ioredis") const env = require("../environment")
// ioredis mock is all in memory
const Redis = env.isTest() ? require("ioredis-mock") : require("ioredis")
const { addDbPrefix, removeDbPrefix, getRedisOptions } = require("./utils") const { addDbPrefix, removeDbPrefix, getRedisOptions } = require("./utils")
const CLUSTERED = false const CLUSTERED = false
let CLIENT // for testing just generate the client once
let CLIENT = env.isTest() ? new Redis(getRedisOptions()) : null
/** /**
* Inits the system, will error if unable to connect to redis cluster (may take up to 10 seconds) otherwise * Inits the system, will error if unable to connect to redis cluster (may take up to 10 seconds) otherwise
@ -12,6 +15,10 @@ let CLIENT
*/ */
function init() { function init() {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
// testing uses a single in memory client
if (env.isTest()) {
return resolve(CLIENT)
}
// if a connection existed, close it and re-create it // if a connection existed, close it and re-create it
if (CLIENT) { if (CLIENT) {
CLIENT.disconnect() CLIENT.disconnect()
@ -108,7 +115,12 @@ class RedisWrapper {
if (response != null && response.key) { if (response != null && response.key) {
response.key = key response.key = key
} }
// if its not an object just return the response
try {
return JSON.parse(response) return JSON.parse(response)
} catch (err) {
return response
}
} }
async store(key, value, expirySeconds = null) { async store(key, value, expirySeconds = null) {

View File

@ -3,6 +3,8 @@ const env = require("../environment")
const SLOT_REFRESH_MS = 2000 const SLOT_REFRESH_MS = 2000
const CONNECT_TIMEOUT_MS = 10000 const CONNECT_TIMEOUT_MS = 10000
const SEPARATOR = "-" const SEPARATOR = "-"
const REDIS_URL = !env.REDIS_URL ? "localhost:6379" : env.REDIS_URL
const REDIS_PASSWORD = !env.REDIS_PASSWORD ? "budibase" : env.REDIS_PASSWORD
exports.Databases = { exports.Databases = {
PW_RESETS: "pwReset", PW_RESETS: "pwReset",
@ -10,20 +12,20 @@ exports.Databases = {
} }
exports.getRedisOptions = (clustered = false) => { exports.getRedisOptions = (clustered = false) => {
const [host, port] = env.REDIS_URL.split(":") const [host, port] = REDIS_URL.split(":")
const opts = { const opts = {
connectTimeout: CONNECT_TIMEOUT_MS, connectTimeout: CONNECT_TIMEOUT_MS,
} }
if (clustered) { if (clustered) {
opts.redisOptions = {} opts.redisOptions = {}
opts.redisOptions.tls = {} opts.redisOptions.tls = {}
opts.redisOptions.password = env.REDIS_PASSWORD opts.redisOptions.password = REDIS_PASSWORD
opts.slotsRefreshTimeout = SLOT_REFRESH_MS opts.slotsRefreshTimeout = SLOT_REFRESH_MS
opts.dnsLookup = (address, callback) => callback(null, address) opts.dnsLookup = (address, callback) => callback(null, address)
} else { } else {
opts.host = host opts.host = host
opts.port = port opts.port = port
opts.password = env.REDIS_PASSWORD opts.password = REDIS_PASSWORD
} }
return { opts, host, port } return { opts, host, port }
} }

View File

@ -46,6 +46,11 @@ aws4@^1.8.0:
resolved "https://registry.yarnpkg.com/aws4/-/aws4-1.11.0.tgz#d61f46d83b2519250e2784daf5b09479a8b41c59" resolved "https://registry.yarnpkg.com/aws4/-/aws4-1.11.0.tgz#d61f46d83b2519250e2784daf5b09479a8b41c59"
integrity sha512-xh1Rl34h6Fi1DC2WWKfxUTVqRsNnr6LsKz2+hfwDxQJWmrx8+c7ylaqBMcHfl1U1r2dsifOvKX3LQuLNZ+XSvA== integrity sha512-xh1Rl34h6Fi1DC2WWKfxUTVqRsNnr6LsKz2+hfwDxQJWmrx8+c7ylaqBMcHfl1U1r2dsifOvKX3LQuLNZ+XSvA==
balanced-match@^1.0.0:
version "1.0.2"
resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee"
integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==
base64url@3.x.x: base64url@3.x.x:
version "3.0.1" version "3.0.1"
resolved "https://registry.yarnpkg.com/base64url/-/base64url-3.0.1.tgz#6399d572e2bc3f90a9a8b22d5dbb0a32d33f788d" resolved "https://registry.yarnpkg.com/base64url/-/base64url-3.0.1.tgz#6399d572e2bc3f90a9a8b22d5dbb0a32d33f788d"
@ -63,6 +68,14 @@ bcryptjs@^2.4.3:
resolved "https://registry.yarnpkg.com/bcryptjs/-/bcryptjs-2.4.3.tgz#9ab5627b93e60621ff7cdac5da9733027df1d0cb" resolved "https://registry.yarnpkg.com/bcryptjs/-/bcryptjs-2.4.3.tgz#9ab5627b93e60621ff7cdac5da9733027df1d0cb"
integrity sha1-mrVie5PmBiH/fNrF2pczAn3x0Ms= integrity sha1-mrVie5PmBiH/fNrF2pczAn3x0Ms=
brace-expansion@^1.1.7:
version "1.1.11"
resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd"
integrity sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==
dependencies:
balanced-match "^1.0.0"
concat-map "0.0.1"
buffer-equal-constant-time@1.0.1: buffer-equal-constant-time@1.0.1:
version "1.0.1" version "1.0.1"
resolved "https://registry.yarnpkg.com/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz#f8e71132f7ffe6e01a5c9697a4c6f3e48d5cc819" resolved "https://registry.yarnpkg.com/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz#f8e71132f7ffe6e01a5c9697a4c6f3e48d5cc819"
@ -85,6 +98,11 @@ combined-stream@^1.0.6, combined-stream@~1.0.6:
dependencies: dependencies:
delayed-stream "~1.0.0" delayed-stream "~1.0.0"
concat-map@0.0.1:
version "0.0.1"
resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b"
integrity sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=
core-util-is@1.0.2: core-util-is@1.0.2:
version "1.0.2" version "1.0.2"
resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7" resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7"
@ -154,6 +172,20 @@ fast-json-stable-stringify@^2.0.0:
resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz#874bf69c6f404c2b5d99c481341399fd55892633" resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz#874bf69c6f404c2b5d99c481341399fd55892633"
integrity sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw== integrity sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==
fengari-interop@^0.1.2:
version "0.1.2"
resolved "https://registry.yarnpkg.com/fengari-interop/-/fengari-interop-0.1.2.tgz#f7731dcdd2ff4449073fb7ac3c451a8841ce1e87"
integrity sha512-8iTvaByZVoi+lQJhHH9vC+c/Yaok9CwOqNQZN6JrVpjmWwW4dDkeblBXhnHC+BoI6eF4Cy5NKW3z6ICEjvgywQ==
fengari@^0.1.4:
version "0.1.4"
resolved "https://registry.yarnpkg.com/fengari/-/fengari-0.1.4.tgz#72416693cd9e43bd7d809d7829ddc0578b78b0bb"
integrity sha512-6ujqUuiIYmcgkGz8MGAdERU57EIluGGPSUgGPTsco657EHa+srq0S3/YUl/r9kx1+D+d4rGfYObd+m8K22gB1g==
dependencies:
readline-sync "^1.4.9"
sprintf-js "^1.1.1"
tmp "^0.0.33"
forever-agent@~0.6.1: forever-agent@~0.6.1:
version "0.6.1" version "0.6.1"
resolved "https://registry.yarnpkg.com/forever-agent/-/forever-agent-0.6.1.tgz#fbc71f0c41adeb37f96c577ad1ed42d8fdacca91" resolved "https://registry.yarnpkg.com/forever-agent/-/forever-agent-0.6.1.tgz#fbc71f0c41adeb37f96c577ad1ed42d8fdacca91"
@ -233,6 +265,17 @@ http-signature@~1.2.0:
jsprim "^1.2.2" jsprim "^1.2.2"
sshpk "^1.7.0" sshpk "^1.7.0"
ioredis-mock@^5.5.5:
version "5.5.5"
resolved "https://registry.yarnpkg.com/ioredis-mock/-/ioredis-mock-5.5.5.tgz#dec9fedd238c6ab9f56c026fc366533144f8a256"
integrity sha512-7SxCAwNtDLC8IFDptqIhOC7ajp3fciVtCrXOEOkpyjPboAGRQkJbnpNPy1NYORoWi+0/iOtUPUQckSKtSQj4DA==
dependencies:
fengari "^0.1.4"
fengari-interop "^0.1.2"
lodash "^4.17.21"
minimatch "^3.0.4"
standard-as-callback "^2.1.0"
ioredis@^4.27.1: ioredis@^4.27.1:
version "4.27.1" version "4.27.1"
resolved "https://registry.yarnpkg.com/ioredis/-/ioredis-4.27.1.tgz#4ef947b455a1b995baa4b0d7e2c4e4f75f746421" resolved "https://registry.yarnpkg.com/ioredis/-/ioredis-4.27.1.tgz#4ef947b455a1b995baa4b0d7e2c4e4f75f746421"
@ -379,7 +422,7 @@ lodash.once@^4.0.0:
resolved "https://registry.yarnpkg.com/lodash.once/-/lodash.once-4.1.1.tgz#0dd3971213c7c56df880977d504c88fb471a97ac" resolved "https://registry.yarnpkg.com/lodash.once/-/lodash.once-4.1.1.tgz#0dd3971213c7c56df880977d504c88fb471a97ac"
integrity sha1-DdOXEhPHxW34gJd9UEyI+0cal6w= integrity sha1-DdOXEhPHxW34gJd9UEyI+0cal6w=
lodash@^4.14.0: lodash@^4.14.0, lodash@^4.17.21:
version "4.17.21" version "4.17.21"
resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c" resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c"
integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg== integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==
@ -401,6 +444,13 @@ mime@^1.4.1:
resolved "https://registry.yarnpkg.com/mime/-/mime-1.6.0.tgz#32cd9e5c64553bd58d19a568af452acff04981b1" resolved "https://registry.yarnpkg.com/mime/-/mime-1.6.0.tgz#32cd9e5c64553bd58d19a568af452acff04981b1"
integrity sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg== integrity sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==
minimatch@^3.0.4:
version "3.0.4"
resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083"
integrity sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==
dependencies:
brace-expansion "^1.1.7"
ms@2.1.2: ms@2.1.2:
version "2.1.2" version "2.1.2"
resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009" resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009"
@ -426,6 +476,11 @@ oauth@0.9.x:
resolved "https://registry.yarnpkg.com/oauth/-/oauth-0.9.15.tgz#bd1fefaf686c96b75475aed5196412ff60cfb9c1" resolved "https://registry.yarnpkg.com/oauth/-/oauth-0.9.15.tgz#bd1fefaf686c96b75475aed5196412ff60cfb9c1"
integrity sha1-vR/vr2hslrdUda7VGWQS/2DPucE= integrity sha1-vR/vr2hslrdUda7VGWQS/2DPucE=
os-tmpdir@~1.0.2:
version "1.0.2"
resolved "https://registry.yarnpkg.com/os-tmpdir/-/os-tmpdir-1.0.2.tgz#bbe67406c79aa85c5cfec766fe5734555dfa1274"
integrity sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=
p-map@^2.1.0: p-map@^2.1.0:
version "2.1.0" version "2.1.0"
resolved "https://registry.yarnpkg.com/p-map/-/p-map-2.1.0.tgz#310928feef9c9ecc65b68b17693018a665cea175" resolved "https://registry.yarnpkg.com/p-map/-/p-map-2.1.0.tgz#310928feef9c9ecc65b68b17693018a665cea175"
@ -534,6 +589,11 @@ qs@~6.5.2:
resolved "https://registry.yarnpkg.com/qs/-/qs-6.5.2.tgz#cb3ae806e8740444584ef154ce8ee98d403f3e36" resolved "https://registry.yarnpkg.com/qs/-/qs-6.5.2.tgz#cb3ae806e8740444584ef154ce8ee98d403f3e36"
integrity sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA== integrity sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==
readline-sync@^1.4.9:
version "1.4.10"
resolved "https://registry.yarnpkg.com/readline-sync/-/readline-sync-1.4.10.tgz#41df7fbb4b6312d673011594145705bf56d8873b"
integrity sha512-gNva8/6UAe8QYepIQH/jQ2qn91Qj0B9sYjMBBs3QOB8F2CXcKgLxQaJRP76sWVRQt+QU+8fAkCbCvjjMFu7Ycw==
redis-commands@1.7.0: redis-commands@1.7.0:
version "1.7.0" version "1.7.0"
resolved "https://registry.yarnpkg.com/redis-commands/-/redis-commands-1.7.0.tgz#15a6fea2d58281e27b1cd1acfb4b293e278c3a89" resolved "https://registry.yarnpkg.com/redis-commands/-/redis-commands-1.7.0.tgz#15a6fea2d58281e27b1cd1acfb4b293e278c3a89"
@ -592,6 +652,11 @@ semver@^5.6.0:
resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7" resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7"
integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ== integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==
sprintf-js@^1.1.1:
version "1.1.2"
resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.1.2.tgz#da1765262bf8c0f571749f2ad6c26300207ae673"
integrity sha512-VE0SOVEHCk7Qc8ulkWw3ntAzXuqf7S2lvwQaDLRnUeIEaKNQJzV6BwmLKhOqT61aGhfUMrXeaBk+oDGCzvhcug==
sshpk@^1.7.0: sshpk@^1.7.0:
version "1.16.1" version "1.16.1"
resolved "https://registry.yarnpkg.com/sshpk/-/sshpk-1.16.1.tgz#fb661c0bef29b39db40769ee39fa70093d6f6877" resolved "https://registry.yarnpkg.com/sshpk/-/sshpk-1.16.1.tgz#fb661c0bef29b39db40769ee39fa70093d6f6877"
@ -617,6 +682,13 @@ string-template@~1.0.0:
resolved "https://registry.yarnpkg.com/string-template/-/string-template-1.0.0.tgz#9e9f2233dc00f218718ec379a28a5673ecca8b96" resolved "https://registry.yarnpkg.com/string-template/-/string-template-1.0.0.tgz#9e9f2233dc00f218718ec379a28a5673ecca8b96"
integrity sha1-np8iM9wA8hhxjsN5oopWc+zKi5Y= integrity sha1-np8iM9wA8hhxjsN5oopWc+zKi5Y=
tmp@^0.0.33:
version "0.0.33"
resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.0.33.tgz#6d34335889768d21b2bcda0aa277ced3b1bfadf9"
integrity sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==
dependencies:
os-tmpdir "~1.0.2"
tough-cookie@~2.5.0: tough-cookie@~2.5.0:
version "2.5.0" version "2.5.0"
resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-2.5.0.tgz#cd9fb2a0aa1d5a12b473bd9fb96fa3dcff65ade2" resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-2.5.0.tgz#cd9fb2a0aa1d5a12b473bd9fb96fa3dcff65ade2"

View File

@ -5,13 +5,16 @@ const compress = require("koa-compress")
const zlib = require("zlib") const zlib = require("zlib")
const { mainRoutes, staticRoutes } = require("./routes") const { mainRoutes, staticRoutes } = require("./routes")
const pkg = require("../../package.json") const pkg = require("../../package.json")
const bullboard = require("bull-board") const env = require("../environment")
const expressApp = require("express")()
expressApp.use("/bulladmin", bullboard.router) if (!env.isTest()) {
const bullboard = require("bull-board")
const expressApp = require("express")()
expressApp.use("/bulladmin", bullboard.router)
}
const router = new Router() const router = new Router()
const env = require("../environment")
router router
.use( .use(

View File

@ -402,14 +402,16 @@ describe("/rows", () => {
name: "test", name: "test",
description: "test", description: "test",
attachment: [{ attachment: [{
url: "/test/thing", key: `/assets/${config.getAppId()}/attachment/test/thing.csv`,
}], }],
tableId: table._id, tableId: table._id,
}) })
// the environment needs configured for this // the environment needs configured for this
await setup.switchToSelfHosted(async () => { await setup.switchToSelfHosted(async () => {
const enriched = await outputProcessing(config.getAppId(), table, [row]) const enriched = await outputProcessing(config.getAppId(), table, [row])
expect(enriched[0].attachment[0].url).toBe(`/app-assets/assets/${config.getAppId()}/test/thing`) expect(enriched[0].attachment[0].url).toBe(
`/prod-budi-app-assets/assets/${config.getAppId()}/attachment/test/thing.csv`
)
}) })
}) })
}) })

View File

@ -1,6 +1,7 @@
const CouchDB = require("../db") const CouchDB = require("../db")
const emitter = require("../events/index") const emitter = require("../events/index")
const Queue = require("bull") const env = require("../environment")
const Queue = env.isTest() ? require("../utilities/queue/inMemoryQueue") : require("bull")
const { setQueues, BullAdapter } = require("bull-board") const { setQueues, BullAdapter } = require("bull-board")
const { getAutomationParams } = require("../db/utils") const { getAutomationParams } = require("../db/utils")
const { coerce } = require("../utilities/rowProcessor") const { coerce } = require("../utilities/rowProcessor")

View File

@ -14,7 +14,8 @@
"scripts": { "scripts": {
"run:docker": "node src/index.js", "run:docker": "node src/index.js",
"dev:stack:init": "node ./scripts/dev/manage.js init", "dev:stack:init": "node ./scripts/dev/manage.js init",
"dev:builder": "npm run dev:stack:init && nodemon src/index.js" "dev:builder": "npm run dev:stack:init && nodemon src/index.js",
"test": "jest --runInBand"
}, },
"author": "Budibase", "author": "Budibase",
"license": "AGPL-3.0-or-later", "license": "AGPL-3.0-or-later",
@ -50,5 +51,11 @@
"nodemon": "^2.0.7", "nodemon": "^2.0.7",
"pouchdb-adapter-memory": "^7.2.2", "pouchdb-adapter-memory": "^7.2.2",
"supertest": "^6.1.3" "supertest": "^6.1.3"
},
"jest": {
"testEnvironment": "node",
"setupFiles": [
"./scripts/jestSetup.js"
]
} }
} }

View File

@ -0,0 +1,5 @@
const env = require("../src/environment")
env._set("NODE_ENV", "jest")
env._set("JWT_SECRET", "test-jwtsecret")
env._set("LOG_LEVEL", "silent")

View File

@ -1,8 +1,17 @@
const { sendEmail } = require("../../../utilities/email") const { sendEmail } = require("../../../utilities/email")
const CouchDB = require("../../../db")
const authPkg = require("@budibase/auth")
const GLOBAL_DB = authPkg.StaticDatabases.GLOBAL.name
exports.sendEmail = async ctx => { exports.sendEmail = async ctx => {
const { groupId, email, userId, purpose } = ctx.request.body const { groupId, email, userId, purpose } = ctx.request.body
const response = await sendEmail(email, purpose, { groupId, userId }) let user
if (userId) {
const db = new CouchDB(GLOBAL_DB)
user = await db.get(userId)
}
const response = await sendEmail(email, purpose, { groupId, user })
ctx.body = { ctx.body = {
...response, ...response,
message: `Email sent to ${email}.`, message: `Email sent to ${email}.`,

View File

@ -126,7 +126,7 @@ exports.find = async ctx => {
exports.invite = async ctx => { exports.invite = async ctx => {
const { email } = ctx.request.body const { email } = ctx.request.body
const existing = await getGlobalUserByEmail(FIRST_USER_EMAIL) const existing = await getGlobalUserByEmail(email)
if (existing) { if (existing) {
ctx.throw(400, "Email address already in use.") ctx.throw(400, "Email address already in use.")
} }

View File

@ -0,0 +1,49 @@
const setup = require("./utilities")
jest.mock("nodemailer")
const sendMailMock = setup.emailMock()
describe("/api/admin/auth", () => {
let request = setup.getRequest()
let config = setup.getConfig()
let code
beforeAll(async () => {
await config.init()
})
afterAll(setup.afterAll)
it("should be able to generate password reset email", async () => {
// initially configure settings
await config.saveSmtpConfig()
await config.saveSettingsConfig()
await config.createUser("test@test.com")
const res = await request
.post(`/api/admin/auth/reset`)
.send({
email: "test@test.com",
})
.expect("Content-Type", /json/)
.expect(200)
expect(res.body).toEqual({ message: "Please check your email for a reset link." })
expect(sendMailMock).toHaveBeenCalled()
const emailCall = sendMailMock.mock.calls[0][0]
// after this URL there should be a code
const parts = emailCall.html.split("http://localhost:10000/reset/")
code = parts[1].split("\"")[0]
expect(code).toBeDefined()
})
it("should allow resetting user password with code", async () => {
const res = await request
.post(`/api/admin/auth/reset/update`)
.send({
password: "newpassword",
resetCode: code,
})
.expect("Content-Type", /json/)
.expect(200)
expect(res.body).toEqual({ message: "password reset successfully." })
})
})

View File

@ -2,13 +2,8 @@ const setup = require("./utilities")
const { EmailTemplatePurpose } = require("../../../constants") const { EmailTemplatePurpose } = require("../../../constants")
// mock the email system // mock the email system
const sendMailMock = jest.fn()
jest.mock("nodemailer") jest.mock("nodemailer")
const nodemailer = require("nodemailer") const sendMailMock = setup.emailMock()
nodemailer.createTransport.mockReturnValue({
sendMail: sendMailMock,
verify: jest.fn()
})
describe("/api/admin/email", () => { describe("/api/admin/email", () => {
let request = setup.getRequest() let request = setup.getRequest()

View File

@ -16,11 +16,13 @@ describe("/api/admin/email", () => {
async function sendRealEmail(purpose) { async function sendRealEmail(purpose) {
await config.saveEtherealSmtpConfig() await config.saveEtherealSmtpConfig()
await config.saveSettingsConfig() await config.saveSettingsConfig()
const user = await config.getUser("test@test.com")
const res = await request const res = await request
.post(`/api/admin/email/send`) .post(`/api/admin/email/send`)
.send({ .send({
email: "test@test.com", email: "test@test.com",
purpose, purpose,
userId: user._id,
}) })
.set(config.defaultHeaders()) .set(config.defaultHeaders())
.expect("Content-Type", /json/) .expect("Content-Type", /json/)
@ -55,6 +57,6 @@ describe("/api/admin/email", () => {
}) })
it("should be able to send a password recovery email", async () => { it("should be able to send a password recovery email", async () => {
const res = await sendRealEmail(EmailTemplatePurpose.PASSWORD_RECOVERY) await sendRealEmail(EmailTemplatePurpose.PASSWORD_RECOVERY)
}) })
}) })

View File

@ -0,0 +1,52 @@
const setup = require("./utilities")
jest.mock("nodemailer")
const sendMailMock = setup.emailMock()
describe("/api/admin/users", () => {
let request = setup.getRequest()
let config = setup.getConfig()
let code
beforeAll(async () => {
await config.init()
})
afterAll(setup.afterAll)
it("should be able to generate an invitation", async () => {
// initially configure settings
await config.saveSmtpConfig()
await config.saveSettingsConfig()
const res = await request
.post(`/api/admin/users/invite`)
.send({
email: "invite@test.com",
})
.set(config.defaultHeaders())
.expect("Content-Type", /json/)
.expect(200)
expect(res.body).toEqual({ message: "Invitation has been sent." })
expect(sendMailMock).toHaveBeenCalled()
const emailCall = sendMailMock.mock.calls[0][0]
// after this URL there should be a code
const parts = emailCall.html.split("http://localhost:10000/invite/")
code = parts[1].split("\"")[0]
expect(code).toBeDefined()
})
it("should be able to create new user from invite", async () => {
const res = await request
.post(`/api/admin/users/invite/accept`)
.send({
password: "newpassword",
inviteCode: code,
})
.expect("Content-Type", /json/)
.expect(200)
expect(res.body._id).toBeDefined()
const user = await config.getUser("invite@test.com")
expect(user).toBeDefined()
expect(user._id).toEqual(res.body._id)
})
})

View File

@ -4,6 +4,7 @@ const supertest = require("supertest")
const { jwt } = require("@budibase/auth").auth const { jwt } = require("@budibase/auth").auth
const { Cookies } = require("@budibase/auth").constants const { Cookies } = require("@budibase/auth").constants
const { Configs, LOGO_URL } = require("../../../../constants") const { Configs, LOGO_URL } = require("../../../../constants")
const { getGlobalUserByEmail } = require("@budibase/auth").utils
class TestConfiguration { class TestConfiguration {
constructor(openServer = true) { constructor(openServer = true) {
@ -53,6 +54,12 @@ class TestConfiguration {
) )
} }
async end() {
if (this.server) {
await this.server.close()
}
}
defaultHeaders() { defaultHeaders() {
const user = { const user = {
_id: "us_uuid1", _id: "us_uuid1",
@ -65,6 +72,25 @@ class TestConfiguration {
} }
} }
async getUser(email) {
return getGlobalUserByEmail(email)
}
async createUser(email = "test@test.com", password = "test") {
const user = await this.getUser(email)
if (user) {
return user
}
await this._req(
{
email,
password,
},
null,
controllers.users.save
)
}
async deleteConfig(type) { async deleteConfig(type) {
try { try {
const cfg = await this._req( const cfg = await this._req(

View File

@ -7,9 +7,9 @@ exports.beforeAll = () => {
request = config.getRequest() request = config.getRequest()
} }
exports.afterAll = () => { exports.afterAll = async () => {
if (config) { if (config) {
config.end() await config.end()
} }
request = null request = null
config = null config = null
@ -28,3 +28,14 @@ exports.getConfig = () => {
} }
return config return config
} }
exports.emailMock = () => {
// mock the email system
const sendMailMock = jest.fn()
const nodemailer = require("nodemailer")
nodemailer.createTransport.mockReturnValue({
sendMail: sendMailMock,
verify: jest.fn()
})
return sendMailMock
}

View File

@ -11,7 +11,7 @@ function isTest() {
} }
let LOADED = false let LOADED = false
if (!LOADED && isDev()) { if (!LOADED && isDev() && !isTest()) {
require("dotenv").config() require("dotenv").config()
LOADED = true LOADED = true
} }

View File

@ -12,10 +12,6 @@ const api = require("./api")
const app = new Koa() const app = new Koa()
if (!env.SELF_HOSTED) {
throw "Currently this service only supports use in self hosting"
}
// set up top level koa middleware // set up top level koa middleware
app.use(koaBody({ multipart: true })) app.use(koaBody({ multipart: true }))

View File

@ -45,7 +45,7 @@ exports.getSettingsTemplateContext = async (purpose, code = null) => {
case EmailTemplatePurpose.INVITATION: case EmailTemplatePurpose.INVITATION:
context[TemplateBindings.INVITE_CODE] = code context[TemplateBindings.INVITE_CODE] = code
context[TemplateBindings.REGISTRATION_URL] = checkSlashesInUrl( context[TemplateBindings.REGISTRATION_URL] = checkSlashesInUrl(
`${URL}/registration/${code}` `${URL}/invite/${code}`
) )
break break
} }

View File

@ -287,7 +287,7 @@
resolved "https://registry.yarnpkg.com/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz#75a2e8b51cb758a7553d6804a5932d7aace75c39" resolved "https://registry.yarnpkg.com/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz#75a2e8b51cb758a7553d6804a5932d7aace75c39"
integrity sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw== integrity sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==
"@budibase/auth@0.0.1": "@budibase/auth@^0.18.6":
version "0.18.6" version "0.18.6"
resolved "https://registry.yarnpkg.com/@budibase/auth/-/auth-0.18.6.tgz#d893005962afd9425f10e2ac8d1d495047d0d44e" resolved "https://registry.yarnpkg.com/@budibase/auth/-/auth-0.18.6.tgz#d893005962afd9425f10e2ac8d1d495047d0d44e"
integrity sha512-pdyqR8G240lToMe2OZNpw2YzuRwOlOT+cAfVHPMBxJJKF0VvZ0K500NoSUINEQPr4IfWpPSu6CQhq+ROf4pMXA== integrity sha512-pdyqR8G240lToMe2OZNpw2YzuRwOlOT+cAfVHPMBxJJKF0VvZ0K500NoSUINEQPr4IfWpPSu6CQhq+ROf4pMXA==
@ -2505,6 +2505,20 @@ fb-watchman@^2.0.0:
dependencies: dependencies:
bser "2.1.1" bser "2.1.1"
fengari-interop@^0.1.2:
version "0.1.2"
resolved "https://registry.yarnpkg.com/fengari-interop/-/fengari-interop-0.1.2.tgz#f7731dcdd2ff4449073fb7ac3c451a8841ce1e87"
integrity sha512-8iTvaByZVoi+lQJhHH9vC+c/Yaok9CwOqNQZN6JrVpjmWwW4dDkeblBXhnHC+BoI6eF4Cy5NKW3z6ICEjvgywQ==
fengari@^0.1.4:
version "0.1.4"
resolved "https://registry.yarnpkg.com/fengari/-/fengari-0.1.4.tgz#72416693cd9e43bd7d809d7829ddc0578b78b0bb"
integrity sha512-6ujqUuiIYmcgkGz8MGAdERU57EIluGGPSUgGPTsco657EHa+srq0S3/YUl/r9kx1+D+d4rGfYObd+m8K22gB1g==
dependencies:
readline-sync "^1.4.9"
sprintf-js "^1.1.1"
tmp "^0.0.33"
fetch-cookie@0.10.1: fetch-cookie@0.10.1:
version "0.10.1" version "0.10.1"
resolved "https://registry.yarnpkg.com/fetch-cookie/-/fetch-cookie-0.10.1.tgz#5ea88f3d36950543c87997c27ae2aeafb4b5c4d4" resolved "https://registry.yarnpkg.com/fetch-cookie/-/fetch-cookie-0.10.1.tgz#5ea88f3d36950543c87997c27ae2aeafb4b5c4d4"
@ -3130,6 +3144,17 @@ inline-process-browser@^1.0.0:
falafel "^1.0.1" falafel "^1.0.1"
through2 "^0.6.5" through2 "^0.6.5"
ioredis-mock@^5.5.5:
version "5.5.5"
resolved "https://registry.yarnpkg.com/ioredis-mock/-/ioredis-mock-5.5.5.tgz#dec9fedd238c6ab9f56c026fc366533144f8a256"
integrity sha512-7SxCAwNtDLC8IFDptqIhOC7ajp3fciVtCrXOEOkpyjPboAGRQkJbnpNPy1NYORoWi+0/iOtUPUQckSKtSQj4DA==
dependencies:
fengari "^0.1.4"
fengari-interop "^0.1.2"
lodash "^4.17.21"
minimatch "^3.0.4"
standard-as-callback "^2.1.0"
ioredis@^4.27.1: ioredis@^4.27.1:
version "4.27.1" version "4.27.1"
resolved "https://registry.yarnpkg.com/ioredis/-/ioredis-4.27.1.tgz#4ef947b455a1b995baa4b0d7e2c4e4f75f746421" resolved "https://registry.yarnpkg.com/ioredis/-/ioredis-4.27.1.tgz#4ef947b455a1b995baa4b0d7e2c4e4f75f746421"
@ -3146,6 +3171,22 @@ ioredis@^4.27.1:
redis-parser "^3.0.0" redis-parser "^3.0.0"
standard-as-callback "^2.1.0" standard-as-callback "^2.1.0"
ioredis@^4.27.2:
version "4.27.2"
resolved "https://registry.yarnpkg.com/ioredis/-/ioredis-4.27.2.tgz#6a79bca05164482da796f8fa010bccefd3bf4811"
integrity sha512-7OpYymIthonkC2Jne5uGWXswdhlua1S1rWGAERaotn0hGJWTSURvxdHA9G6wNbT/qKCloCja/FHsfKXW8lpTmg==
dependencies:
cluster-key-slot "^1.1.0"
debug "^4.3.1"
denque "^1.1.0"
lodash.defaults "^4.2.0"
lodash.flatten "^4.4.0"
p-map "^2.1.0"
redis-commands "1.7.0"
redis-errors "^1.2.0"
redis-parser "^3.0.0"
standard-as-callback "^2.1.0"
is-accessor-descriptor@^0.1.6: is-accessor-descriptor@^0.1.6:
version "0.1.6" version "0.1.6"
resolved "https://registry.yarnpkg.com/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz#a9e12cb3ae8d876727eeef3843f8a0897b5c98d6" resolved "https://registry.yarnpkg.com/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz#a9e12cb3ae8d876727eeef3843f8a0897b5c98d6"
@ -4403,7 +4444,7 @@ lodash.templatesettings@^4.0.0:
dependencies: dependencies:
lodash._reinterpolate "^3.0.0" lodash._reinterpolate "^3.0.0"
lodash@^4.14.0, lodash@^4.17.19, lodash@^4.17.20, lodash@^4.7.0: lodash@^4.14.0, lodash@^4.17.19, lodash@^4.17.20, lodash@^4.17.21, lodash@^4.7.0:
version "4.17.21" version "4.17.21"
resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c" resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c"
integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg== integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==
@ -4894,6 +4935,11 @@ optionator@^0.8.1:
type-check "~0.3.2" type-check "~0.3.2"
word-wrap "~1.2.3" word-wrap "~1.2.3"
os-tmpdir@~1.0.2:
version "1.0.2"
resolved "https://registry.yarnpkg.com/os-tmpdir/-/os-tmpdir-1.0.2.tgz#bbe67406c79aa85c5cfec766fe5734555dfa1274"
integrity sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=
p-cancelable@^1.0.0: p-cancelable@^1.0.0:
version "1.1.0" version "1.1.0"
resolved "https://registry.yarnpkg.com/p-cancelable/-/p-cancelable-1.1.0.tgz#d078d15a3af409220c886f1d9a0ca2e441ab26cc" resolved "https://registry.yarnpkg.com/p-cancelable/-/p-cancelable-1.1.0.tgz#d078d15a3af409220c886f1d9a0ca2e441ab26cc"
@ -5521,6 +5567,11 @@ readdirp@~3.5.0:
dependencies: dependencies:
picomatch "^2.2.1" picomatch "^2.2.1"
readline-sync@^1.4.9:
version "1.4.10"
resolved "https://registry.yarnpkg.com/readline-sync/-/readline-sync-1.4.10.tgz#41df7fbb4b6312d673011594145705bf56d8873b"
integrity sha512-gNva8/6UAe8QYepIQH/jQ2qn91Qj0B9sYjMBBs3QOB8F2CXcKgLxQaJRP76sWVRQt+QU+8fAkCbCvjjMFu7Ycw==
recast@^0.10.1: recast@^0.10.1:
version "0.10.43" version "0.10.43"
resolved "https://registry.yarnpkg.com/recast/-/recast-0.10.43.tgz#b95d50f6d60761a5f6252e15d80678168491ce7f" resolved "https://registry.yarnpkg.com/recast/-/recast-0.10.43.tgz#b95d50f6d60761a5f6252e15d80678168491ce7f"
@ -6045,6 +6096,11 @@ split2@^3.1.1:
dependencies: dependencies:
readable-stream "^3.0.0" readable-stream "^3.0.0"
sprintf-js@^1.1.1:
version "1.1.2"
resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.1.2.tgz#da1765262bf8c0f571749f2ad6c26300207ae673"
integrity sha512-VE0SOVEHCk7Qc8ulkWw3ntAzXuqf7S2lvwQaDLRnUeIEaKNQJzV6BwmLKhOqT61aGhfUMrXeaBk+oDGCzvhcug==
sprintf-js@~1.0.2: sprintf-js@~1.0.2:
version "1.0.3" version "1.0.3"
resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c" resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c"
@ -6327,6 +6383,13 @@ tiny-queue@^0.2.0:
resolved "https://registry.yarnpkg.com/tiny-queue/-/tiny-queue-0.2.1.tgz#25a67f2c6e253b2ca941977b5ef7442ef97a6046" resolved "https://registry.yarnpkg.com/tiny-queue/-/tiny-queue-0.2.1.tgz#25a67f2c6e253b2ca941977b5ef7442ef97a6046"
integrity sha1-JaZ/LG4lOyypQZd7XvdELvl6YEY= integrity sha1-JaZ/LG4lOyypQZd7XvdELvl6YEY=
tmp@^0.0.33:
version "0.0.33"
resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.0.33.tgz#6d34335889768d21b2bcda0aa277ced3b1bfadf9"
integrity sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==
dependencies:
os-tmpdir "~1.0.2"
tmpl@1.0.x: tmpl@1.0.x:
version "1.0.4" version "1.0.4"
resolved "https://registry.yarnpkg.com/tmpl/-/tmpl-1.0.4.tgz#23640dd7b42d00433911140820e5cf440e521dd1" resolved "https://registry.yarnpkg.com/tmpl/-/tmpl-1.0.4.tgz#23640dd7b42d00433911140820e5cf440e521dd1"