diff --git a/qa-core/src/account-api/api/AccountInternalAPIClient.ts b/qa-core/src/account-api/api/AccountInternalAPIClient.ts index 85807fd87a..b1e32cac1f 100644 --- a/qa-core/src/account-api/api/AccountInternalAPIClient.ts +++ b/qa-core/src/account-api/api/AccountInternalAPIClient.ts @@ -1,6 +1,8 @@ +import { Response } from "node-fetch" import env from "../../environment" import fetch, { HeadersInit } from "node-fetch" import { State } from "../../types" +import { Header } from "@budibase/backend-core" type APIMethod = "GET" | "POST" | "PUT" | "PATCH" | "DELETE" @@ -28,7 +30,7 @@ export default class AccountInternalAPIClient { apiCall = (method: APIMethod) => - async (url = "", options: ApiOptions = {}) => { + async (url = "", options: ApiOptions = {}): Promise<[Response, any]> => { const requestOptions = { method, body: JSON.stringify(options.body), @@ -46,7 +48,7 @@ export default class AccountInternalAPIClient { if (options.internal) { requestOptions.headers = { ...requestOptions.headers, - ...{ "x-budibase-api-key": env.ACCOUNT_PORTAL_API_KEY }, + ...{ [Header.API_KEY] : env.ACCOUNT_PORTAL_API_KEY }, } } diff --git a/qa-core/src/account-api/fixtures/accounts.ts b/qa-core/src/account-api/fixtures/accounts.ts index c6c6fa163f..6c52cb52b0 100644 --- a/qa-core/src/account-api/fixtures/accounts.ts +++ b/qa-core/src/account-api/fixtures/accounts.ts @@ -1,6 +1,7 @@ import { generator } from "../../shared" import { Hosting, CreateAccountRequest } from "@budibase/types" +// TODO: Refactor me to central location export const generateAccount = (): CreateAccountRequest => { const uuid = generator.guid() diff --git a/qa-core/src/account-api/tests/accounts/accounts.internal.spec.ts b/qa-core/src/account-api/tests/accounts/accounts.internal.spec.ts new file mode 100644 index 0000000000..47b0ffb12a --- /dev/null +++ b/qa-core/src/account-api/tests/accounts/accounts.internal.spec.ts @@ -0,0 +1,29 @@ +import TestConfiguration from "../../config/TestConfiguration" +import * as fixtures from "../../fixtures" +import { generator } from "../../../shared" + +describe("Account Internal Operations", () => { + const config = new TestConfiguration() + + beforeAll(async () => { + await config.beforeAll() + }) + + afterAll(async () => { + await config.afterAll() + }) + + it("performs account deletion", async () => { + // Deleting by unknown id doesn't work + const accountId = generator.string() + await config.api.accounts.delete(accountId, { status: 404 }) + + // Create new account + const [_, account] = await config.api.accounts.create({ + ...fixtures.accounts.generateAccount(), + }) + + // New account can be deleted + await config.api.accounts.delete(account.accountId) + }) +}) diff --git a/qa-core/src/account-api/tests/accounts/accounts.spec.ts b/qa-core/src/account-api/tests/accounts/accounts.spec.ts new file mode 100644 index 0000000000..e61e2089c2 --- /dev/null +++ b/qa-core/src/account-api/tests/accounts/accounts.spec.ts @@ -0,0 +1,80 @@ +import TestConfiguration from "../../config/TestConfiguration" +import * as fixtures from "../../fixtures" +import { generator } from "../../../shared" + +describe("accounts", () => { + const config = new TestConfiguration() + + beforeAll(async () => { + await config.beforeAll() + }) + + afterAll(async () => { + await config.afterAll() + }) + + it("performs signup and deletion flow", async () => { + await config.doInNewState(async () => { + const createAccountRequest = fixtures.accounts.generateAccount() + const email = createAccountRequest.email + const tenantId = createAccountRequest.tenantId + + // Validation - email and tenant id allowed + await config.api.accounts.validateEmail(email) + await config.api.accounts.validateTenantId(tenantId) + + // Create unverified account + await config.api.accounts.create(createAccountRequest) + + // Validation - email and tenant id no longer valid + await config.api.accounts.validateEmail(email, { status: 400 }) + await config.api.accounts.validateTenantId(tenantId, { status: 400 }) + + // Attempt to log in using unverified account + await config.loginAsAccount(createAccountRequest, { status: 400 }) + + // Re-send verification email to get access to code + const [_, code] = await config.accountsApi.accounts.sendVerificationEmail(email) + + // Send the verification request + await config.accountsApi.accounts.verifyAccount(code!) + + // Can now login to the account + await config.loginAsAccount(createAccountRequest) + + // Delete account + await config.api.accounts.deleteCurrentAccount() + + // Can't login + await config.loginAsAccount(createAccountRequest, { status: 403 }) + }) + }) + + describe("searching accounts", () => { + it("searches by tenant id", async () => { + const tenantId = generator.string() + + // empty result + const [emptyRes, emptyBody] = await config.api.accounts.search(tenantId, "tenantId") + expect(emptyBody.length).toBe(0) + + // hit result + const [hitRes, hitBody] = await config.api.accounts.search(config.state.tenantId!, "tenantId") + expect(hitBody.length).toBe(1) + expect(hitBody[0].tenantId).toBe(config.state.tenantId) + }) + + it("searches by email", async () => { + const email = generator.email() + + // empty result + const [emptyRes, emptyBody] = await config.api.accounts.search(email, "email") + expect(emptyBody.length).toBe(0) + + // hit result + const [hitRes, hitBody] = await config.api.accounts.search(config.state.email!, "email") + expect(hitBody.length).toBe(1) + expect(hitBody[0].email).toBe(config.state.email) + }) + }) +}) diff --git a/qa-core/src/account-api/tests/accounts/create.spec.ts b/qa-core/src/account-api/tests/accounts/create.spec.ts deleted file mode 100644 index 11a243a8ba..0000000000 --- a/qa-core/src/account-api/tests/accounts/create.spec.ts +++ /dev/null @@ -1,23 +0,0 @@ -import TestConfiguration from "../../config/TestConfiguration" -import * as fixtures from "../../fixtures" - -describe("Account API - Create Account", () => { - const config = new TestConfiguration() - - beforeAll(async () => { - await config.beforeAll() - }) - - afterAll(async () => { - await config.afterAll() - }) - - describe("POST /api/accounts/", () => { - it("Returns 201", async () => { - const [res, account] = await config.api.accounts.create({ - ...fixtures.accounts.generateAccount() - }) - expect(res.status).toBe(201) - }) - }) -}) diff --git a/qa-core/src/account-api/tests/accounts/delete.spec.ts b/qa-core/src/account-api/tests/accounts/delete.spec.ts deleted file mode 100644 index c3abeed12c..0000000000 --- a/qa-core/src/account-api/tests/accounts/delete.spec.ts +++ /dev/null @@ -1,52 +0,0 @@ -import TestConfiguration from "../../config/TestConfiguration" -import * as fixtures from "../../fixtures" -import { generator } from "../../../shared" - -describe("Account API - Delete Account", () => { - const config = new TestConfiguration() - - beforeAll(async () => { - await config.beforeAll() - }) - - afterAll(async () => { - await config.afterAll() - }) - - describe("DEL /api/accounts", () => { - it("Returns 204", async () => { - await config.doInNewState(async () => { - // Create account - const createAccountRequest = fixtures.accounts.generateAccount() - await config.api.accounts.create(createAccountRequest) - - // Login - Get cookie - await config.login( - createAccountRequest.email, - createAccountRequest.password, - createAccountRequest.tenantId - ) - - // Delete account - const res = await config.api.accounts.deleteCurrentAccount() - expect(res.status).toBe(204) - }) - }) - }) - - describe("DEL /api/accounts/{accountId}", () => { - it("Returns 204", async () => { - const [response, account] = await config.api.accounts.create({ - ...fixtures.accounts.generateAccount() - }) - // Delete account by ID - const res = await config.api.accounts.delete(account.accountId) - expect(res.status).toBe(204) - }) - - it("returns 404 - Account not found", async () => { - const accountId = generator.string() - await config.api.accounts.delete(accountId, {status:404}) - }) - }) -}) diff --git a/qa-core/src/account-api/tests/accounts/search.spec.ts b/qa-core/src/account-api/tests/accounts/search.spec.ts deleted file mode 100644 index d928c23996..0000000000 --- a/qa-core/src/account-api/tests/accounts/search.spec.ts +++ /dev/null @@ -1,70 +0,0 @@ -import TestConfiguration from "../../config/TestConfiguration" -import { generator } from "../../../shared" - -describe("Account API - Search for Account", () => { - const config = new TestConfiguration() - - beforeAll(async () => { - await config.beforeAll() - }) - - afterAll(async () => { - await config.afterAll() - }) - - describe("POST /api/accounts/search", () => { - describe("by tenant", () => { - it("returns 200 + empty", async () => { - const tenantId = generator.string() - const [res, body] = - await config.api.accounts.search(tenantId, "tenantId") - expect(res.status).toBe(200) - expect(body.length).toBe(0) - }) - - it("returns 200 + found", async () => { - const [res, body] = - await config.api.accounts.search(config.state.tenantId!, "tenantId") - expect(res.status).toBe(200) - expect(body.length).toBe(1) - expect(body[0].tenantId).toBe(config.state.tenantId) - }) - - it("returns 400 + error: Invalid body - tenantId is not allowed to be empty", async () => { - const [res, body] = - await config.api.accounts.search("", "tenantId") - expect(body).toEqual({ - message: "Invalid body - \"tenantId\" is not allowed to be empty", - status: 400 - }) - }) - }) - - describe("by email", () => { - it("returns 200 + empty", async () => { - const email = generator.email() - const [res, body] = - await config.api.accounts.search(email, "email") - expect(res.status).toBe(200) - expect(body.length).toBe(0) - }) - - it("returns 200 + found", async () => { - const [res, body] = - await config.api.accounts.search(config.state.email!, "email") - expect(res.status).toBe(200) - expect(body.length).toBe(1) - expect(body[0].email).toBe(config.state.email) - }) - - it("returns 400 + error: Invalid body - email is not allowed to be empty", async () => { - const [res, body] = - await config.api.accounts.search("", "email") - expect(body).toEqual({ - message: "Invalid body - \"email\" is not allowed to be empty", - status: 400 - }) - }) - }) - }) -}) diff --git a/qa-core/src/account-api/tests/accounts/validate.spec.ts b/qa-core/src/account-api/tests/accounts/validate.spec.ts deleted file mode 100644 index 16a32ecbe0..0000000000 --- a/qa-core/src/account-api/tests/accounts/validate.spec.ts +++ /dev/null @@ -1,46 +0,0 @@ -import TestConfiguration from "../../config/TestConfiguration" -import * as fixtures from "../../fixtures" -import { generator } from "../../../shared" - -describe("Account API - Validate Account", () => { - const config = new TestConfiguration() - - beforeAll(async () => { - await config.beforeAll() - }) - - afterAll(async () => { - await config.afterAll() - }) - - describe("POST /api/accounts/validate/email", () => { - it("Returns 200", async () => { - const email = generator.email() - const res = await config.api.accounts.validateEmail(email) - expect(res.status).toBe(200) - }) - - it("returns 400", async () => { - const [response, account] = await config.api.accounts.create({ - ...fixtures.accounts.generateAccount() - }) - const res = await config.api.accounts.validateEmail(account.email) - expect(res.status).toBe(400) - }) - }) - - describe("POST /api/accounts/validate/tenantId", () => { - it("Returns 200", async () => { - const res = await config.api.accounts.validateTenantId("randomtenant") - expect(res.status).toBe(200) - }) - - it("Returns 400", async () => { - const [response, account] = await config.api.accounts.create({ - ...fixtures.accounts.generateAccount() - }) - const res = await config.api.accounts.validateTenantId(account.tenantId) - expect(res.status).toBe(400) - }) - }) -}) diff --git a/qa-core/src/account-api/tests/accounts/verify.spec.ts b/qa-core/src/account-api/tests/accounts/verify.spec.ts deleted file mode 100644 index 6c14b4a4d9..0000000000 --- a/qa-core/src/account-api/tests/accounts/verify.spec.ts +++ /dev/null @@ -1,56 +0,0 @@ -import TestConfiguration from "../../config/TestConfiguration" -import * as fixtures from "../../fixtures" - -describe("Account API - Verify Account", () => { - const config = new TestConfiguration() - - beforeAll(async () => { - await config.beforeAll() - }) - - afterAll(async () => { - await config.afterAll() - }) - - describe("POST /api/accounts/verify", () => { - it("returns 200", async () => { - // Create unverified account - const createAccountRequest = fixtures.accounts.generateAccount() - const [res, acc] = await config.api.accounts.create( - createAccountRequest, - { doExpect: true, autoVerify: false }) - - // Attempt to log in using unverified account - const [loginResponse, cookie] = await config.accountsApi.auth.login( - createAccountRequest.email, - createAccountRequest.password, - ) - - // await config.login( - // createAccountRequest.email, - // createAccountRequest.password, - // createAccountRequest.tenantId, - // ) - - // Expect response - cannot login via unverified account - - - // Verify account via code - // await config.api.accounts.verifyAccount() - - // Expect response - login successful - }) - }) - - describe("POST /api/accounts/verify/send", () => { - it("Send account verification email ", async () => { - // Create account - await config.api.accounts.create({ - ...fixtures.accounts.generateAccount() - }) - - // Verify account via email - //await config.api.accounts.verifyAccountSendEmail() - }) - }) -}) diff --git a/qa-core/src/jest/globalTeardown.ts b/qa-core/src/jest/globalTeardown.ts index 52696a72f8..f1b8c1d541 100644 --- a/qa-core/src/jest/globalTeardown.ts +++ b/qa-core/src/jest/globalTeardown.ts @@ -10,8 +10,11 @@ const API_OPTS: APIRequestOpts = { doExpect: false } async function deleteAccount() { // @ts-ignore const accountID = global.qa.accountId - // can't run 'expect' blocks in teardown - await accountsApi.accounts.delete(accountID) + + const [response] = await accountsApi.accounts.delete(accountID, { doExpect: false }) + if (response.status !== 204) { + throw new Error(`status: ${response.status} not equal to expected: 201`) + } } async function teardown() { diff --git a/qa-core/src/shared/BudibaseTestConfiguration.ts b/qa-core/src/shared/BudibaseTestConfiguration.ts index c6955fd87f..9841b8d163 100644 --- a/qa-core/src/shared/BudibaseTestConfiguration.ts +++ b/qa-core/src/shared/BudibaseTestConfiguration.ts @@ -1,7 +1,8 @@ import { BudibaseInternalAPI } from "../internal-api" import { AccountInternalAPI } from "../account-api" -import { CreateAppRequest, State } from "../types" +import { APIRequestOpts, CreateAppRequest, State } from "../types" import * as fixtures from "../internal-api/fixtures" +import { CreateAccountRequest } from "@budibase/types" export default class BudibaseTestConfiguration { // apis @@ -42,12 +43,12 @@ export default class BudibaseTestConfiguration { // AUTH - async doInNewState(task: any) { + async doInNewState(task: () => Promise) { return this.doWithState(task, {}) } - async doWithState(task: any, state: State) { - const original = this.state + async doWithState(task: () => Promise, state: State) { + const original = { ...this.state } // override the state this.state.apiKey = state.apiKey @@ -68,6 +69,15 @@ export default class BudibaseTestConfiguration { this.state.email = original.email } + async loginAsAccount(account: CreateAccountRequest, opts: APIRequestOpts = {}) { + const [_, cookie] = await this.accountsApi.auth.login( + account.email, + account.password, + opts + ) + this.state.cookie = cookie + } + async login(email: string, password: string, tenantId?: string) { if (!tenantId && this.state.tenantId) { tenantId = this.state.tenantId