mirror of
https://github.com/maybe-finance/maybe.git
synced 2025-08-09 15:35:22 +02:00
Merge pull request #175 from tmyracle/remove-plaid-from-tests
Remove Plaid from tests
This commit is contained in:
commit
0d5d7d5a7f
7 changed files with 124 additions and 143 deletions
|
@ -37,7 +37,7 @@ And dozens upon dozens of smaller features.
|
||||||
|
|
||||||
This is the current state of building the app. We're actively working to make this process much more streamlined!
|
This is the current state of building the app. We're actively working to make this process much more streamlined!
|
||||||
|
|
||||||
*You'll need Docker installed to run the app locally.*
|
_You'll need Docker installed to run the app locally._
|
||||||
[Docker Desktop](https://www.docker.com/products/docker-desktop/) is an easy way to get started.
|
[Docker Desktop](https://www.docker.com/products/docker-desktop/) is an easy way to get started.
|
||||||
|
|
||||||
First, copy the `.env.example` file to `.env`:
|
First, copy the `.env.example` file to `.env`:
|
||||||
|
|
|
@ -44,7 +44,7 @@ const WithAuth = function ({ children }: PropsWithChildren) {
|
||||||
}
|
}
|
||||||
}, [session, status, router])
|
}, [session, status, router])
|
||||||
|
|
||||||
if (session) {
|
if (session && status === 'authenticated') {
|
||||||
return (
|
return (
|
||||||
<OnboardingGuard>
|
<OnboardingGuard>
|
||||||
<UserAccountContextProvider>
|
<UserAccountContextProvider>
|
||||||
|
|
|
@ -1,21 +1,14 @@
|
||||||
import type { AxiosInstance } from 'axios'
|
import type { AxiosInstance } from 'axios'
|
||||||
import type { SharedType } from '@maybe-finance/shared'
|
import type { Prisma, AccountConnection, User } from '@prisma/client'
|
||||||
import type { Prisma, AccountConnection, AccountSyncStatus, User } from '@prisma/client'
|
import { AccountConnectionType, AccountSyncStatus } from '@prisma/client'
|
||||||
import type { ItemRemoveResponse } from 'plaid'
|
|
||||||
import { startServer, stopServer } from './utils/server'
|
import { startServer, stopServer } from './utils/server'
|
||||||
import { getAxiosClient } from './utils/axios'
|
import { getAxiosClient } from './utils/axios'
|
||||||
import prisma from '../lib/prisma'
|
import prisma from '../lib/prisma'
|
||||||
import { TestUtil } from '@maybe-finance/shared'
|
|
||||||
import { InMemoryQueue } from '@maybe-finance/server/shared'
|
import { InMemoryQueue } from '@maybe-finance/server/shared'
|
||||||
import { default as _plaid } from '../lib/plaid'
|
|
||||||
import nock from 'nock'
|
import nock from 'nock'
|
||||||
import { resetUser } from './utils/user'
|
import { resetUser } from './utils/user'
|
||||||
|
|
||||||
jest.mock('../middleware/validate-plaid-jwt.ts')
|
jest.mock('../lib/teller.ts')
|
||||||
jest.mock('plaid')
|
|
||||||
|
|
||||||
// For TypeScript support
|
|
||||||
const plaid = jest.mocked(_plaid)
|
|
||||||
|
|
||||||
const authId = '__TEST_USER_ID__'
|
const authId = '__TEST_USER_ID__'
|
||||||
let axios: AxiosInstance
|
let axios: AxiosInstance
|
||||||
|
@ -49,13 +42,13 @@ beforeEach(async () => {
|
||||||
connectionData = {
|
connectionData = {
|
||||||
data: {
|
data: {
|
||||||
name: 'Chase Test',
|
name: 'Chase Test',
|
||||||
type: 'plaid' as SharedType.AccountConnectionType,
|
type: AccountConnectionType.teller,
|
||||||
plaidItemId: 'test-plaid-item-server',
|
tellerEnrollmentId: 'test-teller-item-workers',
|
||||||
plaidInstitutionId: 'ins_3',
|
tellerInstitutionId: 'chase_test',
|
||||||
plaidAccessToken:
|
tellerAccessToken:
|
||||||
'U2FsdGVkX1+WMq9lfTS9Zkbgrn41+XT1hvSK5ain/udRPujzjVCAx/lyPG7EumVZA+nVKXPauGwI+d7GZgtqTA9R3iCZNusU6LFPnmFOCE4=',
|
'U2FsdGVkX1+WMq9lfTS9Zkbgrn41+XT1hvSK5ain/udRPujzjVCAx/lyPG7EumVZA+nVKXPauGwI+d7GZgtqTA9R3iCZNusU6LFPnmFOCE4=', // need correct encoding here
|
||||||
userId: user!.id,
|
userId: user.id,
|
||||||
syncStatus: 'PENDING' as AccountSyncStatus,
|
syncStatus: AccountSyncStatus.PENDING,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -91,16 +84,9 @@ describe('/v1/connections API', () => {
|
||||||
})
|
})
|
||||||
|
|
||||||
it('DELETE /:id', async () => {
|
it('DELETE /:id', async () => {
|
||||||
plaid.itemRemove.mockResolvedValueOnce(
|
|
||||||
TestUtil.axiosSuccess<ItemRemoveResponse>({
|
|
||||||
request_id: 'test request id',
|
|
||||||
})
|
|
||||||
)
|
|
||||||
|
|
||||||
const res = await axios.delete<AccountConnection>(`/connections/${connection.id}`)
|
const res = await axios.delete<AccountConnection>(`/connections/${connection.id}`)
|
||||||
|
|
||||||
expect(res.status).toEqual(200)
|
expect(res.status).toEqual(200)
|
||||||
expect(plaid.itemRemove).toHaveBeenCalledTimes(1)
|
|
||||||
|
|
||||||
const res2 = await axios.get<AccountConnection>(`/connections/${connection.id}`)
|
const res2 = await axios.get<AccountConnection>(`/connections/${connection.id}`)
|
||||||
|
|
||||||
|
|
|
@ -5,7 +5,6 @@ import { DateTime } from 'luxon'
|
||||||
import { PgService } from '@maybe-finance/server/shared'
|
import { PgService } from '@maybe-finance/server/shared'
|
||||||
import { AccountQueryService, UserService } from '@maybe-finance/server/features'
|
import { AccountQueryService, UserService } from '@maybe-finance/server/features'
|
||||||
import { resetUser } from './utils/user'
|
import { resetUser } from './utils/user'
|
||||||
jest.mock('plaid')
|
|
||||||
|
|
||||||
const prisma = new PrismaClient()
|
const prisma = new PrismaClient()
|
||||||
|
|
||||||
|
|
|
@ -1,26 +1,21 @@
|
||||||
// =====================================================
|
// =====================================================
|
||||||
// Keep these imports above the rest to avoid errors
|
// Keep these imports above the rest to avoid errors
|
||||||
// =====================================================
|
// =====================================================
|
||||||
import type { SharedType } from '@maybe-finance/shared'
|
import { TellerGenerator } from 'tools/generators'
|
||||||
import type { AccountsGetResponse, TransactionsGetResponse } from 'plaid'
|
import type { User, AccountConnection } from '@prisma/client'
|
||||||
import type { AccountConnection, User } from '@prisma/client'
|
import { AccountConnectionType } from '@prisma/client'
|
||||||
import { TestUtil } from '@maybe-finance/shared'
|
|
||||||
import { PlaidTestData } from '../../../../../tools/test-data'
|
|
||||||
import { Prisma } from '@prisma/client'
|
|
||||||
import prisma from '../lib/prisma'
|
import prisma from '../lib/prisma'
|
||||||
import { default as _plaid } from '../lib/plaid'
|
import { default as _teller } from '../lib/teller'
|
||||||
import nock from 'nock'
|
|
||||||
import { DateTime } from 'luxon'
|
|
||||||
import { resetUser } from './helpers/user.test-helper'
|
import { resetUser } from './helpers/user.test-helper'
|
||||||
|
import { Interval } from 'luxon'
|
||||||
|
|
||||||
// Import the workers process
|
// Import the workers process
|
||||||
import '../../main'
|
import '../../main'
|
||||||
import { queueService, securityPricingService } from '../lib/di'
|
import { queueService } from '../lib/di'
|
||||||
|
|
||||||
jest.mock('plaid')
|
|
||||||
|
|
||||||
// For TypeScript support
|
// For TypeScript support
|
||||||
const plaid = jest.mocked(_plaid)
|
jest.mock('../lib/teller')
|
||||||
|
const teller = jest.mocked(_teller)
|
||||||
|
|
||||||
let user: User | null
|
let user: User | null
|
||||||
let connection: AccountConnection
|
let connection: AccountConnection
|
||||||
|
@ -30,25 +25,6 @@ if (process.env.IS_VSCODE_DEBUG === 'true') {
|
||||||
jest.setTimeout(100000)
|
jest.setTimeout(100000)
|
||||||
}
|
}
|
||||||
|
|
||||||
beforeAll(() => {
|
|
||||||
nock.disableNetConnect()
|
|
||||||
|
|
||||||
nock('https://api.polygon.io')
|
|
||||||
.get((uri) => uri.includes('v2/aggs/ticker/AAPL/range/1/day'))
|
|
||||||
.reply(200, PlaidTestData.AAPL)
|
|
||||||
.persist()
|
|
||||||
|
|
||||||
nock('https://api.polygon.io')
|
|
||||||
.get((uri) => uri.includes('v2/aggs/ticker/WMT/range/1/day'))
|
|
||||||
.reply(200, PlaidTestData.WMT)
|
|
||||||
.persist()
|
|
||||||
|
|
||||||
nock('https://api.polygon.io')
|
|
||||||
.get((uri) => uri.includes('v2/aggs/ticker/VOO/range/1/day'))
|
|
||||||
.reply(200, PlaidTestData.VOO)
|
|
||||||
.persist()
|
|
||||||
})
|
|
||||||
|
|
||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
jest.clearAllMocks()
|
jest.clearAllMocks()
|
||||||
|
|
||||||
|
@ -57,10 +33,10 @@ beforeEach(async () => {
|
||||||
connection = await prisma.accountConnection.create({
|
connection = await prisma.accountConnection.create({
|
||||||
data: {
|
data: {
|
||||||
name: 'Chase Test',
|
name: 'Chase Test',
|
||||||
type: 'plaid' as SharedType.AccountConnectionType,
|
type: AccountConnectionType.teller,
|
||||||
plaidItemId: 'test-plaid-item-workers',
|
tellerEnrollmentId: 'test-teller-item-workers',
|
||||||
plaidInstitutionId: 'ins_3',
|
tellerInstitutionId: 'chase_test',
|
||||||
plaidAccessToken:
|
tellerAccessToken:
|
||||||
'U2FsdGVkX1+WMq9lfTS9Zkbgrn41+XT1hvSK5ain/udRPujzjVCAx/lyPG7EumVZA+nVKXPauGwI+d7GZgtqTA9R3iCZNusU6LFPnmFOCE4=', // need correct encoding here
|
'U2FsdGVkX1+WMq9lfTS9Zkbgrn41+XT1hvSK5ain/udRPujzjVCAx/lyPG7EumVZA+nVKXPauGwI+d7GZgtqTA9R3iCZNusU6LFPnmFOCE4=', // need correct encoding here
|
||||||
userId: user.id,
|
userId: user.id,
|
||||||
syncStatus: 'PENDING',
|
syncStatus: 'PENDING',
|
||||||
|
@ -84,7 +60,7 @@ describe('Message queue tests', () => {
|
||||||
it('Should handle sync errors', async () => {
|
it('Should handle sync errors', async () => {
|
||||||
const syncQueue = queueService.getQueue('sync-account-connection')
|
const syncQueue = queueService.getQueue('sync-account-connection')
|
||||||
|
|
||||||
plaid.accountsGet.mockRejectedValueOnce('forced error for Jest tests')
|
teller.getAccounts.mockRejectedValueOnce(new Error('forced error for Jest tests'))
|
||||||
|
|
||||||
await syncQueue.add('sync-connection', { accountConnectionId: connection.id })
|
await syncQueue.add('sync-connection', { accountConnectionId: connection.id })
|
||||||
|
|
||||||
|
@ -92,7 +68,7 @@ describe('Message queue tests', () => {
|
||||||
where: { id: connection.id },
|
where: { id: connection.id },
|
||||||
})
|
})
|
||||||
|
|
||||||
expect(plaid.accountsGet).toHaveBeenCalledTimes(1)
|
expect(teller.getAccounts).toHaveBeenCalledTimes(1)
|
||||||
expect(updatedConnection?.status).toEqual('ERROR')
|
expect(updatedConnection?.status).toEqual('ERROR')
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -117,28 +93,23 @@ describe('Message queue tests', () => {
|
||||||
const syncQueue = queueService.getQueue('sync-account-connection')
|
const syncQueue = queueService.getQueue('sync-account-connection')
|
||||||
|
|
||||||
// Mock will return a basic banking checking account
|
// Mock will return a basic banking checking account
|
||||||
plaid.accountsGet.mockResolvedValueOnce(
|
const mockAccounts = TellerGenerator.generateAccountsWithBalances({
|
||||||
TestUtil.axiosSuccess<AccountsGetResponse>({
|
count: 1,
|
||||||
accounts: [PlaidTestData.checkingAccount],
|
institutionId: 'chase_test',
|
||||||
item: PlaidTestData.item,
|
enrollmentId: 'test-teller-item-workers',
|
||||||
request_id: 'bkVE1BHWMAZ9Rnr',
|
institutionName: 'Chase Test',
|
||||||
}) as any
|
accountType: 'depository',
|
||||||
)
|
accountSubType: 'checking',
|
||||||
|
})
|
||||||
|
teller.getAccounts.mockResolvedValueOnce(mockAccounts)
|
||||||
|
|
||||||
plaid.transactionsGet.mockResolvedValueOnce(
|
const mockTransactions = TellerGenerator.generateTransactions(10, mockAccounts[0].id)
|
||||||
TestUtil.axiosSuccess<TransactionsGetResponse>({
|
teller.getTransactions.mockResolvedValueOnce(mockTransactions)
|
||||||
accounts: [PlaidTestData.checkingAccount],
|
|
||||||
transactions: PlaidTestData.checkingTransactions,
|
|
||||||
item: PlaidTestData.item,
|
|
||||||
total_transactions: PlaidTestData.checkingTransactions.length,
|
|
||||||
request_id: '45QSn',
|
|
||||||
}) as any
|
|
||||||
)
|
|
||||||
|
|
||||||
await syncQueue.add('sync-connection', { accountConnectionId: connection.id })
|
await syncQueue.add('sync-connection', { accountConnectionId: connection.id })
|
||||||
|
|
||||||
expect(plaid.accountsGet).toHaveBeenCalledTimes(1)
|
expect(teller.getAccounts).toHaveBeenCalledTimes(1)
|
||||||
expect(plaid.transactionsGet).toHaveBeenCalledTimes(1)
|
expect(teller.getTransactions).toHaveBeenCalledTimes(1)
|
||||||
|
|
||||||
const item = await prisma.accountConnection.findUniqueOrThrow({
|
const item = await prisma.accountConnection.findUniqueOrThrow({
|
||||||
where: { id: connection.id },
|
where: { id: connection.id },
|
||||||
|
@ -146,7 +117,7 @@ describe('Message queue tests', () => {
|
||||||
accounts: {
|
accounts: {
|
||||||
include: {
|
include: {
|
||||||
balances: {
|
balances: {
|
||||||
where: PlaidTestData.testDates.prismaWhereFilter,
|
where: TellerGenerator.testDates.prismaWhereFilter,
|
||||||
orderBy: { date: 'asc' },
|
orderBy: { date: 'asc' },
|
||||||
},
|
},
|
||||||
transactions: true,
|
transactions: true,
|
||||||
|
@ -162,61 +133,25 @@ describe('Message queue tests', () => {
|
||||||
|
|
||||||
const [account] = item.accounts
|
const [account] = item.accounts
|
||||||
|
|
||||||
expect(account.transactions).toHaveLength(PlaidTestData.checkingTransactions.length)
|
const intervalDates = Interval.fromDateTimes(
|
||||||
expect(account.balances.map((b) => b.balance)).toEqual(
|
TellerGenerator.lowerBound,
|
||||||
[
|
TellerGenerator.now
|
||||||
3630,
|
|
||||||
5125,
|
|
||||||
5125,
|
|
||||||
5125,
|
|
||||||
5125,
|
|
||||||
5125,
|
|
||||||
5125,
|
|
||||||
5125,
|
|
||||||
5125,
|
|
||||||
5125,
|
|
||||||
5115,
|
|
||||||
5115,
|
|
||||||
5115,
|
|
||||||
5089.45,
|
|
||||||
5089.45,
|
|
||||||
PlaidTestData.checkingAccount.balances.current!,
|
|
||||||
].map((v) => new Prisma.Decimal(v))
|
|
||||||
)
|
)
|
||||||
|
.splitBy({ day: 1 })
|
||||||
|
.map((date: Interval) => date.start.toISODate())
|
||||||
|
|
||||||
|
const startingBalance = Number(mockAccounts[0].balance.available)
|
||||||
|
|
||||||
|
const balances = TellerGenerator.calculateDailyBalances(
|
||||||
|
startingBalance,
|
||||||
|
mockTransactions,
|
||||||
|
intervalDates
|
||||||
|
)
|
||||||
|
|
||||||
|
expect(account.transactions).toHaveLength(10)
|
||||||
|
expect(account.balances.map((b) => b.balance)).toEqual(balances)
|
||||||
expect(account.holdings).toHaveLength(0)
|
expect(account.holdings).toHaveLength(0)
|
||||||
expect(account.valuations).toHaveLength(0)
|
expect(account.valuations).toHaveLength(0)
|
||||||
expect(account.investmentTransactions).toHaveLength(0)
|
expect(account.investmentTransactions).toHaveLength(0)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('Should sync valid security prices', async () => {
|
|
||||||
const security = await prisma.security.create({
|
|
||||||
data: {
|
|
||||||
name: 'Walmart Inc.',
|
|
||||||
symbol: 'WMT',
|
|
||||||
cusip: '93114210310',
|
|
||||||
pricingLastSyncedAt: new Date(),
|
|
||||||
},
|
|
||||||
})
|
|
||||||
|
|
||||||
await securityPricingService.sync(security)
|
|
||||||
|
|
||||||
const prices = await prisma.securityPricing.findMany({
|
|
||||||
where: { securityId: security.id },
|
|
||||||
orderBy: { date: 'asc' },
|
|
||||||
})
|
|
||||||
|
|
||||||
expect(prices).toHaveLength(PlaidTestData.WMT.results.length)
|
|
||||||
|
|
||||||
expect(
|
|
||||||
prices.map((p) => ({
|
|
||||||
date: DateTime.fromJSDate(p.date, { zone: 'utc' }).toISODate(),
|
|
||||||
price: p.priceClose.toNumber(),
|
|
||||||
}))
|
|
||||||
).toEqual(
|
|
||||||
PlaidTestData.WMT.results.map((p) => ({
|
|
||||||
date: DateTime.fromMillis(p.t, { zone: 'utc' }).toISODate(),
|
|
||||||
price: p.c,
|
|
||||||
}))
|
|
||||||
)
|
|
||||||
})
|
|
||||||
})
|
})
|
||||||
|
|
|
@ -8,17 +8,30 @@ const prisma = new PrismaClient()
|
||||||
*/
|
*/
|
||||||
async function main() {
|
async function main() {
|
||||||
const institutions: (Pick<Institution, 'id' | 'name'> & {
|
const institutions: (Pick<Institution, 'id' | 'name'> & {
|
||||||
providers: { provider: Provider; providerId: string; rank?: number }[]
|
providers: { provider: Provider; providerId: string; logoUrl: string; rank?: number }[]
|
||||||
})[] = [
|
})[] = [
|
||||||
{
|
{
|
||||||
id: 1,
|
id: 1,
|
||||||
name: 'Capital One',
|
name: 'Capital One',
|
||||||
providers: [{ provider: 'PLAID', providerId: 'ins_9', rank: 1 }],
|
providers: [
|
||||||
|
{
|
||||||
|
provider: Provider.TELLER,
|
||||||
|
providerId: 'capital_one',
|
||||||
|
logoUrl: 'https://teller.io/images/banks/capital_one.jpg',
|
||||||
|
rank: 1,
|
||||||
|
},
|
||||||
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 2,
|
id: 2,
|
||||||
name: 'Discover Bank',
|
name: 'Wells Fargo',
|
||||||
providers: [{ provider: 'PLAID', providerId: 'ins_33' }],
|
providers: [
|
||||||
|
{
|
||||||
|
provider: Provider.TELLER,
|
||||||
|
providerId: 'wells_fargo',
|
||||||
|
logoUrl: 'https://teller.io/images/banks/wells_fargo.jpg',
|
||||||
|
},
|
||||||
|
],
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
import { faker } from '@faker-js/faker'
|
import { faker } from '@faker-js/faker'
|
||||||
import type { TellerTypes } from '../../libs/teller-api/src'
|
import type { TellerTypes } from '../../libs/teller-api/src'
|
||||||
|
import type { Prisma } from '@prisma/client'
|
||||||
|
import { DateTime } from 'luxon'
|
||||||
|
|
||||||
function generateSubType(
|
function generateSubType(
|
||||||
type: TellerTypes.AccountTypes
|
type: TellerTypes.AccountTypes
|
||||||
|
@ -23,6 +25,8 @@ type GenerateAccountsParams = {
|
||||||
enrollmentId: string
|
enrollmentId: string
|
||||||
institutionName: string
|
institutionName: string
|
||||||
institutionId: string
|
institutionId: string
|
||||||
|
accountType?: TellerTypes.AccountTypes
|
||||||
|
accountSubType?: TellerTypes.DepositorySubtypes | TellerTypes.CreditSubtype
|
||||||
}
|
}
|
||||||
|
|
||||||
export function generateAccounts({
|
export function generateAccounts({
|
||||||
|
@ -30,12 +34,15 @@ export function generateAccounts({
|
||||||
enrollmentId,
|
enrollmentId,
|
||||||
institutionName,
|
institutionName,
|
||||||
institutionId,
|
institutionId,
|
||||||
|
accountType,
|
||||||
|
accountSubType,
|
||||||
}: GenerateAccountsParams) {
|
}: GenerateAccountsParams) {
|
||||||
const accounts: TellerTypes.Account[] = []
|
const accounts: TellerTypes.Account[] = []
|
||||||
for (let i = 0; i < count; i++) {
|
for (let i = 0; i < count; i++) {
|
||||||
const accountId = faker.string.uuid()
|
const accountId = faker.string.uuid()
|
||||||
const lastFour = faker.finance.creditCardNumber().slice(-4)
|
const lastFour = faker.finance.creditCardNumber().slice(-4)
|
||||||
const type: TellerTypes.AccountTypes = faker.helpers.arrayElement(['depository', 'credit'])
|
const type: TellerTypes.AccountTypes =
|
||||||
|
accountType ?? faker.helpers.arrayElement(['depository', 'credit'])
|
||||||
let subType: TellerTypes.DepositorySubtypes | TellerTypes.CreditSubtype
|
let subType: TellerTypes.DepositorySubtypes | TellerTypes.CreditSubtype
|
||||||
subType = generateSubType(type)
|
subType = generateSubType(type)
|
||||||
|
|
||||||
|
@ -99,6 +106,8 @@ type GenerateAccountsWithBalancesParams = {
|
||||||
enrollmentId: string
|
enrollmentId: string
|
||||||
institutionName: string
|
institutionName: string
|
||||||
institutionId: string
|
institutionId: string
|
||||||
|
accountType?: TellerTypes.AccountTypes
|
||||||
|
accountSubType?: TellerTypes.DepositorySubtypes | TellerTypes.CreditSubtype
|
||||||
}
|
}
|
||||||
|
|
||||||
export function generateAccountsWithBalances({
|
export function generateAccountsWithBalances({
|
||||||
|
@ -106,7 +115,9 @@ export function generateAccountsWithBalances({
|
||||||
enrollmentId,
|
enrollmentId,
|
||||||
institutionName,
|
institutionName,
|
||||||
institutionId,
|
institutionId,
|
||||||
}: GenerateAccountsWithBalancesParams): TellerTypes.AccountWithBalances[] {
|
accountType,
|
||||||
|
accountSubType,
|
||||||
|
}: GenerateAccountsWithBalancesParams): TellerTypes.GetAccountsResponse {
|
||||||
const accountsWithBalances: TellerTypes.AccountWithBalances[] = []
|
const accountsWithBalances: TellerTypes.AccountWithBalances[] = []
|
||||||
for (let i = 0; i < count; i++) {
|
for (let i = 0; i < count; i++) {
|
||||||
const account = generateAccounts({
|
const account = generateAccounts({
|
||||||
|
@ -114,6 +125,8 @@ export function generateAccountsWithBalances({
|
||||||
enrollmentId,
|
enrollmentId,
|
||||||
institutionName,
|
institutionName,
|
||||||
institutionId,
|
institutionId,
|
||||||
|
accountType,
|
||||||
|
accountSubType,
|
||||||
})[0]
|
})[0]
|
||||||
const balance = generateBalance(account.id)
|
const balance = generateBalance(account.id)
|
||||||
accountsWithBalances.push({
|
accountsWithBalances.push({
|
||||||
|
@ -170,7 +183,10 @@ export function generateTransactions(count: number, accountId: string): TellerTy
|
||||||
running_balance: null,
|
running_balance: null,
|
||||||
description: faker.word.words({ count: { min: 3, max: 10 } }),
|
description: faker.word.words({ count: { min: 3, max: 10 } }),
|
||||||
id: transactionId,
|
id: transactionId,
|
||||||
date: faker.date.recent({ days: 30 }).toISOString().split('T')[0], // recent date in 'YYYY-MM-DD' format
|
date: faker.date
|
||||||
|
.between({ from: lowerBound.toJSDate(), to: now.toJSDate() })
|
||||||
|
.toISOString()
|
||||||
|
.split('T')[0], // recent date in 'YYYY-MM-DD' format
|
||||||
account_id: accountId,
|
account_id: accountId,
|
||||||
links: {
|
links: {
|
||||||
account: `https://api.teller.io/accounts/${accountId}`,
|
account: `https://api.teller.io/accounts/${accountId}`,
|
||||||
|
@ -246,3 +262,35 @@ export function generateConnection(): GenerateConnectionsResponse {
|
||||||
transactions,
|
transactions,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const now = DateTime.fromISO('2022-01-03', { zone: 'utc' })
|
||||||
|
|
||||||
|
export const lowerBound = DateTime.fromISO('2021-12-01', { zone: 'utc' })
|
||||||
|
|
||||||
|
export const testDates = {
|
||||||
|
now,
|
||||||
|
lowerBound,
|
||||||
|
totalDays: now.diff(lowerBound, 'days').days,
|
||||||
|
prismaWhereFilter: {
|
||||||
|
date: {
|
||||||
|
gte: lowerBound.toJSDate(),
|
||||||
|
lte: now.toJSDate(),
|
||||||
|
},
|
||||||
|
} as Prisma.AccountBalanceWhereInput,
|
||||||
|
}
|
||||||
|
|
||||||
|
export function calculateDailyBalances(startingBalance, transactions, dateInterval) {
|
||||||
|
transactions.sort((a, b) => new Date(a.date).getTime() - new Date(b.date).getTime())
|
||||||
|
|
||||||
|
const balanceChanges = {}
|
||||||
|
|
||||||
|
transactions.forEach((transaction) => {
|
||||||
|
const date = new Date(transaction.date).toISOString().split('T')[0]
|
||||||
|
balanceChanges[date] = (balanceChanges[date] || 0) + Number(transaction.amount)
|
||||||
|
})
|
||||||
|
return dateInterval.map((date) => {
|
||||||
|
return Object.keys(balanceChanges)
|
||||||
|
.filter((d) => d <= date)
|
||||||
|
.reduce((acc, d) => acc + balanceChanges[d], startingBalance)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue