UNPKG

@accounter/server

Version:
178 lines • 7.3 kB
import { describe, expect, it, vi } from 'vitest'; import { GraphQLError } from 'graphql'; import { AdminContextProvider } from '../providers/admin-context.provider.js'; function createProvider(options) { const query = vi.fn().mockImplementation((_statement, values) => { if (options.rowsByOwnerId) { const serializedValues = JSON.stringify(values ?? []); for (const [ownerId, row] of Object.entries(options.rowsByOwnerId)) { if (serializedValues.includes(ownerId)) { return Promise.resolve({ rows: [row], rowCount: 1 }); } } return Promise.resolve({ rows: [], rowCount: 0 }); } return Promise.resolve({ rows: options.row ? [options.row] : [], rowCount: options.row ? 1 : 0, }); }); const db = { query, }; const auth = { getAuthContext: vi.fn().mockResolvedValue(options.businessId ? { authType: 'jwt', tenant: { businessId: options.businessId }, ...(options.activeReadScopeBusinessIds ? { activeReadScope: { businessIds: options.activeReadScopeBusinessIds, }, } : {}), } : null), }; return { provider: new AdminContextProvider(auth, db), query, auth, }; } describe('AdminContext DI Integration', () => { it('loads admin context through provider injection', async () => { const { provider, query, auth } = createProvider({ businessId: 'owner-a', row: { owner_id: 'owner-a', default_local_currency: 'USD', default_fiat_currency_for_crypto_conversions: 'USD', date_established: null, initial_accounter_year: null, }, }); const context = await provider.getVerifiedAdminContext(); expect(context.ownerId).toBe('owner-a'); expect(context.defaultLocalCurrency).toBe('USD'); expect(auth.getAuthContext).toHaveBeenCalledTimes(1); expect(query).toHaveBeenCalledTimes(1); }); it('uses single-business active scope as owner selection', async () => { const primaryOwnerId = 'owner-primary'; const scopedOwnerId = 'owner-scoped'; const { provider } = createProvider({ businessId: primaryOwnerId, activeReadScopeBusinessIds: [scopedOwnerId], rowsByOwnerId: { [scopedOwnerId]: { owner_id: scopedOwnerId, default_local_currency: 'USD', default_fiat_currency_for_crypto_conversions: 'USD', date_established: null, initial_accounter_year: null, }, }, }); const context = await provider.getVerifiedAdminContext(); expect(context.ownerId).toBe(scopedOwnerId); }); it('prefers primary tenant business when it is inside multi-business scope', async () => { const primaryOwnerId = 'owner-primary'; const { provider } = createProvider({ businessId: primaryOwnerId, activeReadScopeBusinessIds: ['owner-scoped-a', primaryOwnerId, 'owner-scoped-b'], rowsByOwnerId: { [primaryOwnerId]: { owner_id: primaryOwnerId, default_local_currency: 'USD', default_fiat_currency_for_crypto_conversions: 'USD', date_established: null, initial_accounter_year: null, }, }, }); const context = await provider.getVerifiedAdminContext(); expect(context.ownerId).toBe(primaryOwnerId); }); it('falls back to first scoped business when primary tenant business is outside scope', async () => { const primaryOwnerId = 'owner-primary'; const firstScopedOwnerId = 'owner-scoped-a'; const { provider } = createProvider({ businessId: primaryOwnerId, activeReadScopeBusinessIds: [firstScopedOwnerId, 'owner-scoped-b'], rowsByOwnerId: { [firstScopedOwnerId]: { owner_id: firstScopedOwnerId, default_local_currency: 'USD', default_fiat_currency_for_crypto_conversions: 'USD', date_established: null, initial_accounter_year: null, }, }, }); const context = await provider.getVerifiedAdminContext(); expect(context.ownerId).toBe(firstScopedOwnerId); }); it('isolates admin context between concurrent requests', async () => { const requestA = createProvider({ businessId: 'owner-a', row: { owner_id: 'owner-a', default_local_currency: 'USD', default_fiat_currency_for_crypto_conversions: 'USD', date_established: null, initial_accounter_year: null, }, }); const requestB = createProvider({ businessId: 'owner-b', row: { owner_id: 'owner-b', default_local_currency: 'EUR', default_fiat_currency_for_crypto_conversions: 'EUR', date_established: null, initial_accounter_year: null, }, }); const [contextA, contextB] = await Promise.all([ requestA.provider.getVerifiedAdminContext(), requestB.provider.getVerifiedAdminContext(), ]); expect(contextA.ownerId).toBe('owner-a'); expect(contextB.ownerId).toBe('owner-b'); expect(requestA.query).toHaveBeenCalledTimes(1); expect(requestB.query).toHaveBeenCalledTimes(1); }); it('caches admin context within a single request', async () => { const { provider, query } = createProvider({ businessId: 'owner-a', row: { owner_id: 'owner-a', default_local_currency: 'USD', default_fiat_currency_for_crypto_conversions: 'USD', date_established: null, initial_accounter_year: null, }, }); const [first, second] = await Promise.all([ provider.getVerifiedAdminContext(), provider.getVerifiedAdminContext(), ]); expect(first.ownerId).toBe('owner-a'); expect(second.ownerId).toBe('owner-a'); expect(query).toHaveBeenCalledTimes(1); }); it('fails closed when auth context is unavailable (RLS guard path)', async () => { const { provider, query } = createProvider({ businessId: null, }); await expect(provider.getAdminContext()).rejects.toBeInstanceOf(GraphQLError); await expect(provider.getAdminContext()).rejects.toMatchObject({ extensions: { code: 'UNAUTHENTICATED' }, }); expect(query).not.toHaveBeenCalled(); }); }); //# sourceMappingURL=admin-context-integration.test.js.map