1
0
Fork 0
mirror of synced 2024-07-06 15:00:49 +12:00

Tests complete + backwards compatibility for deployment

This commit is contained in:
Rory Powell 2022-03-22 00:23:22 +00:00
parent 715d42d3e6
commit 0a4b1eb552
43 changed files with 294 additions and 5109 deletions

View file

@ -98,10 +98,6 @@ spec:
value: http://worker-service:{{ .Values.services.worker.port }}
- name: PLATFORM_URL
value: {{ .Values.globals.platformUrl | quote }}
- name: USE_QUOTAS
value: {{ .Values.globals.useQuotas | quote }}
- name: EXCLUDE_QUOTAS_TENANTS
value: {{ .Values.globals.excludeQuotasTenants | quote }}
- name: ACCOUNT_PORTAL_URL
value: {{ .Values.globals.accountPortalUrl | quote }}
- name: ACCOUNT_PORTAL_API_KEY

View file

@ -93,15 +93,13 @@ globals:
logLevel: info
selfHosted: "1" # set to 0 for budibase cloud environment, set to 1 for self-hosted setup
multiTenancy: "0" # set to 0 to disable multiple orgs, set to 1 to enable multiple orgs
useQuotas: "0"
excludeQuotasTenants: "" # comma seperated list of tenants to exclude from quotas
accountPortalUrl: ""
accountPortalApiKey: ""
cookieDomain: ""
platformUrl: ""
httpMigrations: "0"
google:
clientId: ""
clientId: ""
secret: ""
createSecrets: true # creates an internal API key, JWT secrets and redis password for you

View file

@ -56,7 +56,8 @@ exports.createApiKeyView = async () => {
await db.put(designDoc)
}
exports.createUserBuildersView = async db => {
exports.createUserBuildersView = async () => {
const db = getGlobalDB()
let designDoc
try {
designDoc = await db.get("_design/database")
@ -82,6 +83,7 @@ exports.queryGlobalView = async (viewName, params, db = null) => {
const CreateFuncByName = {
[ViewNames.USER_BY_EMAIL]: exports.createUserEmailView,
[ViewNames.BY_API_KEY]: exports.createApiKeyView,
[ViewNames.USER_BY_BUILDERS]: exports.createUserBuildersView,
}
// can pass DB in if working with something specific
if (!db) {

View file

@ -28,8 +28,7 @@ module.exports = {
SELF_HOSTED: !!parseInt(process.env.SELF_HOSTED),
COOKIE_DOMAIN: process.env.COOKIE_DOMAIN,
PLATFORM_URL: process.env.PLATFORM_URL,
USE_QUOTAS: process.env.USE_QUOTAS,
EXCLUDE_QUOTAS_TENANTS: process.env.EXCLUDE_QUOTAS_TENANTS,
TENANT_FEATURE_FLAGS: process.env.TENANT_FEATURE_FLAGS,
isTest,
_set(key, value) {
process.env[key] = value

View file

@ -0,0 +1,52 @@
const env = require("../environment")
const tenancy = require("../tenancy")
/**
* Read the TENANT_FEATURE_FLAGS env var and return an array of features flags for each tenant.
* The env var is formatted as:
* tenant1:feature1:feature2,tenant2:feature1
*/
const getFeatureFlags = () => {
if (!env.TENANT_FEATURE_FLAGS) {
return
}
const tenantFeatureFlags = {}
env.TENANT_FEATURE_FLAGS.split(",").forEach(tenantToFeatures => {
const [tenantId, ...features] = tenantToFeatures.split(":")
features.forEach(feature => {
if (!tenantFeatureFlags[tenantId]) {
tenantFeatureFlags[tenantId] = []
}
tenantFeatureFlags[tenantId].push(feature)
})
})
return tenantFeatureFlags
}
const TENANT_FEATURE_FLAGS = getFeatureFlags()
exports.isEnabled = featureFlag => {
const tenantId = tenancy.getTenantId()
return (
TENANT_FEATURE_FLAGS &&
TENANT_FEATURE_FLAGS[tenantId] &&
TENANT_FEATURE_FLAGS[tenantId].includes(featureFlag)
)
}
exports.getTenantFeatureFlags = tenantId => {
if (TENANT_FEATURE_FLAGS && TENANT_FEATURE_FLAGS[tenantId]) {
return TENANT_FEATURE_FLAGS[tenantId]
}
return []
}
exports.FeatureFlag = {
LICENSING: "LICENSING",
}

View file

@ -19,4 +19,5 @@ module.exports = {
env: require("./environment"),
accounts: require("./cloud/accounts"),
tenancy: require("./tenancy"),
featureFlags: require("./featureFlags"),
}

View file

@ -55,4 +55,4 @@ exports.strategyFactory = async function (config, callbackUrl, saveUserFn) {
}
}
// expose for testing
exports.authenticate = buildVerifyFn
exports.buildVerifyFn = buildVerifyFn

View file

@ -129,4 +129,4 @@ exports.strategyFactory = async function (config, callbackUrl, saveUserFn) {
}
// expose for testing
exports.authenticate = buildVerifyFn
exports.buildVerifyFn = buildVerifyFn

View file

@ -58,8 +58,10 @@ describe("google", () => {
it("delegates authentication to third party common", async () => {
const google = require("../google")
const mockSaveUserFn = jest.fn()
const authenticate = await google.buildVerifyFn(mockSaveUserFn)
await google.authenticate(
await authenticate(
data.accessToken,
data.refreshToken,
profile,
@ -69,7 +71,8 @@ describe("google", () => {
expect(authenticateThirdParty).toHaveBeenCalledWith(
user,
true,
mockDone)
mockDone,
mockSaveUserFn)
})
})
})

View file

@ -83,8 +83,10 @@ describe("oidc", () => {
async function doAuthenticate() {
const oidc = require("../oidc")
const mockSaveUserFn = jest.fn()
const authenticate = await oidc.buildVerifyFn(mockSaveUserFn)
await oidc.authenticate(
await authenticate(
issuer,
sub,
profile,

View file

@ -147,7 +147,9 @@ exports.getGlobalUserByEmail = async email => {
}
exports.getBuildersCount = async () => {
const builders = await queryGlobalView(ViewNames.BUILDERS)
const builders = await queryGlobalView(ViewNames.USER_BY_BUILDERS, {
include_docs: false,
})
return builders.total_rows
}

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -9,7 +9,7 @@
"url": "https://github.com/Budibase/budibase.git"
},
"scripts": {
"build": "rimraf dist/ && tsc && mv dist/src/* dist/ && rmdir dist/src/ && yarn postbuild",
"build": "rimraf dist/ && tsc -p tsconfig.build.json && mv dist/src/* dist/ && rmdir dist/src/ && yarn postbuild",
"postbuild": "copyfiles -u 1 src/**/*.svelte dist/ && copyfiles -u 1 src/**/*.hbs dist/ && copyfiles -u 1 src/**/*.json dist/",
"test": "jest --coverage --maxWorkers=2",
"test:watch": "jest --watch",
@ -81,6 +81,7 @@
"@koa/router": "8.0.0",
"@sendgrid/mail": "7.1.1",
"@sentry/node": "6.17.7",
"@types/koa__router": "^8.0.11",
"airtable": "0.10.1",
"arangojs": "7.2.0",
"aws-sdk": "^2.767.0",
@ -146,7 +147,7 @@
"@types/apidoc": "^0.50.0",
"@types/bull": "^3.15.1",
"@types/google-spreadsheet": "^3.1.5",
"@types/jest": "^26.0.23",
"@types/jest": "^27.4.1",
"@types/koa": "^2.13.3",
"@types/koa-router": "^7.4.2",
"@types/lodash": "^4.14.179",

View file

@ -389,7 +389,7 @@ const destroyApp = async (ctx: any) => {
const db = getAppDB()
const result = await db.destroy()
if (ctx.query.unpublish) {
if (ctx.query?.unpublish) {
await quotas.removePublishedApp()
} else {
await quotas.removeApp()
@ -408,7 +408,7 @@ const destroyApp = async (ctx: any) => {
}
const preDestroyApp = async (ctx: any) => {
const rows = await getUniqueRows([ctx.appId])
const rows = await getUniqueRows([ctx.params.appId])
ctx.rowCount = rows.length
}

View file

@ -1,9 +1,9 @@
const Router = require("@koa/router")
import Router from "@koa/router"
import * as controller from "../controllers/application"
import authorized from "../../middleware/authorized"
const { BUILDER } = require("@budibase/backend-core/permissions")
import { BUILDER } from "@budibase/backend-core/permissions"
const router = Router()
const router = new Router()
router
.post("/api/applications/:appId/sync", authorized(BUILDER), controller.sync)

View file

@ -127,4 +127,4 @@ applyRoutes(queryEndpoints, PermissionTypes.QUERY, "queryId")
// needs to be applied last for routing purposes, don't override other endpoints
applyRoutes(rowEndpoints, PermissionTypes.TABLE, "tableId", "rowId")
module.exports = publicRouter
export default publicRouter

View file

@ -1,4 +1,4 @@
const Router = require("@koa/router")
import Router from "@koa/router"
import * as rowController from "../controllers/row"
import authorized from "../../middleware/authorized"
import { paramResource, paramSubResource } from "../../middleware/resourceId"
@ -8,7 +8,7 @@ const {
} = require("@budibase/backend-core/permissions")
const { internalSearchValidator } = require("./utils/validators")
const router = Router()
const router = new Router()
router
/**

View file

@ -1,4 +1,4 @@
const Router = require("@koa/router")
import Router from "@koa/router"
import * as controller from "../controllers/static"
import { budibaseTempDir } from "../../utilities/budibaseDir"
import authorized from "../../middleware/authorized"
@ -10,10 +10,10 @@ import {
import * as env from "../../environment"
import { paramResource } from "../../middleware/resourceId"
const router = Router()
const router = new Router()
/* istanbul ignore next */
router.param("file", async (file, ctx, next) => {
router.param("file", async (file: any, ctx: any, next: any) => {
ctx.file = file && file.includes(".") ? file : "index.html"
if (!ctx.file.startsWith("budibase-client")) {
return next()

View file

@ -1,31 +1,38 @@
const rowController = require("../../../controllers/row")
const appController = require("../../../controllers/application")
const { AppStatus } = require("../../../../db/utils")
const { BUILTIN_ROLE_IDS } = require("@budibase/backend-core/roles")
const { TENANT_ID } = require("../../../../tests/utilities/structures")
const { getAppDB, doInAppContext } = require("@budibase/backend-core/context")
const env = require("../../../../environment")
import * as rowController from "../../../controllers/row"
import * as appController from "../../../controllers/application"
import { AppStatus } from "../../../../db/utils"
import { BUILTIN_ROLE_IDS } from "@budibase/backend-core/roles"
import { TENANT_ID } from "../../../../tests/utilities/structures"
import { getAppDB, doInAppContext } from "@budibase/backend-core/context"
import * as env from "../../../../environment"
function Request(appId, params) {
this.appId = appId
this.params = params
this.request = {}
class Request {
appId: any
params: any
request: any
body: any
constructor(appId: any, params: any) {
this.appId = appId
this.params = params
this.request = {}
}
}
function runRequest(appId, controlFunc, request) {
function runRequest(appId: any, controlFunc: any, request?: any) {
return doInAppContext(appId, async () => {
return controlFunc(request)
})
}
exports.getAllTableRows = async config => {
export const getAllTableRows = async (config: any) => {
const req = new Request(config.appId, { tableId: config.table._id })
await runRequest(config.appId, rowController.fetch, req)
return req.body
}
exports.clearAllApps = async (tenantId = TENANT_ID) => {
const req = { query: { status: AppStatus.DEV }, user: { tenantId } }
export const clearAllApps = async (tenantId = TENANT_ID) => {
const req: any = { query: { status: AppStatus.DEV }, user: { tenantId } }
await appController.fetch(req)
const apps = req.body
if (!apps || apps.length <= 0) {
@ -34,11 +41,11 @@ exports.clearAllApps = async (tenantId = TENANT_ID) => {
for (let app of apps) {
const { appId } = app
const req = new Request(null, { appId })
await runRequest(appId, appController.delete, req)
await runRequest(appId, appController.destroy, req)
}
}
exports.clearAllAutomations = async config => {
export const clearAllAutomations = async (config: any) => {
const automations = await config.getAllAutomations()
for (let auto of automations) {
await doInAppContext(config.appId, async () => {
@ -47,7 +54,12 @@ exports.clearAllAutomations = async config => {
}
}
exports.createRequest = (request, method, url, body) => {
export const createRequest = (
request: any,
method: any,
url: any,
body: any
) => {
let req
if (method === "POST") req = request.post(url).send(body)
@ -59,7 +71,12 @@ exports.createRequest = (request, method, url, body) => {
return req
}
exports.checkBuilderEndpoint = async ({ config, method, url, body }) => {
export const checkBuilderEndpoint = async ({
config,
method,
url,
body,
}: any) => {
const headers = await config.login({
userId: "us_fail",
builder: false,
@ -71,14 +88,14 @@ exports.checkBuilderEndpoint = async ({ config, method, url, body }) => {
.expect(403)
}
exports.checkPermissionsEndpoint = async ({
export const checkPermissionsEndpoint = async ({
config,
method,
url,
body,
passRole,
failRole,
}) => {
}: any) => {
const passHeader = await config.login({
roleId: passRole,
prodApp: true,
@ -106,11 +123,11 @@ exports.checkPermissionsEndpoint = async ({
.expect(403)
}
exports.getDB = () => {
export const getDB = () => {
return getAppDB()
}
exports.testAutomation = async (config, automation) => {
export const testAutomation = async (config: any, automation: any) => {
return runRequest(automation.appId, async () => {
return await config.request
.post(`/api/automations/${automation._id}/test`)
@ -126,7 +143,7 @@ exports.testAutomation = async (config, automation) => {
})
}
exports.runInProd = async func => {
export const runInProd = async (func: any) => {
const nodeEnv = env.NODE_ENV
const workerId = env.JEST_WORKER_ID
env._set("NODE_ENV", "PRODUCTION")

View file

@ -1,4 +1,3 @@
import { quotas } from "@budibase/pro"
import { save } from "../../api/controllers/row"
import { cleanUpRow, getError } from "../automationUtils"
import { buildCtx } from "./utils"
@ -78,7 +77,7 @@ export async function run({ inputs, appId, emitter }: any) {
try {
inputs.row = await cleanUpRow(inputs.row.tableId, inputs.row)
await quotas.addRow(() => save(ctx))
await save(ctx)
return {
row: inputs.row,
response: ctx.body,

View file

@ -1,7 +1,6 @@
import { destroy } from "../../api/controllers/row"
import { buildCtx } from "./utils"
import { getError } from "../automationUtils"
import { quotas } from "@budibase/pro"
export const definition = {
description: "Delete a row from your database",
@ -74,7 +73,6 @@ export async function run({ inputs, appId, emitter }: any) {
try {
await destroy(ctx)
await quotas.removeRow()
return {
response: ctx.body,
row: ctx.row,

View file

@ -1,4 +1,3 @@
jest.mock("../../utilities/usageQuota")
jest.mock("../../threads/automation")
jest.mock("../../utilities/redis", () => ({
init: jest.fn(),

View file

@ -1,10 +1,8 @@
jest.mock("../../utilities/usageQuota")
const usageQuota = require("../../utilities/usageQuota")
const setup = require("./utilities")
import * as setup from "./utilities"
describe("test the create row action", () => {
let table, row
let table: any
let row: any
let config = setup.getConfig()
beforeEach(async () => {
@ -36,20 +34,11 @@ describe("test the create row action", () => {
row: {
tableId: "invalid",
invalid: "invalid",
}
},
})
expect(res.success).toEqual(false)
})
it("check usage quota attempts", async () => {
await setup.runInProd(async () => {
await setup.runStep(setup.actions.CREATE_ROW.stepId, {
row
})
expect(usageQuota.update).toHaveBeenCalledWith("rows", 1)
})
})
it("should check invalid inputs return an error", async () => {
const res = await setup.runStep(setup.actions.CREATE_ROW.stepId, {})
expect(res.success).toEqual(false)

View file

@ -1,10 +1,9 @@
jest.mock("../../utilities/usageQuota")
const usageQuota = require("../../utilities/usageQuota")
const setup = require("./utilities")
describe("test the delete row action", () => {
let table, row, inputs
let table: any
let row: any
let inputs: any
let config = setup.getConfig()
beforeEach(async () => {
@ -37,7 +36,6 @@ describe("test the delete row action", () => {
it("check usage quota attempts", async () => {
await setup.runInProd(async () => {
await setup.runStep(setup.actions.DELETE_ROW.stepId, inputs)
expect(usageQuota.update).toHaveBeenCalledWith("rows", -1)
})
})

View file

@ -18,7 +18,6 @@ exports.afterAll = () => {
exports.runInProd = async fn => {
env._set("NODE_ENV", "production")
env._set("USE_QUOTAS", 1)
let error
try {
await fn()
@ -26,7 +25,6 @@ exports.runInProd = async fn => {
error = err
}
env._set("NODE_ENV", "jest")
env._set("USE_QUOTAS", null)
if (error) {
throw error
}

View file

@ -38,8 +38,6 @@ module.exports = {
ENABLE_ANALYTICS: process.env.ENABLE_ANALYTICS,
MINIO_ACCESS_KEY: process.env.MINIO_ACCESS_KEY,
MINIO_SECRET_KEY: process.env.MINIO_SECRET_KEY,
USE_QUOTAS: process.env.USE_QUOTAS,
EXCLUDE_QUOTAS_TENANTS: process.env.EXCLUDE_QUOTAS_TENANTS,
REDIS_URL: process.env.REDIS_URL,
REDIS_PASSWORD: process.env.REDIS_PASSWORD,
INTERNAL_API_KEY: process.env.INTERNAL_API_KEY,

View file

@ -1,8 +1,8 @@
const {
import {
getUserRoleHierarchy,
getRequiredResourceRole,
BUILTIN_ROLE_IDS,
} = require("@budibase/backend-core/roles")
} from "@budibase/backend-core/roles"
const {
PermissionTypes,
doesHaveBasePermission,
@ -12,7 +12,7 @@ const { isWebhookEndpoint } = require("./utils")
const { buildCsrfMiddleware } = require("@budibase/backend-core/auth")
const { getAppId } = require("@budibase/backend-core/context")
function hasResource(ctx) {
function hasResource(ctx: any) {
return ctx.resourceId != null
}
@ -24,7 +24,12 @@ const csrf = buildCsrfMiddleware()
* - Builders can access all resources.
* - Otherwise the user must have the required role.
*/
const checkAuthorized = async (ctx, resourceRoles, permType, permLevel) => {
const checkAuthorized = async (
ctx: any,
resourceRoles: any,
permType: any,
permLevel: any
) => {
// check if this is a builder api and the user is not a builder
const isBuilder = ctx.user && ctx.user.builder && ctx.user.builder.global
const isBuilderApi = permType === PermissionTypes.BUILDER
@ -39,10 +44,10 @@ const checkAuthorized = async (ctx, resourceRoles, permType, permLevel) => {
}
const checkAuthorizedResource = async (
ctx,
resourceRoles,
permType,
permLevel
ctx: any,
resourceRoles: any,
permType: any,
permLevel: any
) => {
// get the user's roles
const roleId = ctx.roleId || BUILTIN_ROLE_IDS.PUBLIC
@ -53,7 +58,9 @@ const checkAuthorizedResource = async (
// check if the user has the required role
if (resourceRoles.length > 0) {
// deny access if the user doesn't have the required resource role
const found = userRoles.find(role => resourceRoles.indexOf(role._id) !== -1)
const found = userRoles.find(
(role: any) => resourceRoles.indexOf(role._id) !== -1
)
if (!found) {
ctx.throw(403, permError)
}
@ -63,9 +70,8 @@ const checkAuthorizedResource = async (
}
}
module.exports =
(permType, permLevel = null) =>
async (ctx, next) => {
export = (permType: any, permLevel: any = null) =>
async (ctx: any, next: any) => {
// webhooks don't need authentication, each webhook unique
// also internal requests (between services) don't need authorized
if (isWebhookEndpoint(ctx) || ctx.internal) {
@ -81,7 +87,7 @@ module.exports =
await builderMiddleware(ctx, permType)
// get the resource roles
let resourceRoles = []
let resourceRoles: any = []
const appId = getAppId()
if (appId && hasResource(ctx)) {
resourceRoles = await getRequiredResourceRole(permLevel, ctx)

View file

@ -1,134 +0,0 @@
jest.mock("../../db")
jest.mock("../../utilities/usageQuota")
jest.mock("@budibase/backend-core/tenancy", () => ({
getTenantId: () => "testing123"
}))
const usageQuotaMiddleware = require("../usageQuota")
const usageQuota = require("../../utilities/usageQuota")
const CouchDB = require("../../db")
const env = require("../../environment")
class TestConfiguration {
constructor() {
this.throw = jest.fn()
this.next = jest.fn()
this.middleware = usageQuotaMiddleware
this.ctx = {
throw: this.throw,
next: this.next,
appId: "test",
request: {
body: {}
},
req: {
method: "POST",
url: "/applications"
}
}
usageQuota.useQuotas = () => true
}
executeMiddleware() {
return this.middleware(this.ctx, this.next)
}
setProd(bool) {
if (bool) {
env.isDev = () => false
env.isProd = () => true
this.ctx.user = { tenantId: "test" }
} else {
env.isDev = () => true
env.isProd = () => false
}
}
setMethod(method) {
this.ctx.req.method = method
}
setUrl(url) {
this.ctx.req.url = url
}
setBody(body) {
this.ctx.request.body = body
}
setFiles(files) {
this.ctx.request.files = { file: files }
}
}
describe("usageQuota middleware", () => {
let config
beforeEach(() => {
config = new TestConfiguration()
})
it("skips the middleware if there is no usage property or method", async () => {
await config.executeMiddleware()
expect(config.next).toHaveBeenCalled()
})
it("passes through to next middleware if document already exists", async () => {
config.setProd(true)
config.setBody({
_id: "test",
_rev: "test",
})
CouchDB.mockImplementationOnce(() => ({
get: async () => true
}))
await config.executeMiddleware()
expect(config.next).toHaveBeenCalled()
})
it("throws if request has _id, but the document no longer exists", async () => {
config.setBody({
_id: "123",
_rev: "test",
})
config.setProd(true)
CouchDB.mockImplementationOnce(() => ({
get: async () => {
throw new Error()
}
}))
await config.executeMiddleware()
expect(config.throw).toHaveBeenCalledWith(404, `${config.ctx.request.body._id} does not exist`)
})
it("calculates and persists the correct usage quota for the relevant action", async () => {
config.setUrl("/rows")
await config.executeMiddleware()
expect(usageQuota.update).toHaveBeenCalledWith("rows", 1)
expect(config.next).toHaveBeenCalled()
})
// it("calculates the correct file size from a file upload call and adds it to quota", async () => {
// config.setUrl("/upload")
// config.setProd(true)
// config.setFiles([
// {
// size: 100
// },
// {
// size: 10000
// },
// ])
// await config.executeMiddleware()
// expect(usageQuota.update).toHaveBeenCalledWith("storage", 10100)
// expect(config.next).toHaveBeenCalled()
// })
})

View file

@ -1,4 +1,3 @@
const env = require("../../../environment")
const TestConfig = require("../../../tests/utilities/TestConfiguration")
const syncApps = jest.fn()
@ -14,7 +13,6 @@ describe("run", () => {
beforeEach(async () => {
await config.init()
env._set("USE_QUOTAS", 1)
})
afterAll(config.end)

View file

@ -1,37 +0,0 @@
const { getGlobalDB } = require("@budibase/backend-core/tenancy")
const TestConfig = require("../../../../tests/utilities/TestConfiguration")
const { getUsageQuotaDoc, update, Properties } = require("../../../../utilities/usageQuota")
const syncApps = require("../syncApps")
const env = require("../../../../environment")
describe("syncApps", () => {
let config = new TestConfig(false)
beforeEach(async () => {
await config.init()
env._set("USE_QUOTAS", 1)
})
afterAll(config.end)
it("runs successfully", async () => {
// create the usage quota doc and mock usages
const db = getGlobalDB()
await getUsageQuotaDoc(db)
await update(Properties.APPS, 3)
let usageDoc = await getUsageQuotaDoc(db)
expect(usageDoc.usageQuota.apps).toEqual(3)
// create an extra app to test the migration
await config.createApp("quota-test")
// migrate
await syncApps.run()
// assert the migration worked
usageDoc = await getUsageQuotaDoc(db)
expect(usageDoc.usageQuota.apps).toEqual(2)
})
})

View file

@ -0,0 +1,32 @@
import TestConfig from "../../../../tests/utilities/TestConfiguration"
import * as syncApps from "../syncApps"
import { quotas, QuotaUsageType, StaticQuotaName } from "@budibase/pro"
describe("syncApps", () => {
let config = new TestConfig(false)
beforeEach(async () => {
await config.init()
})
afterAll(config.end)
it("runs successfully", async () => {
// create the usage quota doc and mock usages
await quotas.getQuotaUsage()
await quotas.setUsage(3, StaticQuotaName.APPS, QuotaUsageType.STATIC)
let usageDoc = await quotas.getQuotaUsage()
expect(usageDoc.usageQuota.apps).toEqual(3)
// create an extra app to test the migration
await config.createApp("quota-test")
// migrate
await syncApps.run()
// assert the migration worked
usageDoc = await quotas.getQuotaUsage()
expect(usageDoc.usageQuota.apps).toEqual(2)
})
})

View file

@ -1,43 +0,0 @@
const { getGlobalDB } = require("@budibase/backend-core/tenancy")
const TestConfig = require("../../../../tests/utilities/TestConfiguration")
const { getUsageQuotaDoc, update, Properties } = require("../../../../utilities/usageQuota")
const syncRows = require("../syncRows")
const env = require("../../../../environment")
describe("syncRows", () => {
let config = new TestConfig(false)
beforeEach(async () => {
await config.init()
env._set("USE_QUOTAS", 1)
})
afterAll(config.end)
it("runs successfully", async () => {
// create the usage quota doc and mock usages
const db = getGlobalDB()
await getUsageQuotaDoc(db)
await update(Properties.ROW, 300)
let usageDoc = await getUsageQuotaDoc(db)
expect(usageDoc.usageQuota.rows).toEqual(300)
// app 1
await config.createTable()
await config.createRow()
// app 2
await config.createApp("second-app")
await config.createTable()
await config.createRow()
await config.createRow()
// migrate
await syncRows.run()
// assert the migration worked
usageDoc = await getUsageQuotaDoc(db)
expect(usageDoc.usageQuota.rows).toEqual(3)
})
})

View file

@ -0,0 +1,38 @@
import TestConfig from "../../../../tests/utilities/TestConfiguration"
import * as syncRows from "../syncRows"
import { quotas, QuotaUsageType, StaticQuotaName } from "@budibase/pro"
describe("syncRows", () => {
let config = new TestConfig(false)
beforeEach(async () => {
await config.init()
})
afterAll(config.end)
it("runs successfully", async () => {
// create the usage quota doc and mock usages
await quotas.getQuotaUsage()
await quotas.setUsage(300, StaticQuotaName.ROWS, QuotaUsageType.STATIC)
let usageDoc = await quotas.getQuotaUsage()
expect(usageDoc.usageQuota.rows).toEqual(300)
// app 1
await config.createTable()
await config.createRow()
// app 2
await config.createApp("second-app")
await config.createTable()
await config.createRow()
await config.createRow()
// migrate
await syncRows.run()
// assert the migration worked
usageDoc = await quotas.getQuotaUsage()
expect(usageDoc.usageQuota.rows).toEqual(3)
})
})

View file

@ -3,3 +3,5 @@ declare module "@budibase/backend-core/tenancy"
declare module "@budibase/backend-core/db"
declare module "@budibase/backend-core/context"
declare module "@budibase/backend-core/cache"
declare module "@budibase/backend-core/permissions"
declare module "@budibase/backend-core/roles"

View file

@ -1,72 +0,0 @@
const getTenantId = jest.fn()
jest.mock("@budibase/backend-core/tenancy", () => ({
getTenantId
}))
const usageQuota = require("../../usageQuota")
const env = require("../../../environment")
class TestConfiguration {
constructor() {
this.enableQuotas()
}
enableQuotas = () => {
env.USE_QUOTAS = 1
}
disableQuotas = () => {
env.USE_QUOTAS = null
}
setTenantId = (tenantId) => {
getTenantId.mockReturnValue(tenantId)
}
setExcludedTenants = (tenants) => {
env.EXCLUDE_QUOTAS_TENANTS = tenants
}
reset = () => {
this.disableQuotas()
this.setExcludedTenants(null)
}
}
describe("usageQuota", () => {
let config
beforeEach(() => {
config = new TestConfiguration()
})
afterEach(() => {
config.reset()
})
describe("useQuotas", () => {
it("works when no settings have been provided", () => {
config.reset()
expect(usageQuota.useQuotas()).toBe(false)
})
it("honours USE_QUOTAS setting", () => {
config.disableQuotas()
expect(usageQuota.useQuotas()).toBe(false)
config.enableQuotas()
expect(usageQuota.useQuotas()).toBe(true)
})
it("honours EXCLUDE_QUOTAS_TENANTS setting", () => {
config.setTenantId("test")
// tenantId is in the list
config.setExcludedTenants("test, test2, test2")
expect(usageQuota.useQuotas()).toBe(false)
config.setExcludedTenants("test,test2,test2")
expect(usageQuota.useQuotas()).toBe(false)
// tenantId is not in the list
config.setTenantId("other")
expect(usageQuota.useQuotas()).toBe(true)
})
})
})

View file

@ -0,0 +1,10 @@
{
// Used for building with tsc
"extends": "./tsconfig.json",
"exclude": [
"node_modules",
"**/*.json",
"**/*.spec.js",
"**/*.spec.ts"
]
}

View file

@ -19,7 +19,7 @@
"exclude": [
"node_modules",
"**/*.json",
"**/*.spec.ts",
"**/*.spec.js"
"**/*.spec.js",
// "**/*.spec.ts" // don't exclude spec.ts files for editor support
]
}

View file

@ -2531,13 +2531,13 @@
dependencies:
"@types/istanbul-lib-report" "*"
"@types/jest@^26.0.23":
version "26.0.24"
resolved "https://registry.yarnpkg.com/@types/jest/-/jest-26.0.24.tgz#943d11976b16739185913a1936e0de0c4a7d595a"
integrity sha512-E/X5Vib8BWqZNRlDxj9vYXhsDwPYbPINqKF9BsnSoon4RQ0D9moEuLD8txgyypFLH7J4+Lho9Nr/c8H0Fi+17w==
"@types/jest@^27.4.1":
version "27.4.1"
resolved "https://registry.yarnpkg.com/@types/jest/-/jest-27.4.1.tgz#185cbe2926eaaf9662d340cc02e548ce9e11ab6d"
integrity sha512-23iPJADSmicDVrWk+HT58LMJtzLAnB2AgIzplQuq/bSrGaxCrlvRFjGbXmamnnk/mAmCdLStiGqggu28ocUyiw==
dependencies:
jest-diff "^26.0.0"
pretty-format "^26.0.0"
jest-matcher-utils "^27.0.0"
pretty-format "^27.0.0"
"@types/json-schema@*", "@types/json-schema@^7.0.6", "@types/json-schema@^7.0.8":
version "7.0.9"
@ -2577,6 +2577,13 @@
"@types/koa-compose" "*"
"@types/node" "*"
"@types/koa__router@^8.0.11":
version "8.0.11"
resolved "https://registry.yarnpkg.com/@types/koa__router/-/koa__router-8.0.11.tgz#d7b37e6db934fc072ea1baa2ab92bc8ac4564f3e"
integrity sha512-WXgKWpBsbS14kzmzD9LeFapOIa678h7zvUHxDwXwSx4ETKXhXLVUAToX6jZ/U7EihM7qwyD9W/BZvB0MRu7MTQ==
dependencies:
"@types/koa" "*"
"@types/lodash@^4.14.179":
version "4.14.180"
resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.14.180.tgz#4ab7c9ddfc92ec4a887886483bc14c79fb380670"
@ -7375,7 +7382,7 @@ jest-diff@^24.9.0:
jest-get-type "^24.9.0"
pretty-format "^24.9.0"
jest-diff@^26.0.0, jest-diff@^26.6.2:
jest-diff@^26.6.2:
version "26.6.2"
resolved "https://registry.yarnpkg.com/jest-diff/-/jest-diff-26.6.2.tgz#1aa7468b52c3a68d7d5c5fdcdfcd5e49bd164394"
integrity sha512-6m+9Z3Gv9wN0WFVasqjCL/06+EFCMTqDEUl/b87HYK2rAPTyfz4ZIuSlPhY51PIQRWx5TaxeF1qmXKe9gfN3sA==
@ -7614,7 +7621,7 @@ jest-matcher-utils@^26.6.2:
jest-get-type "^26.3.0"
pretty-format "^26.6.2"
jest-matcher-utils@^27.5.1:
jest-matcher-utils@^27.0.0, jest-matcher-utils@^27.5.1:
version "27.5.1"
resolved "https://registry.yarnpkg.com/jest-matcher-utils/-/jest-matcher-utils-27.5.1.tgz#9c0cdbda8245bc22d2331729d1091308b40cf8ab"
integrity sha512-z2uTx/T6LBaCoNWNFWwChLBKYxTMcGBRjAt+2SbP929/Fflb9aa5LGma654Rz8z9HLxsrUaYzxE9T/EFIL/PAw==
@ -10413,7 +10420,7 @@ pretty-format@^24.9.0:
ansi-styles "^3.2.0"
react-is "^16.8.4"
pretty-format@^26.0.0, pretty-format@^26.6.2:
pretty-format@^26.6.2:
version "26.6.2"
resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-26.6.2.tgz#e35c2705f14cb7fe2fe94fa078345b444120fc93"
integrity sha512-7AeGuCYNGmycyQbCqd/3PWH4eOoX/OiCa0uphp57NVTeAGdJGaAliecxwBDHYQCIvrW7aDBZCYeNTP/WX69mkg==
@ -10423,7 +10430,7 @@ pretty-format@^26.0.0, pretty-format@^26.6.2:
ansi-styles "^4.0.0"
react-is "^17.0.1"
pretty-format@^27.5.1:
pretty-format@^27.0.0, pretty-format@^27.5.1:
version "27.5.1"
resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-27.5.1.tgz#2181879fdea51a7a5851fb39d920faa63f01d88e"
integrity sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ==

View file

@ -75,7 +75,8 @@ describe("/api/global/auth", () => {
afterEach(() => {
expect(strategyFactory).toBeCalledWith(
chosenConfig,
`http://localhost:10000/api/global/auth/${TENANT_ID}/oidc/callback`
`http://localhost:10000/api/global/auth/${TENANT_ID}/oidc/callback`,
expect.any(Function)
)
})

File diff suppressed because it is too large Load diff