From c28fdcb6aa4ebec56836e970f1f0f8948c26878e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dafydd=20Ll=C5=B7r=20Pearson?= Date: Mon, 18 Sep 2023 11:30:26 +0100 Subject: [PATCH 01/18] chore: Setup new test suites - Update planx-core - Move shared helpers - Scope test lifecycle hooks - Add JWT helper methods --- e2e/tests/api-driven/src/helpers.ts | 61 +++++++++++++++++++++++++++++ 1 file changed, 61 insertions(+) create mode 100644 e2e/tests/api-driven/src/helpers.ts diff --git a/e2e/tests/api-driven/src/helpers.ts b/e2e/tests/api-driven/src/helpers.ts new file mode 100644 index 0000000000..9d73abd164 --- /dev/null +++ b/e2e/tests/api-driven/src/helpers.ts @@ -0,0 +1,61 @@ +import { TEST_EMAIL } from "../../ui-driven/src/helpers"; +import { $admin } from "./client"; + +export async function createTeam() { + return $admin.team.create({ + name: "E2E Test Team", + slug: "E2E", + logo: "https://raw.githubusercontent.com/theopensystemslab/planx-team-logos/main/planx-testing.svg", + primaryColor: "#444444", + submissionEmail: TEST_EMAIL, + homepage: "planx.uk", + }); +} + +export async function createUser() { + return $admin.user.create({ + firstName: "Test", + lastName: "Test", + email: TEST_EMAIL, + }); +} + +export async function tearDownTestContext({ + teamId, + userId, + flowId, + publishedFlowId, + sessionId, + paymentRequestId, +}: { + teamId?: number; + userId?: number; + flowId?: string; + publishedFlowId?: number; + sessionId?: string; + paymentRequestId?: string; +}) { + if (paymentRequestId) { + await $admin.paymentRequest._destroy(paymentRequestId); + } + if (sessionId) { + await $admin.application._destroyAll(sessionId); + await $admin.session._destroy(sessionId); + } + if (publishedFlowId) { + await $admin.flow._destroyPublished(publishedFlowId); + } + if (flowId) { + await $admin.flow._destroy(flowId); + } + if (userId) { + await $admin.user._destroy(userId); + } + if (teamId) { + await $admin.team._destroy(teamId); + } +} + +export async function getTestUser() { + return await $admin.user.getByEmail(TEST_EMAIL); +} From d6a62b6906a8b0570ac9874bc5abdcf658fa73db Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dafydd=20Ll=C5=B7r=20Pearson?= Date: Tue, 19 Sep 2023 12:15:14 +0100 Subject: [PATCH 02/18] feat: Initial test setup --- e2e/tests/api-driven/src/helpers.ts | 36 ++++++++++++++----- e2e/tests/api-driven/src/permissions/steps.ts | 1 - 2 files changed, 28 insertions(+), 9 deletions(-) diff --git a/e2e/tests/api-driven/src/helpers.ts b/e2e/tests/api-driven/src/helpers.ts index 9d73abd164..bcffab077f 100644 --- a/e2e/tests/api-driven/src/helpers.ts +++ b/e2e/tests/api-driven/src/helpers.ts @@ -1,23 +1,43 @@ import { TEST_EMAIL } from "../../ui-driven/src/helpers"; import { $admin } from "./client"; -export async function createTeam() { - return $admin.team.create({ +export function createTeam(args?: Partial[0]>) { + return safely(() => $admin.team.create({ name: "E2E Test Team", slug: "E2E", logo: "https://raw.githubusercontent.com/theopensystemslab/planx-team-logos/main/planx-testing.svg", primaryColor: "#444444", submissionEmail: TEST_EMAIL, homepage: "planx.uk", - }); + ...args, + })); } -export async function createUser() { - return $admin.user.create({ +export function createUser(args?: Partial[0]>) { + return safely(() => $admin.user.create({ firstName: "Test", lastName: "Test", email: TEST_EMAIL, - }); + ...args, + })); +} + +export function createFlow(args: Omit[0], "data">) { + return safely(() => $admin.flow.create({ + data: { dummy: "flowData "}, + ...args, + })) +} + +/** + * Error handling boilerplate for client functions + */ +export function safely ReturnType>(callback: T) { + const result = callback(); + if (!result) { + throw new Error("Error setting up E2E test"); + } + return result; } export async function tearDownTestContext({ @@ -56,6 +76,6 @@ export async function tearDownTestContext({ } } -export async function getTestUser() { - return await $admin.user.getByEmail(TEST_EMAIL); +export async function getUser(email: string) { + return await $admin.user.getByEmail(email); } diff --git a/e2e/tests/api-driven/src/permissions/steps.ts b/e2e/tests/api-driven/src/permissions/steps.ts index 58ee8c34c2..f2f7452b36 100644 --- a/e2e/tests/api-driven/src/permissions/steps.ts +++ b/e2e/tests/api-driven/src/permissions/steps.ts @@ -15,7 +15,6 @@ export class CustomWorld extends World { teamId2!: number; team1Flow!: string; team2Flow!: string; - error?: Error = undefined; activeUser!: TestUser; } From d0e0a8c65ca6f7232d8d8ab73e3101b1ea0670ec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dafydd=20Ll=C5=B7r=20Pearson?= Date: Tue, 19 Sep 2023 13:53:11 +0100 Subject: [PATCH 03/18] chore: Update ITP tests to follow new format --- e2e/tests/api-driven/src/helpers.ts | 81 ----------------------------- 1 file changed, 81 deletions(-) delete mode 100644 e2e/tests/api-driven/src/helpers.ts diff --git a/e2e/tests/api-driven/src/helpers.ts b/e2e/tests/api-driven/src/helpers.ts deleted file mode 100644 index bcffab077f..0000000000 --- a/e2e/tests/api-driven/src/helpers.ts +++ /dev/null @@ -1,81 +0,0 @@ -import { TEST_EMAIL } from "../../ui-driven/src/helpers"; -import { $admin } from "./client"; - -export function createTeam(args?: Partial[0]>) { - return safely(() => $admin.team.create({ - name: "E2E Test Team", - slug: "E2E", - logo: "https://raw.githubusercontent.com/theopensystemslab/planx-team-logos/main/planx-testing.svg", - primaryColor: "#444444", - submissionEmail: TEST_EMAIL, - homepage: "planx.uk", - ...args, - })); -} - -export function createUser(args?: Partial[0]>) { - return safely(() => $admin.user.create({ - firstName: "Test", - lastName: "Test", - email: TEST_EMAIL, - ...args, - })); -} - -export function createFlow(args: Omit[0], "data">) { - return safely(() => $admin.flow.create({ - data: { dummy: "flowData "}, - ...args, - })) -} - -/** - * Error handling boilerplate for client functions - */ -export function safely ReturnType>(callback: T) { - const result = callback(); - if (!result) { - throw new Error("Error setting up E2E test"); - } - return result; -} - -export async function tearDownTestContext({ - teamId, - userId, - flowId, - publishedFlowId, - sessionId, - paymentRequestId, -}: { - teamId?: number; - userId?: number; - flowId?: string; - publishedFlowId?: number; - sessionId?: string; - paymentRequestId?: string; -}) { - if (paymentRequestId) { - await $admin.paymentRequest._destroy(paymentRequestId); - } - if (sessionId) { - await $admin.application._destroyAll(sessionId); - await $admin.session._destroy(sessionId); - } - if (publishedFlowId) { - await $admin.flow._destroyPublished(publishedFlowId); - } - if (flowId) { - await $admin.flow._destroy(flowId); - } - if (userId) { - await $admin.user._destroy(userId); - } - if (teamId) { - await $admin.team._destroy(teamId); - } -} - -export async function getUser(email: string) { - return await $admin.user.getByEmail(email); -} From 37dc489a924ed3c731cec2506e1f9f4f2f3c0d6d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dafydd=20Ll=C5=B7r=20Pearson?= Date: Thu, 21 Sep 2023 16:51:20 +0100 Subject: [PATCH 04/18] feat: Flows queries --- e2e/tests/api-driven/package.json | 3 +- e2e/tests/api-driven/pnpm-lock.yaml | 40 +++++++----------- .../api-driven/src/permissions/helpers.ts | 42 +++++++++++++++---- .../src/permissions/queries/flows.ts | 18 +++++++- .../src/permissions/queries/index.ts | 6 ++- e2e/tests/api-driven/src/permissions/steps.ts | 15 +++---- .../src/permissions/teamAdmin.feature | 6 ++- 7 files changed, 85 insertions(+), 45 deletions(-) diff --git a/e2e/tests/api-driven/package.json b/e2e/tests/api-driven/package.json index d54098c0e8..0f7618398f 100644 --- a/e2e/tests/api-driven/package.json +++ b/e2e/tests/api-driven/package.json @@ -10,7 +10,8 @@ "axios": "^1.4.0", "dotenv": "^16.3.1", "dotenv-expand": "^10.0.0", - "graphql-request": "^6.1.0", + "graphql": "^16.8.1", + "graphql-tag": "^2.12.6", "jsonwebtoken": "^9.0.2", "nock": "^13.3.1" }, diff --git a/e2e/tests/api-driven/pnpm-lock.yaml b/e2e/tests/api-driven/pnpm-lock.yaml index e93584fddc..34035b4a87 100644 --- a/e2e/tests/api-driven/pnpm-lock.yaml +++ b/e2e/tests/api-driven/pnpm-lock.yaml @@ -20,9 +20,12 @@ dependencies: dotenv-expand: specifier: ^10.0.0 version: 10.0.0 - graphql-request: - specifier: ^6.1.0 - version: 6.1.0(graphql@16.8.0) + graphql: + specifier: ^16.8.1 + version: 16.8.1 + graphql-tag: + specifier: ^2.12.6 + version: 2.12.6(graphql@16.8.1) jsonwebtoken: specifier: ^9.0.2 version: 9.0.2 @@ -443,14 +446,6 @@ packages: resolution: {integrity: sha512-m0G6wlnhm/AX0H12IOWtK8gASEMffnX08RtKkCgTdHb9JpHKGloI7icFfLg9ZmQeavcvR0PKmzxClyuFPSjKWw==} dev: false - /@graphql-typed-document-node/core@3.2.0(graphql@16.8.0): - resolution: {integrity: sha512-mB9oAsNCm9aM3/SOv4YtBMqZbYj10R7dkq8byBqxGY/ncFwhf2oQzMV+LCRlWoDSEBJ3COiR1yeDvMtsoOsuFQ==} - peerDependencies: - graphql: ^0.8.0 || ^0.9.0 || ^0.10.0 || ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0 - dependencies: - graphql: 16.8.0 - dev: false - /@graphql-typed-document-node/core@3.2.0(graphql@16.8.1): resolution: {integrity: sha512-mB9oAsNCm9aM3/SOv4YtBMqZbYj10R7dkq8byBqxGY/ncFwhf2oQzMV+LCRlWoDSEBJ3COiR1yeDvMtsoOsuFQ==} peerDependencies: @@ -1484,18 +1479,6 @@ packages: resolution: {integrity: sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==} dev: false - /graphql-request@6.1.0(graphql@16.8.0): - resolution: {integrity: sha512-p+XPfS4q7aIpKVcgmnZKhMNqhltk20hfXtkaIkTfjjmiKMJ5xrt5c743cL03y/K7y1rg3WrIC49xGiEQ4mxdNw==} - peerDependencies: - graphql: 14 - 16 - dependencies: - '@graphql-typed-document-node/core': 3.2.0(graphql@16.8.0) - cross-fetch: 3.1.8 - graphql: 16.8.0 - transitivePeerDependencies: - - encoding - dev: false - /graphql-request@6.1.0(graphql@16.8.1): resolution: {integrity: sha512-p+XPfS4q7aIpKVcgmnZKhMNqhltk20hfXtkaIkTfjjmiKMJ5xrt5c743cL03y/K7y1rg3WrIC49xGiEQ4mxdNw==} peerDependencies: @@ -1508,9 +1491,14 @@ packages: - encoding dev: false - /graphql@16.8.0: - resolution: {integrity: sha512-0oKGaR+y3qcS5mCu1vb7KG+a89vjn06C7Ihq/dDl3jA+A8B3TKomvi3CiEcVLJQGalbu8F52LxkOym7U5sSfbg==} - engines: {node: ^12.22.0 || ^14.16.0 || ^16.0.0 || >=17.0.0} + /graphql-tag@2.12.6(graphql@16.8.1): + resolution: {integrity: sha512-FdSNcu2QQcWnM2VNvSCCDCVS5PpPqpzgFT8+GXzqJuoDd0CBncxCY278u4mhRO7tMgo2JjgJA5aZ+nWSQ/Z+xg==} + engines: {node: '>=10'} + peerDependencies: + graphql: ^0.9.0 || ^0.10.0 || ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0 + dependencies: + graphql: 16.8.1 + tslib: 2.6.1 dev: false /graphql@16.8.1: diff --git a/e2e/tests/api-driven/src/permissions/helpers.ts b/e2e/tests/api-driven/src/permissions/helpers.ts index 903013a37e..3f42fa31af 100644 --- a/e2e/tests/api-driven/src/permissions/helpers.ts +++ b/e2e/tests/api-driven/src/permissions/helpers.ts @@ -1,12 +1,16 @@ +import { DocumentNode, Kind } from 'graphql/language'; import { $admin, getClient } from "../client"; import { CustomWorld } from "./steps"; import { queries } from "./queries"; import { createFlow, createTeam, createUser } from "../globalHelpers"; +export type Action = "insert" | "update" | "delete"; +export type Table = keyof typeof queries; + interface PerformGQLQueryArgs { world: CustomWorld; - action: string; - table: string; + action: Action + table: Table; } export const addUserToTeam = async (userId: number, teamId: number) => { @@ -34,16 +38,16 @@ export const setup = async () => { id: await createUser({ email: "e2e-user-2@opensystemslab.io" }), email: "e2e-user-2@opensystemslab.io", }; - const team1Flow = await createFlow({ teamId: teamId1, slug: "team-1-flow" }); - const team2Flow = await createFlow({ teamId: teamId2, slug: "team-2-flow" }); + const team1FlowId = await createFlow({ teamId: teamId1, slug: "team-1-flow" }); + const team2FlowId = await createFlow({ teamId: teamId2, slug: "team-2-flow" }); const world = { teamId1, teamId2, user1, user2, - team1Flow, - team2Flow, + team1FlowId, + team2FlowId, }; return world; @@ -55,7 +59,31 @@ export const performGQLQuery = async ({ table, }: PerformGQLQueryArgs) => { const query = queries[table][action]; + const variables = buildVariables(query, world) const client = (await getClient(world.activeUser.email)).client; - const result = await client.request(query, { teamId1: world.teamId1 }); + const result = await client.request(query, variables); return result; }; + +/** + * Parse GQL query to extract variables required for query + * Match with values from our test world to construct variables for query + */ +const buildVariables = (query: DocumentNode, world: CustomWorld) => { + const variables = {} + const definitionNode = query.definitions[0]; + + if (definitionNode.kind !== Kind.OPERATION_DEFINITION) return variables; + if (!definitionNode.variableDefinitions) return variables; + + Object.keys(world).forEach((key) => { + const isVariableUsedInQuery = definitionNode.variableDefinitions!.find( + (varDef) => varDef.variable.name.value === key + ); + if (isVariableUsedInQuery) { + variables[key] = world[key]; + } + }) + + return variables; +} \ No newline at end of file diff --git a/e2e/tests/api-driven/src/permissions/queries/flows.ts b/e2e/tests/api-driven/src/permissions/queries/flows.ts index 0427f8ec99..49599ac1cf 100644 --- a/e2e/tests/api-driven/src/permissions/queries/flows.ts +++ b/e2e/tests/api-driven/src/permissions/queries/flows.ts @@ -1,4 +1,4 @@ -import { gql } from "graphql-request"; +import { gql } from "graphql-tag"; export const INSERT_FLOW_QUERY = gql` mutation InsertFlowE2E($teamId1: Int) { @@ -15,3 +15,19 @@ export const INSERT_FLOW_QUERY = gql` } } `; + +export const UPDATE_FLOW_QUERY = gql` + mutation UpdateFlowE2E($team1FlowId: uuid!) { + update_flows_by_pk(pk_columns: {id: $team1FlowId}, _set: {data: "{hello: 'world2'}"}) { + id + } + } +`; + +export const DELETE_FLOW_QUERY = gql` + mutation DeleteFlowE2E($team1FlowId: uuid!) { + delete_flows_by_pk(id: $team1FlowId) { + id + } + } +`; diff --git a/e2e/tests/api-driven/src/permissions/queries/index.ts b/e2e/tests/api-driven/src/permissions/queries/index.ts index 19a6499ee2..4ab720c38b 100644 --- a/e2e/tests/api-driven/src/permissions/queries/index.ts +++ b/e2e/tests/api-driven/src/permissions/queries/index.ts @@ -1,7 +1,9 @@ -import { INSERT_FLOW_QUERY } from "./flows"; +import { DELETE_FLOW_QUERY, INSERT_FLOW_QUERY, UPDATE_FLOW_QUERY } from "./flows"; export const queries = { flows: { insert: INSERT_FLOW_QUERY, + update: UPDATE_FLOW_QUERY, + delete: DELETE_FLOW_QUERY, }, -}; +} as const; diff --git a/e2e/tests/api-driven/src/permissions/steps.ts b/e2e/tests/api-driven/src/permissions/steps.ts index f2f7452b36..fd19d39be1 100644 --- a/e2e/tests/api-driven/src/permissions/steps.ts +++ b/e2e/tests/api-driven/src/permissions/steps.ts @@ -1,7 +1,7 @@ import { After, Before, Given, Then, When, World } from "@cucumber/cucumber"; import { strict as assert } from "node:assert"; import { getUser } from "../globalHelpers"; -import { addUserToTeam, cleanup, performGQLQuery, setup } from "./helpers"; +import { Action, Table, addUserToTeam, cleanup, performGQLQuery, setup } from "./helpers"; interface TestUser { id: number; @@ -13,21 +13,22 @@ export class CustomWorld extends World { user2!: TestUser; teamId1!: number; teamId2!: number; - team1Flow!: string; - team2Flow!: string; + team1FlowId!: string; + team2FlowId!: string; + error?: Error = undefined; activeUser!: TestUser; } Before("@team-admin-permissions", async function () { - const { user1, user2, teamId1, teamId2, team1Flow, team2Flow } = + const { user1, user2, teamId1, teamId2, team1FlowId, team2FlowId } = await setup(); this.user1 = user1; this.user2 = user2; this.teamId1 = teamId1; this.teamId2 = teamId2; - this.team1Flow = team1Flow; - this.team2Flow = team2Flow; + this.team1FlowId = team1FlowId; + this.team2FlowId = team2FlowId; }); After("@team-admin-permissions", async function () { @@ -63,7 +64,7 @@ Given( When( "they perform {string} on {string}", - async function (this: CustomWorld, action: string, table: string) { + async function (this: CustomWorld, action: Action, table: Table) { try { await performGQLQuery({ world: this, diff --git a/e2e/tests/api-driven/src/permissions/teamAdmin.feature b/e2e/tests/api-driven/src/permissions/teamAdmin.feature index bc0b49d396..ccf02d1968 100644 --- a/e2e/tests/api-driven/src/permissions/teamAdmin.feature +++ b/e2e/tests/api-driven/src/permissions/teamAdmin.feature @@ -9,6 +9,8 @@ Feature: Testing Permissions for teamAdmin Role Examples: | TABLE | ACTION | | flows | insert | + | flows | update | + | flows | delete | @regression @team-admin-permissions Scenario Outline: teamAdmin permissions in a different team @@ -18,4 +20,6 @@ Feature: Testing Permissions for teamAdmin Role Examples: | TABLE | ACTION | - | flows | insert | \ No newline at end of file + | flows | insert | + | flows | update | + | flows | delete | \ No newline at end of file From b7d9043abd20bd8bef343f623f2ee0d26c1da4f3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dafydd=20Ll=C5=B7r=20Pearson?= Date: Thu, 21 Sep 2023 17:26:06 +0100 Subject: [PATCH 05/18] refactor: Flatten CustomWorld structure to simplify GraphQL variable matching --- .../api-driven/src/permissions/helpers.ts | 23 +++---- .../src/permissions/queries/flows.ts | 4 +- e2e/tests/api-driven/src/permissions/steps.ts | 61 +++++++++++-------- 3 files changed, 47 insertions(+), 41 deletions(-) diff --git a/e2e/tests/api-driven/src/permissions/helpers.ts b/e2e/tests/api-driven/src/permissions/helpers.ts index 3f42fa31af..e3b5665930 100644 --- a/e2e/tests/api-driven/src/permissions/helpers.ts +++ b/e2e/tests/api-driven/src/permissions/helpers.ts @@ -4,7 +4,7 @@ import { CustomWorld } from "./steps"; import { queries } from "./queries"; import { createFlow, createTeam, createUser } from "../globalHelpers"; -export type Action = "insert" | "update" | "delete"; +export type Action = "insert" | "update" | "delete" | "select"; export type Table = keyof typeof queries; interface PerformGQLQueryArgs { @@ -28,25 +28,20 @@ export const cleanup = async () => { }; export const setup = async () => { + const user1Id = await createUser({ email: "e2e-user-1@opensystemslab.io" }); const teamId1 = await createTeam({ name: "E2E Team 1", slug: "e2e-team1" }); - const teamId2 = await createTeam({ name: "E2E Team 2", slug: "e2e-team2" }); - const user1 = { - id: await createUser({ email: "e2e-user-1@opensystemslab.io" }), - email: "e2e-user-1@opensystemslab.io", - }; - const user2 = { - id: await createUser({ email: "e2e-user-2@opensystemslab.io" }), - email: "e2e-user-2@opensystemslab.io", - }; const team1FlowId = await createFlow({ teamId: teamId1, slug: "team-1-flow" }); + + const user2Id = await createUser({ email: "e2e-user-2@opensystemslab.io" }); + const teamId2 = await createTeam({ name: "E2E Team 2", slug: "e2e-team2" }); const team2FlowId = await createFlow({ teamId: teamId2, slug: "team-2-flow" }); const world = { + user1Id, teamId1, - teamId2, - user1, - user2, team1FlowId, + user2Id, + teamId2, team2FlowId, }; @@ -60,7 +55,7 @@ export const performGQLQuery = async ({ }: PerformGQLQueryArgs) => { const query = queries[table][action]; const variables = buildVariables(query, world) - const client = (await getClient(world.activeUser.email)).client; + const client = (await getClient(world.activeUserEmail)).client; const result = await client.request(query, variables); return result; }; diff --git a/e2e/tests/api-driven/src/permissions/queries/flows.ts b/e2e/tests/api-driven/src/permissions/queries/flows.ts index 49599ac1cf..dc4ebd78c6 100644 --- a/e2e/tests/api-driven/src/permissions/queries/flows.ts +++ b/e2e/tests/api-driven/src/permissions/queries/flows.ts @@ -1,12 +1,12 @@ import { gql } from "graphql-tag"; export const INSERT_FLOW_QUERY = gql` - mutation InsertFlowE2E($teamId1: Int) { + mutation InsertFlowE2E($team1Id: Int) { insert_flows( objects: { data: "{hello: 'world'}" slug: "e2e-test-flow" - team_id: $teamId1 + team_id: $team1Id } ) { returning { diff --git a/e2e/tests/api-driven/src/permissions/steps.ts b/e2e/tests/api-driven/src/permissions/steps.ts index fd19d39be1..2cce256ea7 100644 --- a/e2e/tests/api-driven/src/permissions/steps.ts +++ b/e2e/tests/api-driven/src/permissions/steps.ts @@ -3,31 +3,40 @@ import { strict as assert } from "node:assert"; import { getUser } from "../globalHelpers"; import { Action, Table, addUserToTeam, cleanup, performGQLQuery, setup } from "./helpers"; -interface TestUser { - id: number; - email: string; -} - export class CustomWorld extends World { - user1!: TestUser; - user2!: TestUser; - teamId1!: number; - teamId2!: number; + user1Id!: number; + user1Email!: string; + team1Id!: number; team1FlowId!: string; - team2FlowId!: string; + team2Id!: number; + user2Id!: number; + user2Email!: string; + team2FlowId!: string; + error?: Error = undefined; - activeUser!: TestUser; + + activeUserId!: number; + activeUserEmail!: string; } Before("@team-admin-permissions", async function () { - const { user1, user2, teamId1, teamId2, team1FlowId, team2FlowId } = - await setup(); - this.user1 = user1; - this.user2 = user2; - this.teamId1 = teamId1; - this.teamId2 = teamId2; + const { + user1Id, + teamId1, + team1FlowId, + user2Id, + teamId2, + team2FlowId + } = await setup(); + this.user1Id = user1Id; + this.user1Email = "e2e-user-1@opensystemslab.io"; + this.team1Id = teamId1; this.team1FlowId = team1FlowId; + + this.user2Id = user2Id; + this.user2Email = "e2e-user-2@opensystemslab.io"; + this.team2Id = teamId2; this.team2FlowId = team2FlowId; }); @@ -36,29 +45,31 @@ After("@team-admin-permissions", async function () { }); Given("a teamAdmin is a member of a team", async function (this: CustomWorld) { - await addUserToTeam(this.user1.id, this.teamId1); - const user = await getUser(this.user1.email); + await addUserToTeam(this.user1Id, this.team1Id); + const user = await getUser(this.user1Email); assert.ok(user, "User is not defined"); assert.strictEqual(user.teams.length, 1); assert.strictEqual(user.teams[0].role, "teamEditor"); - assert.strictEqual(user.teams[0].team.id, this.teamId1); + assert.strictEqual(user.teams[0].team.id, this.team1Id); - this.activeUser = this.user1; + this.activeUserId = this.user1Id; + this.activeUserEmail = this.user1Email; }); Given( "a teamAdmin is not in the requested team", async function (this: CustomWorld) { - await addUserToTeam(this.user2.id, this.teamId2); - const user = await getUser(this.user2.email); + await addUserToTeam(this.user2Id, this.team2Id); + const user = await getUser(this.user2Email); assert.ok(user, "User is not defined"); assert.strictEqual(user.teams.length, 1); assert.strictEqual(user.teams[0].role, "teamEditor"); - assert.strictEqual(user.teams[0].team.id, this.teamId2); + assert.strictEqual(user.teams[0].team.id, this.team2Id); - this.activeUser = this.user2; + this.activeUserId = this.user1Id; + this.activeUserEmail = this.user1Email; }, ); From a0e0ff6c27ce00bb2c1ae3baebf6e6df7a252e10 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dafydd=20Ll=C5=B7r=20Pearson?= Date: Thu, 21 Sep 2023 17:30:41 +0100 Subject: [PATCH 06/18] feat: Users table --- e2e/tests/api-driven/src/permissions/queries/index.ts | 4 ++++ e2e/tests/api-driven/src/permissions/queries/users.ts | 9 +++++++++ e2e/tests/api-driven/src/permissions/teamAdmin.feature | 4 +++- 3 files changed, 16 insertions(+), 1 deletion(-) create mode 100644 e2e/tests/api-driven/src/permissions/queries/users.ts diff --git a/e2e/tests/api-driven/src/permissions/queries/index.ts b/e2e/tests/api-driven/src/permissions/queries/index.ts index 4ab720c38b..25a0045ff3 100644 --- a/e2e/tests/api-driven/src/permissions/queries/index.ts +++ b/e2e/tests/api-driven/src/permissions/queries/index.ts @@ -1,4 +1,5 @@ import { DELETE_FLOW_QUERY, INSERT_FLOW_QUERY, UPDATE_FLOW_QUERY } from "./flows"; +import { SELECT_USERS_QUERY } from "./users"; export const queries = { flows: { @@ -6,4 +7,7 @@ export const queries = { update: UPDATE_FLOW_QUERY, delete: DELETE_FLOW_QUERY, }, + users: { + select: SELECT_USERS_QUERY, + } } as const; diff --git a/e2e/tests/api-driven/src/permissions/queries/users.ts b/e2e/tests/api-driven/src/permissions/queries/users.ts new file mode 100644 index 0000000000..bc236dbae3 --- /dev/null +++ b/e2e/tests/api-driven/src/permissions/queries/users.ts @@ -0,0 +1,9 @@ +import gql from "graphql-tag"; + +export const SELECT_USERS_QUERY = gql` + query SelectUsersE2E($user1Id: Int!) { + users_by_pk(id: $user1Id) { + id + } + } +` \ No newline at end of file diff --git a/e2e/tests/api-driven/src/permissions/teamAdmin.feature b/e2e/tests/api-driven/src/permissions/teamAdmin.feature index ccf02d1968..7b9217dbb4 100644 --- a/e2e/tests/api-driven/src/permissions/teamAdmin.feature +++ b/e2e/tests/api-driven/src/permissions/teamAdmin.feature @@ -11,6 +11,7 @@ Feature: Testing Permissions for teamAdmin Role | flows | insert | | flows | update | | flows | delete | + | users | select | @regression @team-admin-permissions Scenario Outline: teamAdmin permissions in a different team @@ -22,4 +23,5 @@ Feature: Testing Permissions for teamAdmin Role | TABLE | ACTION | | flows | insert | | flows | update | - | flows | delete | \ No newline at end of file + | flows | delete | + | users | select | \ No newline at end of file From af3f0ec7131c61a9b98bb152baee35bbf0fd9a6c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dafydd=20Ll=C5=B7r=20Pearson?= Date: Thu, 21 Sep 2023 17:42:51 +0100 Subject: [PATCH 07/18] feat: Published flows --- .../src/permissions/queries/index.ts | 4 ++++ .../src/permissions/queries/publishedFlows.ts | 11 +++++++++ .../src/permissions/teamAdmin.feature | 23 +++++++++++-------- 3 files changed, 28 insertions(+), 10 deletions(-) create mode 100644 e2e/tests/api-driven/src/permissions/queries/publishedFlows.ts diff --git a/e2e/tests/api-driven/src/permissions/queries/index.ts b/e2e/tests/api-driven/src/permissions/queries/index.ts index 25a0045ff3..3b6d0c0340 100644 --- a/e2e/tests/api-driven/src/permissions/queries/index.ts +++ b/e2e/tests/api-driven/src/permissions/queries/index.ts @@ -1,4 +1,5 @@ import { DELETE_FLOW_QUERY, INSERT_FLOW_QUERY, UPDATE_FLOW_QUERY } from "./flows"; +import { INSERT_PUBLISHED_FLOW_QUERY } from "./publishedFlows"; import { SELECT_USERS_QUERY } from "./users"; export const queries = { @@ -9,5 +10,8 @@ export const queries = { }, users: { select: SELECT_USERS_QUERY, + }, + published_flows: { + insert: INSERT_PUBLISHED_FLOW_QUERY, } } as const; diff --git a/e2e/tests/api-driven/src/permissions/queries/publishedFlows.ts b/e2e/tests/api-driven/src/permissions/queries/publishedFlows.ts new file mode 100644 index 0000000000..166aef7401 --- /dev/null +++ b/e2e/tests/api-driven/src/permissions/queries/publishedFlows.ts @@ -0,0 +1,11 @@ +import gql from "graphql-tag"; + +export const INSERT_PUBLISHED_FLOW_QUERY = gql` + mutation InsertPublishedFlowsE2E($team1FlowId: uuid!, $activeUserId: Int) { + insert_published_flows(objects: {data: "{}", flow_id: $team1FlowId, publisher_id: $activeUserId}) { + returning { + id + } + } + } +` \ No newline at end of file diff --git a/e2e/tests/api-driven/src/permissions/teamAdmin.feature b/e2e/tests/api-driven/src/permissions/teamAdmin.feature index 7b9217dbb4..d6a1567864 100644 --- a/e2e/tests/api-driven/src/permissions/teamAdmin.feature +++ b/e2e/tests/api-driven/src/permissions/teamAdmin.feature @@ -7,11 +7,13 @@ Feature: Testing Permissions for teamAdmin Role Then they have access Examples: - | TABLE | ACTION | - | flows | insert | - | flows | update | - | flows | delete | - | users | select | + | TABLE | ACTION | + | flows | insert | + | flows | update | + | flows | delete | + | users | select | + # | opertations | select | + | published_flows | insert | @regression @team-admin-permissions Scenario Outline: teamAdmin permissions in a different team @@ -20,8 +22,9 @@ Feature: Testing Permissions for teamAdmin Role Then they do not have access Examples: - | TABLE | ACTION | - | flows | insert | - | flows | update | - | flows | delete | - | users | select | \ No newline at end of file + | TABLE | ACTION | + | flows | insert | + | flows | update | + | flows | delete | + | users | select | + | published_flows | insert | \ No newline at end of file From 14f12f1eb9e9df15559c3eee261099b3b6efa6e1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dafydd=20Ll=C5=B7r=20Pearson?= Date: Fri, 22 Sep 2023 07:47:35 +0100 Subject: [PATCH 08/18] refactor: Update syntax to be more explicit --- e2e/tests/api-driven/src/permissions/steps.ts | 6 +++--- .../api-driven/src/permissions/teamAdmin.feature | 11 ++++------- 2 files changed, 7 insertions(+), 10 deletions(-) diff --git a/e2e/tests/api-driven/src/permissions/steps.ts b/e2e/tests/api-driven/src/permissions/steps.ts index 2cce256ea7..6dbb519708 100644 --- a/e2e/tests/api-driven/src/permissions/steps.ts +++ b/e2e/tests/api-driven/src/permissions/steps.ts @@ -44,7 +44,7 @@ After("@team-admin-permissions", async function () { await cleanup(); }); -Given("a teamAdmin is a member of a team", async function (this: CustomWorld) { +Given("a teamAdmin from team1", async function (this: CustomWorld) { await addUserToTeam(this.user1Id, this.team1Id); const user = await getUser(this.user1Email); @@ -58,7 +58,7 @@ Given("a teamAdmin is a member of a team", async function (this: CustomWorld) { }); Given( - "a teamAdmin is not in the requested team", + "a teamAdmin from team2", async function (this: CustomWorld) { await addUserToTeam(this.user2Id, this.team2Id); const user = await getUser(this.user2Email); @@ -74,7 +74,7 @@ Given( ); When( - "they perform {string} on {string}", + "they perform {string} on team1's {string}", async function (this: CustomWorld, action: Action, table: Table) { try { await performGQLQuery({ diff --git a/e2e/tests/api-driven/src/permissions/teamAdmin.feature b/e2e/tests/api-driven/src/permissions/teamAdmin.feature index d6a1567864..9d3c7ce21a 100644 --- a/e2e/tests/api-driven/src/permissions/teamAdmin.feature +++ b/e2e/tests/api-driven/src/permissions/teamAdmin.feature @@ -2,8 +2,8 @@ Feature: Testing Permissions for teamAdmin Role @regression @team-admin-permissions Scenario Outline: teamAdmin permissions - Given a teamAdmin is a member of a team - When they perform "" on "" + Given a teamAdmin from team1 + When they perform "" on team1's "
" Then they have access Examples: @@ -11,14 +11,12 @@ Feature: Testing Permissions for teamAdmin Role | flows | insert | | flows | update | | flows | delete | - | users | select | - # | opertations | select | | published_flows | insert | @regression @team-admin-permissions Scenario Outline: teamAdmin permissions in a different team - Given a teamAdmin is not in the requested team - When they perform "" on "
" + Given a teamAdmin from team2 + When they perform "" on team1's "
" Then they do not have access Examples: @@ -26,5 +24,4 @@ Feature: Testing Permissions for teamAdmin Role | flows | insert | | flows | update | | flows | delete | - | users | select | | published_flows | insert | \ No newline at end of file From f5b5fbbb20ef965347ed3c09b3a53bab5d7187f7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dafydd=20Ll=C5=B7r=20Pearson?= Date: Fri, 22 Sep 2023 08:16:33 +0100 Subject: [PATCH 09/18] fix: Harden tests --- e2e/tests/api-driven/src/permissions/helpers.ts | 2 +- .../api-driven/src/permissions/queries/flows.ts | 6 +++--- .../src/permissions/queries/publishedFlows.ts | 2 +- .../api-driven/src/permissions/queries/users.ts | 2 +- e2e/tests/api-driven/src/permissions/steps.ts | 17 +++++++++++------ 5 files changed, 17 insertions(+), 12 deletions(-) diff --git a/e2e/tests/api-driven/src/permissions/helpers.ts b/e2e/tests/api-driven/src/permissions/helpers.ts index e3b5665930..4a9b5c5dc3 100644 --- a/e2e/tests/api-driven/src/permissions/helpers.ts +++ b/e2e/tests/api-driven/src/permissions/helpers.ts @@ -56,7 +56,7 @@ export const performGQLQuery = async ({ const query = queries[table][action]; const variables = buildVariables(query, world) const client = (await getClient(world.activeUserEmail)).client; - const result = await client.request(query, variables); + const { result } = await client.request>(query, variables); return result; }; diff --git a/e2e/tests/api-driven/src/permissions/queries/flows.ts b/e2e/tests/api-driven/src/permissions/queries/flows.ts index dc4ebd78c6..282a89b05b 100644 --- a/e2e/tests/api-driven/src/permissions/queries/flows.ts +++ b/e2e/tests/api-driven/src/permissions/queries/flows.ts @@ -2,7 +2,7 @@ import { gql } from "graphql-tag"; export const INSERT_FLOW_QUERY = gql` mutation InsertFlowE2E($team1Id: Int) { - insert_flows( + result: insert_flows( objects: { data: "{hello: 'world'}" slug: "e2e-test-flow" @@ -18,7 +18,7 @@ export const INSERT_FLOW_QUERY = gql` export const UPDATE_FLOW_QUERY = gql` mutation UpdateFlowE2E($team1FlowId: uuid!) { - update_flows_by_pk(pk_columns: {id: $team1FlowId}, _set: {data: "{hello: 'world2'}"}) { + result: update_flows_by_pk(pk_columns: {id: $team1FlowId}, _set: {data: "{hello: 'world2'}"}) { id } } @@ -26,7 +26,7 @@ export const UPDATE_FLOW_QUERY = gql` export const DELETE_FLOW_QUERY = gql` mutation DeleteFlowE2E($team1FlowId: uuid!) { - delete_flows_by_pk(id: $team1FlowId) { + result: delete_flows_by_pk(id: $team1FlowId) { id } } diff --git a/e2e/tests/api-driven/src/permissions/queries/publishedFlows.ts b/e2e/tests/api-driven/src/permissions/queries/publishedFlows.ts index 166aef7401..0cfc3947fb 100644 --- a/e2e/tests/api-driven/src/permissions/queries/publishedFlows.ts +++ b/e2e/tests/api-driven/src/permissions/queries/publishedFlows.ts @@ -2,7 +2,7 @@ import gql from "graphql-tag"; export const INSERT_PUBLISHED_FLOW_QUERY = gql` mutation InsertPublishedFlowsE2E($team1FlowId: uuid!, $activeUserId: Int) { - insert_published_flows(objects: {data: "{}", flow_id: $team1FlowId, publisher_id: $activeUserId}) { + result: insert_published_flows(objects: {data: "{}", flow_id: $team1FlowId, publisher_id: $activeUserId}) { returning { id } diff --git a/e2e/tests/api-driven/src/permissions/queries/users.ts b/e2e/tests/api-driven/src/permissions/queries/users.ts index bc236dbae3..db463f9e01 100644 --- a/e2e/tests/api-driven/src/permissions/queries/users.ts +++ b/e2e/tests/api-driven/src/permissions/queries/users.ts @@ -2,7 +2,7 @@ import gql from "graphql-tag"; export const SELECT_USERS_QUERY = gql` query SelectUsersE2E($user1Id: Int!) { - users_by_pk(id: $user1Id) { + result: users_by_pk(id: $user1Id) { id } } diff --git a/e2e/tests/api-driven/src/permissions/steps.ts b/e2e/tests/api-driven/src/permissions/steps.ts index 6dbb519708..48d80bbb44 100644 --- a/e2e/tests/api-driven/src/permissions/steps.ts +++ b/e2e/tests/api-driven/src/permissions/steps.ts @@ -14,10 +14,11 @@ export class CustomWorld extends World { user2Email!: string; team2FlowId!: string; - error?: Error = undefined; - activeUserId!: number; activeUserEmail!: string; + + error?: Error = undefined; + result: unknown = null; } Before("@team-admin-permissions", async function () { @@ -77,7 +78,7 @@ When( "they perform {string} on team1's {string}", async function (this: CustomWorld, action: Action, table: Table) { try { - await performGQLQuery({ + this.result = await performGQLQuery({ world: this, action, table, @@ -93,13 +94,17 @@ When( ); Then("they have access", function (this: CustomWorld) { - if (this.error) { + if (!this.result) { + assert.fail("Permission should have been granted - check test setup"); + } else if (this.error) { assert.fail(`Permission query failed with error: ${this.error.message}`); } }); Then("they do not have access", function (this: CustomWorld) { - if (this.error) { - assert.ok(`Permission query failed with error: ${this.error.message}`); + if (this.result) { + assert.fail("Permission should not have been granted - check test setup"); + } else if (this.error) { + assert.ok(`Permission query failed with error: ${this.error.message}`) } }); From 3e120bc2199eaa282b0b46eba2290751b4f40b08 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dafydd=20Ll=C5=B7r=20Pearson?= Date: Fri, 22 Sep 2023 08:49:17 +0100 Subject: [PATCH 10/18] feat: Tests for teamAdmin selecting users --- .../src/permissions/queries/index.ts | 6 ++- .../src/permissions/queries/teamMembers.ts | 9 ++++ e2e/tests/api-driven/src/permissions/steps.ts | 50 ++++++++++++++++++- .../src/permissions/teamAdmin.feature | 24 ++++++++- 4 files changed, 85 insertions(+), 4 deletions(-) create mode 100644 e2e/tests/api-driven/src/permissions/queries/teamMembers.ts diff --git a/e2e/tests/api-driven/src/permissions/queries/index.ts b/e2e/tests/api-driven/src/permissions/queries/index.ts index 3b6d0c0340..b751f47b9e 100644 --- a/e2e/tests/api-driven/src/permissions/queries/index.ts +++ b/e2e/tests/api-driven/src/permissions/queries/index.ts @@ -1,5 +1,6 @@ import { DELETE_FLOW_QUERY, INSERT_FLOW_QUERY, UPDATE_FLOW_QUERY } from "./flows"; import { INSERT_PUBLISHED_FLOW_QUERY } from "./publishedFlows"; +import { SELECT_TEAM_MEMBERS_QUERY } from "./teamMembers"; import { SELECT_USERS_QUERY } from "./users"; export const queries = { @@ -13,5 +14,8 @@ export const queries = { }, published_flows: { insert: INSERT_PUBLISHED_FLOW_QUERY, - } + }, + team_members: { + select: SELECT_TEAM_MEMBERS_QUERY, + }, } as const; diff --git a/e2e/tests/api-driven/src/permissions/queries/teamMembers.ts b/e2e/tests/api-driven/src/permissions/queries/teamMembers.ts new file mode 100644 index 0000000000..1bd66b7099 --- /dev/null +++ b/e2e/tests/api-driven/src/permissions/queries/teamMembers.ts @@ -0,0 +1,9 @@ +import gql from "graphql-tag"; + +export const SELECT_TEAM_MEMBERS_QUERY = gql` + query SelectTeamMembersE2E($user1Id: Int) { + result: team_members(where: {user_id: {_eq: $user1Id}}) { + id + } + } +` \ No newline at end of file diff --git a/e2e/tests/api-driven/src/permissions/steps.ts b/e2e/tests/api-driven/src/permissions/steps.ts index 48d80bbb44..0e532a1c5e 100644 --- a/e2e/tests/api-driven/src/permissions/steps.ts +++ b/e2e/tests/api-driven/src/permissions/steps.ts @@ -93,6 +93,48 @@ When( }, ); +When( + "they perform {string} on themselves in {string}", + async function (this: CustomWorld, action: Action, table: Table) { + try { + this.result = await performGQLQuery({ + world: this, + action, + table, + }); + } catch (error) { + if (error instanceof Error) { + this.error = error; + return; + } + throw error; + } + }, +); + +When( + "they perform {string} on a different user in {string}", + async function (this: CustomWorld, action: Action, table: Table) { + try { + this.result = await performGQLQuery({ + world: { + ...this, + // Point query to a different user + user1Id: this.user2Id + }, + action, + table, + }); + } catch (error) { + if (error instanceof Error) { + this.error = error; + return; + } + throw error; + } + }, +); + Then("they have access", function (this: CustomWorld) { if (!this.result) { assert.fail("Permission should have been granted - check test setup"); @@ -102,9 +144,13 @@ Then("they have access", function (this: CustomWorld) { }); Then("they do not have access", function (this: CustomWorld) { - if (this.result) { - assert.fail("Permission should not have been granted - check test setup"); + const isResultEmpty = Array.isArray(this.result) && !this.result.length; + + if (isResultEmpty) { + assert.ok(`Permission query did not return results`) } else if (this.error) { assert.ok(`Permission query failed with error: ${this.error.message}`) + } else if (this.result) { + assert.fail("Permission should not have been granted - check test setup"); } }); diff --git a/e2e/tests/api-driven/src/permissions/teamAdmin.feature b/e2e/tests/api-driven/src/permissions/teamAdmin.feature index 9d3c7ce21a..232b4511b3 100644 --- a/e2e/tests/api-driven/src/permissions/teamAdmin.feature +++ b/e2e/tests/api-driven/src/permissions/teamAdmin.feature @@ -24,4 +24,26 @@ Feature: Testing Permissions for teamAdmin Role | flows | insert | | flows | update | | flows | delete | - | published_flows | insert | \ No newline at end of file + | published_flows | insert | + + @regression @team-admin-permissions + Scenario Outline: teamAdmin permissions - querying themselves + Given a teamAdmin from team1 + When they perform "" on themselves in "
" + Then they have access + + Examples: + | TABLE | ACTION | + | users | select | + | team_members | select | + + @regression @team-admin-permissions + Scenario Outline: teamAdmin permissions - querying other users + Given a teamAdmin from team1 + When they perform "" on a different user in "
" + Then they do not have access + + Examples: + | TABLE | ACTION | + | users | select | + | team_members | select | \ No newline at end of file From 5af14511341e6863846ae3d503e811d422f06dbf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dafydd=20Ll=C5=B7r=20Pearson?= Date: Fri, 22 Sep 2023 09:06:54 +0100 Subject: [PATCH 11/18] fix: Operations permissions --- hasura.planx.uk/metadata/tables.yaml | 42 ++++++++++++++++++++++++---- hasura.planx.uk/tests/flows.test.js | 26 ++++++++++++++--- 2 files changed, 58 insertions(+), 10 deletions(-) diff --git a/hasura.planx.uk/metadata/tables.yaml b/hasura.planx.uk/metadata/tables.yaml index 085cf4e6b7..57371da076 100644 --- a/hasura.planx.uk/metadata/tables.yaml +++ b/hasura.planx.uk/metadata/tables.yaml @@ -526,7 +526,15 @@ - flow_id - role: teamEditor permission: - check: {} + check: + flow: + team: + members: + _and: + - user_id: + _eq: x-hasura-user-id + - role: + _eq: teamEditor columns: - id - actor_id @@ -561,14 +569,36 @@ update_permissions: - role: platformAdmin permission: - columns: [] + columns: + - id + - actor_id + - version + - data + - created_at + - updated_at + - flow_id filter: {} - check: null + check: {} - role: teamEditor permission: - columns: [] - filter: {} - check: null + columns: + - id + - actor_id + - version + - data + - created_at + - updated_at + - flow_id + filter: + flow: + team: + members: + _and: + - user_id: + _eq: x-hasura-user-id + - role: + _eq: teamEditor + check: {} - table: schema: public name: payment_requests diff --git a/hasura.planx.uk/tests/flows.test.js b/hasura.planx.uk/tests/flows.test.js index c7c0075612..e3c06cdab5 100644 --- a/hasura.planx.uk/tests/flows.test.js +++ b/hasura.planx.uk/tests/flows.test.js @@ -48,14 +48,23 @@ describe("flows and operations", () => { expect(i.queries).toContain("operations"); }); - test("can update flows", () => { + test("can update flows and their associated operations", () => { expect(i.mutations).toContain("update_flows_by_pk"); expect(i.mutations).toContain("update_flows"); + expect(i.mutations).toContain("update_operations_by_pk"); + expect(i.mutations).toContain("update_operations"); }); - test("can create flows", () => { + test("can create flows and their associated operations", () => { expect(i.mutations).toContain("insert_flows_one"); expect(i.mutations).toContain("insert_flows"); + expect(i.mutations).toContain("insert_operations_one"); + expect(i.mutations).toContain("insert_operations"); + }); + + test("cannot delete operations", () => { + expect(i.mutations).not.toContain("delete_operations_by_pk"); + expect(i.mutations).not.toContain("delete_operations"); }); test("can query published flows", () => { @@ -86,14 +95,18 @@ describe("flows and operations", () => { expect(i.queries).toContain("operations"); }); - test("can update flows", () => { + test("can update flows and their associated operations", () => { expect(i.mutations).toContain("update_flows_by_pk"); expect(i.mutations).toContain("update_flows"); + expect(i.mutations).toContain("update_operations_by_pk"); + expect(i.mutations).toContain("update_operations"); }); - test("can create flows", () => { + test("can create flows and their associated operations", () => { expect(i.mutations).toContain("insert_flows_one"); expect(i.mutations).toContain("insert_flows"); + expect(i.mutations).toContain("insert_operations_one"); + expect(i.mutations).toContain("insert_operations"); }); test("can delete flows", () => { @@ -101,6 +114,11 @@ describe("flows and operations", () => { expect(i.mutations).toContain("delete_flows"); }); + test("cannot delete operations", () => { + expect(i.mutations).not.toContain("delete_operations_by_pk"); + expect(i.mutations).not.toContain("delete_operations"); + }); + test("can query published flows", () => { expect(i.queries).toContain("published_flows"); }); From 31413e591fcf2f0f917435cbd32ca7925144a5c3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dafydd=20Ll=C5=B7r=20Pearson?= Date: Fri, 22 Sep 2023 09:36:36 +0100 Subject: [PATCH 12/18] feat: Operations E2E permission tests --- .../src/permissions/queries/index.ts | 5 +++++ .../src/permissions/queries/operations.ts | 22 +++++++++++++++++++ e2e/tests/api-driven/src/permissions/steps.ts | 7 +++--- .../src/permissions/teamAdmin.feature | 4 ++++ 4 files changed, 35 insertions(+), 3 deletions(-) create mode 100644 e2e/tests/api-driven/src/permissions/queries/operations.ts diff --git a/e2e/tests/api-driven/src/permissions/queries/index.ts b/e2e/tests/api-driven/src/permissions/queries/index.ts index b751f47b9e..273852cf10 100644 --- a/e2e/tests/api-driven/src/permissions/queries/index.ts +++ b/e2e/tests/api-driven/src/permissions/queries/index.ts @@ -1,4 +1,5 @@ import { DELETE_FLOW_QUERY, INSERT_FLOW_QUERY, UPDATE_FLOW_QUERY } from "./flows"; +import { INSERT_OPERATION_QUERY, UPDATE_OPERATION_QUERY } from "./operations"; import { INSERT_PUBLISHED_FLOW_QUERY } from "./publishedFlows"; import { SELECT_TEAM_MEMBERS_QUERY } from "./teamMembers"; import { SELECT_USERS_QUERY } from "./users"; @@ -18,4 +19,8 @@ export const queries = { team_members: { select: SELECT_TEAM_MEMBERS_QUERY, }, + operations: { + insert: INSERT_OPERATION_QUERY, + update: UPDATE_OPERATION_QUERY, + } } as const; diff --git a/e2e/tests/api-driven/src/permissions/queries/operations.ts b/e2e/tests/api-driven/src/permissions/queries/operations.ts new file mode 100644 index 0000000000..eb070b8992 --- /dev/null +++ b/e2e/tests/api-driven/src/permissions/queries/operations.ts @@ -0,0 +1,22 @@ +import { gql } from "graphql-tag"; + +export const INSERT_OPERATION_QUERY = gql` + mutation InsertOperationE2E($team1FlowId: uuid!) { + result: insert_operations_one(object: { + data: "{}", + flow_id: $team1FlowId, + }) { + id + } + } +`; + +export const UPDATE_OPERATION_QUERY = gql` + mutation UpdateOperationE2E($team1FlowId: uuid!) { + result: update_operations(where: {flow_id: {_eq: $team1FlowId}}, _set: {data: ""}) { + returning { + id + } + } + } +`; \ No newline at end of file diff --git a/e2e/tests/api-driven/src/permissions/steps.ts b/e2e/tests/api-driven/src/permissions/steps.ts index 0e532a1c5e..60b2ab121d 100644 --- a/e2e/tests/api-driven/src/permissions/steps.ts +++ b/e2e/tests/api-driven/src/permissions/steps.ts @@ -18,7 +18,7 @@ export class CustomWorld extends World { activeUserEmail!: string; error?: Error = undefined; - result: unknown = null; + result: any[] | Record<"returning", any[]> | null = null; } Before("@team-admin-permissions", async function () { @@ -144,9 +144,10 @@ Then("they have access", function (this: CustomWorld) { }); Then("they do not have access", function (this: CustomWorld) { - const isResultEmpty = Array.isArray(this.result) && !this.result.length; + const isResultEmpty = (Array.isArray(this.result) && !this.result.length); + const isResultSetEmpty = this.result && !Array.isArray(this.result) && !this.result.returning.length; - if (isResultEmpty) { + if (isResultEmpty || isResultSetEmpty) { assert.ok(`Permission query did not return results`) } else if (this.error) { assert.ok(`Permission query failed with error: ${this.error.message}`) diff --git a/e2e/tests/api-driven/src/permissions/teamAdmin.feature b/e2e/tests/api-driven/src/permissions/teamAdmin.feature index 232b4511b3..456280366b 100644 --- a/e2e/tests/api-driven/src/permissions/teamAdmin.feature +++ b/e2e/tests/api-driven/src/permissions/teamAdmin.feature @@ -10,6 +10,8 @@ Feature: Testing Permissions for teamAdmin Role | TABLE | ACTION | | flows | insert | | flows | update | + | operations | insert | + | operations | update | | flows | delete | | published_flows | insert | @@ -23,6 +25,8 @@ Feature: Testing Permissions for teamAdmin Role | TABLE | ACTION | | flows | insert | | flows | update | + | operations | insert | + | operations | update | | flows | delete | | published_flows | insert | From 367744ff1266c1cef0586423a3381ac166c31917 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dafydd=20Ll=C5=B7r=20Pearson?= Date: Fri, 22 Sep 2023 09:38:25 +0100 Subject: [PATCH 13/18] fix: Linting - Will look into why husky wasn't picking these up in another PR --- .../api-driven/src/permissions/helpers.ts | 31 ++++++---- .../src/permissions/queries/flows.ts | 5 +- .../src/permissions/queries/index.ts | 8 ++- .../src/permissions/queries/operations.ts | 14 +++-- .../src/permissions/queries/publishedFlows.ts | 10 +++- .../src/permissions/queries/teamMembers.ts | 4 +- .../src/permissions/queries/users.ts | 2 +- e2e/tests/api-driven/src/permissions/steps.ts | 59 +++++++++---------- 8 files changed, 78 insertions(+), 55 deletions(-) diff --git a/e2e/tests/api-driven/src/permissions/helpers.ts b/e2e/tests/api-driven/src/permissions/helpers.ts index 4a9b5c5dc3..ee62b7d322 100644 --- a/e2e/tests/api-driven/src/permissions/helpers.ts +++ b/e2e/tests/api-driven/src/permissions/helpers.ts @@ -1,4 +1,4 @@ -import { DocumentNode, Kind } from 'graphql/language'; +import { DocumentNode, Kind } from "graphql/language"; import { $admin, getClient } from "../client"; import { CustomWorld } from "./steps"; import { queries } from "./queries"; @@ -9,7 +9,7 @@ export type Table = keyof typeof queries; interface PerformGQLQueryArgs { world: CustomWorld; - action: Action + action: Action; table: Table; } @@ -30,11 +30,17 @@ export const cleanup = async () => { export const setup = async () => { const user1Id = await createUser({ email: "e2e-user-1@opensystemslab.io" }); const teamId1 = await createTeam({ name: "E2E Team 1", slug: "e2e-team1" }); - const team1FlowId = await createFlow({ teamId: teamId1, slug: "team-1-flow" }); - + const team1FlowId = await createFlow({ + teamId: teamId1, + slug: "team-1-flow", + }); + const user2Id = await createUser({ email: "e2e-user-2@opensystemslab.io" }); const teamId2 = await createTeam({ name: "E2E Team 2", slug: "e2e-team2" }); - const team2FlowId = await createFlow({ teamId: teamId2, slug: "team-2-flow" }); + const team2FlowId = await createFlow({ + teamId: teamId2, + slug: "team-2-flow", + }); const world = { user1Id, @@ -54,9 +60,12 @@ export const performGQLQuery = async ({ table, }: PerformGQLQueryArgs) => { const query = queries[table][action]; - const variables = buildVariables(query, world) + const variables = buildVariables(query, world); const client = (await getClient(world.activeUserEmail)).client; - const { result } = await client.request>(query, variables); + const { result } = await client.request>( + query, + variables, + ); return result; }; @@ -65,7 +74,7 @@ export const performGQLQuery = async ({ * Match with values from our test world to construct variables for query */ const buildVariables = (query: DocumentNode, world: CustomWorld) => { - const variables = {} + const variables = {}; const definitionNode = query.definitions[0]; if (definitionNode.kind !== Kind.OPERATION_DEFINITION) return variables; @@ -73,12 +82,12 @@ const buildVariables = (query: DocumentNode, world: CustomWorld) => { Object.keys(world).forEach((key) => { const isVariableUsedInQuery = definitionNode.variableDefinitions!.find( - (varDef) => varDef.variable.name.value === key + (varDef) => varDef.variable.name.value === key, ); if (isVariableUsedInQuery) { variables[key] = world[key]; } - }) + }); return variables; -} \ No newline at end of file +}; diff --git a/e2e/tests/api-driven/src/permissions/queries/flows.ts b/e2e/tests/api-driven/src/permissions/queries/flows.ts index 282a89b05b..fb2051943c 100644 --- a/e2e/tests/api-driven/src/permissions/queries/flows.ts +++ b/e2e/tests/api-driven/src/permissions/queries/flows.ts @@ -18,7 +18,10 @@ export const INSERT_FLOW_QUERY = gql` export const UPDATE_FLOW_QUERY = gql` mutation UpdateFlowE2E($team1FlowId: uuid!) { - result: update_flows_by_pk(pk_columns: {id: $team1FlowId}, _set: {data: "{hello: 'world2'}"}) { + result: update_flows_by_pk( + pk_columns: { id: $team1FlowId } + _set: { data: "{hello: 'world2'}" } + ) { id } } diff --git a/e2e/tests/api-driven/src/permissions/queries/index.ts b/e2e/tests/api-driven/src/permissions/queries/index.ts index 273852cf10..de10fd1385 100644 --- a/e2e/tests/api-driven/src/permissions/queries/index.ts +++ b/e2e/tests/api-driven/src/permissions/queries/index.ts @@ -1,4 +1,8 @@ -import { DELETE_FLOW_QUERY, INSERT_FLOW_QUERY, UPDATE_FLOW_QUERY } from "./flows"; +import { + DELETE_FLOW_QUERY, + INSERT_FLOW_QUERY, + UPDATE_FLOW_QUERY, +} from "./flows"; import { INSERT_OPERATION_QUERY, UPDATE_OPERATION_QUERY } from "./operations"; import { INSERT_PUBLISHED_FLOW_QUERY } from "./publishedFlows"; import { SELECT_TEAM_MEMBERS_QUERY } from "./teamMembers"; @@ -22,5 +26,5 @@ export const queries = { operations: { insert: INSERT_OPERATION_QUERY, update: UPDATE_OPERATION_QUERY, - } + }, } as const; diff --git a/e2e/tests/api-driven/src/permissions/queries/operations.ts b/e2e/tests/api-driven/src/permissions/queries/operations.ts index eb070b8992..fc89ee7a19 100644 --- a/e2e/tests/api-driven/src/permissions/queries/operations.ts +++ b/e2e/tests/api-driven/src/permissions/queries/operations.ts @@ -2,10 +2,9 @@ import { gql } from "graphql-tag"; export const INSERT_OPERATION_QUERY = gql` mutation InsertOperationE2E($team1FlowId: uuid!) { - result: insert_operations_one(object: { - data: "{}", - flow_id: $team1FlowId, - }) { + result: insert_operations_one( + object: { data: "{}", flow_id: $team1FlowId } + ) { id } } @@ -13,10 +12,13 @@ export const INSERT_OPERATION_QUERY = gql` export const UPDATE_OPERATION_QUERY = gql` mutation UpdateOperationE2E($team1FlowId: uuid!) { - result: update_operations(where: {flow_id: {_eq: $team1FlowId}}, _set: {data: ""}) { + result: update_operations( + where: { flow_id: { _eq: $team1FlowId } } + _set: { data: "" } + ) { returning { id } } } -`; \ No newline at end of file +`; diff --git a/e2e/tests/api-driven/src/permissions/queries/publishedFlows.ts b/e2e/tests/api-driven/src/permissions/queries/publishedFlows.ts index 0cfc3947fb..ff2e13165a 100644 --- a/e2e/tests/api-driven/src/permissions/queries/publishedFlows.ts +++ b/e2e/tests/api-driven/src/permissions/queries/publishedFlows.ts @@ -2,10 +2,16 @@ import gql from "graphql-tag"; export const INSERT_PUBLISHED_FLOW_QUERY = gql` mutation InsertPublishedFlowsE2E($team1FlowId: uuid!, $activeUserId: Int) { - result: insert_published_flows(objects: {data: "{}", flow_id: $team1FlowId, publisher_id: $activeUserId}) { + result: insert_published_flows( + objects: { + data: "{}" + flow_id: $team1FlowId + publisher_id: $activeUserId + } + ) { returning { id } } } -` \ No newline at end of file +`; diff --git a/e2e/tests/api-driven/src/permissions/queries/teamMembers.ts b/e2e/tests/api-driven/src/permissions/queries/teamMembers.ts index 1bd66b7099..5432a2bd9a 100644 --- a/e2e/tests/api-driven/src/permissions/queries/teamMembers.ts +++ b/e2e/tests/api-driven/src/permissions/queries/teamMembers.ts @@ -2,8 +2,8 @@ import gql from "graphql-tag"; export const SELECT_TEAM_MEMBERS_QUERY = gql` query SelectTeamMembersE2E($user1Id: Int) { - result: team_members(where: {user_id: {_eq: $user1Id}}) { + result: team_members(where: { user_id: { _eq: $user1Id } }) { id } } -` \ No newline at end of file +`; diff --git a/e2e/tests/api-driven/src/permissions/queries/users.ts b/e2e/tests/api-driven/src/permissions/queries/users.ts index db463f9e01..b83638e8f7 100644 --- a/e2e/tests/api-driven/src/permissions/queries/users.ts +++ b/e2e/tests/api-driven/src/permissions/queries/users.ts @@ -6,4 +6,4 @@ export const SELECT_USERS_QUERY = gql` id } } -` \ No newline at end of file +`; diff --git a/e2e/tests/api-driven/src/permissions/steps.ts b/e2e/tests/api-driven/src/permissions/steps.ts index 60b2ab121d..a24ef5120c 100644 --- a/e2e/tests/api-driven/src/permissions/steps.ts +++ b/e2e/tests/api-driven/src/permissions/steps.ts @@ -1,7 +1,14 @@ import { After, Before, Given, Then, When, World } from "@cucumber/cucumber"; import { strict as assert } from "node:assert"; import { getUser } from "../globalHelpers"; -import { Action, Table, addUserToTeam, cleanup, performGQLQuery, setup } from "./helpers"; +import { + Action, + Table, + addUserToTeam, + cleanup, + performGQLQuery, + setup, +} from "./helpers"; export class CustomWorld extends World { user1Id!: number; @@ -13,28 +20,22 @@ export class CustomWorld extends World { user2Id!: number; user2Email!: string; team2FlowId!: string; - + activeUserId!: number; activeUserEmail!: string; - + error?: Error = undefined; result: any[] | Record<"returning", any[]> | null = null; } Before("@team-admin-permissions", async function () { - const { - user1Id, - teamId1, - team1FlowId, - user2Id, - teamId2, - team2FlowId - } = await setup(); + const { user1Id, teamId1, team1FlowId, user2Id, teamId2, team2FlowId } = + await setup(); this.user1Id = user1Id; this.user1Email = "e2e-user-1@opensystemslab.io"; this.team1Id = teamId1; this.team1FlowId = team1FlowId; - + this.user2Id = user2Id; this.user2Email = "e2e-user-2@opensystemslab.io"; this.team2Id = teamId2; @@ -58,21 +59,18 @@ Given("a teamAdmin from team1", async function (this: CustomWorld) { this.activeUserEmail = this.user1Email; }); -Given( - "a teamAdmin from team2", - async function (this: CustomWorld) { - await addUserToTeam(this.user2Id, this.team2Id); - const user = await getUser(this.user2Email); +Given("a teamAdmin from team2", async function (this: CustomWorld) { + await addUserToTeam(this.user2Id, this.team2Id); + const user = await getUser(this.user2Email); - assert.ok(user, "User is not defined"); - assert.strictEqual(user.teams.length, 1); - assert.strictEqual(user.teams[0].role, "teamEditor"); - assert.strictEqual(user.teams[0].team.id, this.team2Id); + assert.ok(user, "User is not defined"); + assert.strictEqual(user.teams.length, 1); + assert.strictEqual(user.teams[0].role, "teamEditor"); + assert.strictEqual(user.teams[0].team.id, this.team2Id); - this.activeUserId = this.user1Id; - this.activeUserEmail = this.user1Email; - }, -); + this.activeUserId = this.user1Id; + this.activeUserEmail = this.user1Email; +}); When( "they perform {string} on team1's {string}", @@ -120,7 +118,7 @@ When( world: { ...this, // Point query to a different user - user1Id: this.user2Id + user1Id: this.user2Id, }, action, table, @@ -144,13 +142,14 @@ Then("they have access", function (this: CustomWorld) { }); Then("they do not have access", function (this: CustomWorld) { - const isResultEmpty = (Array.isArray(this.result) && !this.result.length); - const isResultSetEmpty = this.result && !Array.isArray(this.result) && !this.result.returning.length; + const isResultEmpty = Array.isArray(this.result) && !this.result.length; + const isResultSetEmpty = + this.result && !Array.isArray(this.result) && !this.result.returning.length; if (isResultEmpty || isResultSetEmpty) { - assert.ok(`Permission query did not return results`) + assert.ok(`Permission query did not return results`); } else if (this.error) { - assert.ok(`Permission query failed with error: ${this.error.message}`) + assert.ok(`Permission query failed with error: ${this.error.message}`); } else if (this.result) { assert.fail("Permission should not have been granted - check test setup"); } From 2df8311e8bd13ae2c4d080d64642c2ec0be60e2f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dafydd=20Ll=C5=B7r=20Pearson?= Date: Fri, 22 Sep 2023 10:42:56 +0100 Subject: [PATCH 14/18] chore: Bump planx-core - Resolves CVE-2023-26144 - e2e/ui still to do in a follow on PR --- e2e/tests/ui-driven/package.json | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/e2e/tests/ui-driven/package.json b/e2e/tests/ui-driven/package.json index f5bc9accbf..57f3894a20 100644 --- a/e2e/tests/ui-driven/package.json +++ b/e2e/tests/ui-driven/package.json @@ -4,8 +4,7 @@ "test": "playwright test --grep-invert @regression", "test:regression": "playwright test", "test:debug": "DEBUG_LOG=true pnpm test", - "ui": "serve ../../../editor.planx.uk/build --single --listen 3000", - "postinstall": "./install-dependencies.sh" + "ui": "serve ../../../editor.planx.uk/build --single --listen 3000" }, "dependencies": { "@opensystemslab/planx-core": "git+https://github.com/theopensystemslab/planx-core#44e9ebe", From 6a562fc1c639f80082b948d028744e7908b41136 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dafydd=20Ll=C5=B7r=20Pearson?= Date: Thu, 28 Sep 2023 07:47:02 +0100 Subject: [PATCH 15/18] fix: Add back postinstall step... --- e2e/tests/ui-driven/package.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/e2e/tests/ui-driven/package.json b/e2e/tests/ui-driven/package.json index 57f3894a20..f5bc9accbf 100644 --- a/e2e/tests/ui-driven/package.json +++ b/e2e/tests/ui-driven/package.json @@ -4,7 +4,8 @@ "test": "playwright test --grep-invert @regression", "test:regression": "playwright test", "test:debug": "DEBUG_LOG=true pnpm test", - "ui": "serve ../../../editor.planx.uk/build --single --listen 3000" + "ui": "serve ../../../editor.planx.uk/build --single --listen 3000", + "postinstall": "./install-dependencies.sh" }, "dependencies": { "@opensystemslab/planx-core": "git+https://github.com/theopensystemslab/planx-core#44e9ebe", From d15795d192f4a499c4eb584666fd24f480aa6714 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dafydd=20Ll=C5=B7r=20Pearson?= Date: Fri, 29 Sep 2023 12:01:31 +0100 Subject: [PATCH 16/18] fix: Use safe throwaway emails --- e2e/tests/api-driven/src/permissions/helpers.ts | 4 ++-- e2e/tests/api-driven/src/permissions/steps.ts | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/e2e/tests/api-driven/src/permissions/helpers.ts b/e2e/tests/api-driven/src/permissions/helpers.ts index ee62b7d322..0a56840c8c 100644 --- a/e2e/tests/api-driven/src/permissions/helpers.ts +++ b/e2e/tests/api-driven/src/permissions/helpers.ts @@ -28,14 +28,14 @@ export const cleanup = async () => { }; export const setup = async () => { - const user1Id = await createUser({ email: "e2e-user-1@opensystemslab.io" }); + const user1Id = await createUser({ email: "e2e-user-1@example.com" }); const teamId1 = await createTeam({ name: "E2E Team 1", slug: "e2e-team1" }); const team1FlowId = await createFlow({ teamId: teamId1, slug: "team-1-flow", }); - const user2Id = await createUser({ email: "e2e-user-2@opensystemslab.io" }); + const user2Id = await createUser({ email: "e2e-user-2@example.com" }); const teamId2 = await createTeam({ name: "E2E Team 2", slug: "e2e-team2" }); const team2FlowId = await createFlow({ teamId: teamId2, diff --git a/e2e/tests/api-driven/src/permissions/steps.ts b/e2e/tests/api-driven/src/permissions/steps.ts index a24ef5120c..a9ef329a00 100644 --- a/e2e/tests/api-driven/src/permissions/steps.ts +++ b/e2e/tests/api-driven/src/permissions/steps.ts @@ -32,12 +32,12 @@ Before("@team-admin-permissions", async function () { const { user1Id, teamId1, team1FlowId, user2Id, teamId2, team2FlowId } = await setup(); this.user1Id = user1Id; - this.user1Email = "e2e-user-1@opensystemslab.io"; + this.user1Email = "e2e-user-1@example.com"; this.team1Id = teamId1; this.team1FlowId = team1FlowId; this.user2Id = user2Id; - this.user2Email = "e2e-user-2@opensystemslab.io"; + this.user2Email = "e2e-user-2@example.com"; this.team2Id = teamId2; this.team2FlowId = team2FlowId; }); From 6ae654d338345710f625456dd7ebda9b227c609b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dafydd=20Ll=C5=B7r=20Pearson?= Date: Fri, 29 Sep 2023 12:06:50 +0100 Subject: [PATCH 17/18] fix: any -> unknown --- e2e/tests/api-driven/src/permissions/steps.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/e2e/tests/api-driven/src/permissions/steps.ts b/e2e/tests/api-driven/src/permissions/steps.ts index a9ef329a00..80f6ad5cf5 100644 --- a/e2e/tests/api-driven/src/permissions/steps.ts +++ b/e2e/tests/api-driven/src/permissions/steps.ts @@ -25,7 +25,7 @@ export class CustomWorld extends World { activeUserEmail!: string; error?: Error = undefined; - result: any[] | Record<"returning", any[]> | null = null; + result: unknown[] | Record<"returning", unknown[]> | null = null; } Before("@team-admin-permissions", async function () { From 1833a1356eb7687ba231799fc7c30b8df053d733 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dafydd=20Ll=C5=B7r=20Pearson?= Date: Fri, 29 Sep 2023 12:44:17 +0100 Subject: [PATCH 18/18] fix: PR comments - clarify setup --- .../api-driven/src/permissions/helpers.ts | 8 +++++-- e2e/tests/api-driven/src/permissions/steps.ts | 11 +++++---- .../src/permissions/teamAdmin.feature | 23 +++++++++++-------- 3 files changed, 27 insertions(+), 15 deletions(-) diff --git a/e2e/tests/api-driven/src/permissions/helpers.ts b/e2e/tests/api-driven/src/permissions/helpers.ts index 0a56840c8c..aefdd32c57 100644 --- a/e2e/tests/api-driven/src/permissions/helpers.ts +++ b/e2e/tests/api-driven/src/permissions/helpers.ts @@ -28,14 +28,18 @@ export const cleanup = async () => { }; export const setup = async () => { - const user1Id = await createUser({ email: "e2e-user-1@example.com" }); + const user1Id = await createUser({ + email: "team1-teamEditor-user@example.com@example.com", + }); const teamId1 = await createTeam({ name: "E2E Team 1", slug: "e2e-team1" }); const team1FlowId = await createFlow({ teamId: teamId1, slug: "team-1-flow", }); - const user2Id = await createUser({ email: "e2e-user-2@example.com" }); + const user2Id = await createUser({ + email: "team2-teamEditor-user@example.com@example.com", + }); const teamId2 = await createTeam({ name: "E2E Team 2", slug: "e2e-team2" }); const team2FlowId = await createFlow({ teamId: teamId2, diff --git a/e2e/tests/api-driven/src/permissions/steps.ts b/e2e/tests/api-driven/src/permissions/steps.ts index 80f6ad5cf5..d0dc8e175c 100644 --- a/e2e/tests/api-driven/src/permissions/steps.ts +++ b/e2e/tests/api-driven/src/permissions/steps.ts @@ -11,16 +11,19 @@ import { } from "./helpers"; export class CustomWorld extends World { + // A teamEditor for team1 user1Id!: number; user1Email!: string; team1Id!: number; team1FlowId!: string; + // A teamEditor for team2 team2Id!: number; user2Id!: number; user2Email!: string; team2FlowId!: string; + // Either user1 or user2, depending on the test suite activeUserId!: number; activeUserEmail!: string; @@ -32,12 +35,12 @@ Before("@team-admin-permissions", async function () { const { user1Id, teamId1, team1FlowId, user2Id, teamId2, team2FlowId } = await setup(); this.user1Id = user1Id; - this.user1Email = "e2e-user-1@example.com"; + this.user1Email = "team1-teamEditor-user@example.com"; this.team1Id = teamId1; this.team1FlowId = team1FlowId; this.user2Id = user2Id; - this.user2Email = "e2e-user-2@example.com"; + this.user2Email = "team2-teamEditor-user@example.com"; this.team2Id = teamId2; this.team2FlowId = team2FlowId; }); @@ -46,7 +49,7 @@ After("@team-admin-permissions", async function () { await cleanup(); }); -Given("a teamAdmin from team1", async function (this: CustomWorld) { +Given("a teamEditor from team1", async function (this: CustomWorld) { await addUserToTeam(this.user1Id, this.team1Id); const user = await getUser(this.user1Email); @@ -59,7 +62,7 @@ Given("a teamAdmin from team1", async function (this: CustomWorld) { this.activeUserEmail = this.user1Email; }); -Given("a teamAdmin from team2", async function (this: CustomWorld) { +Given("a teamEditor from team2", async function (this: CustomWorld) { await addUserToTeam(this.user2Id, this.team2Id); const user = await getUser(this.user2Email); diff --git a/e2e/tests/api-driven/src/permissions/teamAdmin.feature b/e2e/tests/api-driven/src/permissions/teamAdmin.feature index 456280366b..bf4abe1e93 100644 --- a/e2e/tests/api-driven/src/permissions/teamAdmin.feature +++ b/e2e/tests/api-driven/src/permissions/teamAdmin.feature @@ -1,8 +1,10 @@ -Feature: Testing Permissions for teamAdmin Role +Feature: Testing Permissions for teamEditor Role +# Setup - we create two teams, with one teamEditor each + # Test that teamEditor1 can access team1's resources @regression @team-admin-permissions - Scenario Outline: teamAdmin permissions - Given a teamAdmin from team1 + Scenario Outline: teamEditor permissions + Given a teamEditor from team1 When they perform "" on team1's "
" Then they have access @@ -15,9 +17,10 @@ Feature: Testing Permissions for teamAdmin Role | flows | delete | | published_flows | insert | + # Test that teamEditor2 cannot access team1's resources @regression @team-admin-permissions - Scenario Outline: teamAdmin permissions in a different team - Given a teamAdmin from team2 + Scenario Outline: teamEditor permissions in a different team + Given a teamEditor from team2 When they perform "" on team1's "
" Then they do not have access @@ -30,9 +33,10 @@ Feature: Testing Permissions for teamAdmin Role | flows | delete | | published_flows | insert | + # Test that teamEditor1 can access their own records @regression @team-admin-permissions - Scenario Outline: teamAdmin permissions - querying themselves - Given a teamAdmin from team1 + Scenario Outline: teamEditor permissions - querying themselves + Given a teamEditor from team1 When they perform "" on themselves in "
" Then they have access @@ -41,9 +45,10 @@ Feature: Testing Permissions for teamAdmin Role | users | select | | team_members | select | + # Test that teamEditor1 can access teamEditor2's records @regression @team-admin-permissions - Scenario Outline: teamAdmin permissions - querying other users - Given a teamAdmin from team1 + Scenario Outline: teamEditor permissions - querying other users + Given a teamEditor from team1 When they perform "" on a different user in "
" Then they do not have access