# Directory Structure
```
├── .gitignore
├── README.md
└── solution
├── assets
│ ├── architecture_drawio.drawio.png
│ └── erd_drawio-ERD.drawio.png
├── demo.ts
├── jest.config.js
├── package-lock.json
├── package.json
├── README.md
├── services
│ ├── commerce
│ │ ├── index.ts
│ │ ├── methods
│ │ │ ├── addToCart.ts
│ │ │ ├── expireCartItems.ts
│ │ │ ├── getAvailableProductQuantity.ts
│ │ │ ├── index.ts
│ │ │ ├── purchaseCart.ts
│ │ │ ├── removeFromCart.ts
│ │ │ ├── syncInventory.ts
│ │ │ └── test.ts
│ │ ├── test.ts
│ │ └── types
│ │ ├── Cart.ts
│ │ ├── CartItem.ts
│ │ ├── CommerceMessageHandler.ts
│ │ ├── CommerceService.ts
│ │ ├── CommerceServiceConfig.ts
│ │ ├── CommerceState.ts
│ │ ├── DropshipOrder.ts
│ │ ├── FulfillerInventory.ts
│ │ ├── index.ts
│ │ ├── Order.ts
│ │ └── Product.ts
│ └── warehouse
│ ├── helpers
│ │ ├── createInventoryStakeAdjuster.ts
│ │ └── index.ts
│ ├── index.ts
│ ├── strategies
│ │ ├── index.ts
│ │ ├── mockLocal.ts
│ │ └── vendorFoo
│ │ ├── helpers
│ │ │ ├── createRateLimitChecker.ts
│ │ │ ├── index.ts
│ │ │ └── mockAndDecorateOrderDataForVendor.ts
│ │ └── index.ts
│ ├── test.ts
│ └── types
│ ├── index.ts
│ ├── WarehouseService.ts
│ └── WarehouseServiceConfig.ts
├── tsconfig.json
└── util
├── message
│ ├── index.ts
│ ├── strategies
│ │ ├── mockLocal.ts
│ │ └── rabbitmq.ts
│ ├── test.ts
│ └── types
│ ├── index.ts
│ ├── Message.ts
│ ├── MessageBus.ts
│ ├── MessageHandler.ts
│ └── Subscriber.ts
├── rest
│ └── types
│ ├── HttpClient.ts
│ └── index.ts
└── state
├── index.ts
├── strategies
│ ├── mockLocal.ts
│ └── redis.ts
└── types
├── index.ts
└── StateStore.ts
```
# Files
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
```
.idea
*.iml
out
gen
**/.DS_Store
solution/node_modules/
solution/coverage/
```
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
```markdown
# Problem Statement
The Nabis platform relies on a WMS for inventory quantity. When an order is created we must first validate the available quantity of the line items of the order. This is typically facilitated by accessing our WMS's API.
The core problem is **inventory inconsistency** and **overselling** caused by technical limitations external to our system. The WMS performs critical inventory allocation via a batch job that runs only every **15 minutes** creating a window where Nabis's inventory data can be stale, resulting in overselling. Additionally our typical volume of order placement causes rate limiting from their API.
## Goal
Design and implement the architecture that maintains available inventory quantity for the Nabis platform while taking care of rate limiting and technical limitations of the backing WMS. Please frame your solution with NodeJs and Postgresql.
## Glossary of Terms
| **Term** | **Definition** | **Contextual Relevance** |
| -------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------- |
| **WMS** | **Warehouse Management System**. Software used to control and manage daily warehouse operations from inventory intake to shipment. | The external system Nabis uses as the backing store of inventory information. |
| **3PL** | **Third-Party Logistics.** A service provider that manages a company's warehousing, inventory, and fulfillment (e.g., Nabis acting as a 3PL for brands). | The business model driving the need for WMS flexibility (PaaS). |
| **SKU** | **Stock Keeping Unit.** A unique code for a product, used for inventory tracking. | Represents the core product identifier tracked by the Inventory Service. |
| **SKU-Batch / Item-Batch** | A specific quantity of a SKU/Item that arrived together and might have unique attributes (e.g., expiration date, lot number). | Inventory is tracked at this granular level in the system. |
| **Orderable Quantity** | The quantity of an item that is currently physically available for sale and allocation in the WMS. | The input quantity provided by a WMS |
| **Unallocatable Quantity** | Inventory quantity that is physically present but unavailable for sale (e.g., damaged, quarantined, reserved). | Nabis needs to own and track these extended states |
# Expectations and Deliverables
## Code
Clone the Repository and provide your own version as a ___zip file___. Your repository should include some code to assist in describing your solution. The code must be production-quality, testable, and demonstrate the core logic of your proposed architecture. Some examples (but not required, entirely dependent on your solution):
- WMS API interface.
- ORM models that represent the data and schema.
- What would the core `InventoryService` consist of? How would it manage the platform's **Available Quantity** as the source of truth, tracking both _incoming_ and _outgoing_ inventory changes based on input?
- How would you test your POC? How would you simulate the scenario of two rapid, competing orders and prove that the second order is correctly blocked by your Inventory Service's real-time count.
## Data Modeling
Entity Relationship Diagram (ERD): Provide a simplified ERD focusing on the **Inventory Service's** core entities required to track state. This should include entities like:
- Inventory
- SKU/Item-Batch
- Etc.
## System Design and Strategy
Architecture Diagram: Provide a clear, high-level diagram illustrating the architecture. Show the flow during initial order creation and separate adjustments to Inventory:
- How does the Inventory Service capture and reduce the available quantity when a new order is created on the Nabis Platform?
- How do you reliably capture inventory quantity _increases_ (from receipts/adjustments) process them in an asynchronous manner?
- Propose a resilient technical mechanism to guarantee that an item sold on the Nabis Platform is immediately reserved/unavailable for all subsequent orders.
```
--------------------------------------------------------------------------------
/solution/README.md:
--------------------------------------------------------------------------------
```markdown
# Nabis Inventory Management Strategy
## Core Assumptions
We're working within a drop-shipping model where the actual inventory is out of our control. The product inventory is
shared and being sold by other merchants as well, which means competitors may take some percentage of
available inventory at any moment.
## Calculating Safe Inventory Levels
We can refine our understanding of available inventory by running a report that considers and calculates a few key factors.
First, we need to look at how much of a particular product we move typically or seasonally. Then we factor in how many
direct competitors we have and assume they move about the same, give or take.
From there, we multiply those numbers, remove our take, and what's left is our approximate safe inventory threshold.
Here's an example of how this works: let's say we sell 4oz of OG Kush daily on average, and we have 3 direct competitors
who we can assume sell about the same amount, so that's 12oz total. We can then infer we have about a 25% stake in the inventory.
## Handling Overreach
Any overreach beyond our stake needs to be handled by UX. Supporting back-orders can eat into the following
day's inventory stake and introduce a lot of complexity, so I typically recommend presenting alternative products instead.
Think top sellers that are always in stock, or slow movers with an inconvenience discount applied. There are several options to consider,
like store credit for future orders.
## Using This to Our Advantage
The constraint actually gives us some leverage. FOMO drives sales, so we get to put more "hurry, almost out of stock" CTAs
throughout the experience. We control the percentage and lock it down at the cart level, meaning we should assume that orders placed
after we cross our stake threshold will bounce. This helps us avoid fulfillment claims down the line.
We need to have a strategy in place for "out-of-stock" situations and let the user decide ahead of time with an option menu.
The customer can agree to swap their item with a similar one, or they can agree to accept store credit for future orders.
Either way, they're making the choice upfront rather than dealing with disappointment later.
# Architecture
This is the proposed architecture starting point. It's not meant to be dogmatic or exclusive. Lots of ways to accomplish this, and we can
consider what options make sense on the existing Nabis infrastructure.


# Demo Walkthrough
This demo proves the inventory management strategy can prevent overselling in a shared inventory environment by simulating
real-world race conditions between competing orders.
## What I'm Proving
The core challenge is that the WMS updates inventory via batch jobs every 15 minutes, creating a window where data can be stale.
During this window, multiple orders could theoretically claim the same inventory, leading to overselling and failed fulfillments.
This demo shows how I've solved that by maintaining real-time availability tracking based on a calculated stake percentage.
## How the Demo Works
I start with a realistic scenario: the WMS has 100 units of OG Kush in total inventory. For this demo, I'm using a 25% stake
calculation as an example scenario, which would represent a case where there are roughly 3 direct competitors sharing the same
inventory pool. In production, this percentage would be determined by analyzing actual market share, competitor count, and historical
sales data, but the math and logic apply regardless of the specific percentage used.
The demo then simulates the exact race condition we're trying to prevent. User 1 adds 15 units to their cart, which succeeds and
reduces available inventory to 10 units. Immediately after, User 2 attempts to add 15 units to their cart. This is where the system
proves its value: User 2's request is blocked because only 10 units remain in our stake. The system prevents the oversell before it
happens, not after.
User 2 then adjusts their request to 10 units, which succeeds. Both users complete their purchases, and the orders are submitted to the
WMS for fulfillment. At this point, we've sold exactly our 25% stake and available inventory reads zero.
## What This Solves
This demonstrates that I'm tracking inventory atomically at the cart level, meaning inventory is reserved the moment it's added to a
cart, not when the order is placed. This prevents the race condition entirely. The implementation also respects the calculated stake
percentage, which means we're only claiming what we can reliably fulfill based on market share analysis.
I've also built in cart expiration handling. When items sit in a cart beyond the expiration time (configured at 10 minutes in the demo),
those reserved units are released back into available inventory so other customers can purchase them. This prevents inventory from being
indefinitely locked by abandoned carts or slow shoppers. When a customer does complete their purchase, their claimed quantity is
accounted for locally, and we trust the WMS source will reflect that change on the next sync cycle.
The key is that we're not waiting for the WMS to tell us what's available every 15 minutes. I've designed the system to maintain its
own real-time count based on the initial sync, stake calculation, and order activity. When the WMS does sync, we reconcile any
discrepancies, but in the meantime we're operating within safe boundaries that prevent overselling.
This approach gives us the control we need to avoid fulfillment claims while still operating in a shared inventory model where we
don't control the actual warehouse stock.
## Running The Demo
```bash
npm install
npm run dev
```
## Testing The Logic
I ran out of time, but I tried to get most of the coverage around the core logic and some of the utility functions.
```bash
npm test
```
```
--------------------------------------------------------------------------------
/solution/util/rest/types/index.ts:
--------------------------------------------------------------------------------
```typescript
import type {HttpClient} from './HttpClient';
export {
HttpClient
}
```
--------------------------------------------------------------------------------
/solution/util/state/types/index.ts:
--------------------------------------------------------------------------------
```typescript
import type {StateStore} from './StateStore';
export {
StateStore
}
```
--------------------------------------------------------------------------------
/solution/services/commerce/types/Cart.ts:
--------------------------------------------------------------------------------
```typescript
import type {CartItem} from './CartItem';
export type Cart = {
id: string;
items: CartItem[];
};
```
--------------------------------------------------------------------------------
/solution/services/commerce/types/CartItem.ts:
--------------------------------------------------------------------------------
```typescript
export type CartItem = {
productId: string;
quantity: number;
addedAt: number;
expiresAt: number;
};
```
--------------------------------------------------------------------------------
/solution/util/message/types/MessageHandler.ts:
--------------------------------------------------------------------------------
```typescript
import {Message} from './Message';
export type MessageHandler = (message: Message) => void | Promise<void> | number;
```
--------------------------------------------------------------------------------
/solution/services/warehouse/helpers/index.ts:
--------------------------------------------------------------------------------
```typescript
import {createInventoryStakeAdjuster} from './createInventoryStakeAdjuster';
export {
createInventoryStakeAdjuster,
}
```
--------------------------------------------------------------------------------
/solution/services/commerce/types/CommerceServiceConfig.ts:
--------------------------------------------------------------------------------
```typescript
export type CommerceServiceConfig = {
syncIntervalMs: number;
cartExpirationMs: number;
inventoryStakePercentage: number;
};
```
--------------------------------------------------------------------------------
/solution/services/commerce/types/Product.ts:
--------------------------------------------------------------------------------
```typescript
export type Product = {
id: string;
sku: string;
name: string;
category: string;
batchId: string;
quantity: number;
};
```
--------------------------------------------------------------------------------
/solution/util/rest/types/HttpClient.ts:
--------------------------------------------------------------------------------
```typescript
export type HttpClient = {
get: <T = unknown>(url: string) => Promise<T>;
post: <T = unknown>(url: string, data: any) => Promise<T>;
};
```
--------------------------------------------------------------------------------
/solution/services/commerce/types/Order.ts:
--------------------------------------------------------------------------------
```typescript
import type {CartItem} from './CartItem';
export type Order = {
id: string;
cartId: string;
items: CartItem[];
placedAt: number;
};
```
--------------------------------------------------------------------------------
/solution/services/commerce/types/DropshipOrder.ts:
--------------------------------------------------------------------------------
```typescript
import type {Product} from './Product';
export type DropshipOrder = {
id: string;
nabisOrderId: string;
items: Product[];
placedAt: number;
};
```
--------------------------------------------------------------------------------
/solution/services/commerce/types/FulfillerInventory.ts:
--------------------------------------------------------------------------------
```typescript
import type {Product} from './Product';
export type FulfillerInventory = {
items: Product[];
asOfTimestamp: number;
fetchedTimestamp: number;
};
```
--------------------------------------------------------------------------------
/solution/util/message/types/Subscriber.ts:
--------------------------------------------------------------------------------
```typescript
import {Message} from './Message';
import {CommerceState} from '../../../services/commerce';
export type Subscriber = (msg: Message, state: InventoryState) => void;
```
--------------------------------------------------------------------------------
/solution/util/state/types/StateStore.ts:
--------------------------------------------------------------------------------
```typescript
export interface StateStore<T> {
get(): Promise<T | null>;
set(state: T): Promise<void>;
merge(partial: Partial<T>): Promise<void>;
shutdown(): Promise<void>;
}
```
--------------------------------------------------------------------------------
/solution/tsconfig.json:
--------------------------------------------------------------------------------
```json
{
"compilerOptions": {
"esModuleInterop": true,
"allowSyntheticDefaultImports": true,
"module": "commonjs",
"target": "ES2020",
"moduleResolution": "node"
}
}
```
--------------------------------------------------------------------------------
/solution/services/warehouse/strategies/index.ts:
--------------------------------------------------------------------------------
```typescript
import {mockLocalWarehouseStrategy} from './mockLocal';
import {vendorFooWarehouseStrategy} from './vendorFoo';
export {
mockLocalWarehouseStrategy,
vendorFooWarehouseStrategy,
}
```
--------------------------------------------------------------------------------
/solution/services/warehouse/types/index.ts:
--------------------------------------------------------------------------------
```typescript
import type {WarehouseServiceConfig} from './WarehouseServiceConfig';
import type {WarehouseService} from './WarehouseService';
export {
WarehouseServiceConfig,
WarehouseService,
}
```
--------------------------------------------------------------------------------
/solution/services/warehouse/types/WarehouseServiceConfig.ts:
--------------------------------------------------------------------------------
```typescript
export type WarehouseServiceConfig = {
stakePercentage: number;
baseUrl?: string;
rateLimit?: {
maxRequests: number;
perMs: number;
};
requestOptions?: RequestInit,
};
```
--------------------------------------------------------------------------------
/solution/services/commerce/types/CommerceState.ts:
--------------------------------------------------------------------------------
```typescript
import type {Cart, Order, Product} from './';
export type CommerceState = {
products: Record<string, Product>;
carts: Record<string, Cart>;
orders: Record<string, Order>;
lastSync: number;
};
```
--------------------------------------------------------------------------------
/solution/services/commerce/types/CommerceMessageHandler.ts:
--------------------------------------------------------------------------------
```typescript
import type { Message } from '../../../util/message/types';
import type { CommerceState } from './CommerceState';
export type CommerceMessageHandler = (msg: Message, state: CommerceState) => CommerceState;
```
--------------------------------------------------------------------------------
/solution/services/warehouse/strategies/vendorFoo/helpers/index.ts:
--------------------------------------------------------------------------------
```typescript
import {createRateLimitChecker} from './createRateLimitChecker';
import {mockGetAndDecorateOrderDataForVendor} from './mockAndDecorateOrderDataForVendor';
export {
createRateLimitChecker,
mockGetAndDecorateOrderDataForVendor,
}
```
--------------------------------------------------------------------------------
/solution/util/message/types/index.ts:
--------------------------------------------------------------------------------
```typescript
import type {Message} from './Message';
import type {MessageBus} from './MessageBus';
import type {MessageHandler} from './MessageHandler';
import type {Subscriber} from './Subscriber';
export {
Message,
MessageBus,
MessageHandler,
Subscriber,
}
```
--------------------------------------------------------------------------------
/solution/util/message/types/MessageBus.ts:
--------------------------------------------------------------------------------
```typescript
import {Message} from './Message';
import {MessageHandler} from './MessageHandler';
export type MessageBus<THandler = MessageHandler> = {
publish(message: Message): Promise<void>;
subscribe(handler: THandler): () => void;
shutdown(): Promise<void>;
};
```
--------------------------------------------------------------------------------
/solution/services/warehouse/types/WarehouseService.ts:
--------------------------------------------------------------------------------
```typescript
import type {FulfillerInventory} from '../../commerce/types';
export type WarehouseService = {
getInventory: () => Promise<FulfillerInventory>;
adjustInventoryForLikelyAvailability: (inventory: FulfillerInventory) => FulfillerInventory;
submitOrder: (orderId: string) => Promise<any>;
};
```
--------------------------------------------------------------------------------
/solution/services/commerce/methods/index.ts:
--------------------------------------------------------------------------------
```typescript
import {addToCart} from './addToCart';
import {expireCartItems} from './expireCartItems';
import {getAvailableProductQuantity} from './getAvailableProductQuantity';
import {purchaseCart} from './purchaseCart';
import {removeFromCart} from './removeFromCart';
import {syncInventory} from './syncInventory';
export {
addToCart,
expireCartItems,
getAvailableProductQuantity,
purchaseCart,
removeFromCart,
syncInventory,
}
```
--------------------------------------------------------------------------------
/solution/services/commerce/types/CommerceService.ts:
--------------------------------------------------------------------------------
```typescript
import type {CommerceState} from './CommerceState';
export type CommerceService = {
getAvailability: (productId: string) => Promise<number>;
addToCart: (cartId: string, productId: string, quantity: number) => Promise<boolean>;
removeFromCart: (cartId: string, productId: string) => void;
purchaseCart: (cartId: string, orderId: string) => Promise<boolean>;
getState: () => Promise<CommerceState | null>;
shutdown: () => Promise<void>;
};
```
--------------------------------------------------------------------------------
/solution/package.json:
--------------------------------------------------------------------------------
```json
{
"name": "nabis-inventory-poc",
"version": "1.0.0",
"description": "Functional inventory management POC for Nabis",
"scripts": {
"dev": "tsx demo.ts",
"test": "jest --coverage",
"test:watch": "jest --watch",
"build": "tsc"
},
"author": "",
"license": "MIT",
"devDependencies": {
"@types/jest": "^29.5.11",
"@types/node": "^20.10.6",
"jest": "^29.7.0",
"ts-jest": "^29.1.1",
"tsx": "^4.7.0",
"typescript": "^5.3.3"
},
"dependencies": {
"ramda": "^0.32.0",
"uuid": "^13.0.0"
}
}
```
--------------------------------------------------------------------------------
/solution/services/commerce/methods/expireCartItems.ts:
--------------------------------------------------------------------------------
```typescript
import type {CommerceState} from '../types';
import {removeFromCart} from './removeFromCart';
export const expireCartItems = (state: CommerceState): CommerceState => {
const now = Date.now();
let newState = state;
Object.entries(state.carts).forEach(([cartId, cart]) => {
cart.items.forEach(item => {
if (item.expiresAt <= now) {
console.log(`[DEMO][expireCartItems]⏰ Expiring ${item.productId} from cart ${cartId}`);
newState = removeFromCart(newState, cartId, item.productId);
}
});
});
return newState;
};
```
--------------------------------------------------------------------------------
/solution/jest.config.js:
--------------------------------------------------------------------------------
```javascript
module.exports = {
preset: 'ts-jest',
testEnvironment: 'node',
testMatch: ['**/*.test.ts', '**/test.ts'],
moduleNameMapper: {
'^uuid$': require.resolve('uuid')
},
transform: {
'^.+\\.tsx?$': ['ts-jest', {
isolatedModules: true,
tsconfig: {
esModuleInterop: true,
allowSyntheticDefaultImports: true
}
}],
'^.+\\.js$': ['ts-jest', {
isolatedModules: true,
tsconfig: {
esModuleInterop: true,
allowSyntheticDefaultImports: true
}
}]
},
transformIgnorePatterns: []
};
```
--------------------------------------------------------------------------------
/solution/services/commerce/methods/syncInventory.ts:
--------------------------------------------------------------------------------
```typescript
import type {Product, CommerceState} from '../types';
export const syncInventory =
(state: CommerceState, products: Product[] = []): CommerceState => {
const productsMap = products.reduce((acc, product) => ({
...acc,
[product.id]: product
}), {} as Record<string, Product>);
return {
...state,
products: productsMap,
lastSync: Date.now(),
// ASSUMPTION
// assuming orders have been received by WMS, and subsequent
// warehouse inventory pulls will account for their claimed items offset
orders: {}
};
};
```
--------------------------------------------------------------------------------
/solution/services/warehouse/strategies/vendorFoo/helpers/mockAndDecorateOrderDataForVendor.ts:
--------------------------------------------------------------------------------
```typescript
import { v4 as uuid } from 'uuid';
import type {DropshipOrder} from '../../../../commerce/types';
export const mockGetAndDecorateOrderDataForVendor = (orderId: string): DropshipOrder => {
// TODO fetch order from db, decorate for vendor payload
return {
id: uuid(),
nabisOrderId: orderId,
items: [
{id: 'sour-diesel-1', sku: 'SRD-001', name: 'Sour Diesel', category: 'Flower', batchId: 'B2024-003', quantity: 2},
{id: 'gelato-1', sku: 'GEL-001', name: 'Gelato', category: 'Flower', batchId: 'B2024-004', quantity: 4},
],
placedAt: Date.now(),
};
}
```
--------------------------------------------------------------------------------
/solution/services/commerce/methods/removeFromCart.ts:
--------------------------------------------------------------------------------
```typescript
import type {CommerceState} from '../types/CommerceState';
export const removeFromCart = (
state: CommerceState,
cartId: string,
productId: string
): CommerceState => {
const cart = state.carts[cartId];
if (!cart) return state;
const filteredItems = cart.items.filter(item => item.productId !== productId);
if (filteredItems.length === 0) {
const { [cartId]: _, ...remainingCarts } = state.carts;
return { ...state, carts: remainingCarts };
}
return {
...state,
carts: {
...state.carts,
[cartId]: { ...cart, items: filteredItems }
}
};
};
```
--------------------------------------------------------------------------------
/solution/services/warehouse/index.ts:
--------------------------------------------------------------------------------
```typescript
import type {WarehouseServiceConfig, WarehouseService} from './types';
import {mockLocalWarehouseStrategy, vendorFooWarehouseStrategy} from './strategies';
export type WarehouseStrategy =
| 'mockLocal'
| 'vendorFoo';
export const createWarehouseService = <T>(
strategy: WarehouseStrategy,
config: WarehouseServiceConfig,
): WarehouseService => {
switch (strategy) {
case 'mockLocal':
return mockLocalWarehouseStrategy(config);
case 'vendorFoo':
return vendorFooWarehouseStrategy(config);
default:
throw new Error(`Unknown state strategy: ${strategy}`);
}
};
```
--------------------------------------------------------------------------------
/solution/services/warehouse/helpers/createInventoryStakeAdjuster.ts:
--------------------------------------------------------------------------------
```typescript
import * as R from 'ramda';
import type {FulfillerInventory} from '../../commerce/types';
import {WarehouseServiceConfig} from '../types';
export const createInventoryStakeAdjuster =
(config: WarehouseServiceConfig) =>
(inventory: FulfillerInventory) => {
const itemsLens = R.lensProp('items');
const quantityLens = R.lensProp('quantity');
const adjustQuantity = (q: number) => Math.floor(q * (config.stakePercentage / 100));
const adjustQuantities = R.map(R.over(quantityLens, adjustQuantity));
return R.over(
itemsLens,
adjustQuantities
)(inventory);
}
```
--------------------------------------------------------------------------------
/solution/services/commerce/methods/purchaseCart.ts:
--------------------------------------------------------------------------------
```typescript
import type {CommerceState} from '../types';
export const purchaseCart = (
state: CommerceState,
cartId: string,
orderId: string
): CommerceState | null => {
const cart = state.carts[cartId];
if (!cart || cart.items.length === 0) {
console.log(`[DEMO][purchaseCart] ❌ Cart ${cartId} is empty or doesn't exist`);
return null;
}
const { [cartId]: _, ...remainingCarts } = state.carts;
return {
...state,
carts: remainingCarts,
orders: {
...state.orders,
[orderId]: {
id: orderId,
cartId,
items: cart.items,
placedAt: Date.now()
}
}
};
};
```
--------------------------------------------------------------------------------
/solution/services/warehouse/strategies/vendorFoo/helpers/createRateLimitChecker.ts:
--------------------------------------------------------------------------------
```typescript
import {WarehouseServiceConfig} from '../../../types';
export const createRateLimitChecker =
(config: WarehouseServiceConfig) =>
async (mutRequestTimes: number[]) => {
const now = Date.now();
const recentRequests = mutRequestTimes.filter(t => now - t < config.rateLimit.perMs);
if (recentRequests.length >= config.rateLimit.maxRequests) {
const oldestRequest = recentRequests[0];
const waitTime = config.rateLimit.perMs - (now - oldestRequest);
await new Promise(resolve => setTimeout(resolve, waitTime));
mutRequestTimes.shift();
}
mutRequestTimes.push(Date.now());
};
```
--------------------------------------------------------------------------------
/solution/util/message/index.ts:
--------------------------------------------------------------------------------
```typescript
import type {MessageBus} from './types';
import { mockLocalMessageBusStrategy } from './strategies/mockLocal';
import { rabbitMqMessageBusStrategy } from './strategies/rabbitmq';
export type MessageBusStrategy = 'mockLocal' | 'rabbitmq';
export const createMessageBus = (
strategy: MessageBusStrategy,
options?: { exchangeName?: string; rabbitmqUrl?: string }
): MessageBus => {
switch (strategy) {
case 'mockLocal':
return mockLocalMessageBusStrategy();
case 'rabbitmq':
return rabbitMqMessageBusStrategy(options?.exchangeName, options?.rabbitmqUrl);
default:
throw new Error(`Unknown message bus strategy: ${strategy}`);
}
};
```
--------------------------------------------------------------------------------
/solution/services/commerce/types/index.ts:
--------------------------------------------------------------------------------
```typescript
import type {Cart} from './Cart';
import type {CartItem} from './CartItem';
import type {FulfillerInventory} from './FulfillerInventory';
import type {CommerceMessageHandler} from './CommerceMessageHandler';
import type {CommerceService} from './CommerceService';
import type {CommerceState} from './CommerceState';
import type {CommerceServiceConfig} from './CommerceServiceConfig';
import type {Order} from './Order';
import type {DropshipOrder} from './DropshipOrder';
import type {Product} from './Product';
export {
Cart,
CartItem,
FulfillerInventory,
CommerceMessageHandler,
CommerceService,
CommerceState,
CommerceServiceConfig,
Order,
DropshipOrder,
Product,
}
```
--------------------------------------------------------------------------------
/solution/util/state/index.ts:
--------------------------------------------------------------------------------
```typescript
import { mockLocalStateStrategy } from './strategies/mockLocal';
import { redisStateStrategy } from './strategies/redis';
import type { StateStore } from './types';
export type StateStrategy =
| 'mockLocal'
| 'redis';
const mockLocalGlobalStore: Record<string, any> = {};
export const createStateStore = <T>(
strategy: StateStrategy,
key: string,
initialState: T,
options?: { redisUrl?: string }
): StateStore<T> => {
switch (strategy) {
case 'mockLocal':
return mockLocalStateStrategy(key, initialState, mockLocalGlobalStore);
case 'redis':
return redisStateStrategy(key, initialState, options?.redisUrl);
default:
throw new Error(`Unknown state strategy: ${strategy}`);
}
};
```
--------------------------------------------------------------------------------
/solution/util/message/strategies/mockLocal.ts:
--------------------------------------------------------------------------------
```typescript
import type { MessageBus, MessageHandler, Message } from '../types';
const globalSubscribers = new Set<MessageHandler>();
export const mockLocalMessageBusStrategy = (): MessageBus => {
return {
publish: async (message: Message) => {
const msg = { ...message, timestamp: message.timestamp || Date.now() };
globalSubscribers.forEach(handler => {
try {
handler(msg);
} catch (error) {
console.error('Message handler error:', error);
}
});
},
subscribe: (handler: MessageHandler) => {
globalSubscribers.add(handler);
return () => globalSubscribers.delete(handler);
},
shutdown: async () => {
globalSubscribers.clear();
}
};
};
```
--------------------------------------------------------------------------------
/solution/services/commerce/methods/getAvailableProductQuantity.ts:
--------------------------------------------------------------------------------
```typescript
import type {CommerceState} from '../types';
export const getAvailableProductQuantity =
(state: CommerceState, productId: string): number => {
const product = state.products[productId];
if (!product) return 0;
let mutAvailable = product.quantity;
const _reduceByItemsInCarts = () => Object.values(state.carts).forEach(cart => {
cart.items
.filter(item => item.productId === productId)
.forEach(item => {
mutAvailable -= item.quantity;
});
});
const _reduceByItemsInOrders = () => Object.values(state.orders).forEach(order => {
order.items
.filter(item => item.productId === productId)
.forEach(item => {
mutAvailable -= item.quantity;
});
});
_reduceByItemsInCarts();
_reduceByItemsInOrders();
return Math.max(0, mutAvailable);
};
```
--------------------------------------------------------------------------------
/solution/util/state/strategies/redis.ts:
--------------------------------------------------------------------------------
```typescript
import {StateStore} from '../types';
// conceptual example strategy for Redis, not fully tested...
export const redisStateStrategy = <T>(
key: string,
initialState: T,
redisUrl?: string
): StateStore<T> => {
// const redis = new Redis(redisUrl || process.env.REDIS_URL || 'redis://localhost:6379');
return {
get: async () => {
// const data = await redis.get(key);
// return data ? JSON.parse(data) : null;
throw new Error('Redis not implemented yet');
},
set: async (state: T) => {
// await redis.set(key, JSON.stringify(state));
throw new Error('Redis not implemented yet');
},
merge: async (partial: Partial<T>) => {
// const current = await this.get();
// await this.set({ ...current, ...partial } as T);
throw new Error('Redis not implemented yet');
},
shutdown: async () => {
// await redis.quit();
throw new Error('Redis not implemented yet');
}
};
};
```
--------------------------------------------------------------------------------
/solution/util/message/strategies/rabbitmq.ts:
--------------------------------------------------------------------------------
```typescript
// import amqp from 'amqplib';
import {MessageBus, MessageHandler, Message} from '../types';
// conceptual example strategy for RabbitMQ / AMQP, not fully tested...
export const rabbitMqMessageBusStrategy = (
exchangeName: string = 'inventory',
rabbitmqUrl?: string
): MessageBus => {
// let connection: amqp.Connection;
// let channel: amqp.Channel;
const handlers = new Set<MessageHandler>();
return {
publish: async (message: Message) => {
// const msg = { ...message, timestamp: message.timestamp || Date.now() };
// await channel.publish(exchangeName, '', Buffer.from(JSON.stringify(msg)));
throw new Error('RabbitMQ not implemented yet');
},
subscribe: (handler: MessageHandler) => {
// handlers.add(handler);
throw new Error('RabbitMQ not implemented yet');
},
shutdown: async () => {
// await channel.close();
// await connection.close();
throw new Error('RabbitMQ not implemented yet');
}
};
};
```
--------------------------------------------------------------------------------
/solution/util/message/types/Message.ts:
--------------------------------------------------------------------------------
```typescript
export type Message =
| { type: 'SYNC_INVENTORY'; payload: {}, timestamp: number }
| { type: 'WAREHOUSE_INVENTORY_PULLED'; payload: { productCount: number }, timestamp: number }
| { type: 'INVENTORY_SYNC_FAILED'; payload: any, timestamp: number }
| { type: 'ADD_TO_CART'; payload: { cartId: string; productId: string; quantity: number }, timestamp: number }
| { type: 'CART_ITEM_ADDED'; payload: { cartId: string; productId: string; quantity: number }, timestamp: number }
| { type: 'REMOVE_FROM_CART'; payload: { cartId: string; productId: string }, timestamp: number }
| { type: 'CART_ITEM_REMOVED'; payload: { cartId: string; productId: string; }, timestamp: number }
| { type: 'EXPIRE_CART_ITEM'; payload: { cartId: string; productId: string }, timestamp: number }
| { type: 'CART_ITEM_EXPIRED'; payload: { cartId: string; productId: string }, timestamp: number }
| { type: 'PURCHASE_CART'; payload: { cartId: string; orderId: string }, timestamp: number }
| { type: 'ORDER_PLACED'; payload: { cartId: string; orderId: string }, timestamp: number }
| { type: 'GET_AVAILABILITY'; payload: { productId: string }, timestamp: number };
```
--------------------------------------------------------------------------------
/solution/services/commerce/methods/addToCart.ts:
--------------------------------------------------------------------------------
```typescript
import type {CartItem, CommerceState} from '../types';
import {getAvailableProductQuantity} from './getAvailableProductQuantity';
export const addToCart = (
state: CommerceState,
cartId: string,
productId: string,
quantity: number,
expirationMs: number
): CommerceState | null => {
const available = getAvailableProductQuantity(state, productId);
if (available < quantity) {
console.log(`[DEMO][addToCart] ❌ Insufficient inventory for ${productId}. Available: ${available}, Requested: ${quantity}`);
return null;
}
const cart = state.carts[cartId] || {id: cartId, items: []};
const existingItemIndex = cart.items.findIndex(item => item.productId === productId);
if (existingItemIndex !== -1) {
console.log(`[DEMO][addToCart] ❌ Product ${productId} already in cart ${cartId}. Remove it first to change quantity.`);
return null;
}
const now = Date.now();
const newItem: CartItem = {
productId,
quantity,
addedAt: now,
expiresAt: now + expirationMs
};
return {
...state,
carts: {
...state.carts,
[cartId]: {
...cart,
items: [...cart.items, newItem]
}
}
};
};
```
--------------------------------------------------------------------------------
/solution/services/warehouse/strategies/vendorFoo/index.ts:
--------------------------------------------------------------------------------
```typescript
import type {FulfillerInventory} from '../../../commerce/types';
import type {WarehouseServiceConfig, WarehouseService} from '../../types';
import {createInventoryStakeAdjuster} from '../../helpers';
import {
createRateLimitChecker,
mockGetAndDecorateOrderDataForVendor
} from './helpers';
export const vendorFooWarehouseStrategy = (config: WarehouseServiceConfig): WarehouseService => {
let mutRequestTimes: number[] = [];
const checkRateLimit = createRateLimitChecker(config);
return {
getInventory: async <T>(): Promise<T> => {
await checkRateLimit(mutRequestTimes);
const response = await fetch(`${config.baseUrl}/api/v1/inventory`, config.requestOptions);
return response.json();
},
adjustInventoryForLikelyAvailability: (inventory: FulfillerInventory): FulfillerInventory => {
return createInventoryStakeAdjuster(config)(inventory);
},
submitOrder: async <T>(orderId: string): Promise<T> => {
await checkRateLimit(mutRequestTimes);
const data = mockGetAndDecorateOrderDataForVendor(orderId);
const response = await fetch(`${config.baseUrl}/order`, {
method: 'POST',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify(data)
});
return response.json();
}
};
};
```
--------------------------------------------------------------------------------
/solution/util/state/strategies/mockLocal.ts:
--------------------------------------------------------------------------------
```typescript
import type {StateStore} from '../types';
export const mockLocalStateStrategy = <T>(
key: string,
initialState: T,
store: Record<string, any>
): StateStore<T> => {
console.log(`[DEMO][mockLocalStateStrategy] 🔧 Creating store for key: "${key}"`, {
hasKey: key in store,
storeKeys: Object.keys(store),
initialState
});
if (!store[key]) {
store[key] = initialState;
console.log(`[DEMO][mockLocalStateStrategy] ✅ Set store["${key}"]`, store[key]);
}
return {
get: async () => {
console.log(`[DEMO][mockLocalStateStrategy][get] 📖 Getting store["${key}"]`);
// uncomment to inspect, makes demo noisy
// console.log(`[DEMO][mockLocalStateStrategy][get] 📖 Getting store["${key}"]`, {
// exists: key in store,
// value: store[key],
// allKeys: Object.keys(store)
// });
return await Promise.resolve(store[key]) as T;
},
set: async (state: T) => {
console.log(`[DEMO][mockLocalStateStrategy][set] 💾 Setting store["${key}"]`);
// uncomment to inspect, makes demo noisy
// console.log(`[DEMO][mockLocalStateStrategy][set] 💾 Setting store["${key}"]`, state);
store[key] = state;
return await Promise.resolve(store[key]);
},
// future extension
merge: async (partial: Partial<T>) => {
store[key] = { ...store[key], ...partial };
return await Promise.resolve(store[key]);
},
shutdown: async () => null
};
};
```
--------------------------------------------------------------------------------
/solution/services/warehouse/strategies/mockLocal.ts:
--------------------------------------------------------------------------------
```typescript
import type {FulfillerInventory} from '../../commerce/types';
import type {WarehouseService, WarehouseServiceConfig} from '../types';
import {createInventoryStakeAdjuster} from '../helpers';
export const mockLocalWarehouseStrategy = (config: WarehouseServiceConfig): WarehouseService => ({
getInventory: async <T>(): Promise<T> => {
const mockInventoryCallResponse = {
items: [
{id: 'og-kush-1', sku: 'OGK-001', name: 'OG Kush', category: 'Flower', batchId: 'B2024-001', quantity: 100},
{id: 'blue-dream-1', sku: 'BLD-001', name: 'Blue Dream', category: 'Flower', batchId: 'B2024-002', quantity: 150},
{id: 'sour-diesel-1', sku: 'SRD-001', name: 'Sour Diesel', category: 'Flower', batchId: 'B2024-003', quantity: 80},
{id: 'gelato-1', sku: 'GEL-001', name: 'Gelato', category: 'Flower', batchId: 'B2024-004', quantity: 120},
{id: 'gsc-1', sku: 'GSC-001', name: 'Girl Scout Cookies', category: 'Flower', batchId: 'B2024-005', quantity: 90}
]
};
return mockInventoryCallResponse as T;
},
adjustInventoryForLikelyAvailability: (inventory: FulfillerInventory): FulfillerInventory => {
return createInventoryStakeAdjuster(config)(inventory);
},
submitOrder: async <T>(orderId: string): Promise<T> => {
const mockOrderDetailsFromImaginaryDatabase = nabisOrderId => ({
nabisOrderId,
items: [
{id: 'gsc-1', sku: 'GSC-001', name: 'Girl Scout Cookies', category: 'Flower', batchId: 'B2024-005', quantity: 2},
{id: 'blue-dream-1', sku: 'BLD-001', name: 'Blue Dream', category: 'Flower', batchId: 'B2024-002', quantity: 1},
]
});
return mockOrderDetailsFromImaginaryDatabase(orderId) as T;
}
});
```
--------------------------------------------------------------------------------
/solution/services/warehouse/test.ts:
--------------------------------------------------------------------------------
```typescript
import { createWarehouseService } from './';
import type { WarehouseServiceConfig } from './types';
describe('WarehouseService', () => {
describe('mockLocal strategy', () => {
test('getInventory returns mock data', async () => {
const config: WarehouseServiceConfig = {
stakePercentage: 100
};
const service = createWarehouseService('mockLocal', config);
const inventory = await service.getInventory();
expect(inventory.items).toBeDefined();
expect(inventory.items.length).toBeGreaterThan(0);
expect(inventory.items[0]).toHaveProperty('id');
expect(inventory.items[0]).toHaveProperty('quantity');
});
test('adjustInventoryForLikelyAvailability applies stake percentage', async () => {
const config: WarehouseServiceConfig = {
stakePercentage: 25
};
const service = createWarehouseService('mockLocal', config);
const rawInventory = await service.getInventory();
const adjustedInventory = service.adjustInventoryForLikelyAvailability(rawInventory);
// check that quantities are reduced by 75%
rawInventory.items.forEach((rawItem, index) => {
const adjustedItem = adjustedInventory.items[index];
expect(adjustedItem.quantity).toBe(Math.floor(rawItem.quantity * 0.25));
});
});
test('adjustInventoryForLikelyAvailability with 100% returns original quantities', async () => {
const config: WarehouseServiceConfig = {
stakePercentage: 100
};
const service = createWarehouseService('mockLocal', config);
const rawInventory = await service.getInventory();
const adjustedInventory = service.adjustInventoryForLikelyAvailability(rawInventory);
rawInventory.items.forEach((rawItem, index) => {
const adjustedItem = adjustedInventory.items[index];
expect(adjustedItem.quantity).toBe(rawItem.quantity);
});
});
test('adjustInventoryForLikelyAvailability with 50% returns half quantities', async () => {
const config: WarehouseServiceConfig = {
stakePercentage: 50
};
const service = createWarehouseService('mockLocal', config);
const rawInventory = await service.getInventory();
const adjustedInventory = service.adjustInventoryForLikelyAvailability(rawInventory);
rawInventory.items.forEach((rawItem, index) => {
const adjustedItem = adjustedInventory.items[index];
expect(adjustedItem.quantity).toBe(Math.floor(rawItem.quantity * 0.5));
});
});
test('submitOrder returns mock order data', async () => {
const config: WarehouseServiceConfig = {
stakePercentage: 100
};
const service = createWarehouseService('mockLocal', config);
const result = await service.submitOrder('test-order-123');
expect(result).toBeDefined();
expect(result).toHaveProperty('nabisOrderId', 'test-order-123');
expect(result).toHaveProperty('items');
});
});
describe('vendorFoo strategy', () => {
test('creates service with rate limiting config', () => {
const config: WarehouseServiceConfig = {
stakePercentage: 25,
baseUrl: 'https://test-wms.com',
rateLimit: {
maxRequests: 100,
perMs: 60000
}
};
const service = createWarehouseService('vendorFoo', config);
expect(service).toBeDefined();
expect(service.getInventory).toBeDefined();
expect(service.submitOrder).toBeDefined();
});
});
describe('edge cases', () => {
test('handles zero stake percentage', async () => {
const config: WarehouseServiceConfig = {
stakePercentage: 0
};
const service = createWarehouseService('mockLocal', config);
const rawInventory = await service.getInventory();
const adjustedInventory = service.adjustInventoryForLikelyAvailability(rawInventory);
adjustedInventory.items.forEach(item => {
expect(item.quantity).toBe(0);
});
});
test('handles inventory with zero quantities', async () => {
const config: WarehouseServiceConfig = {
stakePercentage: 25
};
const service = createWarehouseService('mockLocal', config);
const mockInventory = {
items: [
{ id: 'test-1', sku: 'TEST-001', name: 'Test', category: 'Test', batchId: 'B001', quantity: 0 }
],
asOfTimestamp: Date.now(),
fetchedTimestamp: Date.now()
};
const adjusted = service.adjustInventoryForLikelyAvailability(mockInventory);
expect(adjusted.items[0].quantity).toBe(0);
});
});
});
```
--------------------------------------------------------------------------------
/solution/util/message/test.ts:
--------------------------------------------------------------------------------
```typescript
import type { Message } from './types';
import { createMessageBus } from './';
describe('MessageBus', () => {
describe('mockLocal strategy', () => {
test('publishes message to subscribers', async () => {
const bus = createMessageBus('mockLocal');
const received: Message[] = [];
bus.subscribe((msg) => {
received.push(msg);
});
const message: Message = {
type: 'CART_ITEM_ADDED',
payload: { cartId: 'cart-1', productId: 'prod-1', quantity: 5 },
timestamp: Date.now()
};
await bus.publish(message);
expect(received).toHaveLength(1);
expect(received[0]).toMatchObject(message);
});
test('publishes to multiple subscribers', async () => {
const bus = createMessageBus('mockLocal');
const received1: Message[] = [];
const received2: Message[] = [];
bus.subscribe((msg) => received1.push(msg));
bus.subscribe((msg) => received2.push(msg));
const message: Message = {
type: 'ORDER_PLACED',
payload: { cartId: 'cart-1', orderId: 'order-1' },
timestamp: Date.now()
};
await bus.publish(message);
expect(received1).toHaveLength(1);
expect(received2).toHaveLength(1);
expect(received1[0]).toMatchObject(message);
expect(received2[0]).toMatchObject(message);
});
test('unsubscribe removes handler', async () => {
const bus = createMessageBus('mockLocal');
const received: Message[] = [];
const unsubscribe = bus.subscribe((msg) => {
received.push(msg);
});
const message1: Message = {
type: 'SYNC_INVENTORY',
payload: {},
timestamp: Date.now()
};
await bus.publish(message1);
expect(received).toHaveLength(1);
unsubscribe();
const message2: Message = {
type: 'SYNC_INVENTORY',
payload: {},
timestamp: Date.now()
};
await bus.publish(message2);
expect(received).toHaveLength(1); // still 1, not 2
});
test('handles async subscribers', async () => {
const bus = createMessageBus('mockLocal');
const results: string[] = [];
bus.subscribe(async (msg) => {
await new Promise(resolve => setTimeout(resolve, 10));
results.push('handler-1');
});
bus.subscribe(async (msg) => {
await new Promise(resolve => setTimeout(resolve, 5));
results.push('handler-2');
});
const message: Message = {
type: 'INVENTORY_SYNC_FAILED',
payload: { error: 'test error' },
timestamp: Date.now()
};
await bus.publish(message);
// give async handlers time to complete
await new Promise(resolve => setTimeout(resolve, 20));
expect(results).toHaveLength(2);
expect(results).toContain('handler-1');
expect(results).toContain('handler-2');
});
test('continues publishing if one subscriber throws', async () => {
const bus = createMessageBus('mockLocal');
const received: Message[] = [];
// subscriber that throws
bus.subscribe(() => {
throw new Error('Handler error');
});
// subscriber that works
bus.subscribe((msg) => {
received.push(msg);
});
const message: Message = {
type: 'CART_ITEM_REMOVED',
payload: { cartId: 'cart-1', productId: 'prod-1' },
timestamp: Date.now()
};
await bus.publish(message);
// second subscriber should still receive the message
expect(received).toHaveLength(1);
});
test('shutdown clears all subscribers', async () => {
const bus = createMessageBus('mockLocal');
const received: Message[] = [];
bus.subscribe((msg) => received.push(msg));
bus.subscribe((msg) => received.push(msg));
await bus.shutdown();
const message: Message = {
type: 'CART_ITEM_EXPIRED',
payload: { cartId: 'cart-1', productId: 'prod-1' },
timestamp: Date.now()
};
await bus.publish(message);
expect(received).toHaveLength(0);
});
test('adds timestamp if not provided', async () => {
const bus = createMessageBus('mockLocal');
let receivedMessage: Message | null = null;
bus.subscribe((msg) => {
receivedMessage = msg;
});
const messageWithoutTimestamp = {
type: 'WAREHOUSE_INVENTORY_PULLED' as const,
payload: { productCount: 5 },
timestamp: 0
};
await bus.publish(messageWithoutTimestamp);
expect(receivedMessage).not.toBeNull();
expect(receivedMessage!.timestamp).toBeGreaterThan(0);
});
});
describe('rabbitmq strategy', () => {
test('throws error for unimplemented methods', async () => {
const bus = createMessageBus('rabbitmq', {
exchangeName: 'test-exchange',
rabbitmqUrl: 'amqp://localhost'
});
const message: Message = {
type: 'SYNC_INVENTORY',
payload: {},
timestamp: Date.now()
};
await expect(bus.publish(message)).rejects.toThrow('RabbitMQ not implemented yet');
expect(() => bus.subscribe(() => {})).toThrow('RabbitMQ not implemented yet');
});
});
});
```
--------------------------------------------------------------------------------
/solution/services/commerce/test.ts:
--------------------------------------------------------------------------------
```typescript
import {createStateStore} from '../../util/state';
import {createMessageBus} from '../../util/message';
import {createWarehouseService} from '../warehouse';
import {createCommerceService} from './';
import type {WarehouseServiceConfig} from '../warehouse/types';
import type {CommerceServiceConfig, CommerceState} from './types';
const createMockCommerceService = (stakePercentage = 100) => {
const initialInventoryState: CommerceState = {
products: {},
carts: {},
orders: {},
lastSync: 0
};
const warehouseServiceConfig: WarehouseServiceConfig = {
stakePercentage
};
const SHORT_EXPIRATION_FOR_FAST_TESTING_MS = 100;
const inventoryServiceConfig: CommerceServiceConfig = {
syncIntervalMs: 60000,
cartExpirationMs: SHORT_EXPIRATION_FOR_FAST_TESTING_MS,
inventoryStakePercentage: stakePercentage
};
const warehouseStrategy = 'mockLocal';
const warehouseService = createWarehouseService(
warehouseStrategy,
warehouseServiceConfig
);
const uniqueTestKeyToAvoidStatePollution = `test-${Date.now()}-${Math.random()}`;
const inventoryStateStore = createStateStore(
'mockLocal',
uniqueTestKeyToAvoidStatePollution,
initialInventoryState
);
const messageBus = createMessageBus('mockLocal');
return createCommerceService(
inventoryServiceConfig,
warehouseService,
inventoryStateStore,
messageBus
);
};
describe('CommerceService', () => {
let service: ReturnType<typeof createMockCommerceService> | null = null;
afterEach(async () => {
if (service) {
await service.shutdown();
service = null;
}
await new Promise(resolve => setTimeout(resolve, 150));
});
test('blocks second order when inventory insufficient', async () => {
service = createMockCommerceService();
await new Promise(resolve => setTimeout(resolve, 100)); // Wait for sync
const productId = 'og-kush-1';
const initial = await service.getAvailability(productId);
expect(initial).toBe(100);
// user 1 adds 60
const user1 = await service.addToCart('cart-1', productId, 60);
expect(user1).toBe(true);
expect(await service.getAvailability(productId)).toBe(40);
// user 2 tries 60 (should fail)
const user2 = await service.addToCart('cart-2', productId, 60);
expect(user2).toBe(false);
expect(await service.getAvailability(productId)).toBe(40);
// explicit cleanup before test ends
await service.shutdown();
service = null;
});
test('prevents overselling with sequential additions', async () => {
service = createMockCommerceService();
await new Promise(resolve => setTimeout(resolve, 100));
const productId = 'blue-dream-1'; // 150 units
// sequential additions to test availability checking
const add1 = await service.addToCart('cart-1', productId, 80);
expect(add1).toBe(true);
expect(await service.getAvailability(productId)).toBe(70);
const add2 = await service.addToCart('cart-2', productId, 80);
expect(add2).toBe(false); // should fail, only 70 left
expect(await service.getAvailability(productId)).toBe(70);
});
test('releases inventory on cart expiration', async () => {
service = createMockCommerceService();
await new Promise(resolve => setTimeout(resolve, 100));
const productId = 'sour-diesel-1';
await service.addToCart('cart-1', productId, 40);
expect(await service.getAvailability(productId)).toBe(40);
await new Promise(resolve => setTimeout(resolve, 200));
expect(await service.getAvailability(productId)).toBe(80);
});
test('moves cart to order on purchase', async () => {
service = createMockCommerceService();
await new Promise(resolve => setTimeout(resolve, 100));
const productId = 'gelato-1';
await service.addToCart('cart-1', productId, 30);
expect(await service.getAvailability(productId)).toBe(90);
const purchased = await service.purchaseCart('cart-1', 'order-001');
expect(purchased).toBe(true);
const state = await service.getState();
expect(state?.carts['cart-1']).toBeUndefined();
expect(state?.orders['order-001']).toBeDefined();
expect(state?.orders['order-001'].items.length).toBe(1);
});
test('respects inventory stake percentage', async () => {
service = createMockCommerceService(25); // 25% stake
await new Promise(resolve => setTimeout(resolve, 100));
const productId = 'og-kush-1';
const available = await service.getAvailability(productId);
// OG Kush has 100 units, 25% = 25 units
expect(available).toBe(25);
});
test('both users can purchase after reserving in cart', async () => {
service = createMockCommerceService();
await new Promise(resolve => setTimeout(resolve, 100));
const productId = 'gsc-1'; // 90 units
// two users add items that fit
await service.addToCart('cart-1', productId, 40);
await service.addToCart('cart-2', productId, 40);
expect(await service.getAvailability(productId)).toBe(10); // 90 - 40 - 40
// both purchases succeed - items already reserved
const order1 = await service.purchaseCart('cart-1', 'order-001');
expect(order1).toBe(true);
const order2 = await service.purchaseCart('cart-2', 'order-002');
expect(order2).toBe(true);
const state = await service.getState();
expect(state?.orders['order-001']).toBeDefined();
expect(state?.orders['order-002']).toBeDefined();
});
});
```
--------------------------------------------------------------------------------
/solution/demo.ts:
--------------------------------------------------------------------------------
```typescript
import { createCommerceService } from './services/commerce';
import { createStateStore } from './util/state';
import { createMessageBus } from './util/message';
import type {CommerceServiceConfig, CommerceState} from './services/commerce/types';
import {createWarehouseService} from './services/warehouse';
import {WarehouseServiceConfig} from './services/warehouse/types';
const runDemo = async () => {
console.log('[DEMO] === Nabis Inventory POC ===\n');
console.log('[DEMO] Scenario: Two users competing for limited inventory');
console.log('[DEMO] Goal: Prove second order is blocked when inventory insufficient\n');
const STUB_INVENTORY_STAKE_PERCENTAGE = 25;
const FIVE_MINUTES_MS = 5 * 60 * 1000;
const TEN_MINUTES_MS = 10 * 60 * 1000;
const inventoryServiceConfig: CommerceServiceConfig = {
syncIntervalMs: FIVE_MINUTES_MS,
cartExpirationMs: TEN_MINUTES_MS,
inventoryStakePercentage: STUB_INVENTORY_STAKE_PERCENTAGE
};
const warehouseServiceConfig: WarehouseServiceConfig = {
stakePercentage: 25 // assume 3 direct competitors. (100% weed inventory / 4 wholesalers = 25% fair claim)
};
const initialState: CommerceState = {
products: {},
carts: {},
orders: {},
lastSync: 0
};
const stateStore = createStateStore(
'mockLocal',
'inventory-demo',
initialState
);
const messageBus = createMessageBus('mockLocal');
messageBus.subscribe(async (message) => {
if (message.type === 'ORDER_PLACED') {
console.log(`[DEMO] 📨 Event: ORDER_PLACED - Submitting to WMS...`);
// in production, this would trigger warehouse fulfillment passively
}
});
const warehouseStrategy = 'mockLocal';
const warehouseService = createWarehouseService(
warehouseStrategy,
warehouseServiceConfig
);
const commerceService = createCommerceService(
inventoryServiceConfig,
warehouseService,
stateStore,
messageBus
);
// wait for initial sync so we have an inventory to demo
await new Promise(resolve => setTimeout(resolve, 100));
console.log('[DEMO] === Initial State ===\n');
const productId = 'og-kush-1';
const initial = await commerceService.getAvailability(productId);
console.log(`[DEMO] 📦 Total WMS Inventory: 100 units`);
console.log(`[DEMO] 📦 Our Stake (25%): ${initial} units`);
console.log(`[DEMO] 📦 Available for sale: ${initial} units\n`);
console.log('[DEMO] === Competing Orders Scenario ===\n');
// User 1 tries to grab 15 units
console.log('[DEMO] 👤 User 1: Attempting to add 15 units to cart...');
const user1Result = await commerceService.addToCart('cart-1', productId, 15);
const afterUser1 = await commerceService.getAvailability(productId);
if (user1Result) {
console.log(`[DEMO] ✅ SUCCESS: Added 15 units to cart`);
console.log(`[DEMO] 📊 Remaining availability: ${afterUser1} units\n`);
} else {
console.log(`[DEMO] ❌ FAILED: Insufficient inventory\n`);
}
// User 2 tries to grab 15 units (should fail - only 10 left)
console.log('[DEMO] 👤 User 2: Attempting to add 15 units to cart...');
const user2Result = await commerceService.addToCart('cart-2', productId, 15);
const afterUser2Attempt = await commerceService.getAvailability(productId);
if (user2Result) {
console.log(`[DEMO] ✅ SUCCESS: Added 15 units to cart`);
console.log(`[DEMO] 📊 Remaining availability: ${afterUser2Attempt} units\n`);
} else {
console.log(`[DEMO] ❌ BLOCKED: Insufficient inventory (only ${afterUser1} units available)`);
console.log(`[DEMO] ✅ OVERSELLING PREVENTED!\n`);
}
// User 2 adjusts to available amount
console.log('[DEMO] 👤 User 2: Attempting to add 10 units instead...');
const user2Adjusted = await commerceService.addToCart('cart-2', productId, 10);
const afterUser2Success = await commerceService.getAvailability(productId);
if (user2Adjusted) {
console.log(`[DEMO] ✅ SUCCESS: Added 10 units to cart`);
console.log(`[DEMO] 📊 Remaining availability: ${afterUser2Success} units\n`);
} else {
console.log(`[DEMO] ❌ FAILED: Unexpected error\n`);
}
console.log('[DEMO] === Purchase Flow ===\n');
// User 1 completes purchase
console.log('[DEMO] 💳 User 1: Completing purchase...');
const order1 = await commerceService.purchaseCart('cart-1', 'order-001');
if (order1) {
console.log(`[DEMO] ✅ Order order-001 placed successfully`);
console.log(`[DEMO] 📦 4 units moved to pending fulfillment`);
// Submit to warehouse
try {
await warehouseService.submitOrder('order-001');
console.log(`[DEMO] ✅ Order submitted to WMS for fulfillment\n`);
} catch (error) {
console.log(`[DEMO] ⚠️ WMS submission pending (async)\n`);
}
}
// User 2 completes purchase
console.log('[DEMO] 💳 User 2: Completing purchase...');
const order2 = await commerceService.purchaseCart('cart-2', 'order-002');
if (order2) {
console.log(`[DEMO] ✅ Order order-002 placed successfully`);
console.log(`[DEMO] 📦 10 units moved to pending fulfillment`);
// Submit to warehouse
try {
await warehouseService.submitOrder('order-002');
console.log(`[DEMO] ✅ Order submitted to WMS for fulfillment\n`);
} catch (error) {
console.log(`[DEMO] ⚠️ WMS submission pending (async)\n`);
}
}
// Show final state
const state = await commerceService.getState();
const finalAvailability = await commerceService.getAvailability(productId);
console.log('[DEMO] === Final State ===\n');
console.log(`[DEMO] 📊 System State:`);
console.log(`[DEMO] - Products tracked: ${Object.keys(state!.products).length}`);
console.log(`[DEMO] - Active carts: ${Object.keys(state!.carts).length}`);
console.log(`[DEMO] - Orders pending fulfillment: ${Object.keys(state!.orders).length}`);
console.log(`[DEMO] - Remaining inventory: ${finalAvailability} units`);
if (state!.orders['order-001']) {
const order1Items = state!.orders['order-001'].items.reduce((sum, item) => sum + item.quantity, 0);
console.log(`[DEMO] - order-001: ${order1Items} units`);
}
if (state!.orders['order-002']) {
const order2Items = state!.orders['order-002'].items.reduce((sum, item) => sum + item.quantity, 0);
console.log(`[DEMO] - order-002: ${order2Items} units`);
}
console.log('\n[DEMO] === Key Achievements ===\n');
console.log('[DEMO] ✅ Real-time inventory tracking prevents overselling');
console.log('[DEMO] ✅ Second order correctly blocked when insufficient inventory');
console.log('[DEMO] ✅ Cart items atomically reserved during add-to-cart');
console.log('[DEMO] ✅ Orders submitted to WMS for fulfillment');
console.log('[DEMO] ✅ Stake percentage (25%) respected for dropship model');
// Cleanup
console.log('\n[DEMO] 🛑 Shutting down...\n');
await commerceService.shutdown();
};
runDemo().catch(console.error);
```
--------------------------------------------------------------------------------
/solution/services/commerce/index.ts:
--------------------------------------------------------------------------------
```typescript
import type {StateStore} from '../../util/state/types';
import type {MessageBus} from '../../util/message/types';
import type {WarehouseService} from '../warehouse/types';
import type {
CommerceService,
CommerceState,
CommerceMessageHandler,
CommerceServiceConfig,
} from './types';
import {
syncInventory,
addToCart,
removeFromCart,
purchaseCart,
expireCartItems,
getAvailableProductQuantity
} from './methods';
const THIRTY_SECONDS_MS = 30000;
export const createCommerceService = (
config: CommerceServiceConfig,
warehouseService: WarehouseService,
stateStore: StateStore<CommerceState>,
messageBus: MessageBus
): CommerceService => {
let syncInterval: NodeJS.Timeout | null = null;
let expirationInterval: NodeJS.Timeout | null = null;
let isShuttingDown = false;
const cartExpirationTimers = new Map<string, NodeJS.Timeout>();
const handleMessage: CommerceMessageHandler = (msg, state) => {
switch (msg.type) {
case 'CART_ITEM_EXPIRED':
return removeFromCart(state, msg.payload.cartId, msg.payload.productId);
default:
return state;
}
};
messageBus.subscribe(async (message) => {
if (isShuttingDown) return;
const currentState = await stateStore.get();
if (currentState) {
const newState = handleMessage(message, currentState);
if (newState !== currentState) {
await stateStore.set(newState);
}
}
});
const updateState = async (
updater: (state: CommerceState) => CommerceState
): Promise<CommerceState | null> => {
if (isShuttingDown) return null;
const current = await stateStore.get();
if (!current) throw new Error('State not initialized');
const updated = updater(current);
await stateStore.set(updated);
return updated;
};
const maybePullWarehouseInventory = async () => {
if (isShuttingDown) return;
try {
console.log('[DEMO][CommerceService][maybePullWarehouseInventory] 🔄 Syncing inventory from WMS...');
const grossInventory = await warehouseService.getInventory();
const { items: netInventory } = warehouseService.adjustInventoryForLikelyAvailability(grossInventory);
await updateState(state => syncInventory(state, netInventory));
await messageBus.publish({
type: 'WAREHOUSE_INVENTORY_PULLED',
payload: {productCount: netInventory.length},
timestamp: Date.now(),
});
console.log(`[DEMO][CommerceService][maybePullWarehouseInventory] ✅ Synced ${netInventory.length} products`);
} catch (error) {
console.error('❌ Sync failed:', error);
await messageBus.publish({
type: 'INVENTORY_SYNC_FAILED',
payload: {error: String(error)},
timestamp: Date.now()
});
}
};
const startSync = () => {
syncInterval = setInterval(() => {
if (!isShuttingDown) {
maybePullWarehouseInventory();
}
}, config.syncIntervalMs);
};
const startExpirationCheck = () => {
const check = async () => {
if (!isShuttingDown) {
await updateState(state => expireCartItems(state));
}
};
check();
expirationInterval = setInterval(check, THIRTY_SECONDS_MS);
};
Promise.all([maybePullWarehouseInventory()]).then(() => {
if (!isShuttingDown) {
startSync();
startExpirationCheck();
}
});
const scheduleExpiration = (cartId: string, productId: string) => {
const timerKey = `${cartId}:${productId}`;
const timer = setTimeout(async () => {
if (isShuttingDown) return;
console.log(`[DEMO][CommerceService][scheduleExpiration]⏰ Expiring ${productId} from cart ${cartId}`);
await updateState(state => removeFromCart(state, cartId, productId));
await messageBus.publish({
type: 'CART_ITEM_EXPIRED',
payload: {cartId, productId},
timestamp: Date.now()
});
cartExpirationTimers.delete(timerKey);
}, config.cartExpirationMs);
cartExpirationTimers.set(timerKey, timer);
};
const clearCartExpirationTimers = (cartId: string) => {
for (const [key, timer] of cartExpirationTimers.entries()) {
if (key.startsWith(`${cartId}:`)) {
clearTimeout(timer);
cartExpirationTimers.delete(key);
}
}
};
return {
getAvailability: async (productId: string) => {
const state = await stateStore.get();
if (!state) return 0;
return getAvailableProductQuantity(state, productId);
},
addToCart: async (cartId: string, productId: string, quantity: number) => {
try {
const current = await stateStore.get();
if (!current) return false;
const newState = addToCart(current, cartId, productId, quantity, config.cartExpirationMs);
if (newState) {
await stateStore.set(newState);
scheduleExpiration(cartId, productId);
console.log(`[DEMO][CommerceService][addToCart] ✅ Added ${quantity}x ${productId} to cart ${cartId}`);
await messageBus.publish({
type: 'CART_ITEM_ADDED',
payload: {cartId, productId, quantity},
timestamp: Date.now()
});
return true;
}
return false;
} catch (error) {
console.error('Failed to add to cart:', error);
return false;
}
},
removeFromCart: async (cartId: string, productId: string) => {
const timerKey = `${cartId}:${productId}`;
const timer = cartExpirationTimers.get(timerKey);
if (timer) {
clearTimeout(timer);
cartExpirationTimers.delete(timerKey);
}
await updateState(state => removeFromCart(state, cartId, productId));
console.log(`[DEMO][CommerceService][removeFromCart] 🗑️ Removed ${productId} from cart ${cartId}`);
await messageBus.publish({
type: 'CART_ITEM_REMOVED',
payload: {cartId, productId},
timestamp: Date.now(),
});
},
purchaseCart: async (cartId: string, orderId: string) => {
try {
const current = await stateStore.get();
if (!current) return false;
const newState = purchaseCart(current, cartId, orderId);
if (newState) {
await stateStore.set(newState);
clearCartExpirationTimers(cartId);
console.log(`[DEMO][CommerceService][purchaseCart] ✅ Order ${orderId} placed successfully`);
await messageBus.publish({
type: 'ORDER_PLACED',
payload: {cartId, orderId},
timestamp: Date.now(),
});
return true;
}
return false;
} catch (error) {
console.error('Failed to purchase cart:', error);
return false;
}
},
getState: async () => {
const state = await stateStore.get();
return state ? {...state} : null;
},
shutdown: async () => {
isShuttingDown = true; // Set flag first
if (syncInterval) {
clearInterval(syncInterval);
syncInterval = null;
}
if (expirationInterval) {
clearInterval(expirationInterval);
expirationInterval = null;
}
for (const timer of cartExpirationTimers.values()) {
clearTimeout(timer);
}
cartExpirationTimers.clear();
await new Promise(resolve => setImmediate(resolve));
await stateStore.shutdown();
await messageBus.shutdown();
console.log('[DEMO][CommerceService][shutDown] 🛑 Commerce service stopped');
}
};
};
```
--------------------------------------------------------------------------------
/solution/services/commerce/methods/test.ts:
--------------------------------------------------------------------------------
```typescript
import {
addToCart,
expireCartItems,
getAvailableProductQuantity,
purchaseCart,
removeFromCart,
syncInventory
} from './';
import type { CommerceState, Product } from '../types';
describe('Commerce Methods', () => {
const mockProducts: Product[] = [
{ id: 'prod-1', sku: 'SKU-001', name: 'Product 1', category: 'Cat1', batchId: 'B001', quantity: 100 },
{ id: 'prod-2', sku: 'SKU-002', name: 'Product 2', category: 'Cat2', batchId: 'B002', quantity: 50 }
];
const createEmptyState = (): CommerceState => ({
products: {},
carts: {},
orders: {},
lastSync: 0
});
describe('syncInventory', () => {
test('updates products and lastSync timestamp', () => {
const state = createEmptyState();
const newState = syncInventory(state, mockProducts);
expect(Object.keys(newState.products)).toHaveLength(2);
expect(newState.products['prod-1']).toEqual(mockProducts[0]);
expect(newState.products['prod-2']).toEqual(mockProducts[1]);
expect(newState.lastSync).toBeGreaterThan(0);
});
test('clears orders after sync', () => {
const state: CommerceState = {
products: {},
carts: {},
orders: {
'order-1': {
id: 'order-1',
cartId: 'cart-1',
items: [],
placedAt: Date.now()
}
},
lastSync: 0
};
const newState = syncInventory(state, mockProducts);
expect(Object.keys(newState.orders)).toHaveLength(0);
});
test('preserves carts during sync', () => {
const state: CommerceState = {
products: {},
carts: {
'cart-1': {
id: 'cart-1',
items: [
{ productId: 'prod-1', quantity: 5, addedAt: Date.now(), expiresAt: Date.now() + 10000 }
]
}
},
orders: {},
lastSync: 0
};
const newState = syncInventory(state, mockProducts);
expect(newState.carts['cart-1']).toBeDefined();
expect(newState.carts['cart-1'].items).toHaveLength(1);
});
test('handles empty product list', () => {
const state = createEmptyState();
const newState = syncInventory(state, []);
expect(Object.keys(newState.products)).toHaveLength(0);
expect(newState.lastSync).toBeGreaterThan(0);
});
});
describe('getAvailableProductQuantity', () => {
test('returns full quantity when no reservations', () => {
const state: CommerceState = {
products: { 'prod-1': mockProducts[0] },
carts: {},
orders: {},
lastSync: Date.now()
};
const available = getAvailableProductQuantity(state, 'prod-1');
expect(available).toBe(100);
});
test('reduces quantity by cart items', () => {
const state: CommerceState = {
products: { 'prod-1': mockProducts[0] },
carts: {
'cart-1': {
id: 'cart-1',
items: [
{ productId: 'prod-1', quantity: 10, addedAt: Date.now(), expiresAt: Date.now() + 10000 }
]
}
},
orders: {},
lastSync: Date.now()
};
const available = getAvailableProductQuantity(state, 'prod-1');
expect(available).toBe(90);
});
test('reduces quantity by order items', () => {
const state: CommerceState = {
products: { 'prod-1': mockProducts[0] },
carts: {},
orders: {
'order-1': {
id: 'order-1',
cartId: 'cart-1',
items: [
{ productId: 'prod-1', quantity: 15, addedAt: Date.now(), expiresAt: Date.now() + 10000 }
],
placedAt: Date.now()
}
},
lastSync: Date.now()
};
const available = getAvailableProductQuantity(state, 'prod-1');
expect(available).toBe(85);
});
test('reduces quantity by both carts and orders', () => {
const state: CommerceState = {
products: { 'prod-1': mockProducts[0] },
carts: {
'cart-1': {
id: 'cart-1',
items: [
{ productId: 'prod-1', quantity: 20, addedAt: Date.now(), expiresAt: Date.now() + 10000 }
]
}
},
orders: {
'order-1': {
id: 'order-1',
cartId: 'cart-2',
items: [
{ productId: 'prod-1', quantity: 30, addedAt: Date.now(), expiresAt: Date.now() + 10000 }
],
placedAt: Date.now()
}
},
lastSync: Date.now()
};
const available = getAvailableProductQuantity(state, 'prod-1');
expect(available).toBe(50); // 100 - 20 - 30
});
test('returns 0 for non-existent product', () => {
const state = createEmptyState();
const available = getAvailableProductQuantity(state, 'non-existent');
expect(available).toBe(0);
});
test('returns 0 when quantity would be negative', () => {
const state: CommerceState = {
products: { 'prod-1': { ...mockProducts[0], quantity: 10 } },
carts: {
'cart-1': {
id: 'cart-1',
items: [
{ productId: 'prod-1', quantity: 15, addedAt: Date.now(), expiresAt: Date.now() + 10000 }
]
}
},
orders: {},
lastSync: Date.now()
};
const available = getAvailableProductQuantity(state, 'prod-1');
expect(available).toBe(0);
});
test('handles multiple carts with same product', () => {
const state: CommerceState = {
products: { 'prod-1': mockProducts[0] },
carts: {
'cart-1': {
id: 'cart-1',
items: [
{ productId: 'prod-1', quantity: 10, addedAt: Date.now(), expiresAt: Date.now() + 10000 }
]
},
'cart-2': {
id: 'cart-2',
items: [
{ productId: 'prod-1', quantity: 15, addedAt: Date.now(), expiresAt: Date.now() + 10000 }
]
}
},
orders: {},
lastSync: Date.now()
};
const available = getAvailableProductQuantity(state, 'prod-1');
expect(available).toBe(75); // 100 - 10 - 15
});
});
describe('addToCart', () => {
test('adds item to new cart', () => {
const state: CommerceState = {
products: { 'prod-1': mockProducts[0] },
carts: {},
orders: {},
lastSync: Date.now()
};
const newState = addToCart(state, 'cart-1', 'prod-1', 10, 600000);
expect(newState).not.toBeNull();
expect(newState!.carts['cart-1']).toBeDefined();
expect(newState!.carts['cart-1'].items).toHaveLength(1);
expect(newState!.carts['cart-1'].items[0].productId).toBe('prod-1');
expect(newState!.carts['cart-1'].items[0].quantity).toBe(10);
});
test('rejects when insufficient inventory', () => {
const state: CommerceState = {
products: { 'prod-1': mockProducts[0] },
carts: {},
orders: {},
lastSync: Date.now()
};
const newState = addToCart(state, 'cart-1', 'prod-1', 150, 600000);
expect(newState).toBeNull();
});
test('rejects duplicate product in same cart', () => {
const state: CommerceState = {
products: { 'prod-1': mockProducts[0] },
carts: {
'cart-1': {
id: 'cart-1',
items: [
{ productId: 'prod-1', quantity: 10, addedAt: Date.now(), expiresAt: Date.now() + 10000 }
]
}
},
orders: {},
lastSync: Date.now()
};
const newState = addToCart(state, 'cart-1', 'prod-1', 5, 600000);
expect(newState).toBeNull();
});
test('sets expiration timestamp correctly', () => {
const state: CommerceState = {
products: { 'prod-1': mockProducts[0] },
carts: {},
orders: {},
lastSync: Date.now()
};
const expirationMs = 300000; // 5 minutes
const beforeAdd = Date.now();
const newState = addToCart(state, 'cart-1', 'prod-1', 10, expirationMs);
const afterAdd = Date.now();
expect(newState).not.toBeNull();
const item = newState!.carts['cart-1'].items[0];
expect(item.addedAt).toBeGreaterThanOrEqual(beforeAdd);
expect(item.addedAt).toBeLessThanOrEqual(afterAdd);
expect(item.expiresAt).toBe(item.addedAt + expirationMs);
});
});
describe('removeFromCart', () => {
test('removes item from cart', () => {
const state: CommerceState = {
products: {},
carts: {
'cart-1': {
id: 'cart-1',
items: [
{ productId: 'prod-1', quantity: 10, addedAt: Date.now(), expiresAt: Date.now() + 10000 }
]
}
},
orders: {},
lastSync: Date.now()
};
const newState = removeFromCart(state, 'cart-1', 'prod-1');
expect(newState.carts['cart-1']).toBeUndefined();
});
test('removes cart when last item removed', () => {
const state: CommerceState = {
products: {},
carts: {
'cart-1': {
id: 'cart-1',
items: [
{ productId: 'prod-1', quantity: 10, addedAt: Date.now(), expiresAt: Date.now() + 10000 }
]
}
},
orders: {},
lastSync: Date.now()
};
const newState = removeFromCart(state, 'cart-1', 'prod-1');
expect(Object.keys(newState.carts)).toHaveLength(0);
});
test('preserves other items in cart', () => {
const state: CommerceState = {
products: {},
carts: {
'cart-1': {
id: 'cart-1',
items: [
{ productId: 'prod-1', quantity: 10, addedAt: Date.now(), expiresAt: Date.now() + 10000 },
{ productId: 'prod-2', quantity: 5, addedAt: Date.now(), expiresAt: Date.now() + 10000 }
]
}
},
orders: {},
lastSync: Date.now()
};
const newState = removeFromCart(state, 'cart-1', 'prod-1');
expect(newState.carts['cart-1']).toBeDefined();
expect(newState.carts['cart-1'].items).toHaveLength(1);
expect(newState.carts['cart-1'].items[0].productId).toBe('prod-2');
});
test('returns unchanged state for non-existent cart', () => {
const state = createEmptyState();
const newState = removeFromCart(state, 'non-existent', 'prod-1');
expect(newState).toEqual(state);
});
});
describe('expireCartItems', () => {
test('removes expired items', () => {
const now = Date.now();
const state: CommerceState = {
products: {},
carts: {
'cart-1': {
id: 'cart-1',
items: [
{ productId: 'prod-1', quantity: 10, addedAt: now - 20000, expiresAt: now - 10000 }
]
}
},
orders: {},
lastSync: Date.now()
};
const newState = expireCartItems(state);
expect(Object.keys(newState.carts)).toHaveLength(0);
});
test('preserves non-expired items', () => {
const now = Date.now();
const state: CommerceState = {
products: {},
carts: {
'cart-1': {
id: 'cart-1',
items: [
{ productId: 'prod-1', quantity: 10, addedAt: now, expiresAt: now + 10000 }
]
}
},
orders: {},
lastSync: Date.now()
};
const newState = expireCartItems(state);
expect(newState.carts['cart-1']).toBeDefined();
expect(newState.carts['cart-1'].items).toHaveLength(1);
});
test('handles mixed expired and non-expired items', () => {
const now = Date.now();
const state: CommerceState = {
products: {},
carts: {
'cart-1': {
id: 'cart-1',
items: [
{ productId: 'prod-1', quantity: 10, addedAt: now - 20000, expiresAt: now - 10000 },
{ productId: 'prod-2', quantity: 5, addedAt: now, expiresAt: now + 10000 }
]
}
},
orders: {},
lastSync: Date.now()
};
const newState = expireCartItems(state);
expect(newState.carts['cart-1'].items).toHaveLength(1);
expect(newState.carts['cart-1'].items[0].productId).toBe('prod-2');
});
});
describe('purchaseCart', () => {
test('converts cart to order', () => {
const state: CommerceState = {
products: {},
carts: {
'cart-1': {
id: 'cart-1',
items: [
{ productId: 'prod-1', quantity: 10, addedAt: Date.now(), expiresAt: Date.now() + 10000 }
]
}
},
orders: {},
lastSync: Date.now()
};
const newState = purchaseCart(state, 'cart-1', 'order-1');
expect(newState).not.toBeNull();
expect(newState!.carts['cart-1']).toBeUndefined();
expect(newState!.orders['order-1']).toBeDefined();
expect(newState!.orders['order-1'].cartId).toBe('cart-1');
expect(newState!.orders['order-1'].items).toHaveLength(1);
});
test('returns null for non-existent cart', () => {
const state = createEmptyState();
const newState = purchaseCart(state, 'non-existent', 'order-1');
expect(newState).toBeNull();
});
test('returns null for empty cart', () => {
const state: CommerceState = {
products: {},
carts: {
'cart-1': {
id: 'cart-1',
items: []
}
},
orders: {},
lastSync: Date.now()
};
const newState = purchaseCart(state, 'cart-1', 'order-1');
expect(newState).toBeNull();
});
test('sets placedAt timestamp', () => {
const state: CommerceState = {
products: {},
carts: {
'cart-1': {
id: 'cart-1',
items: [
{ productId: 'prod-1', quantity: 10, addedAt: Date.now(), expiresAt: Date.now() + 10000 }
]
}
},
orders: {},
lastSync: Date.now()
};
const beforePurchase = Date.now();
const newState = purchaseCart(state, 'cart-1', 'order-1');
const afterPurchase = Date.now();
expect(newState).not.toBeNull();
expect(newState!.orders['order-1'].placedAt).toBeGreaterThanOrEqual(beforePurchase);
expect(newState!.orders['order-1'].placedAt).toBeLessThanOrEqual(afterPurchase);
});
});
});
```