Browse Source

test(server): Start test suite for OrderCalculator

Just did a few basic tests because the way Promotions work has not been fully figured out yet.
Michael Bromley 7 years ago
parent
commit
56f366117f

+ 106 - 0
server/src/service/helpers/order-calculator/order-calculator.spec.ts

@@ -0,0 +1,106 @@
+import { Test } from '@nestjs/testing';
+import { Omit } from 'shared/omit';
+import { Connection } from 'typeorm';
+
+import { OrderItem } from '../../../entity/order-item/order-item.entity';
+import { OrderLine } from '../../../entity/order-line/order-line.entity';
+import { Order } from '../../../entity/order/order.entity';
+import { TaxCategory } from '../../../entity/tax-category/tax-category.entity';
+import { TaxRateService } from '../../services/tax-rate.service';
+import { ListQueryBuilder } from '../list-query-builder/list-query-builder';
+import { TaxCalculator } from '../tax-calculator/tax-calculator';
+import {
+    createRequestContext,
+    MockConnection,
+    taxCategoryStandard,
+    zoneDefault,
+} from '../tax-calculator/tax-calculator-test-fixtures';
+
+import { OrderCalculator } from './order-calculator';
+
+describe('OrderCalculator', () => {
+    let orderCalculator: OrderCalculator;
+
+    beforeEach(async () => {
+        const module = await Test.createTestingModule({
+            providers: [
+                OrderCalculator,
+                TaxCalculator,
+                TaxRateService,
+                { provide: Connection, useClass: MockConnection },
+                { provide: ListQueryBuilder, useValue: {} },
+            ],
+        }).compile();
+
+        orderCalculator = module.get(OrderCalculator);
+        const taxRateService = module.get(TaxRateService);
+        await taxRateService.initTaxRates();
+    });
+
+    function createOrder(
+        orderConfig: Partial<Omit<Order, 'lines'>> & {
+            lines: Array<{ unitPrice: number; taxCategory: TaxCategory; quantity: number }>;
+        },
+    ): Order {
+        const lines = orderConfig.lines.map(
+            ({ unitPrice, taxCategory, quantity }) =>
+                new OrderLine({
+                    unitPrice,
+                    taxCategory,
+                    items: Array.from({ length: quantity }).map(() => new OrderItem()),
+                }),
+        );
+
+        return new Order({
+            lines,
+        });
+    }
+
+    describe('taxes only', () => {
+        it('single line with taxes not included', () => {
+            const ctx = createRequestContext(false, zoneDefault);
+            const order = createOrder({
+                lines: [{ unitPrice: 123, taxCategory: taxCategoryStandard, quantity: 1 }],
+            });
+            orderCalculator.applyTaxesAndPromotions(ctx, order, []);
+
+            expect(order.totalPrice).toBe(148);
+            expect(order.totalPriceBeforeTax).toBe(123);
+        });
+
+        it('single line with taxes not included, multiple items', () => {
+            const ctx = createRequestContext(false, zoneDefault);
+            const order = createOrder({
+                lines: [{ unitPrice: 123, taxCategory: taxCategoryStandard, quantity: 3 }],
+            });
+            orderCalculator.applyTaxesAndPromotions(ctx, order, []);
+
+            expect(order.totalPrice).toBe(444);
+            expect(order.totalPriceBeforeTax).toBe(369);
+        });
+
+        it('single line with taxes included', () => {
+            const ctx = createRequestContext(true, zoneDefault);
+            const order = createOrder({
+                lines: [{ unitPrice: 123, taxCategory: taxCategoryStandard, quantity: 1 }],
+            });
+            orderCalculator.applyTaxesAndPromotions(ctx, order, []);
+
+            expect(order.totalPrice).toBe(123);
+            expect(order.totalPriceBeforeTax).toBe(102);
+        });
+
+        it('resets totals when lines array is empty', () => {
+            const ctx = createRequestContext(true, zoneDefault);
+            const order = createOrder({
+                lines: [],
+                totalPrice: 148,
+                totalPriceBeforeTax: 123,
+            });
+            orderCalculator.applyTaxesAndPromotions(ctx, order, []);
+
+            expect(order.totalPrice).toBe(0);
+            expect(order.totalPriceBeforeTax).toBe(0);
+        });
+    });
+});

+ 90 - 0
server/src/service/helpers/tax-calculator/tax-calculator-test-fixtures.ts

@@ -0,0 +1,90 @@
+import { LanguageCode } from 'shared/generated-types';
+
+import { RequestContext } from '../../../api/common/request-context';
+import { Channel } from '../../../entity/channel/channel.entity';
+import { TaxCategory } from '../../../entity/tax-category/tax-category.entity';
+import { TaxRate } from '../../../entity/tax-rate/tax-rate.entity';
+import { Zone } from '../../../entity/zone/zone.entity';
+
+export const taxCategoryStandard = new TaxCategory({
+    id: 'taxCategoryStandard',
+    name: 'Standard Tax',
+});
+export const taxCategoryReduced = new TaxCategory({
+    id: 'taxCategoryReduced',
+    name: 'Reduced Tax',
+});
+export const zoneDefault = new Zone({
+    id: 'zoneDefault',
+    name: 'Default Zone',
+});
+export const zoneOther = new Zone({
+    id: 'zoneOther',
+    name: 'Other Zone',
+});
+export const zoneWithNoTaxRate = new Zone({
+    id: 'zoneWithNoTaxRate',
+    name: 'Zone for which no TaxRate is configured',
+});
+export const taxRateDefaultStandard = new TaxRate({
+    id: 'taxRateDefaultStandard',
+    value: 20,
+    enabled: true,
+    zone: zoneDefault,
+    category: taxCategoryStandard,
+});
+export const taxRateDefaultReduced = new TaxRate({
+    id: 'taxRateDefaultReduced',
+    value: 10,
+    enabled: true,
+    zone: zoneDefault,
+    category: taxCategoryReduced,
+});
+export const taxRateOtherStandard = new TaxRate({
+    id: 'taxRateOtherStandard',
+    value: 15,
+    enabled: true,
+    zone: zoneOther,
+    category: taxCategoryStandard,
+});
+export const taxRateOtherReduced = new TaxRate({
+    id: 'taxRateOtherReduced',
+    value: 5,
+    enabled: true,
+    zone: zoneOther,
+    category: taxCategoryReduced,
+});
+
+export class MockConnection {
+    getRepository() {
+        return {
+            find() {
+                return Promise.resolve([
+                    taxRateDefaultStandard,
+                    taxRateDefaultReduced,
+                    taxRateOtherStandard,
+                    taxRateOtherReduced,
+                ]);
+            },
+        };
+    }
+}
+
+export function createRequestContext(pricesIncludeTax: boolean, activeTaxZone: Zone): RequestContext {
+    const channel = new Channel({
+        defaultTaxZone: zoneDefault,
+        pricesIncludeTax,
+    });
+    const ctx = new RequestContext({
+        channel,
+        authorizedAsOwnerOnly: false,
+        languageCode: LanguageCode.en,
+        isAuthorized: true,
+        session: {} as any,
+    });
+    // TODO: Hack until we implement the other ways of
+    // calculating the activeTaxZone (customer billing address etc)
+    delete Object.getPrototypeOf(ctx).activeTaxZone;
+    (ctx as any).activeTaxZone = activeTaxZone;
+    return ctx;
+}

+ 13 - 88
server/src/service/helpers/tax-calculator/tax-calculator.spec.ts

@@ -1,102 +1,27 @@
 import { Test } from '@nestjs/testing';
-import { LanguageCode } from 'shared/generated-types';
 import { Connection } from 'typeorm';
 
-import { RequestContext } from '../../../api/common/request-context';
-import { Channel } from '../../../entity/channel/channel.entity';
-import { TaxCategory } from '../../../entity/tax-category/tax-category.entity';
-import { TaxRate } from '../../../entity/tax-rate/tax-rate.entity';
-import { Zone } from '../../../entity/zone/zone.entity';
 import { TaxRateService } from '../../services/tax-rate.service';
 import { ListQueryBuilder } from '../list-query-builder/list-query-builder';
 
 import { TaxCalculator } from './tax-calculator';
+import {
+    createRequestContext,
+    MockConnection,
+    taxCategoryReduced,
+    taxCategoryStandard,
+    taxRateDefaultReduced,
+    taxRateDefaultStandard,
+    taxRateOtherReduced,
+    taxRateOtherStandard,
+    zoneDefault,
+    zoneOther,
+    zoneWithNoTaxRate,
+} from './tax-calculator-test-fixtures';
 
 describe('TaxCalculator', () => {
     let taxCalculator: TaxCalculator;
     const inputPrice = 6543;
-    const taxCategoryStandard = new TaxCategory({
-        id: 'taxCategoryStandard',
-        name: 'Standard Tax',
-    });
-    const taxCategoryReduced = new TaxCategory({
-        id: 'taxCategoryReduced',
-        name: 'Reduced Tax',
-    });
-    const zoneDefault = new Zone({
-        id: 'zoneDefault',
-        name: 'Default Zone',
-    });
-    const zoneOther = new Zone({
-        id: 'zoneOther',
-        name: 'Other Zone',
-    });
-    const zoneWithNoTaxRate = new Zone({
-        id: 'zoneWithNoTaxRate',
-        name: 'Zone for which no TaxRate is configured',
-    });
-    const taxRateDefaultStandard = new TaxRate({
-        id: 'taxRateDefaultStandard',
-        value: 20,
-        enabled: true,
-        zone: zoneDefault,
-        category: taxCategoryStandard,
-    });
-    const taxRateDefaultReduced = new TaxRate({
-        id: 'taxRateDefaultReduced',
-        value: 10,
-        enabled: true,
-        zone: zoneDefault,
-        category: taxCategoryReduced,
-    });
-    const taxRateOtherStandard = new TaxRate({
-        id: 'taxRateOtherStandard',
-        value: 15,
-        enabled: true,
-        zone: zoneOther,
-        category: taxCategoryStandard,
-    });
-    const taxRateOtherReduced = new TaxRate({
-        id: 'taxRateOtherReduced',
-        value: 5,
-        enabled: true,
-        zone: zoneOther,
-        category: taxCategoryReduced,
-    });
-
-    class MockConnection {
-        getRepository() {
-            return {
-                find() {
-                    return Promise.resolve([
-                        taxRateDefaultStandard,
-                        taxRateDefaultReduced,
-                        taxRateOtherStandard,
-                        taxRateOtherReduced,
-                    ]);
-                },
-            };
-        }
-    }
-
-    function createRequestContext(pricesIncludeTax: boolean, activeTaxZone: Zone): RequestContext {
-        const channel = new Channel({
-            defaultTaxZone: zoneDefault,
-            pricesIncludeTax,
-        });
-        const ctx = new RequestContext({
-            channel,
-            authorizedAsOwnerOnly: false,
-            languageCode: LanguageCode.en,
-            isAuthorized: true,
-            session: {} as any,
-        });
-        // TODO: Hack until we implement the other ways of
-        // calculating the activeTaxZone (customer billing address etc)
-        delete Object.getPrototypeOf(ctx).activeTaxZone;
-        (ctx as any).activeTaxZone = activeTaxZone;
-        return ctx;
-    }
 
     beforeEach(async () => {
         const module = await Test.createTestingModule({