Restructure account tests to test signup and deletion as a single flow
This commit is contained in:
parent
944b6e0baa
commit
4dc558d3e9
|
@ -1,6 +1,8 @@
|
||||||
|
import { Response } from "node-fetch"
|
||||||
import env from "../../environment"
|
import env from "../../environment"
|
||||||
import fetch, { HeadersInit } from "node-fetch"
|
import fetch, { HeadersInit } from "node-fetch"
|
||||||
import { State } from "../../types"
|
import { State } from "../../types"
|
||||||
|
import { Header } from "@budibase/backend-core"
|
||||||
|
|
||||||
type APIMethod = "GET" | "POST" | "PUT" | "PATCH" | "DELETE"
|
type APIMethod = "GET" | "POST" | "PUT" | "PATCH" | "DELETE"
|
||||||
|
|
||||||
|
@ -28,7 +30,7 @@ export default class AccountInternalAPIClient {
|
||||||
|
|
||||||
apiCall =
|
apiCall =
|
||||||
(method: APIMethod) =>
|
(method: APIMethod) =>
|
||||||
async (url = "", options: ApiOptions = {}) => {
|
async (url = "", options: ApiOptions = {}): Promise<[Response, any]> => {
|
||||||
const requestOptions = {
|
const requestOptions = {
|
||||||
method,
|
method,
|
||||||
body: JSON.stringify(options.body),
|
body: JSON.stringify(options.body),
|
||||||
|
@ -46,7 +48,7 @@ export default class AccountInternalAPIClient {
|
||||||
if (options.internal) {
|
if (options.internal) {
|
||||||
requestOptions.headers = {
|
requestOptions.headers = {
|
||||||
...requestOptions.headers,
|
...requestOptions.headers,
|
||||||
...{ "x-budibase-api-key": env.ACCOUNT_PORTAL_API_KEY },
|
...{ [Header.API_KEY] : env.ACCOUNT_PORTAL_API_KEY },
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
import { generator } from "../../shared"
|
import { generator } from "../../shared"
|
||||||
import { Hosting, CreateAccountRequest } from "@budibase/types"
|
import { Hosting, CreateAccountRequest } from "@budibase/types"
|
||||||
|
|
||||||
|
// TODO: Refactor me to central location
|
||||||
export const generateAccount = (): CreateAccountRequest => {
|
export const generateAccount = (): CreateAccountRequest => {
|
||||||
const uuid = generator.guid()
|
const uuid = generator.guid()
|
||||||
|
|
||||||
|
|
|
@ -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)
|
||||||
|
})
|
||||||
|
})
|
|
@ -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)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
|
@ -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)
|
|
||||||
})
|
|
||||||
})
|
|
||||||
})
|
|
|
@ -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})
|
|
||||||
})
|
|
||||||
})
|
|
||||||
})
|
|
|
@ -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
|
|
||||||
})
|
|
||||||
})
|
|
||||||
})
|
|
||||||
})
|
|
||||||
})
|
|
|
@ -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)
|
|
||||||
})
|
|
||||||
})
|
|
||||||
})
|
|
|
@ -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()
|
|
||||||
})
|
|
||||||
})
|
|
||||||
})
|
|
|
@ -10,8 +10,11 @@ const API_OPTS: APIRequestOpts = { doExpect: false }
|
||||||
async function deleteAccount() {
|
async function deleteAccount() {
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
const accountID = global.qa.accountId
|
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() {
|
async function teardown() {
|
||||||
|
|
|
@ -1,7 +1,8 @@
|
||||||
import { BudibaseInternalAPI } from "../internal-api"
|
import { BudibaseInternalAPI } from "../internal-api"
|
||||||
import { AccountInternalAPI } from "../account-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 * as fixtures from "../internal-api/fixtures"
|
||||||
|
import { CreateAccountRequest } from "@budibase/types"
|
||||||
|
|
||||||
export default class BudibaseTestConfiguration {
|
export default class BudibaseTestConfiguration {
|
||||||
// apis
|
// apis
|
||||||
|
@ -42,12 +43,12 @@ export default class BudibaseTestConfiguration {
|
||||||
|
|
||||||
// AUTH
|
// AUTH
|
||||||
|
|
||||||
async doInNewState(task: any) {
|
async doInNewState(task: () => Promise<any>) {
|
||||||
return this.doWithState(task, {})
|
return this.doWithState(task, {})
|
||||||
}
|
}
|
||||||
|
|
||||||
async doWithState(task: any, state: State) {
|
async doWithState(task: () => Promise<any>, state: State) {
|
||||||
const original = this.state
|
const original = { ...this.state }
|
||||||
|
|
||||||
// override the state
|
// override the state
|
||||||
this.state.apiKey = state.apiKey
|
this.state.apiKey = state.apiKey
|
||||||
|
@ -68,6 +69,15 @@ export default class BudibaseTestConfiguration {
|
||||||
this.state.email = original.email
|
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) {
|
async login(email: string, password: string, tenantId?: string) {
|
||||||
if (!tenantId && this.state.tenantId) {
|
if (!tenantId && this.state.tenantId) {
|
||||||
tenantId = this.state.tenantId
|
tenantId = this.state.tenantId
|
||||||
|
|
Loading…
Reference in New Issue