@accounter/server
Version:
Accounter GraphQL server
131 lines • 6.26 kB
JavaScript
import { describe, expect, it } from 'vitest';
import { isBusinessInScope, membershipFromTenant, narrowReadScope, readScopeFromMemberships, resolveReadScopePrecedence, resolveWriteTargetBusinessId, tenantFromMembership, } from '../auth-scope.js';
describe('membershipFromTenant', () => {
it('maps a tenant with role and name to a membership', () => {
expect(membershipFromTenant({ businessId: 'b-1', roleId: 'admin', businessName: 'Acme' })).toEqual({ businessId: 'b-1', roleId: 'admin', businessName: 'Acme' });
});
it('defaults a missing role to an empty string and omits the name', () => {
expect(membershipFromTenant({ businessId: 'b-1' })).toEqual({
businessId: 'b-1',
roleId: '',
});
});
});
describe('tenantFromMembership', () => {
it('round-trips back to a tenant context', () => {
const membership = { businessId: 'b-1', roleId: 'owner' };
expect(tenantFromMembership(membership)).toEqual({ businessId: 'b-1', roleId: 'owner' });
});
it('preserves the business name when present', () => {
expect(tenantFromMembership({ businessId: 'b-1', roleId: 'owner', businessName: 'Acme' })).toEqual({ businessId: 'b-1', roleId: 'owner', businessName: 'Acme' });
});
});
describe('readScopeFromMemberships', () => {
it('returns all membership business ids in order', () => {
expect(readScopeFromMemberships([
{ businessId: 'b-1', roleId: 'owner' },
{ businessId: 'b-2', roleId: 'accountant' },
])).toEqual({ businessIds: ['b-1', 'b-2'] });
});
it('de-duplicates repeated business ids', () => {
expect(readScopeFromMemberships([
{ businessId: 'b-1', roleId: 'owner' },
{ businessId: 'b-1', roleId: 'accountant' },
{ businessId: 'b-2', roleId: 'owner' },
])).toEqual({ businessIds: ['b-1', 'b-2'] });
});
it('returns an empty scope for a user with no memberships', () => {
expect(readScopeFromMemberships([])).toEqual({ businessIds: [] });
});
});
describe('isBusinessInScope', () => {
const scope = { businessIds: ['b-1', 'b-2'] };
it('is true for a business inside the scope', () => {
expect(isBusinessInScope(scope, 'b-2')).toBe(true);
});
it('is false for a business outside the scope', () => {
expect(isBusinessInScope(scope, 'b-3')).toBe(false);
});
});
describe('narrowReadScope', () => {
const memberships = [
{ businessId: 'b-1', roleId: 'owner' },
{ businessId: 'b-2', roleId: 'accountant' },
{ businessId: 'b-3', roleId: 'employee' },
];
it('narrows to a requested subset, preserving request order', () => {
expect(narrowReadScope(memberships, ['b-3', 'b-1'])).toEqual({
businessIds: ['b-3', 'b-1'],
});
});
it('de-duplicates repeated requested ids', () => {
expect(narrowReadScope(memberships, ['b-1', 'b-1', 'b-2'])).toEqual({
businessIds: ['b-1', 'b-2'],
});
});
it('returns null when any requested id is outside the memberships', () => {
expect(narrowReadScope(memberships, ['b-1', 'b-99'])).toBeNull();
});
it('accepts the full membership set', () => {
expect(narrowReadScope(memberships, ['b-1', 'b-2', 'b-3'])).toEqual({
businessIds: ['b-1', 'b-2', 'b-3'],
});
});
});
describe('resolveReadScopePrecedence', () => {
const memberships = [
{ businessId: 'b-1', roleId: 'owner' },
{ businessId: 'b-2', roleId: 'accountant' },
{ businessId: 'b-3', roleId: 'employee' },
];
it('defaults to all memberships when neither header nor args narrow', () => {
expect(resolveReadScopePrecedence({ memberships })).toEqual({
businessIds: ['b-1', 'b-2', 'b-3'],
});
});
it('uses the header scope when no args are provided', () => {
expect(resolveReadScopePrecedence({ memberships, headerBusinessIds: ['b-2', 'b-3'] })).toEqual({ businessIds: ['b-2', 'b-3'] });
});
it('lets args narrow within the header scope (args over header)', () => {
expect(resolveReadScopePrecedence({
memberships,
headerBusinessIds: ['b-2', 'b-3'],
argsBusinessIds: ['b-3'],
})).toEqual({ businessIds: ['b-3'] });
});
it('lets args narrow the memberships when no header is provided', () => {
expect(resolveReadScopePrecedence({ memberships, argsBusinessIds: ['b-1'] })).toEqual({ businessIds: ['b-1'] });
});
it('rejects a header scope outside the memberships', () => {
expect(resolveReadScopePrecedence({ memberships, headerBusinessIds: ['b-9'] })).toBeNull();
});
it('rejects args that fall outside the header scope (args must be a subset, not broaden)', () => {
expect(resolveReadScopePrecedence({
memberships,
headerBusinessIds: ['b-2'],
argsBusinessIds: ['b-1'],
})).toBeNull();
});
it('rejects args outside the memberships when no header is provided', () => {
expect(resolveReadScopePrecedence({ memberships, argsBusinessIds: ['b-9'] })).toBeNull();
});
});
describe('resolveWriteTargetBusinessId', () => {
it('falls back to the primary business when no scope is present', () => {
expect(resolveWriteTargetBusinessId('b-1', undefined)).toBe('b-1');
expect(resolveWriteTargetBusinessId('b-1', { businessIds: [] })).toBe('b-1');
});
it('uses the single scoped business directly', () => {
expect(resolveWriteTargetBusinessId('b-1', { businessIds: ['b-2'] })).toBe('b-2');
});
it('prefers the primary when it is part of a multi-business scope', () => {
expect(resolveWriteTargetBusinessId('b-2', { businessIds: ['b-1', 'b-2'] })).toBe('b-2');
});
it('uses the first scoped business when the primary is outside a multi-business scope', () => {
expect(resolveWriteTargetBusinessId('b-9', { businessIds: ['b-1', 'b-2'] })).toBe('b-1');
});
it('returns null when there is neither a primary nor a scope', () => {
expect(resolveWriteTargetBusinessId(null, undefined)).toBeNull();
});
});
//# sourceMappingURL=auth-scope.test.js.map