| import { mock } from "bun:test"; |
|
|
| |
| |
|
|
| |
| const createMockDb = () => ({ |
| query: { |
| users: { |
| findFirst: mock(() => |
| Promise.resolve({ |
| id: "test-user-id", |
| teamId: "test-team-id", |
| email: "test@example.com", |
| usersOnTeams: [{ id: "membership-1", teamId: "test-team-id" }], |
| }), |
| ), |
| }, |
| }, |
| |
| usePrimaryOnly: () => createMockDb(), |
| $primary: undefined, |
| }); |
|
|
| export const mockDb = createMockDb(); |
|
|
| |
| type MockFn = ReturnType<typeof mock<(...args: any[]) => any>>; |
|
|
| |
| export const mocks = { |
| |
| getTransactions: mock(() => ({ |
| data: [], |
| meta: { hasNextPage: false, hasPreviousPage: false }, |
| })) as MockFn, |
| getTransactionById: mock(() => null) as MockFn, |
| createTransaction: mock(() => ({})) as MockFn, |
| createTransactions: mock(() => []) as MockFn, |
| updateTransaction: mock(() => ({})) as MockFn, |
| updateTransactions: mock(() => ({ data: [], meta: {} })) as MockFn, |
| deleteTransactions: mock(() => []) as MockFn, |
| getTransactionAttachment: mock(() => null) as MockFn, |
| getSimilarTransactions: mock(() => []) as MockFn, |
| searchTransactionMatch: mock(() => []) as MockFn, |
| getTransactionsReadyForExportCount: mock(() => 0) as MockFn, |
| moveTransactionToReview: mock(() => ({})) as MockFn, |
|
|
| |
| getInvoices: mock(() => ({ |
| data: [], |
| meta: { hasNextPage: false, hasPreviousPage: false }, |
| })) as MockFn, |
| getInvoiceById: mock(() => null) as MockFn, |
| createInvoice: mock(() => ({})) as MockFn, |
| updateInvoice: mock(() => ({})) as MockFn, |
| deleteInvoice: mock(() => ({})) as MockFn, |
| draftInvoice: mock(() => ({})) as MockFn, |
| duplicateInvoice: mock(() => ({})) as MockFn, |
| getInvoiceNumber: mock(() => "INV-001") as MockFn, |
| getNextInvoiceNumber: mock(() => "INV-002") as MockFn, |
| getInvoiceSummary: mock(() => ({ |
| paid: { count: 0, amount: 0 }, |
| unpaid: { count: 0, amount: 0 }, |
| overdue: { count: 0, amount: 0 }, |
| draft: { count: 0, amount: 0 }, |
| })) as MockFn, |
| getInvoiceTemplate: mock(() => null) as MockFn, |
| getPaymentStatus: mock(() => ({ status: "unpaid" })) as MockFn, |
| isInvoiceNumberUsed: mock(() => false) as MockFn, |
| searchInvoiceNumber: mock(() => []) as MockFn, |
| getAverageDaysToPayment: mock(() => 15) as MockFn, |
| getAverageInvoiceSize: mock(() => 1000) as MockFn, |
| getInactiveClientsCount: mock(() => 0) as MockFn, |
| getNewCustomersCount: mock(() => 5) as MockFn, |
| getMostActiveClient: mock(() => null) as MockFn, |
| getTopRevenueClient: mock(() => null) as MockFn, |
|
|
| |
| getCustomers: mock(() => ({ |
| data: [], |
| meta: { hasNextPage: false, hasPreviousPage: false }, |
| })) as MockFn, |
| getCustomerById: mock(() => null) as MockFn, |
| createCustomer: mock(() => ({})) as MockFn, |
| updateCustomer: mock(() => ({})) as MockFn, |
| deleteCustomer: mock(() => ({})) as MockFn, |
|
|
| |
| getBankAccounts: mock(() => []) as MockFn, |
| getBankAccountById: mock(() => null) as MockFn, |
| createBankAccount: mock(() => ({})) as MockFn, |
| updateBankAccount: mock(() => ({})) as MockFn, |
| deleteBankAccount: mock(() => ({})) as MockFn, |
|
|
| |
| getInboxItems: mock(() => ({ |
| data: [], |
| meta: { hasNextPage: false, hasPreviousPage: false }, |
| })) as MockFn, |
| getInboxItemById: mock(() => null) as MockFn, |
| updateInboxItem: mock(() => ({})) as MockFn, |
| deleteInboxItem: mock(() => ({})) as MockFn, |
|
|
| |
| getApiKeyByToken: mock(() => null) as MockFn, |
| upsertApiKey: mock(() => ({})) as MockFn, |
| getApiKeysByTeam: mock(() => []) as MockFn, |
| deleteApiKey: mock(() => ({})) as MockFn, |
| updateApiKeyLastUsedAt: mock(() => ({})) as MockFn, |
|
|
| |
| getUserById: mock(() => null) as MockFn, |
| getUser: mock(() => null) as MockFn, |
| updateUser: mock(() => ({})) as MockFn, |
|
|
| |
| getTeamById: mock(() => null) as MockFn, |
| getTeam: mock(() => null) as MockFn, |
| updateTeam: mock(() => ({})) as MockFn, |
|
|
| |
| validateAccessToken: mock(() => null) as MockFn, |
| triggerJob: mock(() => ({ id: "job-123" })) as MockFn, |
| signedUrl: mock(() => ({ |
| data: { signedUrl: "https://example.com/signed" }, |
| error: null, |
| })) as MockFn, |
| formatAmountValue: mock( |
| ({ amount }: { amount: string }) => Number.parseFloat(amount) || 0, |
| ) as MockFn, |
| }; |
|
|
| |
| const createDefaultMock = () => mock(() => null); |
|
|
| |
| const dbQueriesMock = new Proxy( |
| { |
| |
| getTransactions: mocks.getTransactions, |
| getTransactionById: mocks.getTransactionById, |
| createTransaction: mocks.createTransaction, |
| createTransactions: mocks.createTransactions, |
| updateTransaction: mocks.updateTransaction, |
| updateTransactions: mocks.updateTransactions, |
| deleteTransactions: mocks.deleteTransactions, |
| getTransactionAttachment: mocks.getTransactionAttachment, |
| getSimilarTransactions: mocks.getSimilarTransactions, |
| searchTransactionMatch: mocks.searchTransactionMatch, |
| getTransactionsReadyForExportCount: |
| mocks.getTransactionsReadyForExportCount, |
| moveTransactionToReview: mocks.moveTransactionToReview, |
|
|
| |
| getInvoices: mocks.getInvoices, |
| getInvoiceById: mocks.getInvoiceById, |
| createInvoice: mocks.createInvoice, |
| updateInvoice: mocks.updateInvoice, |
| deleteInvoice: mocks.deleteInvoice, |
| draftInvoice: mocks.draftInvoice, |
| duplicateInvoice: mocks.duplicateInvoice, |
| getInvoiceNumber: mocks.getInvoiceNumber, |
| getNextInvoiceNumber: mocks.getNextInvoiceNumber, |
| getInvoiceSummary: mocks.getInvoiceSummary, |
| getInvoiceTemplate: mocks.getInvoiceTemplate, |
| getPaymentStatus: mocks.getPaymentStatus, |
| isInvoiceNumberUsed: mocks.isInvoiceNumberUsed, |
| searchInvoiceNumber: mocks.searchInvoiceNumber, |
| getAverageDaysToPayment: mocks.getAverageDaysToPayment, |
| getAverageInvoiceSize: mocks.getAverageInvoiceSize, |
| getInactiveClientsCount: mocks.getInactiveClientsCount, |
| getNewCustomersCount: mocks.getNewCustomersCount, |
| getMostActiveClient: mocks.getMostActiveClient, |
| getTopRevenueClient: mocks.getTopRevenueClient, |
|
|
| |
| getCustomers: mocks.getCustomers, |
| getCustomerById: mocks.getCustomerById, |
| createCustomer: mocks.createCustomer, |
| updateCustomer: mocks.updateCustomer, |
| deleteCustomer: mocks.deleteCustomer, |
| upsertCustomer: mock(() => ({})), |
| getCustomerInvoiceSummary: mock(() => ({ total: 0, paid: 0, overdue: 0 })), |
| clearCustomerEnrichment: mock(() => ({})), |
| updateCustomerEnrichmentStatus: mock(() => ({})), |
| toggleCustomerPortal: mock(() => ({})), |
| getCustomerByPortalId: mock(() => null), |
| getCustomerPortalInvoices: mock(() => ({ data: [], meta: {} })), |
|
|
| |
| getBankAccounts: mocks.getBankAccounts, |
| getBankAccountById: mocks.getBankAccountById, |
| createBankAccount: mocks.createBankAccount, |
| updateBankAccount: mocks.updateBankAccount, |
| deleteBankAccount: mocks.deleteBankAccount, |
| getBankAccountDetails: mock(() => null), |
| getBankAccountsBalances: mock(() => []), |
| getBankAccountsCurrencies: mock(() => []), |
| getBankAccountsWithPaymentInfo: mock(() => []), |
|
|
| |
| getInboxItems: mocks.getInboxItems, |
| getInboxItemById: mocks.getInboxItemById, |
| updateInboxItem: mocks.updateInboxItem, |
| deleteInboxItem: mocks.deleteInboxItem, |
| getInbox: mock(() => ({ |
| data: [], |
| meta: { hasNextPage: false, hasPreviousPage: false }, |
| })), |
| getInboxById: mock(() => null), |
| createInbox: mock(() => ({})), |
| updateInbox: mock(() => ({})), |
| deleteInbox: mock(() => ({})), |
| deleteInboxMany: mock(() => []), |
| getInboxByStatus: mock(() => ({ pending: 0, completed: 0 })), |
| getInboxSearch: mock(() => []), |
| getInboxBlocklist: mock(() => []), |
| createInboxBlocklist: mock(() => ({})), |
| deleteInboxBlocklist: mock(() => ({})), |
| checkInboxAttachments: mock(() => []), |
| matchTransaction: mock(() => ({})), |
| unmatchTransaction: mock(() => ({})), |
| confirmSuggestedMatch: mock(() => ({})), |
| declineSuggestedMatch: mock(() => ({})), |
| deleteInboxEmbedding: mock(() => ({})), |
|
|
| |
| getApiKeyByToken: mocks.getApiKeyByToken, |
| upsertApiKey: mocks.upsertApiKey, |
| getApiKeysByTeam: mocks.getApiKeysByTeam, |
| deleteApiKey: mocks.deleteApiKey, |
| updateApiKeyLastUsedAt: mocks.updateApiKeyLastUsedAt, |
|
|
| |
| getUserById: mocks.getUserById, |
| getUser: mocks.getUser, |
| updateUser: mocks.updateUser, |
| getUserTeamId: mock(() => "test-team-id"), |
|
|
| |
| getTeamById: mocks.getTeamById, |
| getTeam: mocks.getTeam, |
| updateTeam: mocks.updateTeam, |
|
|
| |
| getTrackerProjectById: mock(() => null), |
| getTrackerRecordsByRange: mock(() => []), |
|
|
| |
| validateAccessToken: mocks.validateAccessToken, |
| } as Record<string, any>, |
| { |
| get(target, prop) { |
| if (prop in target) { |
| return target[prop as string]; |
| } |
| |
| target[prop as string] = createDefaultMock(); |
| return target[prop as string]; |
| }, |
| }, |
| ); |
|
|
| mock.module("@midday/db/queries", () => dbQueriesMock); |
|
|
| |
| mock.module("@midday/supabase/storage", () => ({ |
| signedUrl: mocks.signedUrl, |
| remove: mock(() => Promise.resolve({ error: null })), |
| })); |
|
|
| |
| mock.module("@midday/job-client", () => ({ |
| triggerJob: mocks.triggerJob, |
| getQueue: mock(() => ({ |
| getJob: mock(() => null), |
| getJobs: mock(() => []), |
| })), |
| decodeJobId: mock((id: string) => ({ id, queue: "default" })), |
| })); |
|
|
| |
| mock.module("@midday/import", () => ({ |
| formatAmountValue: mocks.formatAmountValue, |
| })); |
|
|
| |
| mock.module("@midday/invoice/calculate", () => ({ |
| calculateTotal: mock(({ lineItems }: { lineItems: any[] }) => ({ |
| subTotal: lineItems.reduce( |
| (sum: number, item: any) => sum + item.price * item.quantity, |
| 0, |
| ), |
| total: lineItems.reduce( |
| (sum: number, item: any) => sum + item.price * item.quantity, |
| 0, |
| ), |
| vat: 0, |
| tax: 0, |
| })), |
| })); |
|
|
| mock.module("@midday/invoice/utils", () => ({ |
| transformCustomerToContent: mock((customer: any) => ({ |
| type: "doc", |
| content: [ |
| { |
| type: "paragraph", |
| content: [{ type: "text", text: customer?.name || "" }], |
| }, |
| ], |
| })), |
| })); |
|
|
| mock.module("@midday/invoice/token", () => ({ |
| verify: mock(() => ({ id: "invoice-123", teamId: "test-team-id" })), |
| })); |
|
|
| mock.module("@midday/invoice", () => ({ |
| DEFAULT_TEMPLATE: {}, |
| })); |
|
|
| |
| mock.module("@api/services/supabase", () => ({ |
| createClient: mock(async () => ({})), |
| createAdminClient: mock(async () => ({})), |
| })); |
|
|
| |
| mock.module("@api/utils/auth", () => ({ |
| verifyAccessToken: mock(async () => ({ |
| user: { |
| id: "test-user-id", |
| email: "test@example.com", |
| user_metadata: {}, |
| app_metadata: {}, |
| aud: "authenticated", |
| created_at: new Date().toISOString(), |
| }, |
| access_token: "test-access-token", |
| token_type: "bearer", |
| expires_in: 3600, |
| expires_at: Math.floor(Date.now() / 1000) + 3600, |
| refresh_token: "test-refresh-token", |
| })), |
| })); |
|
|
| |
| mock.module("@midday/db/client", () => ({ |
| db: mockDb, |
| primaryDb: mockDb, |
| connectDb: mock(async () => mockDb), |
| })); |
|
|
| |
| mock.module("@midday/cache/team-cache", () => ({ |
| teamCache: { |
| get: mock(async () => true), |
| set: mock(async () => {}), |
| }, |
| })); |
|
|
| |
| process.env.SUPABASE_URL = |
| process.env.SUPABASE_URL || "https://test.supabase.co"; |
| process.env.SUPABASE_SERVICE_KEY = |
| process.env.SUPABASE_SERVICE_KEY || "test-service-key"; |
| process.env.DATABASE_PRIMARY_URL = |
| process.env.DATABASE_PRIMARY_URL || |
| "postgres://test:test@localhost:5432/test"; |
| process.env.DATABASE_FRA_URL = |
| process.env.DATABASE_FRA_URL || "postgres://test:test@localhost:5432/test"; |
| process.env.DATABASE_SJC_URL = |
| process.env.DATABASE_SJC_URL || "postgres://test:test@localhost:5432/test"; |
| process.env.DATABASE_IAD_URL = |
| process.env.DATABASE_IAD_URL || "postgres://test:test@localhost:5432/test"; |
| process.env.MIDDAY_DASHBOARD_URL = |
| process.env.MIDDAY_DASHBOARD_URL || "https://app.midday.ai"; |
|
|