Add feature and quota overrides to qa core for cloud tests (#10417)

* Fix qa core suite

* Update package.json

* Lint
This commit is contained in:
Rory Powell 2023-04-25 20:36:27 +01:00 committed by GitHub
parent 71fcfa1a02
commit aee52af7f0
16 changed files with 172 additions and 49 deletions

View File

@ -47,6 +47,8 @@
"passport-jwt": "4.0.0",
"passport-local": "1.0.0",
"passport-oauth2-refresh": "^2.1.0",
"pino": "8.11.0",
"pino-http": "8.3.3",
"posthog-node": "1.3.0",
"pouchdb": "7.3.0",
"pouchdb-find": "7.2.2",
@ -80,7 +82,6 @@
"jest-serial-runner": "^1.2.1",
"koa": "2.13.4",
"nodemon": "2.0.16",
"pino": "7.11.0",
"pino-pretty": "10.0.0",
"pouchdb-adapter-memory": "7.2.2",
"timekeeper": "2.2.0",

View File

@ -1,4 +1,5 @@
import { QuotaUsage } from "../../documents"
import { LicenseOverrides, QuotaUsage } from "../../documents"
import { PlanType } from "../../sdk"
export interface GetLicenseRequest {
// All fields should be optional to cater for
@ -20,3 +21,8 @@ export interface QuotaTriggeredRequest {
export interface LicenseActivateRequest {
installVersion?: string
}
export interface UpdateLicenseRequest {
planType?: PlanType
overrides?: LicenseOverrides
}

View File

@ -1,4 +1,5 @@
import { Feature, Hosting, License, PlanType, Quotas } from "../../sdk"
import { DeepPartial } from "../../shared"
import { QuotaUsage } from "../global"
export interface CreateAccount {
@ -25,7 +26,7 @@ export const isCreatePasswordAccount = (
export interface LicenseOverrides {
features?: Feature[]
quotas?: Quotas
quotas?: DeepPartial<Quotas>
}
export interface Account extends CreateAccount {

View File

@ -0,0 +1 @@
export * from "./typeUtils"

View File

@ -0,0 +1,3 @@
export type DeepPartial<T> = {
[P in keyof T]?: T[P] extends object ? DeepPartial<T[P]> : T[P]
}

View File

@ -1,7 +1,7 @@
import { Account, AccountMetadata } from "@budibase/types"
import { Account, AccountMetadata, Ctx } from "@budibase/types"
import * as accounts from "../../../sdk/accounts"
export const save = async (ctx: any) => {
export const save = async (ctx: Ctx<Account, AccountMetadata>) => {
const account = ctx.request.body as Account
let metadata: AccountMetadata = {
_id: accounts.metadata.formatAccountMetadataId(account.accountId),

View File

@ -8,8 +8,10 @@ function init() {
const envFileJson = {
BUDIBASE_URL: "http://localhost:10000",
ACCOUNT_PORTAL_URL: "http://localhost:10001",
ACCOUNT_PORTAL_API_KEY: "budibase",
BB_ADMIN_USER_EMAIL: "admin",
BB_ADMIN_USER_PASSWORD: "admin",
LOG_LEVEL: "info",
}
let envFile = ""
Object.keys(envFileJson).forEach(key => {

View File

@ -8,6 +8,7 @@ interface ApiOptions {
method?: APIMethod
body?: object
headers?: HeadersInit | undefined
internal?: boolean
}
export default class AccountInternalAPIClient {
@ -18,6 +19,9 @@ export default class AccountInternalAPIClient {
if (!env.ACCOUNT_PORTAL_URL) {
throw new Error("Must set ACCOUNT_PORTAL_URL env var")
}
if (!env.ACCOUNT_PORTAL_API_KEY) {
throw new Error("Must set ACCOUNT_PORTAL_API_KEY env var")
}
this.host = `${env.ACCOUNT_PORTAL_URL}`
this.state = state
}
@ -39,6 +43,13 @@ export default class AccountInternalAPIClient {
credentials: "include",
}
if (options.internal) {
requestOptions.headers = {
...requestOptions.headers,
...{ "x-budibase-api-key": env.ACCOUNT_PORTAL_API_KEY },
}
}
// @ts-ignore
const response = await fetch(`${this.host}${url}`, requestOptions)
@ -50,15 +61,20 @@ export default class AccountInternalAPIClient {
body = await response.text()
}
const message = `${method} ${url} - ${response.status}
Response body: ${JSON.stringify(body)}
Request body: ${requestOptions.body}`
const data = {
request: requestOptions.body,
response: body,
}
const message = `${method} ${url} - ${response.status}`
if (response.status > 499) {
console.error(message)
console.error(message, data)
} else if (response.status >= 400) {
console.warn(message)
console.warn(message, data)
} else {
console.debug(message, data)
}
return [response, body]
}

View File

@ -1,4 +1,6 @@
import AccountInternalAPIClient from "../AccountInternalAPIClient"
import { Account, UpdateLicenseRequest } from "@budibase/types"
import { Response } from "node-fetch"
export default class LicenseAPI {
client: AccountInternalAPIClient
@ -6,4 +8,18 @@ export default class LicenseAPI {
constructor(client: AccountInternalAPIClient) {
this.client = client
}
async updateLicense(
accountId: string,
body: UpdateLicenseRequest
): Promise<[Response, Account]> {
const [response, json] = await this.client.put(
`/api/accounts/${accountId}/license`,
{
body,
internal: true,
}
)
return [response, json]
}
}

View File

@ -11,6 +11,7 @@ if (!LOADED) {
const env = {
BUDIBASE_URL: process.env.BUDIBASE_URL,
ACCOUNT_PORTAL_URL: process.env.ACCOUNT_PORTAL_URL,
ACCOUNT_PORTAL_API_KEY: process.env.ACCOUNT_PORTAL_API_KEY,
BB_ADMIN_USER_EMAIL: process.env.BB_ADMIN_USER_EMAIL,
BB_ADMIN_USER_PASSWORD: process.env.BB_ADMIN_USER_PASSWORD,
}

View File

@ -18,7 +18,6 @@ class BudibaseInternalAPIClient {
if (!env.BUDIBASE_URL) {
throw new Error("Must set BUDIBASE_URL env var")
}
this.host = `${env.ACCOUNT_PORTAL_URL}/api`
this.host = `${env.BUDIBASE_URL}/api`
this.state = state
}
@ -53,14 +52,18 @@ class BudibaseInternalAPIClient {
body = await response.text()
}
const message = `${method} ${url} - ${response.status}
Response body: ${JSON.stringify(body)}
Request body: ${requestOptions.body}`
const data = {
request: requestOptions.body,
response: body,
}
const message = `${method} ${url} - ${response.status}`
if (response.status > 499) {
console.error(message)
console.error(message, data)
} else if (response.status >= 400) {
console.warn(message)
console.warn(message, data)
} else {
console.debug(message, data)
}
return [response, body]

View File

@ -1,8 +1,8 @@
import { DEFAULT_TENANT_ID, logging } from "@budibase/backend-core"
import { AccountInternalAPI } from "../account-api"
import * as fixtures from "../internal-api/fixtures"
import { BudibaseInternalAPI } from "../internal-api"
import { DEFAULT_TENANT_ID, logging } from "@budibase/backend-core"
import { CreateAccountRequest } from "@budibase/types"
import { CreateAccountRequest, Feature } from "@budibase/types"
import env from "../environment"
import { APIRequestOpts } from "../types"
@ -22,10 +22,35 @@ async function createAccount() {
const account = fixtures.accounts.generateAccount()
await accountsApi.accounts.validateEmail(account.email, API_OPTS)
await accountsApi.accounts.validateTenantId(account.tenantId, API_OPTS)
await accountsApi.accounts.create(account, API_OPTS)
const [res, newAccount] = await accountsApi.accounts.create(account, API_OPTS)
await updateLicense(newAccount.accountId)
return account
}
const UNLIMITED = { value: -1 }
async function updateLicense(accountId: string) {
await accountsApi.licenses.updateLicense(accountId, {
overrides: {
// add all features
features: Object.values(Feature),
quotas: {
usage: {
monthly: {
automations: UNLIMITED,
},
static: {
rows: UNLIMITED,
users: UNLIMITED,
userGroups: UNLIMITED,
plugins: UNLIMITED,
},
},
},
},
})
}
async function loginAsAdmin() {
const username = env.BB_ADMIN_USER_EMAIL!
const password = env.BB_ADMIN_USER_PASSWORD!

View File

@ -1 +1,4 @@
import { logging } from "@budibase/backend-core"
logging.LOG_CONTEXT = false
jest.setTimeout(60000)

View File

@ -51,14 +51,18 @@ class BudibasePublicAPIClient {
body = await response.text()
}
const message = `${method} ${url} - ${response.status}
Response body: ${JSON.stringify(body)}
Request body: ${requestOptions.body}`
const data = {
request: requestOptions.body,
response: body,
}
const message = `${method} ${url} - ${response.status}`
if (response.status > 499) {
console.error(message)
console.error(message, data)
} else if (response.status >= 400) {
console.warn(message)
console.warn(message, data)
} else {
console.debug(message, data)
}
return [response, body]

View File

@ -3,9 +3,6 @@ import { AccountInternalAPI } from "../account-api"
import { CreateAppRequest, State } from "../types"
import * as fixtures from "../internal-api/fixtures"
// TEMP
import setup from "../jest/globalSetup"
export default class BudibaseTestConfiguration {
// apis
internalApi: BudibaseInternalAPI
@ -23,11 +20,6 @@ export default class BudibaseTestConfiguration {
// LIFECYCLE
async beforeAll() {
// TEMP - move back to single tenant when we integrate licensing with
// the test run - need to use multiple tenants in cloud to get around
// app limit restrictions
await setup()
// @ts-ignore
this.state.tenantId = global.qa.tenantId
// @ts-ignore

View File

@ -11129,7 +11129,7 @@ fast-levenshtein@^2.0.6, fast-levenshtein@~2.0.6:
resolved "https://registry.yarnpkg.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz#3d8a5c66883a16a30ca8643e851f19baa7797917"
integrity sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=
fast-redact@^3.0.0:
fast-redact@^3.0.0, fast-redact@^3.1.1:
version "3.1.2"
resolved "https://registry.yarnpkg.com/fast-redact/-/fast-redact-3.1.2.tgz#d58e69e9084ce9fa4c1a6fa98a3e1ecf5d7839aa"
integrity sha512-+0em+Iya9fKGfEQGcd62Yv6onjBmmhV1uh86XVfOU8VwAe6kaFdQCWI9s0/Nnugx5Vd9tdbZ7e6gE2tR9dzXdw==
@ -19203,7 +19203,7 @@ pinkie@^2.0.0:
resolved "https://registry.yarnpkg.com/pinkie/-/pinkie-2.0.4.tgz#72556b80cfa0d48a974e80e77248e80ed4f7f870"
integrity sha512-MnUuEycAemtSaeFSjXKW/aroV7akBbY+Sv+RkyqFjgAe73F+MR0TBWKBRDkmfWq/HiFmdavfZ1G7h4SPZXaCSg==
pino-abstract-transport@^1.0.0:
pino-abstract-transport@^1.0.0, pino-abstract-transport@v1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/pino-abstract-transport/-/pino-abstract-transport-1.0.0.tgz#cc0d6955fffcadb91b7b49ef220a6cc111d48bb3"
integrity sha512-c7vo5OpW4wIS42hUVcT5REsL8ZljsUfBjqV/e2sFxmFEFZiq1XLUp5EYLtuDH6PEHq9W1egWqRbnLUP5FuZmOA==
@ -19219,6 +19219,16 @@ pino-abstract-transport@v0.5.0:
duplexify "^4.1.2"
split2 "^4.0.0"
pino-http@8.3.3:
version "8.3.3"
resolved "https://registry.yarnpkg.com/pino-http/-/pino-http-8.3.3.tgz#2b140e734bfc6babe0df272a43bb8f36f2b525c0"
integrity sha512-p4umsNIXXVu95HD2C8wie/vXH7db5iGRpc+yj1/ZQ3sRtTQLXNjoS6Be5+eI+rQbqCRxen/7k/KSN+qiZubGDw==
dependencies:
get-caller-file "^2.0.5"
pino "^8.0.0"
pino-std-serializers "^6.0.0"
process-warning "^2.0.0"
pino-http@^6.5.0:
version "6.6.0"
resolved "https://registry.yarnpkg.com/pino-http/-/pino-http-6.6.0.tgz#d0a1deacada8c93327fdaa48f5bdc94bc43d3407"
@ -19264,7 +19274,42 @@ pino-std-serializers@^5.0.0:
resolved "https://registry.yarnpkg.com/pino-std-serializers/-/pino-std-serializers-5.6.0.tgz#31b141155d6520967c5ec72944d08fb45c490fd3"
integrity sha512-VdUXCw8gO+xhir7sFuoYSjTnzB+TMDGxhAC/ph3YS3sdHnXNdsK0wMtADNUltfeGkn2KDxEM21fnjF3RwXyC8A==
pino@7.11.0, pino@^7.5.0:
pino-std-serializers@^6.0.0:
version "6.2.0"
resolved "https://registry.yarnpkg.com/pino-std-serializers/-/pino-std-serializers-6.2.0.tgz#169048c0df3f61352fce56aeb7fb962f1b66ab43"
integrity sha512-IWgSzUL8X1w4BIWTwErRgtV8PyOGOOi60uqv0oKuS/fOA8Nco/OeI6lBuc4dyP8MMfdFwyHqTMcBIA7nDiqEqA==
pino@8.11.0, pino@^8.0.0:
version "8.11.0"
resolved "https://registry.yarnpkg.com/pino/-/pino-8.11.0.tgz#2a91f454106b13e708a66c74ebc1c2ab7ab38498"
integrity sha512-Z2eKSvlrl2rH8p5eveNUnTdd4AjJk8tAsLkHYZQKGHP4WTh2Gi1cOSOs3eWPqaj+niS3gj4UkoreoaWgF3ZWYg==
dependencies:
atomic-sleep "^1.0.0"
fast-redact "^3.1.1"
on-exit-leak-free "^2.1.0"
pino-abstract-transport v1.0.0
pino-std-serializers "^6.0.0"
process-warning "^2.0.0"
quick-format-unescaped "^4.0.3"
real-require "^0.2.0"
safe-stable-stringify "^2.3.1"
sonic-boom "^3.1.0"
thread-stream "^2.0.0"
pino@^6.11.2:
version "6.14.0"
resolved "https://registry.yarnpkg.com/pino/-/pino-6.14.0.tgz#b745ea87a99a6c4c9b374e4f29ca7910d4c69f78"
integrity sha512-iuhEDel3Z3hF9Jfe44DPXR8l07bhjuFY3GMHIXbjnY9XcafbyDDwl2sN2vw2GjMPf5Nkoe+OFao7ffn9SXaKDg==
dependencies:
fast-redact "^3.0.0"
fast-safe-stringify "^2.0.8"
flatstr "^1.0.12"
pino-std-serializers "^3.1.0"
process-warning "^1.0.0"
quick-format-unescaped "^4.0.3"
sonic-boom "^1.0.2"
pino@^7.5.0:
version "7.11.0"
resolved "https://registry.yarnpkg.com/pino/-/pino-7.11.0.tgz#0f0ea5c4683dc91388081d44bff10c83125066f6"
integrity sha512-dMACeu63HtRLmCG8VKdy4cShCPKaYDR4youZqoSWLxl5Gu99HUw8bw75thbPv9Nip+H+QYX8o3ZJbTdVZZ2TVg==
@ -19281,19 +19326,6 @@ pino@7.11.0, pino@^7.5.0:
sonic-boom "^2.2.1"
thread-stream "^0.15.1"
pino@^6.11.2:
version "6.14.0"
resolved "https://registry.yarnpkg.com/pino/-/pino-6.14.0.tgz#b745ea87a99a6c4c9b374e4f29ca7910d4c69f78"
integrity sha512-iuhEDel3Z3hF9Jfe44DPXR8l07bhjuFY3GMHIXbjnY9XcafbyDDwl2sN2vw2GjMPf5Nkoe+OFao7ffn9SXaKDg==
dependencies:
fast-redact "^3.0.0"
fast-safe-stringify "^2.0.8"
flatstr "^1.0.12"
pino-std-serializers "^3.1.0"
process-warning "^1.0.0"
quick-format-unescaped "^4.0.3"
sonic-boom "^1.0.2"
pirates@^4.0.1, pirates@^4.0.4:
version "4.0.5"
resolved "https://registry.yarnpkg.com/pirates/-/pirates-4.0.5.tgz#feec352ea5c3268fb23a37c702ab1699f35a5f3b"
@ -20169,6 +20201,11 @@ process-warning@^1.0.0:
resolved "https://registry.yarnpkg.com/process-warning/-/process-warning-1.0.0.tgz#980a0b25dc38cd6034181be4b7726d89066b4616"
integrity sha512-du4wfLyj4yCZq1VupnVSZmRsPJsNuxoDQFdCFHLaYiEbFBD7QE0a+I4D7hOxrVnh78QE/YipFAj9lXHiXocV+Q==
process-warning@^2.0.0:
version "2.2.0"
resolved "https://registry.yarnpkg.com/process-warning/-/process-warning-2.2.0.tgz#008ec76b579820a8e5c35d81960525ca64feb626"
integrity sha512-/1WZ8+VQjR6avWOgHeEPd7SDQmFQ1B5mC1eRXsCm5TarlNmx/wCsa5GEaxGm05BORRtyG/Ex/3xq3TuRvq57qg==
process@^0.11.10:
version "0.11.10"
resolved "https://registry.yarnpkg.com/process/-/process-0.11.10.tgz#7332300e840161bda3e69a1d1d91a7d4bc16f182"
@ -20772,6 +20809,11 @@ real-require@^0.1.0:
resolved "https://registry.yarnpkg.com/real-require/-/real-require-0.1.0.tgz#736ac214caa20632847b7ca8c1056a0767df9381"
integrity sha512-r/H9MzAWtrv8aSVjPCMFpDMl5q66GqtmmRkRjpHTsp4zBAa+snZyiQNlMONiUmEJcsnaw0wCauJ2GWODr/aFkg==
real-require@^0.2.0:
version "0.2.0"
resolved "https://registry.yarnpkg.com/real-require/-/real-require-0.2.0.tgz#209632dea1810be2ae063a6ac084fee7e33fba78"
integrity sha512-57frrGM/OCTLqLOAh0mhVA9VBMHd+9U7Zb2THMGdBUoZVOtGbJzjxsYGDJ3A9AYYCP4hn6y1TVbaOfzWtm5GFg==
recast@^0.10.1:
version "0.10.43"
resolved "https://registry.yarnpkg.com/recast/-/recast-0.10.43.tgz#b95d50f6d60761a5f6252e15d80678168491ce7f"
@ -22151,7 +22193,7 @@ sonic-boom@^2.2.1:
dependencies:
atomic-sleep "^1.0.0"
sonic-boom@^3.0.0:
sonic-boom@^3.0.0, sonic-boom@^3.1.0:
version "3.3.0"
resolved "https://registry.yarnpkg.com/sonic-boom/-/sonic-boom-3.3.0.tgz#cffab6dafee3b2bcb88d08d589394198bee1838c"
integrity sha512-LYxp34KlZ1a2Jb8ZQgFCK3niIHzibdwtwNUWKg0qQRzsDoJ3Gfgkf8KdBTFU3SkejDEIlWwnSnpVdOZIhFMl/g==
@ -23323,6 +23365,13 @@ thread-stream@^0.15.1:
dependencies:
real-require "^0.1.0"
thread-stream@^2.0.0:
version "2.3.0"
resolved "https://registry.yarnpkg.com/thread-stream/-/thread-stream-2.3.0.tgz#4fc07fb39eff32ae7bad803cb7dd9598349fed33"
integrity sha512-kaDqm1DET9pp3NXwR8382WHbnpXnRkN9xGN9dQt3B2+dmXiW8X1SOwmFOxAErEQ47ObhZ96J6yhZNXuyCOL7KA==
dependencies:
real-require "^0.2.0"
throat@^5.0.0:
version "5.0.0"
resolved "https://registry.yarnpkg.com/throat/-/throat-5.0.0.tgz#c5199235803aad18754a667d659b5e72ce16764b"