Skip to content

Commit

Permalink
Development (#455)
Browse files Browse the repository at this point in the history
* get orders

* unit test for order service

* remove failing test

* update create order service

* user order status as a model not string

* on create order, create order notes

* add quantity available props to menu

* create the delete category api

* write init test for order service

* fix failing test

---------

Co-authored-by: Olasunkanmi Oyinlola <olasunkanmioyinlola@Olasunkanmis-MacBook-Air.local>
  • Loading branch information
olasunkanmiraymond and Olasunkanmi Oyinlola committed Nov 22, 2023
1 parent 6161bf7 commit 91dc8b3
Show file tree
Hide file tree
Showing 24 changed files with 241 additions and 78 deletions.
2 changes: 1 addition & 1 deletion backend/.vscode/launch.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
"request": "launch",
"name": "Test",
"runtimeExecutable": "npm",
"runtimeArgs": ["run", "test:unit", "src/order/order-service.spec.ts", "--inspect-brk"],
"runtimeArgs": ["run", "test:unit", "src/order/order.service.spec.ts", "--inspect-brk"],
// "src/infrastructure/data_access/db_repositories/worker_sql_repository.spec.ts",
"console": "integratedTerminal",
"restart": true,
Expand Down
25 changes: 25 additions & 0 deletions backend/src/cart/cart-item-mock-data.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import { Types } from 'mongoose';
import { auditMockData } from '../audit/audit-mock-data';
import { Audit } from '../domain';
import { CreateCartItemsDTO } from '../order/dto/create-order.dto';
import { ICartItem } from './cart-entity.interface';
import { CartItem } from './cart-item';
import { createSelectedCartItems, selectedItemsMock } from './selectedItems/selected-item-mock-data';
const id = new Types.ObjectId();
const cartItemMockData: ICartItem = {
menuId: id,
orderId: id,
total: 5,
selectedItems: [selectedItemsMock],
audit: Audit.create(auditMockData).getValue(),
};

export const cartItemMock = CartItem.create(cartItemMockData);

export const createItem: CreateCartItemsDTO = {
menuId: id,
orderId: id,
note: '',
total: 5,
selectedItems: [createSelectedCartItems],
};
9 changes: 4 additions & 5 deletions backend/src/cart/cart-item.mapper.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,11 @@
import { HttpStatus, Injectable } from '@nestjs/common';
import { AuditMapper } from 'src/audit';
import { IMapper } from 'src/domain';
import { CartItemDataModel } from 'src/infrastructure/data_access/repositories/schemas/cartItem.schema';
import { SelectedCartItemDataModel } from 'src/infrastructure/data_access/repositories/schemas/selected-cart-item.schema';
import { AuditMapper } from '../audit';
import { IMapper } from '../domain';
import { CartItemDataModel } from '../infrastructure/data_access/repositories/schemas/cartItem.schema';
import { throwApplicationError } from '../infrastructure/utilities/exception-instance';
import { CartItem } from './cart-item';
import { SelectedCartItemMapper } from './selectedItems/selected-cart-item.mapper';
import { SelectedCartItem } from './selectedItems/selectedCartItem';
import { throwApplicationError } from 'src/infrastructure/utilities/exception-instance';

@Injectable()
export class CartItemMapper implements IMapper<CartItem, CartItemDataModel> {
Expand Down
2 changes: 1 addition & 1 deletion backend/src/cart/cart-item.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { Types } from 'mongoose';
import { Audit, Entity, Result } from 'src/domain';
import { Audit, Entity, Result } from '../domain';
import { ICartItem } from './cart-entity.interface';
import { SelectedCartItem } from './selectedItems/selectedCartItem';

Expand Down
6 changes: 3 additions & 3 deletions backend/src/cart/selectedItems/selected-cart-item.mapper.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { AuditMapper } from 'src/audit';
import { IMapper } from 'src/domain';
import { SelectedCartItemDataModel } from 'src/infrastructure/data_access/repositories/schemas/selected-cart-item.schema';
import { AuditMapper } from '../../audit';
import { IMapper } from '../../domain';
import { SelectedCartItemDataModel } from '../../infrastructure/data_access/repositories/schemas/selected-cart-item.schema';
import { SelectedCartItem } from './selectedCartItem';

export class SelectedCartItemMapper implements IMapper<SelectedCartItem, SelectedCartItemDataModel> {
Expand Down
26 changes: 26 additions & 0 deletions backend/src/cart/selectedItems/selected-item-mock-data.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import { Types } from 'mongoose';
import { auditMockData } from '../../audit/audit-mock-data';
import { Audit } from '../../domain';
import { CreateSelectedItemsDTO } from '../../order/dto/create-order.dto';
import { ISelectedCartItem } from './selected-cart-items-entity.interface';
import { SelectedCartItem } from './selectedCartItem';

const id = new Types.ObjectId();
const selectedItemsMockData: ISelectedCartItem = {
cartItemId: id,
itemId: id,
menuId: id,
price: 2,
quantity: 3,
audit: Audit.create(auditMockData).getValue(),
};

export const selectedItemsMock = SelectedCartItem.create(selectedItemsMockData);

export const createSelectedCartItems: CreateSelectedItemsDTO = {
cartItemId: id.toString(),
menuId: id.toString(),
itemId: id.toString(),
price: 1,
quantity: 1,
};
2 changes: 1 addition & 1 deletion backend/src/cart/selectedItems/selectedCartItem.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Audit, Entity, Result } from 'src/domain';
import { Audit, Entity, Result } from '../../domain';
import { ISelectedCartItem } from './selected-cart-items-entity.interface';
import { Types } from 'mongoose';

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ export class OrderRepository extends GenericDocumentRepository<Order, OrderDocum
cartItems: { $elemMatch: { $in: selectedItemIds } },
auditCreatedDateTime: {
$gte: initDuplicateTimeInMilliSeconds,
$lt: new Date(currentTime.getMilliseconds() + finalDuplicateTimeFrameInMinutes),
$lte: new Date(currentTime.getMilliseconds() + finalDuplicateTimeFrameInMinutes),
},
});
const potentialDuplicateOrder = result.getValue();
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { Prop, Schema, SchemaFactory } from '@nestjs/mongoose';
import { Type } from 'class-transformer';
import mongoose, { Document, Types } from 'mongoose';
import { BaseDocument } from 'src/infrastructure/database';
import { BaseDocument } from '../../../../infrastructure/database';
import { ICartItemModel } from '../models/cart-item-data.model';
import { MenuDataModel } from './menu.schema';
import { OrderDataModel } from './order.schema';
Expand All @@ -21,7 +21,7 @@ export class CartItemDataModel extends BaseDocument implements ICartItemModel {
@Prop({ type: String, required: true })
total: number;

@Prop({ type: [{ type: mongoose.Schema.Types.ObjectId, ref: SelectedCartItemDataModel.name }] })
@Prop({ type: [{ type: mongoose.Schema.Types.ObjectId }] })
@Type(() => SelectedCartItemDataModel)
selectedItems?: SelectedCartItemDataModel[];
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { Prop, Schema, SchemaFactory } from '@nestjs/mongoose';
import { Document } from 'mongoose';
import { BaseDocument } from 'src/infrastructure/database';
import { BaseDocument } from '../../../../infrastructure/database';
import { IOrderStatusModel } from '../models/order-status-model.interface';

export type OrderStatusDocument = OrderStatusModel & Document;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
import { Prop, Schema, SchemaFactory } from '@nestjs/mongoose';
import { Type } from 'class-transformer';
import mongoose, { Document, Types } from 'mongoose';
import { BaseDocument } from 'src/infrastructure/database';
import { BaseDocument } from '../../../../infrastructure/database';
import { IOrderDataModel } from '../models/order-model.interface';
import { currentStatus, dinningType } from './../../../../order/order-entity.interface';
import { dinningType } from './../../../../order/order-entity.interface';
import { CartItemDataModel } from './cartItem.schema';
import { MerchantDataModel } from './merchant.schema';
import { OrderManagerDataModel } from './order-manger.schema';
import { CartItemDataModel } from './cartItem.schema';
import { OrderStatusModel } from './order-status.schema';

export type OrderDocument = OrderDataModel & Document;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { Prop, Schema, SchemaFactory } from '@nestjs/mongoose';
import { Type } from 'class-transformer';
import mongoose, { Document, Types } from 'mongoose';
import { BaseDocument } from 'src/infrastructure/database';
import { BaseDocument } from '../../../../infrastructure/database';
import { ISelectedCartItemDataModel } from '../models/selected-item-model.interface';
import { CartItemDataModel } from './cartItem.schema';
import { ItemDataModel } from './item.schema';
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import { SelectedCartItemMapper } from './../../../cart/selectedItems/selected-cart-item.mapper';
import { SelectedCartItem } from 'src/cart/selectedItems/selectedCartItem';
import { SelectedCartItem } from '../../../cart/selectedItems/selectedCartItem';
import { SelectedCartItemDataModel, SelectedCartItemDocument } from './schemas/selected-cart-item.schema';
import { InjectConnection, InjectModel } from '@nestjs/mongoose';
import { Connection, Model } from 'mongoose';
import { GenericDocumentRepository } from 'src/infrastructure/database';
import { GenericDocumentRepository } from '../../../infrastructure/database';

export class SelectedCartItemRepository extends GenericDocumentRepository<SelectedCartItem, SelectedCartItemDocument> {
selectedCartItemMapper: SelectedCartItemMapper;
Expand Down
4 changes: 2 additions & 2 deletions backend/src/order/dto/create-order.dto.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { IsArray, IsNotEmpty, IsNumber, IsOptional, IsString } from 'class-validator';
import { currentStatus, dinningType } from '../order-entity.interface';
import { Types } from 'mongoose';
import { currentStatus, dinningType } from '../order-entity.interface';

export class CreateOrderDTO {
@IsString()
Expand Down Expand Up @@ -45,7 +45,7 @@ export class CreateCartItemsDTO {
selectedItems: CreateSelectedItemsDTO[];
}

class CreateSelectedItemsDTO {
export class CreateSelectedItemsDTO {
@IsNotEmpty()
@IsString()
cartItemId: string;
Expand Down
28 changes: 28 additions & 0 deletions backend/src/order/order-mock-data.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import { Types } from 'mongoose';
import { auditMockData } from '../audit/audit-mock-data';
import { cartItemMock, createItem } from '../cart/cart-item-mock-data';
import { Audit } from '../domain';
import { orderStatusMockData } from '../order_statuses/order-status-mock';
import { OrderStatus } from '../order_statuses/order_status';
import { CreateOrderDTO } from './dto/create-order.dto';
import { IOrder } from './order-entity.interface';

const id = new Types.ObjectId();

export const orderMockData: IOrder = {
state: OrderStatus.create(orderStatusMockData),
type: 'DINE_IN',
merchantId: id,
customerId: id,
total: 1,
audit: Audit.create(auditMockData).getValue(),
cartItems: [cartItemMock],
};

export const orderMock: CreateOrderDTO = {
state: 'CREATED',
type: 'DINE_IN',
merchantId: id.toString(),
total: 1,
cartItems: [createItem],
};
8 changes: 4 additions & 4 deletions backend/src/order/order.mapper.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import { CartItemMapper } from './../cart/cart-item.mapper';
import { Injectable } from '@nestjs/common';
import { IMapper } from 'src/domain';
import { OrderDataModel } from 'src/infrastructure/data_access/repositories/schemas/order.schema';
import { IMapper } from '../domain';
import { OrderDataModel } from '../infrastructure/data_access/repositories/schemas/order.schema';
import { Order } from './order';
import { AuditMapper } from 'src/audit';
import { OrderStatusMapper } from 'src/order_statuses/order_status.mapper';
import { AuditMapper } from '../audit';
import { OrderStatusMapper } from '../order_statuses/order_status.mapper';

@Injectable()
export class OrderMapper implements IMapper<Order, OrderDataModel> {
Expand Down
50 changes: 25 additions & 25 deletions backend/src/order/order.module.ts
Original file line number Diff line number Diff line change
@@ -1,50 +1,50 @@
import { MiddlewareConsumer, Module, NestModule } from '@nestjs/common';
import { JwtService } from '@nestjs/jwt';
import { MongooseModule } from '@nestjs/mongoose';
import { TYPES } from 'src/application';
import { AuditMapper } from 'src/audit';
import { CartItemMapper } from 'src/cart/cart-item.mapper';
import { SelectedCartItemMapper } from 'src/cart/selectedItems/selected-cart-item.mapper';
import { TYPES } from '../application';
import { AuditMapper } from '../audit';
import { CartItemMapper } from '../cart/cart-item.mapper';
import { SelectedCartItemMapper } from '../cart/selectedItems/selected-cart-item.mapper';
import {
ContextService,
ItemDataModel,
ItemSchema,
MerchantDataModel,
MerchantRepository,
MerchantSchema,
} from 'src/infrastructure';
import { CartItemRepository } from 'src/infrastructure/data_access/repositories/cart-item.repository';
import { OrderRepository } from 'src/infrastructure/data_access/repositories/order.repository';
import { CartItemDataModel, CartItemSchema } from 'src/infrastructure/data_access/repositories/schemas/cartItem.schema';
import { OrderDataModel, OrderSchema } from 'src/infrastructure/data_access/repositories/schemas/order.schema';
} from '../infrastructure';
import { CartItemRepository } from '../infrastructure/data_access/repositories/cart-item.repository';
import { OrderRepository } from '../infrastructure/data_access/repositories/order.repository';
import { CartItemDataModel, CartItemSchema } from '../infrastructure/data_access/repositories/schemas/cartItem.schema';
import { OrderDataModel, OrderSchema } from '../infrastructure/data_access/repositories/schemas/order.schema';
import {
SelectedCartItemDataModel,
SelectedCartItemSchema,
} from 'src/infrastructure/data_access/repositories/schemas/selected-cart-item.schema';
import { SelectedCartItemRepository } from 'src/infrastructure/data_access/repositories/selected-cart-item.repository';
import { MerchantMapper, MerchantService } from 'src/merchant';
import { ValidateUser } from 'src/utils';
} from '../infrastructure/data_access/repositories/schemas/selected-cart-item.schema';
import { SelectedCartItemRepository } from '../infrastructure/data_access/repositories/selected-cart-item.repository';
import { MerchantMapper, MerchantService } from '../merchant';
import { ValidateUser } from '../utils';
import { OrderMapper } from './order.mapper';
import { OrderService } from './order.service';
import { OrderController } from './order.controller';
import { ContextMiddleWare } from 'src/infrastructure/middlewares';
import { OrderStatusRepository } from 'src/infrastructure/data_access/repositories/order-status.repository';
import { ContextMiddleWare } from '../infrastructure/middlewares';
import { OrderStatusRepository } from '../infrastructure/data_access/repositories/order-status.repository';
import {
OrderStatusModel,
OrderStatusSchema,
} from 'src/infrastructure/data_access/repositories/schemas/order-status.schema';
import { OrderStatusMapper } from 'src/order_statuses/order_status.mapper';
import { OrderNoteMapper } from 'src/order_notes/order_note.mapper';
import { OrderNoteRepository } from 'src/infrastructure/data_access/repositories/order-note.repository';
import { OrderNoteModel, OrderNoteSchema } from 'src/infrastructure/data_access/repositories/schemas/order-note.schema';
import { OrderNoteService } from 'src/order_notes/order_note.service';
import { OrderProcessingQueueService } from 'src/order_processing_queue/order_processing_queue.service';
} from '../infrastructure/data_access/repositories/schemas/order-status.schema';
import { OrderStatusMapper } from '../order_statuses/order_status.mapper';
import { OrderNoteMapper } from '../order_notes/order_note.mapper';
import { OrderNoteRepository } from '../infrastructure/data_access/repositories/order-note.repository';
import { OrderNoteModel, OrderNoteSchema } from '../infrastructure/data_access/repositories/schemas/order-note.schema';
import { OrderNoteService } from '../order_notes/order_note.service';
import { OrderProcessingQueueService } from '../order_processing_queue/order_processing_queue.service';
import {
OrderProcessingQueueModel,
OrderProcessingQueueSchema,
} from 'src/infrastructure/data_access/repositories/schemas/order-processing-queue.schema';
import { OrderProcessingQueueRepository } from 'src/infrastructure/data_access/repositories/order-processing-queue.repository';
import { OrderProcessingQueueMapper } from 'src/order_processing_queue/order_processing_queue.mapper';
} from '../infrastructure/data_access/repositories/schemas/order-processing-queue.schema';
import { OrderProcessingQueueRepository } from '../infrastructure/data_access/repositories/order-processing-queue.repository';
import { OrderProcessingQueueMapper } from '../order_processing_queue/order_processing_queue.mapper';

@Module({
imports: [
Expand Down
6 changes: 3 additions & 3 deletions backend/src/order/order.parser.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { OrderNote } from 'src/order_notes/order_note';
import { OrderNoteParser } from 'src/order_notes/order_note_parser';
import { OrderStatusParser } from 'src/order_statuses/order_status_parser';
import { OrderNote } from '../order_notes/order_note';
import { OrderNoteParser } from '../order_notes/order_note_parser';
import { OrderStatusParser } from '../order_statuses/order_status_parser';
import { AuditParser } from './../audit/audit.parser';
import { Order } from './order';
import { IOrderResponseDTO } from './order-response.dto';
Expand Down
75 changes: 75 additions & 0 deletions backend/src/order/order.service.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
import { expect } from 'chai';
import mongoose, { Connection } from 'mongoose';
import * as sinon from 'ts-sinon';
import { CartItemMapper } from '../cart/cart-item.mapper';
import { SelectedCartItemMapper } from '../cart/selectedItems/selected-cart-item.mapper';
import { IContextService, MerchantRepository } from '../infrastructure';
import { ICartItemRepository } from '../infrastructure/data_access/repositories/interfaces/cart-item-repository.interface';
import { IOrderRepository } from '../infrastructure/data_access/repositories/interfaces/order-repository.interface';
import { IOrderStatusRespository } from '../infrastructure/data_access/repositories/interfaces/order-status.repository';
import { SelectedCartItemRepository } from '../infrastructure/data_access/repositories/selected-cart-item.repository';
import { IMerchantService } from '../merchant';
import { IOrderNoteService } from '../order_notes/interface/order-note-service.interface';
import { IOrderProcessingQueueService } from '../order_processing_queue/interface/order-processing-queue-service.interface';
import { OrderStatusMapper } from '../order_statuses/order_status.mapper';
import { AuditMapper } from './../audit/audit.mapper';
import { IOrderService } from './interface/order-service.interface';
import { orderMock } from './order-mock-data';
import { OrderMapper } from './order.mapper';
import { OrderService } from './order.service';

describe('Order Service', () => {
let connection: Connection;
beforeEach(async () => {
connection = new Connection();
});
afterEach(async () => {
connection.close();
mongoose.disconnect();
});

const orderRepositoryStub: IOrderRepository = sinon.stubInterface<IOrderRepository>();
const merchantServiceStub: IMerchantService = sinon.stubInterface<IMerchantService>();
const contextServiceStub: IContextService = sinon.stubInterface<IContextService>();
const merchantRepositoryStub: MerchantRepository = sinon.stubInterface<MerchantRepository>();
const selectedCartItemRepositoryStub: SelectedCartItemRepository = sinon.stubInterface<SelectedCartItemRepository>();
const auditMapperStub = new AuditMapper();
const selectedCartItemMapperStub = new SelectedCartItemMapper();
const cartItemMapperStub = new CartItemMapper(auditMapperStub, selectedCartItemMapperStub);
const cartItemRepositoryStub: ICartItemRepository = sinon.stubInterface<ICartItemRepository>();
const orderStatusMapperStub = new OrderStatusMapper(auditMapperStub);
const orderMapperStub = new OrderMapper(auditMapperStub, cartItemMapperStub, orderStatusMapperStub);
const orderStatusRepositoryStub: IOrderStatusRespository = sinon.stubInterface<IOrderStatusRespository>();
const orderNoteServiceStub: IOrderNoteService = sinon.stubInterface<IOrderNoteService>();
const orderProcessingQueueServiceStub: IOrderProcessingQueueService =
sinon.stubInterface<IOrderProcessingQueueService>();
const orderService: IOrderService = new OrderService(
orderRepositoryStub,
merchantServiceStub,
contextServiceStub,
merchantRepositoryStub,
selectedCartItemRepositoryStub,
orderMapperStub,
selectedCartItemMapperStub,
cartItemMapperStub,
cartItemRepositoryStub,
orderStatusRepositoryStub,
orderNoteServiceStub,
orderProcessingQueueServiceStub,
);

it('Should not create an order, It should throw a duplicate order warning', async () => {
try {
merchantServiceStub.validateContext = async () => {
return Promise.resolve(true);
};
orderRepositoryStub.getDuplicateOrder = async () => {
return Promise.resolve(true);
};
await orderService.createOrder(orderMock);
} catch (error: any) {
expect(error.response.status).to.equal(404);
expect(error.response.error).to.equal('Duplicate order detected. Please confirm.');
}
});
});
Loading

0 comments on commit 91dc8b3

Please sign in to comment.