Browse Source

smtp events + centralise worker test config

pull/5694/head
Rory Powell 4 years ago
parent
commit
b650863a2b
  1. 202
      packages/server/src/tests/utilities/TestConfiguration.js
  2. 14
      packages/worker/src/api/routes/tests/auth.spec.js
  3. 68
      packages/worker/src/api/routes/tests/configs.spec.js
  4. 15
      packages/worker/src/api/routes/tests/email.spec.js
  5. 10
      packages/worker/src/api/routes/tests/realEmail.spec.js
  6. 12
      packages/worker/src/api/routes/tests/users.spec.js
  7. 7
      packages/worker/src/api/routes/tests/utilities/controllers.js
  8. 41
      packages/worker/src/api/routes/tests/utilities/index.js
  9. 1
      packages/worker/src/api/routes/tests/utilities/mocks/index.js
  10. 2
      packages/worker/src/api/routes/tests/utilities/structures.js
  11. 169
      packages/worker/src/tests/TestConfiguration.js
  12. 7
      packages/worker/src/tests/controllers.js
  13. 12
      packages/worker/src/tests/index.js
  14. 0
      packages/worker/src/tests/mocks/core.js
  15. 10
      packages/worker/src/tests/mocks/email.js
  16. 6
      packages/worker/src/tests/mocks/index.js
  17. 76
      packages/worker/src/tests/structures/configs.js
  18. 10
      packages/worker/src/tests/structures/index.js

202
packages/server/src/tests/utilities/TestConfiguration.js

@ -60,6 +60,26 @@ class TestConfiguration {
return this.prodAppId
}
// SETUP / TEARDOWN
// use a new id as the name to avoid name collisions
async init(appName = newid()) {
await this.globalUser()
return this.createApp(appName)
}
end() {
if (!this) {
return
}
if (this.server) {
this.server.close()
}
cleanup(this.allApps.map(app => app.appId))
}
// UTILS
async _req(config, params, controlFunc) {
const request = {}
// fake cookies, we don't need them
@ -88,19 +108,7 @@ class TestConfiguration {
}
}
async generateApiKey(userId = GLOBAL_USER_ID) {
const db = getGlobalDB(TENANT_ID)
const id = generateDevInfoID(userId)
let devInfo
try {
devInfo = await db.get(id)
} catch (err) {
devInfo = { _id: id, userId }
}
devInfo.apiKey = encrypt(`${TENANT_ID}${SEPARATOR}${newid()}`)
await db.put(devInfo)
return devInfo.apiKey
}
// USER / AUTH
async globalUser({
id = GLOBAL_USER_ID,
@ -138,20 +146,59 @@ class TestConfiguration {
}
}
// use a new id as the name to avoid name collisions
async init(appName = newid()) {
await this.globalUser()
return this.createApp(appName)
async createUser(id = null, email = EMAIL) {
const globalId = !id ? `us_${Math.random()}` : `us_${id}`
const resp = await this.globalUser({ id: globalId, email })
await userCache.invalidateUser(globalId)
return {
...resp,
globalId,
}
}
end() {
if (!this) {
return
}
if (this.server) {
this.server.close()
}
cleanup(this.allApps.map(app => app.appId))
async login({ roleId, userId, builder, prodApp = false } = {}) {
const appId = prodApp ? this.prodAppId : this.appId
return context.doInAppContext(appId, async () => {
userId = !userId ? `us_uuid1` : userId
if (!this.request) {
throw "Server has not been opened, cannot login."
}
// make sure the user exists in the global DB
if (roleId !== BUILTIN_ROLE_IDS.PUBLIC) {
await this.globalUser({
id: userId,
builder,
roles: { [this.prodAppId]: roleId },
})
}
await createASession(userId, {
sessionId: "sessionid",
tenantId: TENANT_ID,
})
// have to fake this
const auth = {
userId,
sessionId: "sessionid",
tenantId: TENANT_ID,
}
const app = {
roleId: roleId,
appId,
}
const authToken = jwt.sign(auth, env.JWT_SECRET)
const appToken = jwt.sign(app, env.JWT_SECRET)
// returning necessary request headers
await userCache.invalidateUser(userId)
return {
Accept: "application/json",
Cookie: [
`${Cookies.Auth}=${authToken}`,
`${Cookies.CurrentApp}=${appToken}`,
],
[Headers.APP_ID]: appId,
}
})
}
defaultHeaders(extras = {}) {
@ -202,6 +249,24 @@ class TestConfiguration {
return this.login({ email, roleId, builder, prodApp })
}
// API
async generateApiKey(userId = GLOBAL_USER_ID) {
const db = getGlobalDB(TENANT_ID)
const id = generateDevInfoID(userId)
let devInfo
try {
devInfo = await db.get(id)
} catch (err) {
devInfo = { _id: id, userId }
}
devInfo.apiKey = encrypt(`${TENANT_ID}${SEPARATOR}${newid()}`)
await db.put(devInfo)
return devInfo.apiKey
}
// APP
async createApp(appName) {
// create dev app
this.app = await this._req({ name: appName }, null, controllers.app.create)
@ -231,6 +296,8 @@ class TestConfiguration {
})
}
// TABLE
async updateTable(config = null) {
config = config || basicTable()
this.table = await this._req(config, null, controllers.table.save)
@ -279,6 +346,8 @@ class TestConfiguration {
return this.createTable(table)
}
// ROW
async createRow(config = null) {
if (!this.table) {
throw "Test requires table to be configured."
@ -299,6 +368,8 @@ class TestConfiguration {
return this._req(null, { tableId }, controllers.row.fetch)
}
// ROLE
async createRole(config = null) {
config = config || basicRole()
return this._req(config, null, controllers.role.save)
@ -316,6 +387,8 @@ class TestConfiguration {
)
}
// VIEW
async createView(config) {
if (!this.table) {
throw "Test requires table to be configured."
@ -328,6 +401,8 @@ class TestConfiguration {
return this._req(view, null, controllers.view.save)
}
// AUTOMATION
async createAutomation(config) {
config = config || basicAutomation()
if (config._rev) {
@ -355,6 +430,16 @@ class TestConfiguration {
)
}
async createWebhook(config = null) {
if (!this.automation) {
throw "Must create an automation before creating webhook."
}
config = config || basicWebhook(this.automation._id)
return (await this._req(config, null, controllers.webhook.save)).webhook
}
// DATASOURCE
async createDatasource(config = null) {
config = config || basicDatasource()
const response = await this._req(config, null, controllers.datasource.save)
@ -405,6 +490,8 @@ class TestConfiguration {
return { datasource, query: basedOnQuery }
}
// QUERY
async previewQuery(request, config, datasource, fields) {
return request
.post(`/api/queries/preview`)
@ -428,78 +515,19 @@ class TestConfiguration {
return this._req(config, null, controllers.query.save)
}
// SCREEN
async createScreen(config = null) {
config = config || basicScreen()
return this._req(config, null, controllers.screen.save)
}
async createWebhook(config = null) {
if (!this.automation) {
throw "Must create an automation before creating webhook."
}
config = config || basicWebhook(this.automation._id)
return (await this._req(config, null, controllers.webhook.save)).webhook
}
// LAYOUT
async createLayout(config = null) {
config = config || basicLayout()
return await this._req(config, null, controllers.layout.save)
}
async createUser(id = null, email = EMAIL) {
const globalId = !id ? `us_${Math.random()}` : `us_${id}`
const resp = await this.globalUser({ id: globalId, email })
await userCache.invalidateUser(globalId)
return {
...resp,
globalId,
}
}
async login({ roleId, userId, builder, prodApp = false } = {}) {
const appId = prodApp ? this.prodAppId : this.appId
return context.doInAppContext(appId, async () => {
userId = !userId ? `us_uuid1` : userId
if (!this.request) {
throw "Server has not been opened, cannot login."
}
// make sure the user exists in the global DB
if (roleId !== BUILTIN_ROLE_IDS.PUBLIC) {
await this.globalUser({
id: userId,
builder,
roles: { [this.prodAppId]: roleId },
})
}
await createASession(userId, {
sessionId: "sessionid",
tenantId: TENANT_ID,
})
// have to fake this
const auth = {
userId,
sessionId: "sessionid",
tenantId: TENANT_ID,
}
const app = {
roleId: roleId,
appId,
}
const authToken = jwt.sign(auth, env.JWT_SECRET)
const appToken = jwt.sign(app, env.JWT_SECRET)
// returning necessary request headers
await userCache.invalidateUser(userId)
return {
Accept: "application/json",
Cookie: [
`${Cookies.Auth}=${authToken}`,
`${Cookies.CurrentApp}=${appToken}`,
],
[Headers.APP_ID]: appId,
}
})
}
}
module.exports = TestConfiguration

14
packages/worker/src/api/routes/tests/auth.spec.js

@ -1,20 +1,20 @@
jest.mock("nodemailer")
const setup = require("./utilities")
const sendMailMock = setup.emailMock()
const { config, request, mocks, structures } = require("../../../tests")
const sendMailMock = mocks.email.mock()
const { events } = require("@budibase/backend-core")
const TENANT_ID = "default"
const TENANT_ID = structures.TENANT_ID
describe("/api/global/auth", () => {
let request = setup.getRequest()
let config = setup.getConfig()
let code
beforeAll(async () => {
await config.init()
await config.beforeAll()
})
afterAll(setup.afterAll)
afterAll(async () => {
await config.afterAll()
})
afterEach(() => {
jest.clearAllMocks()

68
packages/worker/src/api/routes/tests/configs.spec.js

@ -1,16 +1,14 @@
// mock the email system
jest.mock("nodemailer")
const setup = require("./utilities")
setup.emailMock()
const { config, structures, mocks, request } = require("../../../tests")
mocks.email.mock()
const { Configs } = require("@budibase/backend-core/constants")
const { events } = require("@budibase/backend-core")
describe("configs", () => {
let request = setup.getRequest()
let config = setup.getConfig()
beforeAll(async () => {
await config.init()
await config.beforeAll()
})
beforeEach(() => {
@ -18,15 +16,14 @@ describe("configs", () => {
})
afterAll(async () => {
await setup.afterAll()
await config.afterAll()
})
describe("post /api/global/configs", () => {
const saveConfig = async (conf, type, _id, _rev) => {
const saveConfig = async (conf, _id, _rev) => {
const data = {
type,
config: conf,
...conf,
_id,
_rev
}
@ -46,14 +43,8 @@ describe("configs", () => {
describe("google", () => {
const saveGoogleConfig = async (conf, _id, _rev) => {
const googleConfig = {
clientID: "clientID",
clientSecret: "clientSecret",
activated: true,
...conf
}
return saveConfig(googleConfig, Configs.GOOGLE, _id, _rev)
const googleConfig = structures.configs.google(conf)
return saveConfig(googleConfig, _id, _rev)
}
describe("create", () => {
@ -106,19 +97,8 @@ describe("configs", () => {
describe("oidc", () => {
const saveOIDCConfig = async (conf, _id, _rev) => {
const oidcConfig = {
configs: [{
clientID: "clientID",
clientSecret: "clientSecret",
configUrl: "http://example.com",
logo: "logo",
name: "oidc",
uuid: "uuid",
activated: true,
...conf
}]
}
return saveConfig(oidcConfig, Configs.OIDC, _id, _rev)
const oidcConfig = structures.configs.oidc(conf)
return saveConfig(oidcConfig, _id, _rev)
}
describe("create", () => {
@ -169,6 +149,34 @@ describe("configs", () => {
})
})
describe("smtp", () => {
const saveSMTPConfig = async (conf, _id, _rev) => {
const smtpConfig = structures.configs.smtp(conf)
return saveConfig(smtpConfig, _id, _rev)
}
describe("create", () => {
it ("should create SMTP config", async () => {
await config.deleteConfig(Configs.SMTP)
await saveSMTPConfig()
expect(events.email.SMTPUpdated).not.toBeCalled()
expect(events.email.SMTPCreated).toBeCalledTimes(1)
await config.deleteConfig(Configs.SMTP)
})
})
describe("update", () => {
it ("should update SMTP config", async () => {
const smtpConf = await saveSMTPConfig()
jest.clearAllMocks()
await saveSMTPConfig(smtpConf.config, smtpConf._id, smtpConf._rev)
expect(events.email.SMTPCreated).not.toBeCalled()
expect(events.email.SMTPUpdated).toBeCalledTimes(1)
await config.deleteConfig(Configs.SMTP)
})
})
})
})
it("should return the correct checklist status based on the state of the budibase installation", async () => {

15
packages/worker/src/api/routes/tests/email.spec.js

@ -1,19 +1,20 @@
jest.mock("nodemailer")
const setup = require("./utilities")
const sendMailMock = setup.emailMock()
const { config, mocks, structures, request } = require("../../../tests")
const sendMailMock = mocks.email.mock()
const { EmailTemplatePurpose } = require("../../../constants")
const { TENANT_ID } = require("./utilities/structures")
const TENANT_ID = structures.TENANT_ID
describe("/api/global/email", () => {
let request = setup.getRequest()
let config = setup.getConfig()
beforeAll(async () => {
await config.init()
await config.beforeAll()
})
afterAll(setup.afterAll)
afterAll(async () => {
await config.afterAll()
})
it("should be able to send an email (with mocking)", async () => {
// initially configure settings

10
packages/worker/src/api/routes/tests/realEmail.spec.js

@ -1,17 +1,17 @@
const setup = require("./utilities")
const { config, request } = require("../../../tests")
const { EmailTemplatePurpose } = require("../../../constants")
const nodemailer = require("nodemailer")
const fetch = require("node-fetch")
describe("/api/global/email", () => {
let request = setup.getRequest()
let config = setup.getConfig()
beforeAll(async () => {
await config.init()
await config.beforeAll()
})
afterAll(setup.afterAll)
afterAll(async () => {
await config.afterAll()
})
async function sendRealEmail(purpose) {
let response, text

12
packages/worker/src/api/routes/tests/users.spec.js

@ -1,17 +1,17 @@
jest.mock("nodemailer")
const setup = require("./utilities")
const sendMailMock = setup.emailMock()
const { config, request, mocks } = require("../../../tests")
const sendMailMock = mocks.email.mock()
describe("/api/global/users", () => {
let request = setup.getRequest()
let config = setup.getConfig()
let code
beforeAll(async () => {
await config.init()
await config.beforeAll()
})
afterAll(setup.afterAll)
afterAll(async () => {
await config.afterAll()
})
it("should be able to generate an invitation", async () => {
// initially configure settings

7
packages/worker/src/api/routes/tests/utilities/controllers.js

@ -1,7 +0,0 @@
module.exports = {
email: require("../../../controllers/global/email"),
workspaces: require("../../../controllers/global/workspaces"),
config: require("../../../controllers/global/configs"),
templates: require("../../../controllers/global/templates"),
users: require("../../../controllers/global/users"),
}

41
packages/worker/src/api/routes/tests/utilities/index.js

@ -1,41 +0,0 @@
const TestConfig = require("./TestConfiguration")
let request, config
exports.beforeAll = () => {
config = new TestConfig()
request = config.getRequest()
}
exports.afterAll = async () => {
if (config) {
await config.end()
}
request = null
config = null
}
exports.getRequest = () => {
if (!request) {
exports.beforeAll()
}
return request
}
exports.getConfig = () => {
if (!config) {
exports.beforeAll()
}
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
}

1
packages/worker/src/api/routes/tests/utilities/mocks/index.js

@ -1 +0,0 @@
require("./core")

2
packages/worker/src/api/routes/tests/utilities/structures.js

@ -1,2 +0,0 @@
exports.TENANT_ID = "default"
exports.CSRF_TOKEN = "e3727778-7af0-4226-b5eb-f43cbe60a306"

169
packages/worker/src/api/routes/tests/utilities/TestConfiguration.js → packages/worker/src/tests/TestConfiguration.js

@ -1,22 +1,22 @@
require("./mocks")
require("../../../../db").init()
const env = require("../../../../environment")
require("../db").init()
const env = require("../environment")
const controllers = require("./controllers")
const supertest = require("supertest")
const { jwt } = require("@budibase/backend-core/auth")
const { Cookies, Headers } = require("@budibase/backend-core/constants")
const { Configs, LOGO_URL } = require("../../../../constants")
const { Configs } = require("../constants")
const { getGlobalUserByEmail } = require("@budibase/backend-core/utils")
const { createASession } = require("@budibase/backend-core/sessions")
const { newid } = require("@budibase/backend-core/src/hashing")
const { TENANT_ID, CSRF_TOKEN } = require("./structures")
const structures = require("./structures")
const { doInTenant } = require("@budibase/backend-core/tenancy")
class TestConfiguration {
constructor(openServer = true) {
if (openServer) {
env.PORT = 4012
this.server = require("../../../../index")
env.PORT = "0" // random port
this.server = require("../index")
// we need the request for logging in, involves cookies, hard to fake
this.request = supertest(this.server)
}
@ -26,6 +26,8 @@ class TestConfiguration {
return this.request
}
// UTILS
async _req(config, params, controlFunc) {
const request = {}
// fake cookies, we don't need them
@ -49,25 +51,37 @@ class TestConfiguration {
return request.body
}
async init(createUser = true) {
if (createUser) {
// create a test user
await this._req(
{
email: "test@test.com",
password: "test",
_id: "us_uuid1",
builder: {
global: true,
},
admin: {
global: true,
},
},
null,
controllers.users.save
)
// SETUP / TEARDOWN
async beforeAll() {
await this.login()
}
async afterAll() {
if (this.server) {
await this.server.close()
}
}
// USER / AUTH
async login() {
// create a test user
await this._req(
{
email: "test@test.com",
password: "test",
_id: "us_uuid1",
builder: {
global: true,
},
admin: {
global: true,
},
},
null,
controllers.users.save
)
await createASession("us_uuid1", {
sessionId: "sessionid",
tenantId: TENANT_ID,
@ -75,12 +89,6 @@ class TestConfiguration {
})
}
async end() {
if (this.server) {
await this.server.close()
}
}
cookieHeader(cookies) {
return {
Cookie: [cookies],
@ -123,6 +131,20 @@ class TestConfiguration {
)
}
async saveAdminUser() {
await this._req(
{
email: "testuser@test.com",
password: "test@test.com",
tenantId: TENANT_ID,
},
null,
controllers.users.adminUser
)
}
// CONFIGS
async deleteConfig(type) {
try {
const cfg = await this._req(
@ -147,37 +169,26 @@ class TestConfiguration {
}
}
// CONFIGS - SETTINGS
async saveSettingsConfig() {
await this.deleteConfig(Configs.SETTINGS)
await this._req(
{
type: Configs.SETTINGS,
config: {
platformUrl: "http://localhost:10000",
logoUrl: LOGO_URL,
company: "Budibase",
},
},
structures.configs.settings(),
null,
controllers.config.save
)
}
async saveOAuthConfig() {
// CONFIGS - GOOGLE
async saveGoogleConfig() {
await this.deleteConfig(Configs.GOOGLE)
await this._req(
{
type: Configs.GOOGLE,
config: {
clientID: "clientId",
clientSecret: "clientSecret",
},
},
null,
controllers.config.save
)
await this._req(structures.configs.google(), null, controllers.config.save)
}
// CONFIGS - OIDC
getOIDConfigCookie(configId) {
const token = jwt.sign(configId, env.JWT_SECRET)
return this.cookieHeader([[`${Cookies.OIDC_CONFIG}=${token}`]])
@ -185,75 +196,27 @@ class TestConfiguration {
async saveOIDCConfig() {
await this.deleteConfig(Configs.OIDC)
const config = {
type: Configs.OIDC,
config: {
configs: [
{
configUrl: "http://someconfigurl",
clientID: "clientId",
clientSecret: "clientSecret",
logo: "Microsoft",
name: "Active Directory",
uuid: newid(),
},
],
},
}
const config = structures.configs.oidc()
await this._req(config, null, controllers.config.save)
return config
}
// CONFIGS - SMTP
async saveSmtpConfig() {
await this.deleteConfig(Configs.SMTP)
await this._req(
{
type: Configs.SMTP,
config: {
port: 12345,
host: "smtptesthost.com",
from: "testfrom@test.com",
subject: "Hello!",
},
},
null,
controllers.config.save
)
await this._req(structures.configs.smtp(), null, controllers.config.save)
}
async saveEtherealSmtpConfig() {
await this.deleteConfig(Configs.SMTP)
await this._req(
{
type: Configs.SMTP,
config: {
port: 587,
host: "smtp.ethereal.email",
secure: false,
auth: {
user: "don.bahringer@ethereal.email",
pass: "yCKSH8rWyUPbnhGYk9",
},
connectionTimeout: 1000, // must be less than the jest default of 5000
},
},
structures.configs.smtpEthereal(),
null,
controllers.config.save
)
}
async saveAdminUser() {
await this._req(
{
email: "testuser@test.com",
password: "test@test.com",
tenantId: TENANT_ID,
},
null,
controllers.users.adminUser
)
}
}
module.exports = TestConfiguration

7
packages/worker/src/tests/controllers.js

@ -0,0 +1,7 @@
module.exports = {
email: require("../api/controllers/global/email"),
workspaces: require("../api/controllers/global/workspaces"),
config: require("../api/controllers/global/configs"),
templates: require("../api/controllers/global/templates"),
users: require("../api/controllers/global/users"),
}

12
packages/worker/src/tests/index.js

@ -0,0 +1,12 @@
const TestConfiguration = require("./TestConfiguration")
const structures = require("./structures")
const mocks = require("./mocks")
const config = new TestConfiguration()
const request = config.getRequest()
module.exports = {
structures,
mocks,
config,
request,
}

0
packages/worker/src/api/routes/tests/utilities/mocks/core.js → packages/worker/src/tests/mocks/core.js

10
packages/worker/src/tests/mocks/email.js

@ -0,0 +1,10 @@
exports.mock = () => {
// mock the email system
const sendMailMock = jest.fn()
const nodemailer = require("nodemailer")
nodemailer.createTransport.mockReturnValue({
sendMail: sendMailMock,
verify: jest.fn(),
})
return sendMailMock
}

6
packages/worker/src/tests/mocks/index.js

@ -0,0 +1,6 @@
require("./core")
const email = require("./email")
module.exports = {
email,
}

76
packages/worker/src/tests/structures/configs.js

@ -0,0 +1,76 @@
const { Configs, LOGO_URL } = require("../../constants")
const { utils } = require("@budibase/backend-core")
exports.oidc = conf => {
return {
type: Configs.OIDC,
config: {
configs: [
{
configUrl: "http://someconfigurl",
clientID: "clientId",
clientSecret: "clientSecret",
logo: "Microsoft",
name: "Active Directory",
uuid: utils.newid(),
activated: true,
...conf,
},
],
},
}
}
exports.google = conf => {
return {
type: Configs.GOOGLE,
config: {
clientID: "clientId",
clientSecret: "clientSecret",
activated: true,
...conf,
},
}
}
exports.smtp = conf => {
return {
type: Configs.SMTP,
config: {
port: 12345,
host: "smtptesthost.com",
from: "testfrom@test.com",
subject: "Hello!",
secure: false,
...conf,
},
}
}
exports.smtpEthereal = () => {
return {
type: Configs.SMTP,
config: {
port: 587,
host: "smtp.ethereal.email",
secure: false,
auth: {
user: "don.bahringer@ethereal.email",
pass: "yCKSH8rWyUPbnhGYk9",
},
connectionTimeout: 1000, // must be less than the jest default of 5000
},
}
}
exports.settings = conf => {
return {
type: Configs.SETTINGS,
config: {
platformUrl: "http://localhost:10000",
logoUrl: LOGO_URL,
company: "Budibase",
...conf,
},
}
}

10
packages/worker/src/tests/structures/index.js

@ -0,0 +1,10 @@
const configs = require("./configs")
const TENANT_ID = "default"
const CSRF_TOKEN = "e3727778-7af0-4226-b5eb-f43cbe60a306"
module.exports = {
configs,
TENANT_ID,
CSRF_TOKEN,
}
Loading…
Cancel
Save