From 614c7130330e4af00eaa6345d79616bb8c698b78 Mon Sep 17 00:00:00 2001 From: Blair Currey <12960453+BlairCurrey@users.noreply.github.com> Date: Fri, 28 Feb 2025 11:14:17 -0500 Subject: [PATCH 01/26] feat(frontend): disallow api secret update and change tenant form to submit sections independently --- packages/frontend/app/lib/validate.server.ts | 26 ++- .../frontend/app/routes/tenants.$tenantId.tsx | 209 +++++++++++------- 2 files changed, 148 insertions(+), 87 deletions(-) diff --git a/packages/frontend/app/lib/validate.server.ts b/packages/frontend/app/lib/validate.server.ts index 8e91af96a2..97e8e4404c 100644 --- a/packages/frontend/app/lib/validate.server.ts +++ b/packages/frontend/app/lib/validate.server.ts @@ -130,20 +130,32 @@ export const updateWalletAddressSchema = z }) .merge(uuidSchema) -export const updateTenantSchema = z +export const updateTenantGeneralSchema = z + .object({ + publicName: z.string().optional(), + email: z.string().email().or(z.literal('')) + }) + .merge(uuidSchema) + +export const updateTenantIpSchema = z + .object({ + idpSecret: z.string().optional(), + idpConsentUrl: z.string().optional() + }) + .merge(uuidSchema) + +export const updateTenantSensitiveSchema = z .object({ apiSecret: z .string() .min(10, { message: 'API Secret should be at least 10 characters long' }) - .max(255, { message: 'Maximum length of API Secret is 255 characters' }), - publicName: z.string().optional(), - email: z.string().email().or(z.literal('')), - idpConsentUrl: z.string().optional(), - idpSecret: z.string().optional() + .max(255, { message: 'Maximum length of API Secret is 255 characters' }) }) .merge(uuidSchema) export const createTenantSchema = z .object({}) - .merge(updateTenantSchema) + .merge(updateTenantGeneralSchema) + .merge(updateTenantIpSchema) + .merge(updateTenantSensitiveSchema) .omit({ id: true }) diff --git a/packages/frontend/app/routes/tenants.$tenantId.tsx b/packages/frontend/app/routes/tenants.$tenantId.tsx index 3cd6a6d38f..3180e668f5 100644 --- a/packages/frontend/app/routes/tenants.$tenantId.tsx +++ b/packages/frontend/app/routes/tenants.$tenantId.tsx @@ -12,7 +12,7 @@ import { useSubmit } from '@remix-run/react' import { type FormEvent, useState, useRef } from 'react' -import { z } from 'zod' +import { z, ZodSchema } from 'zod' import { DangerZone, PageHeader } from '~/components' import { Button, ErrorPanel, Input, PasswordInput } from '~/components/ui' import { @@ -21,8 +21,12 @@ import { } from '~/components/ConfirmationDialog' import { updateTenant, deleteTenant, whoAmI } from '~/lib/api/tenant.server' import { messageStorage, setMessageAndRedirect } from '~/lib/message.server' -import type { createTenantSchema } from '~/lib/validate.server' -import { updateTenantSchema, uuidSchema } from '~/lib/validate.server' +import { + updateTenantGeneralSchema, + updateTenantIpSchema, + updateTenantSensitiveSchema, + uuidSchema +} from '~/lib/validate.server' import type { ZodFieldErrors } from '~/shared/types' import { checkAuthAndRedirect } from '../lib/kratos_checks.server' import { getTenantInfo } from '~/lib/api/tenant.server' @@ -87,18 +91,12 @@ export default function ViewTenantPage() { )}

-
-
@@ -143,31 +141,26 @@ export default function ViewTenantPage() {

Identity Provider Information

- +
-
@@ -188,28 +181,42 @@ export default function ViewTenantPage() {
{/* Identity Provider Information - END */} {/* Sensitive Info */} -
-
-

Sensitive Information

- -
-
- -
-
- - -
-
- + {me.isOperator && ( +
+
+

Sensitive Information

+ +
+
+
+
+
+ + +
+
+ {!tenantDeleted && !me.isOperator && ( + + )} +
+
+
+
-
+ )} {/* Sensitive - END */} {/* DELETE TENANT - Danger zone */} {!tenantDeleted && me.isOperator && me.id !== tenant.id && ( @@ -237,12 +244,36 @@ export default function ViewTenantPage() { } export async function action({ request }: ActionFunctionArgs) { - const errors: { - fieldErrors: ZodFieldErrors - message: string[] + const actionResponse: { + errors: { + general: { + fieldErrors: ZodFieldErrors + message: string[] + } + ip: { + fieldErrors: ZodFieldErrors + message: string[] + } + sensitive: { + fieldErrors: ZodFieldErrors + message: string[] + } + } } = { - fieldErrors: {}, - message: [] + errors: { + general: { + fieldErrors: {}, + message: [] + }, + ip: { + fieldErrors: {}, + message: [] + }, + sensitive: { + fieldErrors: {}, + message: [] + } + } } const session = await messageStorage.getSession(request.headers.get('cookie')) @@ -250,30 +281,60 @@ export async function action({ request }: ActionFunctionArgs) { const intent = formData.get('intent') formData.delete('intent') + async function handleUpdateFormSubmit( + errorKey: keyof typeof actionResponse.errors, + schema: ZodSchema + ) { + const formEntries = Object.fromEntries(formData) + const result = schema.safeParse(formEntries) + + if (!result.success) { + actionResponse.errors[errorKey].fieldErrors = + result.error.flatten().fieldErrors + return json(actionResponse, { status: 400 }) + } + + const response = await updateTenant(request, { ...result.data }) + + if (!response?.tenant) { + actionResponse.errors[errorKey].message = [ + 'Could not update tenant. Please try again!' + ] + return json(actionResponse, { status: 400 }) + } + + return { formEntries } + } + switch (intent) { - case 'general': - case 'ip': + case 'general': { + const result = await handleUpdateFormSubmit( + intent, + updateTenantGeneralSchema + ) + if (!('formEntries' in result)) return result + break + } + case 'ip': { + const result = await handleUpdateFormSubmit(intent, updateTenantIpSchema) + if (!('formEntries' in result)) return result + break + } case 'sensitive': { - const formEntries = Object.fromEntries(formData) - const result = updateTenantSchema.safeParse(formEntries) - if (!result.success) { - errors.fieldErrors = result.error.flatten().fieldErrors - return json({ errors }, { status: 400 }) - } + const result = await handleUpdateFormSubmit( + intent, + updateTenantSensitiveSchema + ) - const response = await updateTenant(request, { - ...result.data - }) - - if (!response?.tenant) { - errors.message = ['Could not update tenant. Please try again!'] - return json({ errors }, { status: 400 }) - } + if (!('formEntries' in result)) return result const me = await whoAmI(request) - // We update the apiSecret of the session in case it changed. - if (formEntries.apiSecret && me.id === formEntries.id) { - session.set('apiSecret', formEntries.apiSecret) + if ( + result.formEntries.apiSecret && + me.id === result.formEntries.id && + !me.isOperator + ) { + session.set('apiSecret', result.formEntries.apiSecret) } break } @@ -282,10 +343,7 @@ export async function action({ request }: ActionFunctionArgs) { if (!result.success) { return setMessageAndRedirect({ session, - message: { - content: 'Invalid tenant ID.', - type: 'error' - }, + message: { content: 'Invalid tenant ID.', type: 'error' }, location: '.' }) } @@ -294,20 +352,14 @@ export async function action({ request }: ActionFunctionArgs) { if (!response) { return setMessageAndRedirect({ session, - message: { - content: 'Could not delete Tenant.', - type: 'error' - }, + message: { content: 'Could not delete Tenant.', type: 'error' }, location: '.' }) } return setMessageAndRedirect({ session, - message: { - content: 'Tenant was deleted.', - type: 'success' - }, + message: { content: 'Tenant was deleted.', type: 'success' }, location: '/tenants' }) } @@ -317,10 +369,7 @@ export async function action({ request }: ActionFunctionArgs) { return setMessageAndRedirect({ session, - message: { - content: 'Tenant information was updated', - type: 'success' - }, + message: { content: 'Tenant information was updated', type: 'success' }, location: '.' }) } From 84edc63f75f05c93d736afa5393ff3faa7198e9f Mon Sep 17 00:00:00 2001 From: Blair Currey <12960453+BlairCurrey@users.noreply.github.com> Date: Fri, 28 Feb 2025 11:14:42 -0500 Subject: [PATCH 02/26] feat(backend): disallow tenant api secret update in admin api --- packages/backend/src/graphql/resolvers/tenant.ts | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/packages/backend/src/graphql/resolvers/tenant.ts b/packages/backend/src/graphql/resolvers/tenant.ts index 6fdddf16da..eae36ebc3a 100644 --- a/packages/backend/src/graphql/resolvers/tenant.ts +++ b/packages/backend/src/graphql/resolvers/tenant.ts @@ -122,6 +122,17 @@ export const updateTenant: MutationResolvers['updateTenan }) } + if (isOperator && 'apiSecret' in args.input) { + throw new GraphQLError( + 'Operator cannot update apiSecret over admin api', + { + extensions: { + code: GraphQLErrorCode.BadUserInput + } + } + ) + } + const tenantService = await ctx.container.use('tenantService') try { const updatedTenant = await tenantService.update(args.input) From e881e3dcc7056569853b0cd713202a7d4d90fb9c Mon Sep 17 00:00:00 2001 From: Blair Currey <12960453+BlairCurrey@users.noreply.github.com> Date: Tue, 4 Mar 2025 13:01:00 -0500 Subject: [PATCH 03/26] feat(backend): add service method to update secret, call on app start --- packages/backend/src/index.ts | 8 +- packages/backend/src/tenants/errors.ts | 13 +++ packages/backend/src/tenants/service.test.ts | 89 +++++++++++++++++++- packages/backend/src/tenants/service.ts | 29 ++++++- 4 files changed, 134 insertions(+), 5 deletions(-) create mode 100644 packages/backend/src/tenants/errors.ts diff --git a/packages/backend/src/index.ts b/packages/backend/src/index.ts index 0dcde43128..adc4411965 100644 --- a/packages/backend/src/index.ts +++ b/packages/backend/src/index.ts @@ -236,7 +236,8 @@ export function initIocContainer( knex: await deps.use('knex'), tenantCache: await deps.use('tenantCache'), authServiceClient: deps.use('authServiceClient'), - tenantSettingService: await deps.use('tenantSettingService') + tenantSettingService: await deps.use('tenantSettingService'), + config: await deps.use('config') }) }) @@ -768,6 +769,11 @@ export const start = async ( Model.knex(knex) + // Update Operator Tenant from config + const tenantService = await container.use('tenantService') + console.log({ tenantService }) + await tenantService.updateOperatorApiSecretFromConfig() + await app.boot() await app.startAdminServer(config.adminPort) logger.info(`Admin listening on ${app.getAdminPort()}`) diff --git a/packages/backend/src/tenants/errors.ts b/packages/backend/src/tenants/errors.ts new file mode 100644 index 0000000000..d3ed0ac0b6 --- /dev/null +++ b/packages/backend/src/tenants/errors.ts @@ -0,0 +1,13 @@ +export enum TenantError { + TenantNotFound = 'TenantNotFound' +} + +// eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/explicit-module-boundary-types +export const isTenantError = (o: any): o is TenantError => + Object.values(TenantError).includes(o) + +export const errorToMessage: { + [key in TenantError]: string +} = { + [TenantError.TenantNotFound]: 'Tenant not found' +} diff --git a/packages/backend/src/tenants/service.test.ts b/packages/backend/src/tenants/service.test.ts index b5c3f01af7..ba64b8af84 100644 --- a/packages/backend/src/tenants/service.test.ts +++ b/packages/backend/src/tenants/service.test.ts @@ -1,7 +1,6 @@ import assert from 'assert' import { faker } from '@faker-js/faker' import { IocContract } from '@adonisjs/fold' -import nock from 'nock' import { Knex } from 'knex' import { AppServices } from '../app' import { initIocContainer } from '..' @@ -18,6 +17,7 @@ import { AuthServiceClient } from '../auth-service-client/client' import { withConfigOverride } from '../tests/helpers' import { TenantSetting } from './settings/model' import { TenantSettingService } from './settings/service' +import { isTenantError, TenantError } from './errors' describe('Tenant Service', (): void => { let deps: IocContract @@ -47,7 +47,6 @@ describe('Tenant Service', (): void => { }) afterAll(async (): Promise => { - nock.cleanAll() await appContainer.shutdown() }) @@ -447,3 +446,89 @@ describe('Tenant Service', (): void => { }) }) }) + +describe('Tenant Service (no tenant truncate)', (): void => { + let deps: IocContract + let config: IAppConfig + let appContainer: TestContainer + let tenantService: TenantService + let knex: Knex + let updateSpyWasCalled: boolean + const dbSchema = 'tenant_service_test_schema2' + + beforeAll(async (): Promise => { + deps = initIocContainer({ + ...Config, + dbSchema + }) + knex = await deps.use('knex') + config = await deps.use('config') + tenantService = await deps.use('tenantService') + + const updateOperatorSecretSpy = jest.spyOn( + tenantService, + 'updateOperatorApiSecretFromConfig' + ) + appContainer = await createTestApp(deps) + updateSpyWasCalled = updateOperatorSecretSpy.mock.calls.length > 0 + }) + + afterEach(async (): Promise => { + await truncateTables(knex, false, dbSchema) + }) + + afterAll(async (): Promise => { + await appContainer.shutdown() + }) + describe('updateOperatorApiSecretFromConfig', () => { + test('called on application start', async (): Promise => { + expect(updateSpyWasCalled).toBe(true) + }) + + test('updates secret if changed', async (): Promise => { + // Setup operator with different secret than the config. + // As-if the api secret was set from a different config value originally. + const initialApiSecret = '123' + assert(initialApiSecret !== config.adminApiSecret) + const tenant = await Tenant.query(knex).patchAndFetchById( + config.operatorTenantId, + { apiSecret: initialApiSecret } + ) + assert(tenant) + expect(tenant.apiSecret).toBe(initialApiSecret) + + const error = await tenantService.updateOperatorApiSecretFromConfig() + expect(error).toBe(undefined) + + const updated = await Tenant.query(knex).findById(tenant.id) + assert(updated) + expect(updated.apiSecret).toBe(config.adminApiSecret) + }) + test('does not update if secret hasnt changed', async (): Promise => { + const tenant = await Tenant.query(knex).findById(config.operatorTenantId) + assert(tenant) + assert(tenant.apiSecret === config.adminApiSecret) + + const error = await tenantService.updateOperatorApiSecretFromConfig() + + expect(error).toBe(undefined) + + const updated = await Tenant.query(knex).findById(tenant.id) + assert(updated) + expect(updated.updatedAt).toStrictEqual(tenant.updatedAt) + }) + test( + 'throws error if operator tenant not found', + withConfigOverride( + () => config, + { operatorTenantId: crypto.randomUUID() }, + async (): Promise => { + const error = await tenantService.updateOperatorApiSecretFromConfig() + + expect(isTenantError(error)).toBe(true) + expect(error).toEqual(TenantError.TenantNotFound) + } + ) + ) + }) +}) diff --git a/packages/backend/src/tenants/service.ts b/packages/backend/src/tenants/service.ts index 64f97fa27a..ea6b7807fc 100644 --- a/packages/backend/src/tenants/service.ts +++ b/packages/backend/src/tenants/service.ts @@ -6,6 +6,8 @@ import { CacheDataStore } from '../middleware/cache/data-stores' import type { AuthServiceClient } from '../auth-service-client/client' import { TenantSettingService } from './settings/service' import { TenantSetting } from './settings/model' +import type { IAppConfig } from '../config/app' +import { TenantError } from './errors' export interface TenantService { get: (id: string, includeDeleted?: boolean) => Promise @@ -13,13 +15,14 @@ export interface TenantService { update: (options: UpdateTenantOptions) => Promise delete: (id: string) => Promise getPage: (pagination?: Pagination, sortOrder?: SortOrder) => Promise + updateOperatorApiSecretFromConfig: () => Promise } - export interface ServiceDependencies extends BaseService { knex: TransactionOrKnex tenantCache: CacheDataStore authServiceClient: AuthServiceClient tenantSettingService: TenantSettingService + config: IAppConfig } export async function createTenantService( @@ -37,7 +40,9 @@ export async function createTenantService( update: (options) => updateTenant(deps, options), delete: (id) => deleteTenant(deps, id), getPage: (pagination, sortOrder) => - getTenantPage(deps, pagination, sortOrder) + getTenantPage(deps, pagination, sortOrder), + updateOperatorApiSecretFromConfig: () => + updateOperatorApiSecretFromConfig(deps) } } @@ -186,3 +191,23 @@ async function deleteTenant( throw err } } + +async function updateOperatorApiSecretFromConfig( + deps: ServiceDependencies +): Promise { + const { adminApiSecret, operatorTenantId } = deps.config + + const tenant = await Tenant.query(deps.knex) + .findById(operatorTenantId) + .whereNull('deletedAt') + .first() + + if (!tenant) { + return TenantError.TenantNotFound + } + if (tenant.apiSecret !== adminApiSecret) { + await Tenant.query(deps.knex) + .patch({ apiSecret: adminApiSecret }) + .where('id', operatorTenantId) + } +} From 7b910d6418129a02576fc0c133aa375011cd8532 Mon Sep 17 00:00:00 2001 From: Blair Currey <12960453+BlairCurrey@users.noreply.github.com> Date: Tue, 4 Mar 2025 13:03:48 -0500 Subject: [PATCH 04/26] chore: rm log --- packages/backend/src/index.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/backend/src/index.ts b/packages/backend/src/index.ts index adc4411965..7c930f4794 100644 --- a/packages/backend/src/index.ts +++ b/packages/backend/src/index.ts @@ -771,7 +771,6 @@ export const start = async ( // Update Operator Tenant from config const tenantService = await container.use('tenantService') - console.log({ tenantService }) await tenantService.updateOperatorApiSecretFromConfig() await app.boot() From 77681a32c1ae06677c6c6682e6789a0927bd991c Mon Sep 17 00:00:00 2001 From: Blair Currey <12960453+BlairCurrey@users.noreply.github.com> Date: Tue, 4 Mar 2025 13:12:15 -0500 Subject: [PATCH 05/26] fix(fronted): 'no any' lint error --- packages/frontend/app/routes/tenants.$tenantId.tsx | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/packages/frontend/app/routes/tenants.$tenantId.tsx b/packages/frontend/app/routes/tenants.$tenantId.tsx index 3180e668f5..b85b80b301 100644 --- a/packages/frontend/app/routes/tenants.$tenantId.tsx +++ b/packages/frontend/app/routes/tenants.$tenantId.tsx @@ -12,7 +12,8 @@ import { useSubmit } from '@remix-run/react' import { type FormEvent, useState, useRef } from 'react' -import { z, ZodSchema } from 'zod' +import type { ZodSchema } from 'zod' +import { z } from 'zod' import { DangerZone, PageHeader } from '~/components' import { Button, ErrorPanel, Input, PasswordInput } from '~/components/ui' import { @@ -30,6 +31,7 @@ import { import type { ZodFieldErrors } from '~/shared/types' import { checkAuthAndRedirect } from '../lib/kratos_checks.server' import { getTenantInfo } from '~/lib/api/tenant.server' +import type { UpdateTenantInput } from '~/generated/graphql' export async function loader({ request, params }: LoaderFunctionArgs) { const cookies = request.headers.get('cookie') @@ -281,9 +283,9 @@ export async function action({ request }: ActionFunctionArgs) { const intent = formData.get('intent') formData.delete('intent') - async function handleUpdateFormSubmit( + async function handleUpdateFormSubmit( errorKey: keyof typeof actionResponse.errors, - schema: ZodSchema + schema: ZodSchema ) { const formEntries = Object.fromEntries(formData) const result = schema.safeParse(formEntries) From 8eab2442a1505cc0bd640ea32493f922c3067560 Mon Sep 17 00:00:00 2001 From: Blair Currey <12960453+BlairCurrey@users.noreply.github.com> Date: Tue, 4 Mar 2025 14:21:29 -0500 Subject: [PATCH 06/26] fix(backend): error on failed secret update from config --- packages/backend/src/index.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/backend/src/index.ts b/packages/backend/src/index.ts index 7c930f4794..922f375c52 100644 --- a/packages/backend/src/index.ts +++ b/packages/backend/src/index.ts @@ -771,7 +771,8 @@ export const start = async ( // Update Operator Tenant from config const tenantService = await container.use('tenantService') - await tenantService.updateOperatorApiSecretFromConfig() + const error = await tenantService.updateOperatorApiSecretFromConfig() + if (error) throw error await app.boot() await app.startAdminServer(config.adminPort) From 52fa4a7333f7b33207670047f38af3a5469e71d4 Mon Sep 17 00:00:00 2001 From: Blair Currey <12960453+BlairCurrey@users.noreply.github.com> Date: Wed, 5 Mar 2025 13:06:37 -0500 Subject: [PATCH 07/26] fix(backend): wallet address resolver tests to be operator/non-operator as needed - previous way of controlling if the request was from an operator or not no longer worked after changing app start to sync apiSecret --- .../graphql/resolvers/wallet_address.test.ts | 87 +++++++++++++++++-- 1 file changed, 81 insertions(+), 6 deletions(-) diff --git a/packages/backend/src/graphql/resolvers/wallet_address.test.ts b/packages/backend/src/graphql/resolvers/wallet_address.test.ts index 88ae1d78d8..5c0910d5ee 100644 --- a/packages/backend/src/graphql/resolvers/wallet_address.test.ts +++ b/packages/backend/src/graphql/resolvers/wallet_address.test.ts @@ -3,7 +3,11 @@ import { gql, ApolloError } from '@apollo/client' import { Knex } from 'knex' import { v4 as uuid } from 'uuid' -import { createTestApp, TestContainer } from '../../tests/app' +import { + createApolloClient, + createTestApp, + TestContainer +} from '../../tests/app' import { IocContract } from '@adonisjs/fold' import { AppServices } from '../../app' import { Asset } from '../../asset/model' @@ -21,7 +25,7 @@ import { WalletAddressEventType } from '../../open_payments/wallet_address/model' import { WalletAddressService } from '../../open_payments/wallet_address/service' -import { createAsset } from '../../tests/asset' +import { createAsset, randomAsset } from '../../tests/asset' import { createWalletAddress } from '../../tests/walletAddress' import { CreateWalletAddressInput, @@ -38,6 +42,7 @@ import { GraphQLErrorCode } from '../errors' import { AssetService } from '../../asset/service' import { faker } from '@faker-js/faker' import { Tenant } from '../../tenants/model' +import { createTenant } from '../../tests/tenant' describe('Wallet Address Resolvers', (): void => { let deps: IocContract @@ -49,8 +54,7 @@ describe('Wallet Address Resolvers', (): void => { beforeAll(async (): Promise => { deps = initIocContainer({ ...Config, - localCacheDuration: 0, - adminApiSecret: '123' //to force not being an operator. + localCacheDuration: 0 }) appContainer = await createTestApp(deps) knex = appContainer.knex @@ -315,14 +319,22 @@ describe('Wallet Address Resolvers', (): void => { }) test('bad input data when not allowed to perform cross tenant create', async (): Promise => { + // Make request as non-operator. + const nonOperatorTenant = await createTenant(deps) + const tenantedApolloClient = await createApolloClient( + appContainer.container, + appContainer.app, + nonOperatorTenant.id + ) + const badInputData = { - tenantId: 'ae4950b6-3e1b-4e50-ad24-25c065bdd3a9', + tenantId: uuid(), // some tenant other than requestor assetId: input.assetId, url: input.url } try { expect.assertions(2) - await appContainer.apolloClient + await tenantedApolloClient .mutate({ mutation: gql` mutation CreateWalletAddress( @@ -362,6 +374,69 @@ describe('Wallet Address Resolvers', (): void => { ) } }) + + test('Operator can perform cross tenant create', async (): Promise => { + // Setup non-tenant operator and form request for it from operator + const nonOperatorTenant = await createTenant(deps) + const asset = await createAsset( + deps, + { + code: 'xyz', + scale: 2 + }, + nonOperatorTenant.id + ) + const input = { + tenantId: nonOperatorTenant.id, + assetId: asset.id, + url: 'https://bob.me/.well-known/pay' + } + const response = await appContainer.apolloClient // operator client + .mutate({ + mutation: gql` + mutation CreateWalletAddress($input: CreateWalletAddressInput!) { + createWalletAddress(input: $input) { + walletAddress { + id + asset { + code + scale + } + url + } + } + } + `, + variables: { + input + } + }) + .then((query): CreateWalletAddressMutationResponse => { + if (query.data) { + return query.data.createWalletAddress + } else { + throw new Error('Data was empty') + } + }) + + assert.ok(response.walletAddress) + expect(response.walletAddress).toEqual({ + __typename: 'WalletAddress', + id: response.walletAddress.id, + url: input.url, + asset: { + __typename: 'Asset', + code: asset.code, + scale: asset.scale + } + }) + await expect( + walletAddressService.get(response.walletAddress.id) + ).resolves.toMatchObject({ + id: response.walletAddress.id, + asset + }) + }) }) describe('Update Wallet Address', (): void => { From 45f9eea462d67bac29bd8ee8b5c337295248250a Mon Sep 17 00:00:00 2001 From: Blair Currey <12960453+BlairCurrey@users.noreply.github.com> Date: Wed, 5 Mar 2025 13:22:13 -0500 Subject: [PATCH 08/26] test(backend): update tenant tests to reflect operator cant update apiSecret --- .../src/graphql/resolvers/tenant.test.ts | 113 ++++++++++++++---- 1 file changed, 92 insertions(+), 21 deletions(-) diff --git a/packages/backend/src/graphql/resolvers/tenant.test.ts b/packages/backend/src/graphql/resolvers/tenant.test.ts index 177ed85015..8afd555905 100644 --- a/packages/backend/src/graphql/resolvers/tenant.test.ts +++ b/packages/backend/src/graphql/resolvers/tenant.test.ts @@ -349,22 +349,87 @@ describe('Tenant Resolvers', (): void => { await truncateTables(appContainer.knex) }) - test.each` - isOperator | description - ${true} | ${'operator'} - ${false} | ${'tenant'} - `( - 'can update a tenant as $description', - async ({ isOperator }): Promise => { - const client = isOperator - ? appContainer.apolloClient - : tenantedApolloClient - const updateInput = { - ...generateTenantInput(), - id: tenant.id - } + test('can update a tenant as operator', async (): Promise => { + // operator cant update apiSecret + const { apiSecret, ...input } = generateTenantInput() + const updateInput = { + ...input, + id: tenant.id + } + + const mutation = await appContainer.apolloClient + .mutate({ + mutation: gql` + mutation UpdateTenant($input: UpdateTenantInput!) { + updateTenant(input: $input) { + tenant { + id + email + apiSecret + idpConsentUrl + idpSecret + publicName + } + } + } + `, + variables: { + input: updateInput + } + }) + .then((query): TenantMutationResponse => query.data?.updateTenant) + + expect(mutation.tenant).toEqual({ + ...updateInput, + __typename: 'Tenant', + apiSecret: expect.any(String) + }) + }) + + test('can update a tenant as tenant', async (): Promise => { + const updateInput = { + ...generateTenantInput(), + id: tenant.id + } + + const mutation = await tenantedApolloClient + .mutate({ + mutation: gql` + mutation UpdateTenant($input: UpdateTenantInput!) { + updateTenant(input: $input) { + tenant { + id + email + apiSecret + idpConsentUrl + idpSecret + publicName + } + } + } + `, + variables: { + input: updateInput + } + }) + .then((query): TenantMutationResponse => query.data?.updateTenant) + + expect(mutation.tenant).toEqual({ + ...updateInput, + __typename: 'Tenant' + }) + }) + + test('Cannot update API secret as operator', async (): Promise => { + const updateInput = { + ...generateTenantInput(), + id: tenant.id, + apiSecret: 'newApiSecretValue' + } - const mutation = await client + try { + expect.assertions(2) + await appContainer.apolloClient .mutate({ mutation: gql` mutation UpdateTenant($input: UpdateTenantInput!) { @@ -385,13 +450,19 @@ describe('Tenant Resolvers', (): void => { } }) .then((query): TenantMutationResponse => query.data?.updateTenant) - - expect(mutation.tenant).toEqual({ - ...updateInput, - __typename: 'Tenant' - }) + } catch (error) { + expect(error).toBeInstanceOf(ApolloError) + expect((error as ApolloError).graphQLErrors).toContainEqual( + expect.objectContaining({ + message: 'Operator cannot update apiSecret over admin api', + extensions: expect.objectContaining({ + code: GraphQLErrorCode.BadUserInput + }) + }) + ) } - ) + }) + test('Cannot update other tenant as non-operator', async (): Promise => { const firstTenant = await createTenant(deps) const secondTenant = await createTenant(deps) From b52208bc6d41b9970419112d1096021bb5892ef7 Mon Sep 17 00:00:00 2001 From: Blair Currey <12960453+BlairCurrey@users.noreply.github.com> Date: Wed, 5 Mar 2025 13:25:31 -0500 Subject: [PATCH 09/26] chore: fix format --- packages/backend/src/graphql/resolvers/tenant.test.ts | 1 + packages/backend/src/graphql/resolvers/wallet_address.test.ts | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/backend/src/graphql/resolvers/tenant.test.ts b/packages/backend/src/graphql/resolvers/tenant.test.ts index 8afd555905..0b1baa0206 100644 --- a/packages/backend/src/graphql/resolvers/tenant.test.ts +++ b/packages/backend/src/graphql/resolvers/tenant.test.ts @@ -351,6 +351,7 @@ describe('Tenant Resolvers', (): void => { test('can update a tenant as operator', async (): Promise => { // operator cant update apiSecret + // eslint-disable-next-line @typescript-eslint/no-unused-vars const { apiSecret, ...input } = generateTenantInput() const updateInput = { ...input, diff --git a/packages/backend/src/graphql/resolvers/wallet_address.test.ts b/packages/backend/src/graphql/resolvers/wallet_address.test.ts index 5c0910d5ee..2137e009f8 100644 --- a/packages/backend/src/graphql/resolvers/wallet_address.test.ts +++ b/packages/backend/src/graphql/resolvers/wallet_address.test.ts @@ -25,7 +25,7 @@ import { WalletAddressEventType } from '../../open_payments/wallet_address/model' import { WalletAddressService } from '../../open_payments/wallet_address/service' -import { createAsset, randomAsset } from '../../tests/asset' +import { createAsset } from '../../tests/asset' import { createWalletAddress } from '../../tests/walletAddress' import { CreateWalletAddressInput, From 23277d7d89efbd143b5eb6c83af023c6aa0727aa Mon Sep 17 00:00:00 2001 From: Blair Currey <12960453+BlairCurrey@users.noreply.github.com> Date: Wed, 5 Mar 2025 13:50:36 -0500 Subject: [PATCH 10/26] chore: debug ci test failure (working locally) --- packages/backend/src/tenants/service.test.ts | 170 +++++++++---------- 1 file changed, 85 insertions(+), 85 deletions(-) diff --git a/packages/backend/src/tenants/service.test.ts b/packages/backend/src/tenants/service.test.ts index ba64b8af84..b0e914003e 100644 --- a/packages/backend/src/tenants/service.test.ts +++ b/packages/backend/src/tenants/service.test.ts @@ -447,88 +447,88 @@ describe('Tenant Service', (): void => { }) }) -describe('Tenant Service (no tenant truncate)', (): void => { - let deps: IocContract - let config: IAppConfig - let appContainer: TestContainer - let tenantService: TenantService - let knex: Knex - let updateSpyWasCalled: boolean - const dbSchema = 'tenant_service_test_schema2' - - beforeAll(async (): Promise => { - deps = initIocContainer({ - ...Config, - dbSchema - }) - knex = await deps.use('knex') - config = await deps.use('config') - tenantService = await deps.use('tenantService') - - const updateOperatorSecretSpy = jest.spyOn( - tenantService, - 'updateOperatorApiSecretFromConfig' - ) - appContainer = await createTestApp(deps) - updateSpyWasCalled = updateOperatorSecretSpy.mock.calls.length > 0 - }) - - afterEach(async (): Promise => { - await truncateTables(knex, false, dbSchema) - }) - - afterAll(async (): Promise => { - await appContainer.shutdown() - }) - describe('updateOperatorApiSecretFromConfig', () => { - test('called on application start', async (): Promise => { - expect(updateSpyWasCalled).toBe(true) - }) - - test('updates secret if changed', async (): Promise => { - // Setup operator with different secret than the config. - // As-if the api secret was set from a different config value originally. - const initialApiSecret = '123' - assert(initialApiSecret !== config.adminApiSecret) - const tenant = await Tenant.query(knex).patchAndFetchById( - config.operatorTenantId, - { apiSecret: initialApiSecret } - ) - assert(tenant) - expect(tenant.apiSecret).toBe(initialApiSecret) - - const error = await tenantService.updateOperatorApiSecretFromConfig() - expect(error).toBe(undefined) - - const updated = await Tenant.query(knex).findById(tenant.id) - assert(updated) - expect(updated.apiSecret).toBe(config.adminApiSecret) - }) - test('does not update if secret hasnt changed', async (): Promise => { - const tenant = await Tenant.query(knex).findById(config.operatorTenantId) - assert(tenant) - assert(tenant.apiSecret === config.adminApiSecret) - - const error = await tenantService.updateOperatorApiSecretFromConfig() - - expect(error).toBe(undefined) - - const updated = await Tenant.query(knex).findById(tenant.id) - assert(updated) - expect(updated.updatedAt).toStrictEqual(tenant.updatedAt) - }) - test( - 'throws error if operator tenant not found', - withConfigOverride( - () => config, - { operatorTenantId: crypto.randomUUID() }, - async (): Promise => { - const error = await tenantService.updateOperatorApiSecretFromConfig() - - expect(isTenantError(error)).toBe(true) - expect(error).toEqual(TenantError.TenantNotFound) - } - ) - ) - }) -}) +// describe('Tenant Service (no tenant truncate)', (): void => { +// let deps: IocContract +// let config: IAppConfig +// let appContainer: TestContainer +// let tenantService: TenantService +// let knex: Knex +// let updateSpyWasCalled: boolean +// const dbSchema = 'tenant_service_test_schema2' + +// beforeAll(async (): Promise => { +// deps = initIocContainer({ +// ...Config, +// dbSchema +// }) +// knex = await deps.use('knex') +// config = await deps.use('config') +// tenantService = await deps.use('tenantService') + +// const updateOperatorSecretSpy = jest.spyOn( +// tenantService, +// 'updateOperatorApiSecretFromConfig' +// ) +// appContainer = await createTestApp(deps) +// updateSpyWasCalled = updateOperatorSecretSpy.mock.calls.length > 0 +// }) + +// afterEach(async (): Promise => { +// await truncateTables(knex, false, dbSchema) +// }) + +// afterAll(async (): Promise => { +// await appContainer.shutdown() +// }) +// describe('updateOperatorApiSecretFromConfig', () => { +// test('called on application start', async (): Promise => { +// expect(updateSpyWasCalled).toBe(true) +// }) + +// test('updates secret if changed', async (): Promise => { +// // Setup operator with different secret than the config. +// // As-if the api secret was set from a different config value originally. +// const initialApiSecret = '123' +// assert(initialApiSecret !== config.adminApiSecret) +// const tenant = await Tenant.query(knex).patchAndFetchById( +// config.operatorTenantId, +// { apiSecret: initialApiSecret } +// ) +// assert(tenant) +// expect(tenant.apiSecret).toBe(initialApiSecret) + +// const error = await tenantService.updateOperatorApiSecretFromConfig() +// expect(error).toBe(undefined) + +// const updated = await Tenant.query(knex).findById(tenant.id) +// assert(updated) +// expect(updated.apiSecret).toBe(config.adminApiSecret) +// }) +// test('does not update if secret hasnt changed', async (): Promise => { +// const tenant = await Tenant.query(knex).findById(config.operatorTenantId) +// assert(tenant) +// assert(tenant.apiSecret === config.adminApiSecret) + +// const error = await tenantService.updateOperatorApiSecretFromConfig() + +// expect(error).toBe(undefined) + +// const updated = await Tenant.query(knex).findById(tenant.id) +// assert(updated) +// expect(updated.updatedAt).toStrictEqual(tenant.updatedAt) +// }) +// test( +// 'throws error if operator tenant not found', +// withConfigOverride( +// () => config, +// { operatorTenantId: crypto.randomUUID() }, +// async (): Promise => { +// const error = await tenantService.updateOperatorApiSecretFromConfig() + +// expect(isTenantError(error)).toBe(true) +// expect(error).toEqual(TenantError.TenantNotFound) +// } +// ) +// ) +// }) +// }) From 6a28574facbf8f230c4b1bc87e1dcb371c468d91 Mon Sep 17 00:00:00 2001 From: Blair Currey <12960453+BlairCurrey@users.noreply.github.com> Date: Wed, 5 Mar 2025 13:52:09 -0500 Subject: [PATCH 11/26] chore: fix format --- packages/backend/src/tenants/service.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/backend/src/tenants/service.test.ts b/packages/backend/src/tenants/service.test.ts index b0e914003e..069a2eaecb 100644 --- a/packages/backend/src/tenants/service.test.ts +++ b/packages/backend/src/tenants/service.test.ts @@ -17,7 +17,7 @@ import { AuthServiceClient } from '../auth-service-client/client' import { withConfigOverride } from '../tests/helpers' import { TenantSetting } from './settings/model' import { TenantSettingService } from './settings/service' -import { isTenantError, TenantError } from './errors' +// import { isTenantError, TenantError } from './errors' describe('Tenant Service', (): void => { let deps: IocContract From bcba0358c033d18fdec867aae2a093c9e95508ac Mon Sep 17 00:00:00 2001 From: Blair Currey <12960453+BlairCurrey@users.noreply.github.com> Date: Wed, 5 Mar 2025 14:13:56 -0500 Subject: [PATCH 12/26] chore: debug ci test failure (working locally) --- packages/backend/src/tenants/service.test.ts | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/packages/backend/src/tenants/service.test.ts b/packages/backend/src/tenants/service.test.ts index 069a2eaecb..71d420aa1c 100644 --- a/packages/backend/src/tenants/service.test.ts +++ b/packages/backend/src/tenants/service.test.ts @@ -34,10 +34,16 @@ describe('Tenant Service', (): void => { ...Config, dbSchema }) - appContainer = await createTestApp(deps) + config = await deps.use('config') + console.log({ config }) + try { + appContainer = await createTestApp(deps) + } catch (err) { + console.error(err) + throw err + } tenantService = await deps.use('tenantService') knex = await deps.use('knex') - config = await deps.use('config') authServiceClient = await deps.use('authServiceClient') tenantSettingsService = await deps.use('tenantSettingService') }) From 7f568de51ba8402ac66d3406457b2ba1a15e086f Mon Sep 17 00:00:00 2001 From: Blair Currey <12960453+BlairCurrey@users.noreply.github.com> Date: Wed, 5 Mar 2025 14:26:30 -0500 Subject: [PATCH 13/26] chore: debug ci test failure (working locally) --- packages/backend/src/tenants/service.test.ts | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/packages/backend/src/tenants/service.test.ts b/packages/backend/src/tenants/service.test.ts index 71d420aa1c..9a05e1d42a 100644 --- a/packages/backend/src/tenants/service.test.ts +++ b/packages/backend/src/tenants/service.test.ts @@ -34,16 +34,11 @@ describe('Tenant Service', (): void => { ...Config, dbSchema }) + knex = await deps.use('knex') config = await deps.use('config') - console.log({ config }) - try { - appContainer = await createTestApp(deps) - } catch (err) { - console.error(err) - throw err - } + console.log({ config, tenants: await Tenant.query(knex) }) + appContainer = await createTestApp(deps) tenantService = await deps.use('tenantService') - knex = await deps.use('knex') authServiceClient = await deps.use('authServiceClient') tenantSettingsService = await deps.use('tenantSettingService') }) From e8d0854ec5411c383b4f9b5f7043ed7519bc1593 Mon Sep 17 00:00:00 2001 From: Blair Currey <12960453+BlairCurrey@users.noreply.github.com> Date: Wed, 5 Mar 2025 14:34:20 -0500 Subject: [PATCH 14/26] chore: debug ci test failure (working locally) --- packages/backend/src/graphql/resolvers/tenant.test.ts | 3 ++- packages/backend/src/tenants/service.test.ts | 4 ++-- packages/backend/src/tests/app.ts | 6 +++++- 3 files changed, 9 insertions(+), 4 deletions(-) diff --git a/packages/backend/src/graphql/resolvers/tenant.test.ts b/packages/backend/src/graphql/resolvers/tenant.test.ts index 0b1baa0206..8cc63911ff 100644 --- a/packages/backend/src/graphql/resolvers/tenant.test.ts +++ b/packages/backend/src/graphql/resolvers/tenant.test.ts @@ -70,8 +70,9 @@ describe('Tenant Resolvers', (): void => { ...Config, dbSchema: 'tenant_service_test_schema' }) - appContainer = await createTestApp(deps) config = await deps.use('config') + console.log({ config }) + appContainer = await createTestApp(deps, 'tenantService resolver test') const authServiceClient = await deps.use('authServiceClient') jest .spyOn(authServiceClient.tenant, 'create') diff --git a/packages/backend/src/tenants/service.test.ts b/packages/backend/src/tenants/service.test.ts index 9a05e1d42a..7fc1d1cfa1 100644 --- a/packages/backend/src/tenants/service.test.ts +++ b/packages/backend/src/tenants/service.test.ts @@ -36,8 +36,8 @@ describe('Tenant Service', (): void => { }) knex = await deps.use('knex') config = await deps.use('config') - console.log({ config, tenants: await Tenant.query(knex) }) - appContainer = await createTestApp(deps) + console.log({ config }) + appContainer = await createTestApp(deps, 'tenantService test') tenantService = await deps.use('tenantService') authServiceClient = await deps.use('authServiceClient') tenantSettingsService = await deps.use('tenantSettingService') diff --git a/packages/backend/src/tests/app.ts b/packages/backend/src/tests/app.ts index 4f3dd370c3..ceb48ff23a 100644 --- a/packages/backend/src/tests/app.ts +++ b/packages/backend/src/tests/app.ts @@ -14,6 +14,7 @@ import { start, gracefulShutdown } from '..' import { onError } from '@apollo/client/link/error' import { App, AppServices } from '../app' +import { Tenant } from '../tenants/model' export const testAccessToken = 'test-app-access' @@ -82,7 +83,8 @@ export const createApolloClient = async ( } export const createTestApp = async ( - container: IocContract + container: IocContract, + caller?: string ): Promise => { const config = await container.use('config') config.adminPort = 0 @@ -110,6 +112,8 @@ export const createTestApp = async ( const knex = await container.use('knex') + console.log(caller, { tenants: Tenant.query(knex) }) + const client = await createApolloClient(container, app) return { From 94dfa000869ca37b855b97551369b0f1b95f5339 Mon Sep 17 00:00:00 2001 From: Blair Currey <12960453+BlairCurrey@users.noreply.github.com> Date: Wed, 5 Mar 2025 15:12:11 -0500 Subject: [PATCH 15/26] chore: debug ci test failure (working locally) --- packages/backend/src/tests/app.ts | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/packages/backend/src/tests/app.ts b/packages/backend/src/tests/app.ts index ceb48ff23a..8b6dfc129e 100644 --- a/packages/backend/src/tests/app.ts +++ b/packages/backend/src/tests/app.ts @@ -94,6 +94,12 @@ export const createTestApp = async ( config.openPaymentsUrl = 'https://op.example' config.walletAddressUrl = 'https://wallet.example/.well-known/pay' + const knex = await container.use('knex') + + if (caller) { + console.log(caller, { tenants: Tenant.query(knex) }) + } + const app = new App(container) await start(container, app) @@ -110,10 +116,6 @@ export const createTestApp = async ( }) .persist() - const knex = await container.use('knex') - - console.log(caller, { tenants: Tenant.query(knex) }) - const client = await createApolloClient(container, app) return { From dd2cd35dfa2118d4a28b8b9ca4fb2b3339c97e9b Mon Sep 17 00:00:00 2001 From: Blair Currey <12960453+BlairCurrey@users.noreply.github.com> Date: Wed, 5 Mar 2025 15:20:17 -0500 Subject: [PATCH 16/26] chore: debug ci test failure (working locally) --- packages/backend/src/tenants/service.test.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packages/backend/src/tenants/service.test.ts b/packages/backend/src/tenants/service.test.ts index 7fc1d1cfa1..17da91f631 100644 --- a/packages/backend/src/tenants/service.test.ts +++ b/packages/backend/src/tenants/service.test.ts @@ -17,6 +17,7 @@ import { AuthServiceClient } from '../auth-service-client/client' import { withConfigOverride } from '../tests/helpers' import { TenantSetting } from './settings/model' import { TenantSettingService } from './settings/service' +import nock from 'nock' // import { isTenantError, TenantError } from './errors' describe('Tenant Service', (): void => { @@ -48,6 +49,7 @@ describe('Tenant Service', (): void => { }) afterAll(async (): Promise => { + nock.cleanAll() // TODO: rm? await appContainer.shutdown() }) From eb56e8236ae16d968513f5196c6abe25d86c5332 Mon Sep 17 00:00:00 2001 From: Blair Currey <12960453+BlairCurrey@users.noreply.github.com> Date: Wed, 5 Mar 2025 15:25:21 -0500 Subject: [PATCH 17/26] chore: debug ci test failure (working locally) --- packages/backend/src/tests/app.ts | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/packages/backend/src/tests/app.ts b/packages/backend/src/tests/app.ts index 8b6dfc129e..2f070491e9 100644 --- a/packages/backend/src/tests/app.ts +++ b/packages/backend/src/tests/app.ts @@ -97,7 +97,12 @@ export const createTestApp = async ( const knex = await container.use('knex') if (caller) { - console.log(caller, { tenants: Tenant.query(knex) }) + // It seems that the `start` method throws an error sometimes because + // it cant find a tenant (by config.operatorTenantId). See TenantNotFund error + // on tenant service, tenant resolver, tenant settings. + // Not sure why - truncateTables that truncates tenants? then they arent there for next test? + // - is that how it works? I think we should never truncate tenants then. or if we do, re-add them if we do. + console.log(caller, { tenants: await Tenant.query(knex) }) } const app = new App(container) From 3b3ab7aeb1bd3ccd652324ad2a286c3d427fc81b Mon Sep 17 00:00:00 2001 From: Blair Currey <12960453+BlairCurrey@users.noreply.github.com> Date: Wed, 5 Mar 2025 15:34:07 -0500 Subject: [PATCH 18/26] chore: debug ci test failure (working locally) --- packages/backend/src/graphql/resolvers/tenant.test.ts | 5 +++-- .../backend/src/graphql/resolvers/tenant_settings.test.ts | 5 +++-- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/packages/backend/src/graphql/resolvers/tenant.test.ts b/packages/backend/src/graphql/resolvers/tenant.test.ts index 8cc63911ff..9ee22226b8 100644 --- a/packages/backend/src/graphql/resolvers/tenant.test.ts +++ b/packages/backend/src/graphql/resolvers/tenant.test.ts @@ -64,11 +64,12 @@ describe('Tenant Resolvers', (): void => { let deps: IocContract let appContainer: TestContainer let config: IAppConfig + const dbSchema = 'tenant_resolver_test_schema' beforeAll(async (): Promise => { deps = await initIocContainer({ ...Config, - dbSchema: 'tenant_service_test_schema' + dbSchema }) config = await deps.use('config') console.log({ config }) @@ -86,7 +87,7 @@ describe('Tenant Resolvers', (): void => { }) afterEach(async (): Promise => { - await truncateTables(appContainer.knex, true) + await truncateTables(appContainer.knex, true, dbSchema) // pass in schema? }) afterAll(async (): Promise => { await appContainer.apolloClient.stop() diff --git a/packages/backend/src/graphql/resolvers/tenant_settings.test.ts b/packages/backend/src/graphql/resolvers/tenant_settings.test.ts index c4afbd6732..992ce339af 100644 --- a/packages/backend/src/graphql/resolvers/tenant_settings.test.ts +++ b/packages/backend/src/graphql/resolvers/tenant_settings.test.ts @@ -59,11 +59,12 @@ function createTenantedApolloClient( describe('Tenant Settings Resolvers', (): void => { let deps: IocContract let appContainer: TestContainer + const dbSchema = 'tenant_settings_resolver_test_schema' beforeAll(async (): Promise => { deps = initIocContainer({ ...Config, - dbSchema: 'tenant_settings_service_test_schema' + dbSchema }) appContainer = await createTestApp(deps) @@ -80,7 +81,7 @@ describe('Tenant Settings Resolvers', (): void => { }) afterEach(async (): Promise => { - await truncateTables(appContainer.knex, true) + await truncateTables(appContainer.knex, true, dbSchema) // TODO: pass in schema? }) afterAll(async (): Promise => { From c065eaa57214e2a138af7d8da859591b2cf61a76 Mon Sep 17 00:00:00 2001 From: Blair Currey <12960453+BlairCurrey@users.noreply.github.com> Date: Wed, 5 Mar 2025 15:38:12 -0500 Subject: [PATCH 19/26] chore: debug ci test failure (working locally) --- packages/backend/src/tests/app.ts | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/packages/backend/src/tests/app.ts b/packages/backend/src/tests/app.ts index 2f070491e9..563335fe85 100644 --- a/packages/backend/src/tests/app.ts +++ b/packages/backend/src/tests/app.ts @@ -96,14 +96,14 @@ export const createTestApp = async ( const knex = await container.use('knex') - if (caller) { - // It seems that the `start` method throws an error sometimes because - // it cant find a tenant (by config.operatorTenantId). See TenantNotFund error - // on tenant service, tenant resolver, tenant settings. - // Not sure why - truncateTables that truncates tenants? then they arent there for next test? - // - is that how it works? I think we should never truncate tenants then. or if we do, re-add them if we do. - console.log(caller, { tenants: await Tenant.query(knex) }) - } + // if (caller) { + // It seems that the `start` method throws an error sometimes because + // it cant find a tenant (by config.operatorTenantId). See TenantNotFund error + // on tenant service, tenant resolver, tenant settings. + // Not sure why - truncateTables that truncates tenants? then they arent there for next test? + // - is that how it works? I think we should never truncate tenants then. or if we do, re-add them if we do. + // console.log(caller, { tenants: await Tenant.query(knex) }) + // } const app = new App(container) await start(container, app) From b9b424a20d8a52cca5b3c344a07368a3803c9173 Mon Sep 17 00:00:00 2001 From: Blair Currey <12960453+BlairCurrey@users.noreply.github.com> Date: Wed, 5 Mar 2025 15:43:15 -0500 Subject: [PATCH 20/26] chore: debug ci test failure (working locally) --- packages/backend/src/tests/app.ts | 17 +++-------------- 1 file changed, 3 insertions(+), 14 deletions(-) diff --git a/packages/backend/src/tests/app.ts b/packages/backend/src/tests/app.ts index 563335fe85..4f3dd370c3 100644 --- a/packages/backend/src/tests/app.ts +++ b/packages/backend/src/tests/app.ts @@ -14,7 +14,6 @@ import { start, gracefulShutdown } from '..' import { onError } from '@apollo/client/link/error' import { App, AppServices } from '../app' -import { Tenant } from '../tenants/model' export const testAccessToken = 'test-app-access' @@ -83,8 +82,7 @@ export const createApolloClient = async ( } export const createTestApp = async ( - container: IocContract, - caller?: string + container: IocContract ): Promise => { const config = await container.use('config') config.adminPort = 0 @@ -94,17 +92,6 @@ export const createTestApp = async ( config.openPaymentsUrl = 'https://op.example' config.walletAddressUrl = 'https://wallet.example/.well-known/pay' - const knex = await container.use('knex') - - // if (caller) { - // It seems that the `start` method throws an error sometimes because - // it cant find a tenant (by config.operatorTenantId). See TenantNotFund error - // on tenant service, tenant resolver, tenant settings. - // Not sure why - truncateTables that truncates tenants? then they arent there for next test? - // - is that how it works? I think we should never truncate tenants then. or if we do, re-add them if we do. - // console.log(caller, { tenants: await Tenant.query(knex) }) - // } - const app = new App(container) await start(container, app) @@ -121,6 +108,8 @@ export const createTestApp = async ( }) .persist() + const knex = await container.use('knex') + const client = await createApolloClient(container, app) return { From 5d451a4aac538a875c14c5e3fb5e0d6755e640d7 Mon Sep 17 00:00:00 2001 From: Blair Currey <12960453+BlairCurrey@users.noreply.github.com> Date: Wed, 5 Mar 2025 15:45:45 -0500 Subject: [PATCH 21/26] chore: debug ci test failure (working locally) --- packages/backend/src/graphql/resolvers/tenant.test.ts | 2 +- packages/backend/src/tenants/service.test.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/backend/src/graphql/resolvers/tenant.test.ts b/packages/backend/src/graphql/resolvers/tenant.test.ts index 9ee22226b8..c4f369a2d9 100644 --- a/packages/backend/src/graphql/resolvers/tenant.test.ts +++ b/packages/backend/src/graphql/resolvers/tenant.test.ts @@ -73,7 +73,7 @@ describe('Tenant Resolvers', (): void => { }) config = await deps.use('config') console.log({ config }) - appContainer = await createTestApp(deps, 'tenantService resolver test') + appContainer = await createTestApp(deps) const authServiceClient = await deps.use('authServiceClient') jest .spyOn(authServiceClient.tenant, 'create') diff --git a/packages/backend/src/tenants/service.test.ts b/packages/backend/src/tenants/service.test.ts index 17da91f631..60ce0b48ab 100644 --- a/packages/backend/src/tenants/service.test.ts +++ b/packages/backend/src/tenants/service.test.ts @@ -38,7 +38,7 @@ describe('Tenant Service', (): void => { knex = await deps.use('knex') config = await deps.use('config') console.log({ config }) - appContainer = await createTestApp(deps, 'tenantService test') + appContainer = await createTestApp(deps) tenantService = await deps.use('tenantService') authServiceClient = await deps.use('authServiceClient') tenantSettingsService = await deps.use('tenantSettingService') From c7399be3d744f86863e10e2180a0c5ebecdf8f30 Mon Sep 17 00:00:00 2001 From: Blair Currey <12960453+BlairCurrey@users.noreply.github.com> Date: Wed, 5 Mar 2025 16:24:02 -0500 Subject: [PATCH 22/26] fix: test side effects --- .../backend/src/accounting/psql/balance.test.ts | 2 +- .../accounting/psql/ledger-account/index.test.ts | 7 +++++-- .../accounting/psql/ledger-transfer/index.test.ts | 7 +++++-- .../accounting/psql/ledger-transfer/model.test.ts | 2 +- .../backend/src/accounting/psql/service.test.ts | 2 +- .../src/accounting/tigerbeetle/service.test.ts | 2 +- packages/backend/src/asset/model.test.ts | 2 +- packages/backend/src/asset/service.test.ts | 4 ++-- packages/backend/src/fee/model.test.ts | 2 +- packages/backend/src/fee/service.test.ts | 5 +---- .../backend/src/graphql/middleware/index.test.ts | 2 +- .../resolvers/accounting_transfer.psql.test.ts | 2 +- .../accounting_transfer.tigerbeetle.test.ts | 2 +- .../backend/src/graphql/resolvers/asset.test.ts | 2 +- .../src/graphql/resolvers/auto-peering.test.ts | 2 +- .../src/graphql/resolvers/combined_payments.test.ts | 2 +- packages/backend/src/graphql/resolvers/fee.test.ts | 2 +- .../src/graphql/resolvers/incoming_payment.test.ts | 2 +- .../backend/src/graphql/resolvers/liquidity.test.ts | 2 +- .../src/graphql/resolvers/outgoing_payment.test.ts | 2 +- packages/backend/src/graphql/resolvers/peer.test.ts | 2 +- .../backend/src/graphql/resolvers/quote.test.ts | 2 +- .../backend/src/graphql/resolvers/tenant.test.ts | 5 ++--- .../src/graphql/resolvers/tenant_settings.test.ts | 2 +- .../src/graphql/resolvers/walletAddressKey.test.ts | 2 +- .../src/graphql/resolvers/wallet_address.test.ts | 2 +- .../backend/src/graphql/resolvers/webhooks.test.ts | 2 +- .../src/open_payments/authServer/service.test.ts | 2 +- .../backend/src/open_payments/grant/service.test.ts | 2 +- .../open_payments/payment/combined/service.test.ts | 5 +---- .../open_payments/payment/incoming/model.test.ts | 2 +- .../open_payments/payment/incoming/routes.test.ts | 2 +- .../open_payments/payment/incoming/service.test.ts | 2 +- .../payment/incoming_remote/service.test.ts | 5 +---- .../open_payments/payment/outgoing/model.test.ts | 2 +- .../open_payments/payment/outgoing/routes.test.ts | 2 +- .../open_payments/payment/outgoing/service.test.ts | 2 +- .../backend/src/open_payments/quote/routes.test.ts | 2 +- .../backend/src/open_payments/quote/service.test.ts | 2 +- .../src/open_payments/receiver/model.test.ts | 2 +- .../src/open_payments/receiver/service.test.ts | 2 +- .../open_payments/wallet_address/key/routes.test.ts | 2 +- .../wallet_address/key/service.test.ts | 2 +- .../open_payments/wallet_address/middleware.test.ts | 2 +- .../src/open_payments/wallet_address/model.test.ts | 2 +- .../src/open_payments/wallet_address/routes.test.ts | 2 +- .../open_payments/wallet_address/service.test.ts | 4 ++-- .../src/payment-method/handler/service.test.ts | 2 +- .../payment-method/ilp/auto-peering/routes.test.ts | 2 +- .../payment-method/ilp/auto-peering/service.test.ts | 2 +- .../ilp/peer-http-token/service.test.ts | 2 +- .../src/payment-method/ilp/peer/model.test.ts | 2 +- .../src/payment-method/ilp/peer/service.test.ts | 2 +- .../backend/src/payment-method/ilp/service.test.ts | 2 +- .../src/payment-method/ilp/spsp/middleware.test.ts | 2 +- .../src/payment-method/ilp/spsp/routes.test.ts | 2 +- .../ilp/stream-credentials/service.test.ts | 2 +- .../src/payment-method/local/service.test.ts | 2 +- packages/backend/src/shared/pagination.test.ts | 2 +- packages/backend/src/shared/utils.test.ts | 2 +- packages/backend/src/tenants/service.test.ts | 3 +-- .../backend/src/tenants/settings/service.test.ts | 5 +---- packages/backend/src/tests/tableManager.ts | 13 ++++++++++--- packages/backend/src/webhook/service.test.ts | 2 +- 64 files changed, 84 insertions(+), 85 deletions(-) diff --git a/packages/backend/src/accounting/psql/balance.test.ts b/packages/backend/src/accounting/psql/balance.test.ts index 027c6f1eff..46a5c9df89 100644 --- a/packages/backend/src/accounting/psql/balance.test.ts +++ b/packages/backend/src/accounting/psql/balance.test.ts @@ -39,7 +39,7 @@ describe('Balances', (): void => { afterEach(async (): Promise => { jest.useRealTimers() - await truncateTables(knex) + await truncateTables(deps) }) afterAll(async (): Promise => { diff --git a/packages/backend/src/accounting/psql/ledger-account/index.test.ts b/packages/backend/src/accounting/psql/ledger-account/index.test.ts index fdb9a898c2..483203ed44 100644 --- a/packages/backend/src/accounting/psql/ledger-account/index.test.ts +++ b/packages/backend/src/accounting/psql/ledger-account/index.test.ts @@ -13,15 +13,18 @@ import { ForeignKeyViolationError } from 'objection' import { createLedgerAccount } from '../../../tests/ledgerAccount' import { ServiceDependencies } from '../service' import { createAccount, getLiquidityAccount } from '.' +import { AppServices } from '../../../app' +import { IocContract } from '@adonisjs/fold' describe('Ledger Account', (): void => { + let deps: IocContract let serviceDeps: ServiceDependencies let appContainer: TestContainer let knex: Knex let asset: Asset beforeAll(async (): Promise => { - const deps = initIocContainer({ ...Config, useTigerBeetle: false }) + deps = initIocContainer({ ...Config, useTigerBeetle: false }) appContainer = await createTestApp(deps) serviceDeps = { logger: await deps.use('logger'), @@ -40,7 +43,7 @@ describe('Ledger Account', (): void => { afterEach(async (): Promise => { jest.useRealTimers() - await truncateTables(knex) + await truncateTables(deps) }) afterAll(async (): Promise => { diff --git a/packages/backend/src/accounting/psql/ledger-transfer/index.test.ts b/packages/backend/src/accounting/psql/ledger-transfer/index.test.ts index 3efb12f752..0a6e9ea452 100644 --- a/packages/backend/src/accounting/psql/ledger-transfer/index.test.ts +++ b/packages/backend/src/accounting/psql/ledger-transfer/index.test.ts @@ -22,15 +22,18 @@ import { } from '.' import { ServiceDependencies } from '../service' import { TransferError } from '../../errors' +import { AppServices } from '../../../app' +import { IocContract } from '@adonisjs/fold' describe('Ledger Transfer', (): void => { + let deps: IocContract let serviceDeps: ServiceDependencies let appContainer: TestContainer let knex: Knex let asset: Asset beforeAll(async (): Promise => { - const deps = initIocContainer({ ...Config, useTigerBeetle: false }) + deps = initIocContainer({ ...Config, useTigerBeetle: false }) appContainer = await createTestApp(deps) serviceDeps = { logger: await deps.use('logger'), @@ -65,7 +68,7 @@ describe('Ledger Transfer', (): void => { afterEach(async (): Promise => { jest.useRealTimers() - await truncateTables(knex) + await truncateTables(deps) }) afterAll(async (): Promise => { diff --git a/packages/backend/src/accounting/psql/ledger-transfer/model.test.ts b/packages/backend/src/accounting/psql/ledger-transfer/model.test.ts index aea03c66f5..9b1853b26e 100644 --- a/packages/backend/src/accounting/psql/ledger-transfer/model.test.ts +++ b/packages/backend/src/accounting/psql/ledger-transfer/model.test.ts @@ -47,7 +47,7 @@ describe('Ledger Transfer Model', (): void => { afterEach(async (): Promise => { jest.useRealTimers() - await truncateTables(knex) + await truncateTables(deps) }) afterAll(async (): Promise => { diff --git a/packages/backend/src/accounting/psql/service.test.ts b/packages/backend/src/accounting/psql/service.test.ts index e799068137..c0f544abe9 100644 --- a/packages/backend/src/accounting/psql/service.test.ts +++ b/packages/backend/src/accounting/psql/service.test.ts @@ -62,7 +62,7 @@ describe('Psql Accounting Service', (): void => { afterEach(async (): Promise => { jest.useRealTimers() - await truncateTables(knex) + await truncateTables(deps) }) afterAll(async (): Promise => { diff --git a/packages/backend/src/accounting/tigerbeetle/service.test.ts b/packages/backend/src/accounting/tigerbeetle/service.test.ts index 686f77ea21..de79ce21d3 100644 --- a/packages/backend/src/accounting/tigerbeetle/service.test.ts +++ b/packages/backend/src/accounting/tigerbeetle/service.test.ts @@ -57,7 +57,7 @@ describe('TigerBeetle Accounting Service', (): void => { }) afterEach(async (): Promise => { - await truncateTables(appContainer.knex) + await truncateTables(deps) }) afterAll(async (): Promise => { diff --git a/packages/backend/src/asset/model.test.ts b/packages/backend/src/asset/model.test.ts index d9464d7844..44d1ac98b7 100644 --- a/packages/backend/src/asset/model.test.ts +++ b/packages/backend/src/asset/model.test.ts @@ -25,7 +25,7 @@ describe('Models', (): void => { }) afterEach(async (): Promise => { - await truncateTables(appContainer.knex) + await truncateTables(deps) }) afterAll(async (): Promise => { diff --git a/packages/backend/src/asset/service.test.ts b/packages/backend/src/asset/service.test.ts index 5fd3042012..3d8e328f41 100644 --- a/packages/backend/src/asset/service.test.ts +++ b/packages/backend/src/asset/service.test.ts @@ -38,7 +38,7 @@ describe('Asset Service', (): void => { }) afterEach(async (): Promise => { - await truncateTables(appContainer.knex) + await truncateTables(deps) }) afterAll(async (): Promise => { @@ -414,7 +414,7 @@ describe('Asset Service using Cache', (): void => { }) afterEach(async (): Promise => { - await truncateTables(appContainer.knex) + await truncateTables(deps) }) afterAll(async (): Promise => { diff --git a/packages/backend/src/fee/model.test.ts b/packages/backend/src/fee/model.test.ts index 35c39d4d1b..b4b044354a 100644 --- a/packages/backend/src/fee/model.test.ts +++ b/packages/backend/src/fee/model.test.ts @@ -23,7 +23,7 @@ describe('Fee Model', (): void => { }) afterEach(async (): Promise => { - await truncateTables(appContainer.knex) + await truncateTables(deps) }) afterAll(async (): Promise => { diff --git a/packages/backend/src/fee/service.test.ts b/packages/backend/src/fee/service.test.ts index e57068f7e3..5e80acde78 100644 --- a/packages/backend/src/fee/service.test.ts +++ b/packages/backend/src/fee/service.test.ts @@ -4,7 +4,6 @@ import { TestContainer, createTestApp } from '../tests/app' import { initIocContainer } from '..' import { Config } from '../config/app' import { FeeService } from './service' -import { Knex } from 'knex' import { truncateTables } from '../tests/tableManager' import { createAsset } from '../tests/asset' import { Asset } from '../asset/model' @@ -18,14 +17,12 @@ import { Pagination, SortOrder } from '../shared/baseModel' describe('Fee Service', (): void => { let deps: IocContract let appContainer: TestContainer - let knex: Knex let feeService: FeeService let asset: Asset beforeAll(async (): Promise => { deps = await initIocContainer(Config) appContainer = await createTestApp(deps) - knex = appContainer.knex feeService = await deps.use('feeService') }) @@ -34,7 +31,7 @@ describe('Fee Service', (): void => { }) afterEach(async (): Promise => { - await truncateTables(knex) + await truncateTables(deps) }) afterAll(async (): Promise => { diff --git a/packages/backend/src/graphql/middleware/index.test.ts b/packages/backend/src/graphql/middleware/index.test.ts index 6456b418df..5728a0170c 100644 --- a/packages/backend/src/graphql/middleware/index.test.ts +++ b/packages/backend/src/graphql/middleware/index.test.ts @@ -33,7 +33,7 @@ describe('GraphQL Middleware', (): void => { }) afterEach(async (): Promise => { - await truncateTables(appContainer.knex) + await truncateTables(deps) }) afterAll(async (): Promise => { diff --git a/packages/backend/src/graphql/resolvers/accounting_transfer.psql.test.ts b/packages/backend/src/graphql/resolvers/accounting_transfer.psql.test.ts index 1ec2f06a61..fea75ff56c 100644 --- a/packages/backend/src/graphql/resolvers/accounting_transfer.psql.test.ts +++ b/packages/backend/src/graphql/resolvers/accounting_transfer.psql.test.ts @@ -37,7 +37,7 @@ describe('Accounting Transfer', (): void => { }) afterEach(async (): Promise => { - await truncateTables(appContainer.knex) + await truncateTables(deps) }) afterAll(async (): Promise => { diff --git a/packages/backend/src/graphql/resolvers/accounting_transfer.tigerbeetle.test.ts b/packages/backend/src/graphql/resolvers/accounting_transfer.tigerbeetle.test.ts index b6fe08cee7..e35a9d41ac 100644 --- a/packages/backend/src/graphql/resolvers/accounting_transfer.tigerbeetle.test.ts +++ b/packages/backend/src/graphql/resolvers/accounting_transfer.tigerbeetle.test.ts @@ -39,7 +39,7 @@ describe('TigerBeetle: Accounting Transfer', (): void => { }) afterEach(async (): Promise => { - await truncateTables(appContainer.knex) + await truncateTables(deps) }) afterAll(async (): Promise => { diff --git a/packages/backend/src/graphql/resolvers/asset.test.ts b/packages/backend/src/graphql/resolvers/asset.test.ts index 8ee9e279bd..12b6fdb2af 100644 --- a/packages/backend/src/graphql/resolvers/asset.test.ts +++ b/packages/backend/src/graphql/resolvers/asset.test.ts @@ -54,7 +54,7 @@ describe('Asset Resolvers', (): void => { }) afterEach(async (): Promise => { - await truncateTables(appContainer.knex) + await truncateTables(deps) }) afterAll(async (): Promise => { diff --git a/packages/backend/src/graphql/resolvers/auto-peering.test.ts b/packages/backend/src/graphql/resolvers/auto-peering.test.ts index 49715fdfe0..0d7862c7db 100644 --- a/packages/backend/src/graphql/resolvers/auto-peering.test.ts +++ b/packages/backend/src/graphql/resolvers/auto-peering.test.ts @@ -93,7 +93,7 @@ describe('Auto Peering Resolvers', (): void => { }) afterEach(async (): Promise => { - await truncateTables(appContainer.knex) + await truncateTables(deps) }) afterAll(async (): Promise => { diff --git a/packages/backend/src/graphql/resolvers/combined_payments.test.ts b/packages/backend/src/graphql/resolvers/combined_payments.test.ts index 2f3ac34706..8cea2d69c0 100644 --- a/packages/backend/src/graphql/resolvers/combined_payments.test.ts +++ b/packages/backend/src/graphql/resolvers/combined_payments.test.ts @@ -34,7 +34,7 @@ describe('Payment', (): void => { }) afterEach(async (): Promise => { - await truncateTables(appContainer.knex) + await truncateTables(deps) }) afterAll(async (): Promise => { diff --git a/packages/backend/src/graphql/resolvers/fee.test.ts b/packages/backend/src/graphql/resolvers/fee.test.ts index ee7306f2ef..8eade969c3 100644 --- a/packages/backend/src/graphql/resolvers/fee.test.ts +++ b/packages/backend/src/graphql/resolvers/fee.test.ts @@ -31,7 +31,7 @@ describe('Fee Resolvers', () => { }) afterEach(async () => { - await truncateTables(appContainer.knex) + await truncateTables(deps) }) afterAll(async () => { diff --git a/packages/backend/src/graphql/resolvers/incoming_payment.test.ts b/packages/backend/src/graphql/resolvers/incoming_payment.test.ts index 77dfc66d96..30cbfa893b 100644 --- a/packages/backend/src/graphql/resolvers/incoming_payment.test.ts +++ b/packages/backend/src/graphql/resolvers/incoming_payment.test.ts @@ -50,7 +50,7 @@ describe('Incoming Payment Resolver', (): void => { }) afterAll(async (): Promise => { - await truncateTables(appContainer.knex) + await truncateTables(deps) await appContainer.apolloClient.stop() await appContainer.shutdown() }) diff --git a/packages/backend/src/graphql/resolvers/liquidity.test.ts b/packages/backend/src/graphql/resolvers/liquidity.test.ts index 457099cb36..5c417e32c1 100644 --- a/packages/backend/src/graphql/resolvers/liquidity.test.ts +++ b/packages/backend/src/graphql/resolvers/liquidity.test.ts @@ -60,7 +60,7 @@ describe('Liquidity Resolvers', (): void => { }) afterAll(async (): Promise => { - await truncateTables(knex) + await truncateTables(deps) await appContainer.apolloClient.stop() await appContainer.shutdown() }) diff --git a/packages/backend/src/graphql/resolvers/outgoing_payment.test.ts b/packages/backend/src/graphql/resolvers/outgoing_payment.test.ts index a4698992dc..9bf4c88788 100644 --- a/packages/backend/src/graphql/resolvers/outgoing_payment.test.ts +++ b/packages/backend/src/graphql/resolvers/outgoing_payment.test.ts @@ -58,7 +58,7 @@ describe('OutgoingPayment Resolvers', (): void => { afterEach(async (): Promise => { jest.restoreAllMocks() - await truncateTables(appContainer.knex) + await truncateTables(deps) }) afterAll(async (): Promise => { diff --git a/packages/backend/src/graphql/resolvers/peer.test.ts b/packages/backend/src/graphql/resolvers/peer.test.ts index 4aec23a2e8..215169895e 100644 --- a/packages/backend/src/graphql/resolvers/peer.test.ts +++ b/packages/backend/src/graphql/resolvers/peer.test.ts @@ -68,7 +68,7 @@ describe('Peer Resolvers', (): void => { }) afterEach(async (): Promise => { - await truncateTables(appContainer.knex) + await truncateTables(deps) }) afterAll(async (): Promise => { diff --git a/packages/backend/src/graphql/resolvers/quote.test.ts b/packages/backend/src/graphql/resolvers/quote.test.ts index 246f7ac486..96157ee50d 100644 --- a/packages/backend/src/graphql/resolvers/quote.test.ts +++ b/packages/backend/src/graphql/resolvers/quote.test.ts @@ -46,7 +46,7 @@ describe('Quote Resolvers', (): void => { afterEach(async (): Promise => { jest.restoreAllMocks() - await truncateTables(appContainer.knex) + await truncateTables(deps) }) afterAll(async (): Promise => { diff --git a/packages/backend/src/graphql/resolvers/tenant.test.ts b/packages/backend/src/graphql/resolvers/tenant.test.ts index c4f369a2d9..99cda31197 100644 --- a/packages/backend/src/graphql/resolvers/tenant.test.ts +++ b/packages/backend/src/graphql/resolvers/tenant.test.ts @@ -72,7 +72,6 @@ describe('Tenant Resolvers', (): void => { dbSchema }) config = await deps.use('config') - console.log({ config }) appContainer = await createTestApp(deps) const authServiceClient = await deps.use('authServiceClient') jest @@ -87,7 +86,7 @@ describe('Tenant Resolvers', (): void => { }) afterEach(async (): Promise => { - await truncateTables(appContainer.knex, true, dbSchema) // pass in schema? + await truncateTables(deps, { truncateTenants: true }) }) afterAll(async (): Promise => { await appContainer.apolloClient.stop() @@ -348,7 +347,7 @@ describe('Tenant Resolvers', (): void => { }) afterEach(async (): Promise => { - await truncateTables(appContainer.knex) + await truncateTables(deps) }) test('can update a tenant as operator', async (): Promise => { diff --git a/packages/backend/src/graphql/resolvers/tenant_settings.test.ts b/packages/backend/src/graphql/resolvers/tenant_settings.test.ts index 992ce339af..473f1e0795 100644 --- a/packages/backend/src/graphql/resolvers/tenant_settings.test.ts +++ b/packages/backend/src/graphql/resolvers/tenant_settings.test.ts @@ -81,7 +81,7 @@ describe('Tenant Settings Resolvers', (): void => { }) afterEach(async (): Promise => { - await truncateTables(appContainer.knex, true, dbSchema) // TODO: pass in schema? + await truncateTables(deps, { truncateTenants: true }) }) afterAll(async (): Promise => { diff --git a/packages/backend/src/graphql/resolvers/walletAddressKey.test.ts b/packages/backend/src/graphql/resolvers/walletAddressKey.test.ts index fc4400f9af..9cd736f973 100644 --- a/packages/backend/src/graphql/resolvers/walletAddressKey.test.ts +++ b/packages/backend/src/graphql/resolvers/walletAddressKey.test.ts @@ -41,7 +41,7 @@ describe('Wallet Address Key Resolvers', (): void => { }) afterEach(async (): Promise => { - await truncateTables(appContainer.knex) + await truncateTables(deps) }) afterAll(async (): Promise => { diff --git a/packages/backend/src/graphql/resolvers/wallet_address.test.ts b/packages/backend/src/graphql/resolvers/wallet_address.test.ts index 2137e009f8..045956da46 100644 --- a/packages/backend/src/graphql/resolvers/wallet_address.test.ts +++ b/packages/backend/src/graphql/resolvers/wallet_address.test.ts @@ -63,7 +63,7 @@ describe('Wallet Address Resolvers', (): void => { }) afterEach(async (): Promise => { - await truncateTables(knex) + await truncateTables(deps) }) afterAll(async (): Promise => { diff --git a/packages/backend/src/graphql/resolvers/webhooks.test.ts b/packages/backend/src/graphql/resolvers/webhooks.test.ts index b5ecda5ccb..9d6afddfca 100644 --- a/packages/backend/src/graphql/resolvers/webhooks.test.ts +++ b/packages/backend/src/graphql/resolvers/webhooks.test.ts @@ -20,7 +20,7 @@ describe('Webhook Events Query', (): void => { }) afterEach(async (): Promise => { - await truncateTables(appContainer.knex) + await truncateTables(deps) }) afterAll(async (): Promise => { diff --git a/packages/backend/src/open_payments/authServer/service.test.ts b/packages/backend/src/open_payments/authServer/service.test.ts index 1fd758a204..9b4a4af00a 100644 --- a/packages/backend/src/open_payments/authServer/service.test.ts +++ b/packages/backend/src/open_payments/authServer/service.test.ts @@ -24,7 +24,7 @@ describe('Auth Server Service', (): void => { }) afterEach(async (): Promise => { - await truncateTables(knex) + await truncateTables(deps) }) afterAll(async (): Promise => { diff --git a/packages/backend/src/open_payments/grant/service.test.ts b/packages/backend/src/open_payments/grant/service.test.ts index eb10c65d71..fe7a0ba84e 100644 --- a/packages/backend/src/open_payments/grant/service.test.ts +++ b/packages/backend/src/open_payments/grant/service.test.ts @@ -46,7 +46,7 @@ describe('Grant Service', (): void => { afterEach(async (): Promise => { jest.useRealTimers() - await truncateTables(knex) + await truncateTables(deps) }) afterAll(async (): Promise => { diff --git a/packages/backend/src/open_payments/payment/combined/service.test.ts b/packages/backend/src/open_payments/payment/combined/service.test.ts index 60251049ec..1f2bf75580 100644 --- a/packages/backend/src/open_payments/payment/combined/service.test.ts +++ b/packages/backend/src/open_payments/payment/combined/service.test.ts @@ -4,7 +4,6 @@ import { TestContainer, createTestApp } from '../../../tests/app' import { initIocContainer } from '../../..' import { Config } from '../../../config/app' import { CombinedPaymentService } from './service' -import { Knex } from 'knex' import { truncateTables } from '../../../tests/tableManager' import { getPageTests } from '../../../shared/baseModel.test' import { createOutgoingPayment } from '../../../tests/outgoingPayment' @@ -25,7 +24,6 @@ import { describe('Combined Payment Service', (): void => { let deps: IocContract let appContainer: TestContainer - let knex: Knex let combinedPaymentService: CombinedPaymentService let tenantId: string let sendAsset: Asset @@ -36,7 +34,6 @@ describe('Combined Payment Service', (): void => { beforeAll(async (): Promise => { deps = await initIocContainer(Config) appContainer = await createTestApp(deps) - knex = appContainer.knex combinedPaymentService = await deps.use('combinedPaymentService') tenantId = Config.operatorTenantId }) @@ -57,7 +54,7 @@ describe('Combined Payment Service', (): void => { }) afterEach(async (): Promise => { - await truncateTables(knex) + await truncateTables(deps) }) afterAll(async (): Promise => { diff --git a/packages/backend/src/open_payments/payment/incoming/model.test.ts b/packages/backend/src/open_payments/payment/incoming/model.test.ts index a71cf23d4b..3f1f91c76e 100644 --- a/packages/backend/src/open_payments/payment/incoming/model.test.ts +++ b/packages/backend/src/open_payments/payment/incoming/model.test.ts @@ -29,7 +29,7 @@ describe('Models', (): void => { afterEach(async (): Promise => { jest.useRealTimers() - await truncateTables(appContainer.knex) + await truncateTables(deps) }) afterAll(async (): Promise => { diff --git a/packages/backend/src/open_payments/payment/incoming/routes.test.ts b/packages/backend/src/open_payments/payment/incoming/routes.test.ts index 06b0c69193..506aadf3ea 100644 --- a/packages/backend/src/open_payments/payment/incoming/routes.test.ts +++ b/packages/backend/src/open_payments/payment/incoming/routes.test.ts @@ -72,7 +72,7 @@ describe('Incoming Payment Routes', (): void => { }) afterEach(async (): Promise => { - await truncateTables(appContainer.knex) + await truncateTables(deps) }) afterAll(async (): Promise => { diff --git a/packages/backend/src/open_payments/payment/incoming/service.test.ts b/packages/backend/src/open_payments/payment/incoming/service.test.ts index afacfd0452..39b436341b 100644 --- a/packages/backend/src/open_payments/payment/incoming/service.test.ts +++ b/packages/backend/src/open_payments/payment/incoming/service.test.ts @@ -65,7 +65,7 @@ describe('Incoming Payment Service', (): void => { afterEach(async (): Promise => { jest.useRealTimers() - await truncateTables(knex) + await truncateTables(deps) }) afterAll(async (): Promise => { diff --git a/packages/backend/src/open_payments/payment/incoming_remote/service.test.ts b/packages/backend/src/open_payments/payment/incoming_remote/service.test.ts index a971c3523a..89d06494eb 100644 --- a/packages/backend/src/open_payments/payment/incoming_remote/service.test.ts +++ b/packages/backend/src/open_payments/payment/incoming_remote/service.test.ts @@ -1,4 +1,3 @@ -import { Knex } from 'knex' import { v4 as uuid } from 'uuid' import { RemoteIncomingPaymentService } from './service' import { createTestApp, TestContainer } from '../../../tests/app' @@ -26,7 +25,6 @@ describe('Remote Incoming Payment Service', (): void => { let deps: IocContract let appContainer: TestContainer let remoteIncomingPaymentService: RemoteIncomingPaymentService - let knex: Knex let openPaymentsClient: OpenPaymentsClient let grantService: GrantService @@ -35,7 +33,6 @@ describe('Remote Incoming Payment Service', (): void => { appContainer = await createTestApp(deps) openPaymentsClient = await deps.use('openPaymentsClient') grantService = await deps.use('grantService') - knex = appContainer.knex remoteIncomingPaymentService = await deps.use( 'remoteIncomingPaymentService' ) @@ -43,7 +40,7 @@ describe('Remote Incoming Payment Service', (): void => { afterEach(async (): Promise => { jest.useRealTimers() - await truncateTables(knex) + await truncateTables(deps) }) afterAll(async (): Promise => { diff --git a/packages/backend/src/open_payments/payment/outgoing/model.test.ts b/packages/backend/src/open_payments/payment/outgoing/model.test.ts index f724192d6c..5c3538c13d 100644 --- a/packages/backend/src/open_payments/payment/outgoing/model.test.ts +++ b/packages/backend/src/open_payments/payment/outgoing/model.test.ts @@ -23,7 +23,7 @@ describe('Outgoing Payment Event Model', (): void => { }) afterEach(async (): Promise => { - await truncateTables(appContainer.knex) + await truncateTables(deps) }) afterAll(async (): Promise => { diff --git a/packages/backend/src/open_payments/payment/outgoing/routes.test.ts b/packages/backend/src/open_payments/payment/outgoing/routes.test.ts index 98a744f1e3..4f04a3642c 100644 --- a/packages/backend/src/open_payments/payment/outgoing/routes.test.ts +++ b/packages/backend/src/open_payments/payment/outgoing/routes.test.ts @@ -88,7 +88,7 @@ describe('Outgoing Payment Routes', (): void => { }) afterEach(async (): Promise => { - await truncateTables(knex) + await truncateTables(deps) }) afterAll(async (): Promise => { diff --git a/packages/backend/src/open_payments/payment/outgoing/service.test.ts b/packages/backend/src/open_payments/payment/outgoing/service.test.ts index 05007973d3..4329933f3e 100644 --- a/packages/backend/src/open_payments/payment/outgoing/service.test.ts +++ b/packages/backend/src/open_payments/payment/outgoing/service.test.ts @@ -324,7 +324,7 @@ describe('OutgoingPaymentService', (): void => { afterEach(async (): Promise => { jest.restoreAllMocks() receiverWalletAddress.scope?.persist(false) - await truncateTables(knex) + await truncateTables(deps) }) afterAll(async (): Promise => { diff --git a/packages/backend/src/open_payments/quote/routes.test.ts b/packages/backend/src/open_payments/quote/routes.test.ts index 3c161305fb..2c480bd45d 100644 --- a/packages/backend/src/open_payments/quote/routes.test.ts +++ b/packages/backend/src/open_payments/quote/routes.test.ts @@ -89,7 +89,7 @@ describe('Quote Routes', (): void => { }) afterEach(async (): Promise => { - await truncateTables(appContainer.knex) + await truncateTables(deps) }) afterAll(async (): Promise => { diff --git a/packages/backend/src/open_payments/quote/service.test.ts b/packages/backend/src/open_payments/quote/service.test.ts index ef91ab9568..9ecc0601ba 100644 --- a/packages/backend/src/open_payments/quote/service.test.ts +++ b/packages/backend/src/open_payments/quote/service.test.ts @@ -131,7 +131,7 @@ describe('QuoteService', (): void => { afterEach(async (): Promise => { jest.restoreAllMocks() - await truncateTables(knex) + await truncateTables(deps) }) afterAll(async (): Promise => { diff --git a/packages/backend/src/open_payments/receiver/model.test.ts b/packages/backend/src/open_payments/receiver/model.test.ts index 5ed02b0144..295de1dec9 100644 --- a/packages/backend/src/open_payments/receiver/model.test.ts +++ b/packages/backend/src/open_payments/receiver/model.test.ts @@ -29,7 +29,7 @@ describe('Receiver Model', (): void => { afterEach(async (): Promise => { jest.useRealTimers() - await truncateTables(appContainer.knex) + await truncateTables(deps) }) afterAll(async (): Promise => { diff --git a/packages/backend/src/open_payments/receiver/service.test.ts b/packages/backend/src/open_payments/receiver/service.test.ts index 2077703d1c..4881e0617f 100644 --- a/packages/backend/src/open_payments/receiver/service.test.ts +++ b/packages/backend/src/open_payments/receiver/service.test.ts @@ -72,7 +72,7 @@ describe('Receiver Service', (): void => { afterEach(async (): Promise => { jest.restoreAllMocks() - await truncateTables(knex) + await truncateTables(deps) }) afterAll(async (): Promise => { diff --git a/packages/backend/src/open_payments/wallet_address/key/routes.test.ts b/packages/backend/src/open_payments/wallet_address/key/routes.test.ts index 0ea2017292..1608b3580f 100644 --- a/packages/backend/src/open_payments/wallet_address/key/routes.test.ts +++ b/packages/backend/src/open_payments/wallet_address/key/routes.test.ts @@ -37,7 +37,7 @@ describe('Wallet Address Keys Routes', (): void => { }) afterEach(async (): Promise => { - await truncateTables(appContainer.knex) + await truncateTables(deps) }) afterAll(async (): Promise => { diff --git a/packages/backend/src/open_payments/wallet_address/key/service.test.ts b/packages/backend/src/open_payments/wallet_address/key/service.test.ts index 57363cb649..c9a0970aa6 100644 --- a/packages/backend/src/open_payments/wallet_address/key/service.test.ts +++ b/packages/backend/src/open_payments/wallet_address/key/service.test.ts @@ -37,7 +37,7 @@ describe('Wallet Address Key Service', (): void => { }) afterEach(async (): Promise => { - await truncateTables(appContainer.knex) + await truncateTables(deps) }) afterAll(async (): Promise => { diff --git a/packages/backend/src/open_payments/wallet_address/middleware.test.ts b/packages/backend/src/open_payments/wallet_address/middleware.test.ts index 3ae2438ec1..af62847107 100644 --- a/packages/backend/src/open_payments/wallet_address/middleware.test.ts +++ b/packages/backend/src/open_payments/wallet_address/middleware.test.ts @@ -53,7 +53,7 @@ describe('Wallet Address Middleware', (): void => { }) afterEach(async (): Promise => { - await truncateTables(appContainer.knex) + await truncateTables(deps) }) afterAll(async (): Promise => { diff --git a/packages/backend/src/open_payments/wallet_address/model.test.ts b/packages/backend/src/open_payments/wallet_address/model.test.ts index 3dd586873d..c46499557b 100644 --- a/packages/backend/src/open_payments/wallet_address/model.test.ts +++ b/packages/backend/src/open_payments/wallet_address/model.test.ts @@ -377,7 +377,7 @@ describe('Models', (): void => { }) afterEach(async (): Promise => { - await truncateTables(appContainer.knex) + await truncateTables(deps) }) afterAll(async (): Promise => { diff --git a/packages/backend/src/open_payments/wallet_address/routes.test.ts b/packages/backend/src/open_payments/wallet_address/routes.test.ts index e30d4056fb..87da55b2ec 100644 --- a/packages/backend/src/open_payments/wallet_address/routes.test.ts +++ b/packages/backend/src/open_payments/wallet_address/routes.test.ts @@ -34,7 +34,7 @@ describe('Wallet Address Routes', (): void => { }) afterEach(async (): Promise => { - await truncateTables(appContainer.knex) + await truncateTables(deps) }) afterAll(async (): Promise => { diff --git a/packages/backend/src/open_payments/wallet_address/service.test.ts b/packages/backend/src/open_payments/wallet_address/service.test.ts index 5d6f139996..1fb01fc48d 100644 --- a/packages/backend/src/open_payments/wallet_address/service.test.ts +++ b/packages/backend/src/open_payments/wallet_address/service.test.ts @@ -50,7 +50,7 @@ describe('Open Payments Wallet Address Service', (): void => { afterEach(async (): Promise => { jest.useRealTimers() - await truncateTables(knex) + await truncateTables(deps) }) afterAll(async (): Promise => { @@ -847,7 +847,7 @@ describe('Open Payments Wallet Address Service using Cache', (): void => { afterEach(async (): Promise => { jest.useRealTimers() - await truncateTables(knex) + await truncateTables(deps) }) afterAll(async (): Promise => { diff --git a/packages/backend/src/payment-method/handler/service.test.ts b/packages/backend/src/payment-method/handler/service.test.ts index 5bcfaf36ea..519904d0d4 100644 --- a/packages/backend/src/payment-method/handler/service.test.ts +++ b/packages/backend/src/payment-method/handler/service.test.ts @@ -36,7 +36,7 @@ describe('PaymentMethodHandlerService', (): void => { afterEach(async (): Promise => { jest.restoreAllMocks() - await truncateTables(appContainer.knex) + await truncateTables(deps) }) afterAll(async (): Promise => { diff --git a/packages/backend/src/payment-method/ilp/auto-peering/routes.test.ts b/packages/backend/src/payment-method/ilp/auto-peering/routes.test.ts index 2a5f16b2a7..8c6a876ca5 100644 --- a/packages/backend/src/payment-method/ilp/auto-peering/routes.test.ts +++ b/packages/backend/src/payment-method/ilp/auto-peering/routes.test.ts @@ -23,7 +23,7 @@ describe('Auto Peering Routes', (): void => { }) afterEach(async (): Promise => { - await truncateTables(appContainer.knex) + await truncateTables(deps) }) afterAll(async (): Promise => { diff --git a/packages/backend/src/payment-method/ilp/auto-peering/service.test.ts b/packages/backend/src/payment-method/ilp/auto-peering/service.test.ts index af2c2e2282..6e5d14be75 100644 --- a/packages/backend/src/payment-method/ilp/auto-peering/service.test.ts +++ b/packages/backend/src/payment-method/ilp/auto-peering/service.test.ts @@ -38,7 +38,7 @@ describe('Auto Peering Service', (): void => { }) afterEach(async (): Promise => { - await truncateTables(appContainer.knex) + await truncateTables(deps) }) afterAll(async (): Promise => { diff --git a/packages/backend/src/payment-method/ilp/peer-http-token/service.test.ts b/packages/backend/src/payment-method/ilp/peer-http-token/service.test.ts index d9624fbb0b..1fc6c5675c 100644 --- a/packages/backend/src/payment-method/ilp/peer-http-token/service.test.ts +++ b/packages/backend/src/payment-method/ilp/peer-http-token/service.test.ts @@ -28,7 +28,7 @@ describe('HTTP Token Service', (): void => { }) afterEach(async (): Promise => { - await truncateTables(appContainer.knex) + await truncateTables(deps) }) afterAll(async (): Promise => { diff --git a/packages/backend/src/payment-method/ilp/peer/model.test.ts b/packages/backend/src/payment-method/ilp/peer/model.test.ts index a664509eb1..355a579fe5 100644 --- a/packages/backend/src/payment-method/ilp/peer/model.test.ts +++ b/packages/backend/src/payment-method/ilp/peer/model.test.ts @@ -33,7 +33,7 @@ describe('Models', (): void => { }) afterEach(async (): Promise => { - await truncateTables(appContainer.knex) + await truncateTables(deps) }) afterAll(async (): Promise => { diff --git a/packages/backend/src/payment-method/ilp/peer/service.test.ts b/packages/backend/src/payment-method/ilp/peer/service.test.ts index 9d0a7ae430..084cd7de63 100644 --- a/packages/backend/src/payment-method/ilp/peer/service.test.ts +++ b/packages/backend/src/payment-method/ilp/peer/service.test.ts @@ -55,7 +55,7 @@ describe('Peer Service', (): void => { }) afterEach(async (): Promise => { - await truncateTables(appContainer.knex) + await truncateTables(deps) }) afterAll(async (): Promise => { diff --git a/packages/backend/src/payment-method/ilp/service.test.ts b/packages/backend/src/payment-method/ilp/service.test.ts index 48e59a88c4..29f380ba61 100644 --- a/packages/backend/src/payment-method/ilp/service.test.ts +++ b/packages/backend/src/payment-method/ilp/service.test.ts @@ -80,7 +80,7 @@ describe('IlpPaymentService', (): void => { }) afterEach(async (): Promise => { - await truncateTables(appContainer.knex) + await truncateTables(deps) jest.restoreAllMocks() nock.cleanAll() diff --git a/packages/backend/src/payment-method/ilp/spsp/middleware.test.ts b/packages/backend/src/payment-method/ilp/spsp/middleware.test.ts index 896c30dac6..8d24d79e25 100644 --- a/packages/backend/src/payment-method/ilp/spsp/middleware.test.ts +++ b/packages/backend/src/payment-method/ilp/spsp/middleware.test.ts @@ -49,7 +49,7 @@ describe('SPSP Middleware', (): void => { }) afterEach(async (): Promise => { - await truncateTables(appContainer.knex) + await truncateTables(deps) }) afterAll(async (): Promise => { diff --git a/packages/backend/src/payment-method/ilp/spsp/routes.test.ts b/packages/backend/src/payment-method/ilp/spsp/routes.test.ts index b02c18274c..fdef34c42e 100644 --- a/packages/backend/src/payment-method/ilp/spsp/routes.test.ts +++ b/packages/backend/src/payment-method/ilp/spsp/routes.test.ts @@ -41,7 +41,7 @@ describe('SPSP Routes', (): void => { }) afterAll(async (): Promise => { - await truncateTables(appContainer.knex) + await truncateTables(deps) await appContainer.shutdown() }) diff --git a/packages/backend/src/payment-method/ilp/stream-credentials/service.test.ts b/packages/backend/src/payment-method/ilp/stream-credentials/service.test.ts index 3092f76491..1587af57c1 100644 --- a/packages/backend/src/payment-method/ilp/stream-credentials/service.test.ts +++ b/packages/backend/src/payment-method/ilp/stream-credentials/service.test.ts @@ -37,7 +37,7 @@ describe('Stream Credentials Service', (): void => { }) afterEach(async (): Promise => { - await truncateTables(knex) + await truncateTables(deps) }) afterAll(async (): Promise => { diff --git a/packages/backend/src/payment-method/local/service.test.ts b/packages/backend/src/payment-method/local/service.test.ts index ee2afc5d2b..4729d2d973 100644 --- a/packages/backend/src/payment-method/local/service.test.ts +++ b/packages/backend/src/payment-method/local/service.test.ts @@ -87,7 +87,7 @@ describe('LocalPaymentService', (): void => { }) afterEach(async (): Promise => { - await truncateTables(appContainer.knex) + await truncateTables(deps) jest.restoreAllMocks() nock.cleanAll() diff --git a/packages/backend/src/shared/pagination.test.ts b/packages/backend/src/shared/pagination.test.ts index 85d3df3e01..1136e50a7a 100644 --- a/packages/backend/src/shared/pagination.test.ts +++ b/packages/backend/src/shared/pagination.test.ts @@ -35,7 +35,7 @@ describe('Pagination', (): void => { }) afterEach(async (): Promise => { - await truncateTables(appContainer.knex) + await truncateTables(deps) }) afterAll(async (): Promise => { diff --git a/packages/backend/src/shared/utils.test.ts b/packages/backend/src/shared/utils.test.ts index 63b00099ca..6adebe8a04 100644 --- a/packages/backend/src/shared/utils.test.ts +++ b/packages/backend/src/shared/utils.test.ts @@ -317,7 +317,7 @@ describe('utils', (): void => { afterEach(async (): Promise => { await redis.flushall() - await truncateTables(appContainer.knex) + await truncateTables(deps) }) afterAll(async (): Promise => { diff --git a/packages/backend/src/tenants/service.test.ts b/packages/backend/src/tenants/service.test.ts index 60ce0b48ab..9cdd591d26 100644 --- a/packages/backend/src/tenants/service.test.ts +++ b/packages/backend/src/tenants/service.test.ts @@ -37,7 +37,6 @@ describe('Tenant Service', (): void => { }) knex = await deps.use('knex') config = await deps.use('config') - console.log({ config }) appContainer = await createTestApp(deps) tenantService = await deps.use('tenantService') authServiceClient = await deps.use('authServiceClient') @@ -45,7 +44,7 @@ describe('Tenant Service', (): void => { }) afterEach(async (): Promise => { - await truncateTables(knex, true, dbSchema) + await truncateTables(deps, { truncateTenants: true }) }) afterAll(async (): Promise => { diff --git a/packages/backend/src/tenants/settings/service.test.ts b/packages/backend/src/tenants/settings/service.test.ts index a95791544e..92592e3f54 100644 --- a/packages/backend/src/tenants/settings/service.test.ts +++ b/packages/backend/src/tenants/settings/service.test.ts @@ -5,7 +5,6 @@ import { Config } from '../../config/app' import { createTestApp, TestContainer } from '../../tests/app' import nock from 'nock' import { truncateTables } from '../../tests/tableManager' -import { Knex } from 'knex' import { Tenant } from '../model' import { TenantService } from '../service' import { faker } from '@faker-js/faker' @@ -26,7 +25,6 @@ import { import { AuthServiceClient } from '../../auth-service-client/client' describe('TenantSetting Service', (): void => { - let knex: Knex let deps: IocContract let appContainer: TestContainer let tenant: Tenant @@ -40,7 +38,6 @@ describe('TenantSetting Service', (): void => { deps = initIocContainer({ ...Config, dbSchema }) appContainer = await createTestApp(deps) - knex = await deps.use('knex') tenantService = await deps.use('tenantService') tenantSettingService = await deps.use('tenantSettingService') authServiceClient = await deps.use('authServiceClient') @@ -64,7 +61,7 @@ describe('TenantSetting Service', (): void => { }) afterEach(async (): Promise => { - await truncateTables(knex, true, dbSchema) + await truncateTables(deps, { truncateTenants: true }) }) afterAll(async (): Promise => { diff --git a/packages/backend/src/tests/tableManager.ts b/packages/backend/src/tests/tableManager.ts index 9467127684..cf293ef36e 100644 --- a/packages/backend/src/tests/tableManager.ts +++ b/packages/backend/src/tests/tableManager.ts @@ -1,4 +1,6 @@ +import { IocContract } from '@adonisjs/fold' import { Knex } from 'knex' +import { AppServices } from '../app' export async function truncateTable( knex: Knex, @@ -9,10 +11,15 @@ export async function truncateTable( } export async function truncateTables( - knex: Knex, - truncateTenants = false, - dbSchema?: string + deps: IocContract, + options?: { truncateTenants?: boolean } ): Promise { + const knex = await deps.use('knex') + const config = await deps.use('config') + const dbSchema = config.dbSchema ?? 'public' + + const truncateTenants = options?.truncateTenants ?? false + const ignoreTables = [ 'knex_migrations', 'knex_migrations_lock', diff --git a/packages/backend/src/webhook/service.test.ts b/packages/backend/src/webhook/service.test.ts index d2d55f09e3..3a574d8cb4 100644 --- a/packages/backend/src/webhook/service.test.ts +++ b/packages/backend/src/webhook/service.test.ts @@ -74,7 +74,7 @@ describe('Webhook Service', (): void => { afterEach(async (): Promise => { jest.useRealTimers() - await truncateTables(knex) + await truncateTables(deps) }) afterAll(async (): Promise => { From 90bf84c690085e7ebc9eead4385f132bd47d2f1b Mon Sep 17 00:00:00 2001 From: Blair Currey <12960453+BlairCurrey@users.noreply.github.com> Date: Thu, 6 Mar 2025 13:44:57 -0500 Subject: [PATCH 23/26] fix(backend): uncomment test --- packages/backend/src/tenants/service.test.ts | 172 +++++++++---------- 1 file changed, 86 insertions(+), 86 deletions(-) diff --git a/packages/backend/src/tenants/service.test.ts b/packages/backend/src/tenants/service.test.ts index 9cdd591d26..03b90aa607 100644 --- a/packages/backend/src/tenants/service.test.ts +++ b/packages/backend/src/tenants/service.test.ts @@ -18,7 +18,7 @@ import { withConfigOverride } from '../tests/helpers' import { TenantSetting } from './settings/model' import { TenantSettingService } from './settings/service' import nock from 'nock' -// import { isTenantError, TenantError } from './errors' +import { isTenantError, TenantError } from './errors' describe('Tenant Service', (): void => { let deps: IocContract @@ -449,88 +449,88 @@ describe('Tenant Service', (): void => { }) }) -// describe('Tenant Service (no tenant truncate)', (): void => { -// let deps: IocContract -// let config: IAppConfig -// let appContainer: TestContainer -// let tenantService: TenantService -// let knex: Knex -// let updateSpyWasCalled: boolean -// const dbSchema = 'tenant_service_test_schema2' - -// beforeAll(async (): Promise => { -// deps = initIocContainer({ -// ...Config, -// dbSchema -// }) -// knex = await deps.use('knex') -// config = await deps.use('config') -// tenantService = await deps.use('tenantService') - -// const updateOperatorSecretSpy = jest.spyOn( -// tenantService, -// 'updateOperatorApiSecretFromConfig' -// ) -// appContainer = await createTestApp(deps) -// updateSpyWasCalled = updateOperatorSecretSpy.mock.calls.length > 0 -// }) - -// afterEach(async (): Promise => { -// await truncateTables(knex, false, dbSchema) -// }) - -// afterAll(async (): Promise => { -// await appContainer.shutdown() -// }) -// describe('updateOperatorApiSecretFromConfig', () => { -// test('called on application start', async (): Promise => { -// expect(updateSpyWasCalled).toBe(true) -// }) - -// test('updates secret if changed', async (): Promise => { -// // Setup operator with different secret than the config. -// // As-if the api secret was set from a different config value originally. -// const initialApiSecret = '123' -// assert(initialApiSecret !== config.adminApiSecret) -// const tenant = await Tenant.query(knex).patchAndFetchById( -// config.operatorTenantId, -// { apiSecret: initialApiSecret } -// ) -// assert(tenant) -// expect(tenant.apiSecret).toBe(initialApiSecret) - -// const error = await tenantService.updateOperatorApiSecretFromConfig() -// expect(error).toBe(undefined) - -// const updated = await Tenant.query(knex).findById(tenant.id) -// assert(updated) -// expect(updated.apiSecret).toBe(config.adminApiSecret) -// }) -// test('does not update if secret hasnt changed', async (): Promise => { -// const tenant = await Tenant.query(knex).findById(config.operatorTenantId) -// assert(tenant) -// assert(tenant.apiSecret === config.adminApiSecret) - -// const error = await tenantService.updateOperatorApiSecretFromConfig() - -// expect(error).toBe(undefined) - -// const updated = await Tenant.query(knex).findById(tenant.id) -// assert(updated) -// expect(updated.updatedAt).toStrictEqual(tenant.updatedAt) -// }) -// test( -// 'throws error if operator tenant not found', -// withConfigOverride( -// () => config, -// { operatorTenantId: crypto.randomUUID() }, -// async (): Promise => { -// const error = await tenantService.updateOperatorApiSecretFromConfig() - -// expect(isTenantError(error)).toBe(true) -// expect(error).toEqual(TenantError.TenantNotFound) -// } -// ) -// ) -// }) -// }) +describe('Tenant Service (no tenant truncate)', (): void => { + let deps: IocContract + let config: IAppConfig + let appContainer: TestContainer + let tenantService: TenantService + let knex: Knex + let updateSpyWasCalled: boolean + const dbSchema = 'tenant_service_test_schema2' + + beforeAll(async (): Promise => { + deps = initIocContainer({ + ...Config, + dbSchema + }) + knex = await deps.use('knex') + config = await deps.use('config') + tenantService = await deps.use('tenantService') + + const updateOperatorSecretSpy = jest.spyOn( + tenantService, + 'updateOperatorApiSecretFromConfig' + ) + appContainer = await createTestApp(deps) + updateSpyWasCalled = updateOperatorSecretSpy.mock.calls.length > 0 + }) + + afterEach(async (): Promise => { + await truncateTables(deps) + }) + + afterAll(async (): Promise => { + await appContainer.shutdown() + }) + describe('updateOperatorApiSecretFromConfig', () => { + test('called on application start', async (): Promise => { + expect(updateSpyWasCalled).toBe(true) + }) + + test('updates secret if changed', async (): Promise => { + // Setup operator with different secret than the config. + // As-if the api secret was set from a different config value originally. + const initialApiSecret = '123' + assert(initialApiSecret !== config.adminApiSecret) + const tenant = await Tenant.query(knex).patchAndFetchById( + config.operatorTenantId, + { apiSecret: initialApiSecret } + ) + assert(tenant) + expect(tenant.apiSecret).toBe(initialApiSecret) + + const error = await tenantService.updateOperatorApiSecretFromConfig() + expect(error).toBe(undefined) + + const updated = await Tenant.query(knex).findById(tenant.id) + assert(updated) + expect(updated.apiSecret).toBe(config.adminApiSecret) + }) + test('does not update if secret hasnt changed', async (): Promise => { + const tenant = await Tenant.query(knex).findById(config.operatorTenantId) + assert(tenant) + assert(tenant.apiSecret === config.adminApiSecret) + + const error = await tenantService.updateOperatorApiSecretFromConfig() + + expect(error).toBe(undefined) + + const updated = await Tenant.query(knex).findById(tenant.id) + assert(updated) + expect(updated.updatedAt).toStrictEqual(tenant.updatedAt) + }) + test( + 'throws error if operator tenant not found', + withConfigOverride( + () => config, + { operatorTenantId: crypto.randomUUID() }, + async (): Promise => { + const error = await tenantService.updateOperatorApiSecretFromConfig() + + expect(isTenantError(error)).toBe(true) + expect(error).toEqual(TenantError.TenantNotFound) + } + ) + ) + }) +}) From eeae56154e4689bcfb83c9f02ccd5aaeb659ab90 Mon Sep 17 00:00:00 2001 From: Blair Currey <12960453+BlairCurrey@users.noreply.github.com> Date: Fri, 7 Mar 2025 09:43:36 -0500 Subject: [PATCH 24/26] Update packages/backend/src/tenants/service.ts Co-authored-by: Max Kurapov --- packages/backend/src/tenants/service.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/backend/src/tenants/service.ts b/packages/backend/src/tenants/service.ts index ea6b7807fc..82946c56ba 100644 --- a/packages/backend/src/tenants/service.ts +++ b/packages/backend/src/tenants/service.ts @@ -200,7 +200,6 @@ async function updateOperatorApiSecretFromConfig( const tenant = await Tenant.query(deps.knex) .findById(operatorTenantId) .whereNull('deletedAt') - .first() if (!tenant) { return TenantError.TenantNotFound From 844e885f1109ef6e7a57bdb3ed83d28c64029cba Mon Sep 17 00:00:00 2001 From: Blair Currey <12960453+BlairCurrey@users.noreply.github.com> Date: Fri, 7 Mar 2025 09:44:56 -0500 Subject: [PATCH 25/26] fix(backend): rm unushesd nock --- packages/backend/src/tenants/service.test.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/backend/src/tenants/service.test.ts b/packages/backend/src/tenants/service.test.ts index 03b90aa607..2e5f686fe5 100644 --- a/packages/backend/src/tenants/service.test.ts +++ b/packages/backend/src/tenants/service.test.ts @@ -48,7 +48,6 @@ describe('Tenant Service', (): void => { }) afterAll(async (): Promise => { - nock.cleanAll() // TODO: rm? await appContainer.shutdown() }) From 9f8f0c1fe822ebebf3d4c044bac65a46fd14b41c Mon Sep 17 00:00:00 2001 From: Blair Currey <12960453+BlairCurrey@users.noreply.github.com> Date: Fri, 7 Mar 2025 10:41:35 -0500 Subject: [PATCH 26/26] feat(backend): set cache on update operator secret method --- packages/backend/src/tenants/service.test.ts | 7 ++++++- packages/backend/src/tenants/service.ts | 5 ++--- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/packages/backend/src/tenants/service.test.ts b/packages/backend/src/tenants/service.test.ts index 2e5f686fe5..6dcb645737 100644 --- a/packages/backend/src/tenants/service.test.ts +++ b/packages/backend/src/tenants/service.test.ts @@ -17,7 +17,6 @@ import { AuthServiceClient } from '../auth-service-client/client' import { withConfigOverride } from '../tests/helpers' import { TenantSetting } from './settings/model' import { TenantSettingService } from './settings/service' -import nock from 'nock' import { isTenantError, TenantError } from './errors' describe('Tenant Service', (): void => { @@ -454,6 +453,7 @@ describe('Tenant Service (no tenant truncate)', (): void => { let appContainer: TestContainer let tenantService: TenantService let knex: Knex + let tenantCache: CacheDataStore let updateSpyWasCalled: boolean const dbSchema = 'tenant_service_test_schema2' @@ -465,6 +465,7 @@ describe('Tenant Service (no tenant truncate)', (): void => { knex = await deps.use('knex') config = await deps.use('config') tenantService = await deps.use('tenantService') + tenantCache = await deps.use('tenantCache') const updateOperatorSecretSpy = jest.spyOn( tenantService, @@ -504,6 +505,10 @@ describe('Tenant Service (no tenant truncate)', (): void => { const updated = await Tenant.query(knex).findById(tenant.id) assert(updated) expect(updated.apiSecret).toBe(config.adminApiSecret) + + const cacheUpdated = await tenantCache.get(tenant.id) + assert(cacheUpdated) + expect(cacheUpdated.apiSecret).toBe(config.adminApiSecret) }) test('does not update if secret hasnt changed', async (): Promise => { const tenant = await Tenant.query(knex).findById(config.operatorTenantId) diff --git a/packages/backend/src/tenants/service.ts b/packages/backend/src/tenants/service.ts index 82946c56ba..4cd56dce85 100644 --- a/packages/backend/src/tenants/service.ts +++ b/packages/backend/src/tenants/service.ts @@ -205,8 +205,7 @@ async function updateOperatorApiSecretFromConfig( return TenantError.TenantNotFound } if (tenant.apiSecret !== adminApiSecret) { - await Tenant.query(deps.knex) - .patch({ apiSecret: adminApiSecret }) - .where('id', operatorTenantId) + await tenant.$query(deps.knex).patch({ apiSecret: adminApiSecret }) + await deps.tenantCache.set(operatorTenantId, tenant) } }