109 lines
3.2 KiB
TypeScript
109 lines
3.2 KiB
TypeScript
import { TellerService } from "../src/teller/teller.service";
|
|
|
|
const createService = () => {
|
|
const prisma = {
|
|
account: {
|
|
findMany: jest.fn(),
|
|
update: jest.fn(),
|
|
upsert: jest.fn(),
|
|
},
|
|
transactionRaw: {
|
|
upsert: jest.fn(),
|
|
},
|
|
};
|
|
const service = Object.create(TellerService.prototype) as TellerService;
|
|
Object.assign(service as any, {
|
|
prisma,
|
|
encryption: {
|
|
encrypt: jest.fn((value: string) => `enc_${value}`),
|
|
decrypt: jest.fn((value: string) => value.replace("enc_", "")),
|
|
},
|
|
logger: {
|
|
warn: jest.fn(),
|
|
},
|
|
planLimits: {
|
|
assertCanAddAccounts: jest.fn(),
|
|
},
|
|
});
|
|
return { service, prisma };
|
|
};
|
|
|
|
describe("TellerService", () => {
|
|
it("imports Teller enrollment accounts with encrypted access tokens", async () => {
|
|
const { service, prisma } = createService();
|
|
prisma.account.findMany.mockResolvedValue([]);
|
|
jest.spyOn(service as any, "api")
|
|
.mockResolvedValueOnce([
|
|
{
|
|
id: "acc_teller_1",
|
|
enrollment_id: "enr_1",
|
|
institution: { name: "Teller Bank" },
|
|
type: "depository",
|
|
subtype: "checking",
|
|
currency: "USD",
|
|
last_four: "1234",
|
|
status: "open",
|
|
links: { balances: "https://api.teller.io/accounts/acc_teller_1/balances" },
|
|
},
|
|
])
|
|
.mockResolvedValueOnce({
|
|
ledger: "100.25",
|
|
available: "95.25",
|
|
});
|
|
|
|
const result = await service.exchangeEnrollment("user_1", {
|
|
accessToken: "token_1",
|
|
enrollment: { id: "enr_1", institution: { name: "Teller Bank" } },
|
|
});
|
|
|
|
expect(result).toEqual({ enrollmentId: "enr_1", accountCount: 1 });
|
|
expect((service as any).planLimits.assertCanAddAccounts).toHaveBeenCalledWith("user_1", 1);
|
|
expect(prisma.account.upsert).toHaveBeenCalledWith(expect.objectContaining({
|
|
where: { tellerAccountId: "acc_teller_1" },
|
|
create: expect.objectContaining({
|
|
userId: "user_1",
|
|
tellerAccessToken: "enc_token_1",
|
|
tellerEnrollmentId: "enr_1",
|
|
tellerAccountId: "acc_teller_1",
|
|
institutionName: "Teller Bank",
|
|
}),
|
|
}));
|
|
});
|
|
|
|
it("syncs Teller transactions into the raw transaction table", async () => {
|
|
const { service, prisma } = createService();
|
|
prisma.account.findMany.mockResolvedValue([
|
|
{
|
|
id: "acct_1",
|
|
tellerAccessToken: "enc_token_1",
|
|
tellerAccountId: "acc_teller_1",
|
|
},
|
|
]);
|
|
prisma.account.update.mockResolvedValue({});
|
|
jest.spyOn(service as any, "api").mockResolvedValue([
|
|
{
|
|
id: "txn_1",
|
|
account_id: "acc_teller_1",
|
|
amount: "-12.34",
|
|
date: "2026-07-15",
|
|
description: "Coffee",
|
|
},
|
|
]);
|
|
|
|
const result = await service.syncTransactionsForUser("user_1");
|
|
|
|
expect(result).toEqual({ created: 1 });
|
|
expect(prisma.transactionRaw.upsert).toHaveBeenCalledWith(expect.objectContaining({
|
|
where: { bankTransactionId: "txn_1" },
|
|
create: expect.objectContaining({
|
|
accountId: "acct_1",
|
|
source: "teller",
|
|
}),
|
|
}));
|
|
expect(prisma.account.update).toHaveBeenCalledWith(expect.objectContaining({
|
|
where: { id: "acct_1" },
|
|
data: expect.objectContaining({ syncStatus: "idle" }),
|
|
}));
|
|
});
|
|
});
|