Tests refactor

This commit is contained in:
Aerilyn Weber 2026-05-19 11:06:03 +09:00
parent 245520fb50
commit 99134d8556
165 changed files with 911 additions and 531 deletions

View file

@ -129,11 +129,13 @@ export default fp(
mealPlanService: asClass(MealPlanService, { lifetime: Lifetime.SINGLETON }),
suggestionEngineService: asClass(SuggestionEngineService, { lifetime: Lifetime.SINGLETON }),
shoppingGapService: asClass(ShoppingGapService, { lifetime: Lifetime.SINGLETON }),
// Make sure other module deps are available for classes instantiated by SuggestionEngine/ShoppingGap
recipesRepository: asClass(RecipesRepository, { lifetime: Lifetime.SINGLETON }),
pantryRepository: asClass(PantryRepository, { lifetime: Lifetime.SINGLETON }),
nutritionTargetRepository: asClass(NutritionTargetRepository, { lifetime: Lifetime.SINGLETON }),
nutritionTargetRepository: asClass(NutritionTargetRepository, {
lifetime: Lifetime.SINGLETON,
}),
productsRepository: asClass(ProductsRepository, { lifetime: Lifetime.SINGLETON }),
});
@ -181,7 +183,7 @@ export default fp(
variety: z.number(),
}),
reasoning: z.array(z.string()),
})
}),
),
},
},
@ -190,7 +192,7 @@ export default fp(
const suggestions = await engine.getSuggestions(
request.params.householdId,
request.user.keycloakId,
{ limit: request.query.limit }
{ limit: request.query.limit },
);
return reply.send(suggestions);
},
@ -215,7 +217,7 @@ export default fp(
const service = fastify.diContainer.resolve('mealPlanService');
const plan = await service.getByWeek(
request.params.householdId,
request.params.weekStartDate
request.params.weekStartDate,
);
if (!plan) {
return reply.status(200).send({ message: 'No meal plan scheduled for this week' });
@ -253,7 +255,7 @@ export default fp(
const plan = await service.create(
request.params.householdId,
request.user.keycloakId,
request.body
request.body,
);
return reply.status(201).send(toPlanResponse(plan as AnyPlanDoc));
},
@ -273,7 +275,7 @@ export default fp(
const plan = await service.update(
request.params.id,
request.params.householdId,
request.body
request.body,
);
return reply.send(toPlanResponse(plan as AnyPlanDoc));
},
@ -295,7 +297,7 @@ export default fp(
const plan = await service.updateStatus(
request.params.id,
request.params.householdId,
request.body.status
request.body.status,
);
return reply.send(toPlanResponse(plan as AnyPlanDoc));
},
@ -334,17 +336,14 @@ export default fp(
pantryQuantity: z.number(),
missingQuantity: z.number(),
unit: z.string(),
})
}),
),
}),
},
},
handler: async (request, reply) => {
const service = fastify.diContainer.resolve('shoppingGapService');
const result = await service.calculateGap(
request.params.householdId,
request.params.id
);
const result = await service.calculateGap(request.params.householdId, request.params.id);
return reply.send(result);
},
});
@ -352,5 +351,5 @@ export default fp(
{
name: 'meal-plans-routes',
dependencies: ['auth-plugin'],
}
},
);

View file

@ -8,7 +8,7 @@ import type {
NutritionInfo,
MealPlanDaySchema,
} from '@meshitrack/shared';
import { z } from 'zod/v4';
import { type z } from 'zod/v4';
type MealPlanDay = z.infer<typeof MealPlanDaySchema>;
@ -39,19 +39,12 @@ export class MealPlanService {
return this.mealPlanRepository.findByWeek(householdId, weekStartDate);
}
public async create(
householdId: string,
createdBy: string,
input: CreateMealPlanInput
) {
public async create(householdId: string, createdBy: string, input: CreateMealPlanInput) {
// Prevent overlapping meal plans for same household/week
const existing = await this.mealPlanRepository.findByWeek(
householdId,
input.weekStartDate
);
const existing = await this.mealPlanRepository.findByWeek(householdId, input.weekStartDate);
if (existing) {
throw new BadRequestError(
`A meal plan already exists for household ${householdId} starting ${input.weekStartDate}`
`A meal plan already exists for household ${householdId} starting ${input.weekStartDate}`,
);
}
@ -66,11 +59,7 @@ export class MealPlanService {
});
}
public async update(
id: string,
householdId: string,
input: UpdateMealPlanInput
) {
public async update(id: string, householdId: string, input: UpdateMealPlanInput) {
const existing = await this.getById(id, householdId);
const data: Record<string, unknown> = {};
@ -90,11 +79,7 @@ export class MealPlanService {
return updated;
}
public async updateStatus(
id: string,
householdId: string,
status: MealPlanStatus
) {
public async updateStatus(id: string, householdId: string, status: MealPlanStatus) {
await this.getById(id, householdId);
const updated = await this.mealPlanRepository.updateStatus(id, householdId, status);
if (!updated) {

View file

@ -44,10 +44,7 @@ export class ShoppingGapService {
this.productsRepository = productsRepository;
}
public async calculateGap(
householdId: string,
mealPlanId: string
): Promise<ShoppingGapResult> {
public async calculateGap(householdId: string, mealPlanId: string): Promise<ShoppingGapResult> {
const plan = await this.mealPlanRepository.findById(mealPlanId, householdId);
if (!plan) {
throw new NotFoundError('Meal plan not found');
@ -75,7 +72,7 @@ export class ShoppingGapService {
// 2. Fetch all referenced recipes in parallel
const recipeIds = Array.from(recipeIdSet);
const recipeDocs = await Promise.all(
recipeIds.map((id) => this.recipesRepository.findById(id, householdId))
recipeIds.map((id) => this.recipesRepository.findById(id, householdId)),
);
const recipesMap = new Map<string, any>();
for (const doc of recipeDocs) {
@ -116,7 +113,7 @@ export class ShoppingGapService {
const activePantry = await this.pantryRepository.findActiveByHousehold(householdId);
const requiredProductIds = Array.from(requiredMap.keys());
const productDocs = await this.productsRepository.findByIds(householdId, requiredProductIds);
const productsInfoMap = new Map<string, any>();
for (const p of productDocs) {
productsInfoMap.set(p._id.toString(), p);
@ -136,7 +133,7 @@ export class ShoppingGapService {
for (const [productId, req] of requiredMap.entries()) {
const pantryQty = pantryMap.get(productId) || 0;
if (pantryQty < req.qty) {
const productDoc = productsInfoMap.get(productId);
const productName = productDoc?.name || 'Unknown Ingredient';

View file

@ -44,7 +44,7 @@ export class SuggestionEngineService {
public async getSuggestions(
householdId: string,
userId: string,
options: { limit?: number } = {}
options: { limit?: number } = {},
): Promise<ScoredRecipe[]> {
const limit = options.limit ?? 5;
@ -132,7 +132,7 @@ export class SuggestionEngineService {
recipeId,
pantryInventory,
activeTarget as Record<string, unknown> | null,
lastEaten
lastEaten,
);
const totalScore =
@ -153,9 +153,7 @@ export class SuggestionEngineService {
}
// 5. Sort descending and limit
return scoredList
.sort((a, b) => b.totalScore - a.totalScore)
.slice(0, limit);
return scoredList.sort((a, b) => b.totalScore - a.totalScore).slice(0, limit);
}
private scoreRecipe(
@ -164,11 +162,11 @@ export class SuggestionEngineService {
recipeId: string,
inventory: Map<string, { qty: number; minDays: number; maxUrgencyWeight: number }>,
target: Record<string, unknown> | null,
lastEaten: Map<string, number>
lastEaten: Map<string, number>,
) {
// Filter non-optional ingredients
const requiredIngs = ingredients.filter((ing) => !ing.isOptional);
// -- COVERAGE & URGENCY --
let coverageScore = 1.0;
let urgencyScore = 0.0;
@ -262,7 +260,7 @@ export class SuggestionEngineService {
private generateReasoning(
scores: { coverage: number; urgency: number; nutrition: number; variety: number },
recipeName: string
recipeName: string,
): string[] {
const reasons: string[] = [];

View file

@ -2,16 +2,11 @@ import { NutritionTargetModel } from '../../schemas/nutrition-target.schema.js';
export class NutritionTargetRepository {
public async findByUser(userId: string, householdId: string) {
return NutritionTargetModel.findOne({ userId, householdId, isActive: true })
.lean()
.exec();
return NutritionTargetModel.findOne({ userId, householdId, isActive: true }).lean().exec();
}
public async findAllByUser(userId: string, householdId: string) {
return NutritionTargetModel.find({ userId, householdId })
.sort({ createdAt: -1 })
.lean()
.exec();
return NutritionTargetModel.find({ userId, householdId }).sort({ createdAt: -1 }).lean().exec();
}
public async create(data: Record<string, unknown>) {
@ -23,15 +18,20 @@ export class NutritionTargetRepository {
public async deactivateAllForUser(userId: string, householdId: string) {
return NutritionTargetModel.updateMany(
{ userId, householdId, isActive: true },
{ $set: { isActive: false } }
{ $set: { isActive: false } },
).exec();
}
public async update(id: string, userId: string, householdId: string, data: Record<string, unknown>) {
public async update(
id: string,
userId: string,
householdId: string,
data: Record<string, unknown>,
) {
return NutritionTargetModel.findOneAndUpdate(
{ _id: id, userId, householdId },
{ $set: data },
{ new: true, lean: true }
{ new: true, lean: true },
).exec();
}
}

View file

@ -2,10 +2,7 @@ import fp from 'fastify-plugin';
import { asClass, Lifetime } from 'awilix';
import type { ZodTypeProvider } from 'fastify-type-provider-zod';
import { z } from 'zod/v4';
import {
NutritionTargetSchema,
NutritionTargetResponseSchema,
} from '@meshitrack/shared';
import { NutritionTargetSchema, NutritionTargetResponseSchema } from '@meshitrack/shared';
import { NutritionTargetRepository } from './nutrition-target.repository.js';
import { NutritionTargetService } from './nutrition-target.service.js';
@ -33,7 +30,9 @@ function toIso(v: string | Date): string {
return typeof v === 'string' ? v : v.toISOString();
}
function toNutritionTargetResponse(doc: AnyTargetDoc): z.infer<typeof NutritionTargetResponseSchema> {
function toNutritionTargetResponse(
doc: AnyTargetDoc,
): z.infer<typeof NutritionTargetResponseSchema> {
return {
_id: toStr(doc._id),
userId: doc.userId,
@ -61,7 +60,9 @@ declare module '@fastify/awilix' {
export default fp(
async (fastify) => {
fastify.diContainer.register({
nutritionTargetRepository: asClass(NutritionTargetRepository, { lifetime: Lifetime.SINGLETON }),
nutritionTargetRepository: asClass(NutritionTargetRepository, {
lifetime: Lifetime.SINGLETON,
}),
nutritionTargetService: asClass(NutritionTargetService, { lifetime: Lifetime.SINGLETON }),
});
@ -85,11 +86,11 @@ export default fp(
const service = fastify.diContainer.resolve('nutritionTargetService');
const userId = request.user.keycloakId;
const target = await service.getActiveByUser(userId, request.params.householdId);
if (!target) {
return reply.status(200).send({ message: 'No active targets defined' });
}
return reply.send(toNutritionTargetResponse(target as AnyTargetDoc));
},
});
@ -124,11 +125,7 @@ export default fp(
handler: async (request, reply) => {
const service = fastify.diContainer.resolve('nutritionTargetService');
const userId = request.user.keycloakId;
const target = await service.setTarget(
userId,
request.params.householdId,
request.body
);
const target = await service.setTarget(userId, request.params.householdId, request.body);
return reply.status(201).send(toNutritionTargetResponse(target as AnyTargetDoc));
},
});
@ -155,5 +152,5 @@ export default fp(
{
name: 'nutrition-targets-routes',
dependencies: ['auth-plugin'],
}
},
);

View file

@ -22,11 +22,7 @@ export class NutritionTargetService {
return this.nutritionTargetRepository.findAllByUser(userId, householdId);
}
public async setTarget(
userId: string,
householdId: string,
input: SetNutritionTargetInput
) {
public async setTarget(userId: string, householdId: string, input: SetNutritionTargetInput) {
// Maintain invariant: only one target is active per user per household
if (input.isActive !== false) {
await this.nutritionTargetRepository.deactivateAllForUser(userId, householdId);
@ -52,20 +48,20 @@ export class NutritionTargetService {
switch (preset) {
case 'loss':
proteinPct = 0.40;
carbsPct = 0.30;
fatPct = 0.30;
proteinPct = 0.4;
carbsPct = 0.3;
fatPct = 0.3;
break;
case 'gain':
proteinPct = 0.25;
carbsPct = 0.50;
carbsPct = 0.5;
fatPct = 0.25;
break;
case 'maintenance':
default:
proteinPct = 0.30;
carbsPct = 0.40;
fatPct = 0.30;
proteinPct = 0.3;
carbsPct = 0.4;
fatPct = 0.3;
break;
}

View file

@ -27,13 +27,13 @@ export class PricesRepository {
public async createMany(data: CreatePriceRecordData[]) {
const records = await PriceRecordModel.insertMany(data);
return records.map(r => r.toObject());
return records.map((r) => r.toObject());
}
public async findByProduct(
householdId: string,
productId: string,
query: PriceHistoryQueryInput
query: PriceHistoryQueryInput,
) {
const filter: Record<string, unknown> = { householdId, productId };

View file

@ -95,7 +95,7 @@ export default fp(
const record = await service.recordPrice(
request.body,
request.params.householdId,
request.user.keycloakId
request.user.keycloakId,
);
return reply.status(201).send(toPriceRecordResponse(record));
},
@ -114,7 +114,7 @@ export default fp(
const records = await service.recordBulkPrices(
request.body,
request.params.householdId,
request.user.keycloakId
request.user.keycloakId,
);
return reply.status(201).send(records.map(toPriceRecordResponse));
},
@ -133,7 +133,7 @@ export default fp(
const result = await service.getPriceHistory(
request.params.productId,
request.params.householdId,
request.query
request.query,
);
return reply.send({
data: result.data.map(toPriceRecordResponse),
@ -153,7 +153,7 @@ export default fp(
const service = fastify.diContainer.resolve('pricesService');
const results = await service.compareStores(
request.params.productId,
request.params.householdId
request.params.householdId,
);
return reply.send({
data: results.map((r) => ({
@ -184,5 +184,5 @@ export default fp(
{
name: 'prices-routes',
dependencies: ['auth-plugin'],
}
},
);

View file

@ -28,11 +28,7 @@ export class PricesService {
/**
* Validates entity existence, computes pricePerUnit, and persists record
*/
public async recordPrice(
data: CreatePriceRecordInput,
householdId: string,
userId: string
) {
public async recordPrice(data: CreatePriceRecordInput, householdId: string, userId: string) {
const [product, store] = await Promise.all([
this.productsRepository.findById(data.productId, householdId),
this.storesRepository.findById(data.storeId, householdId),
@ -64,11 +60,7 @@ export class PricesService {
/**
* Ingests a list of purchased products in a single transaction
*/
public async recordBulkPrices(
data: BulkPriceRecordInput,
householdId: string,
userId: string
) {
public async recordBulkPrices(data: BulkPriceRecordInput, householdId: string, userId: string) {
const store = await this.storesRepository.findById(data.storeId, householdId);
if (!store) throw new NotFoundError(`Store not found: ${data.storeId}`);
@ -109,7 +101,7 @@ export class PricesService {
public async getPriceHistory(
productId: string,
householdId: string,
query: PriceHistoryQueryInput
query: PriceHistoryQueryInput,
) {
return this.pricesRepository.findByProduct(householdId, productId, query);
}
@ -129,13 +121,16 @@ export class PricesService {
public async estimatePrice(
productId: string,
householdId: string,
storeId?: string
storeId?: string,
): Promise<number | null> {
const latest = await this.pricesRepository.getLatestForProduct(householdId, productId, storeId);
if (!latest) {
// If a specific store was requested but has no history, fall back to the generic latest across all stores
if (storeId) {
const genericLatest = await this.pricesRepository.getLatestForProduct(householdId, productId);
const genericLatest = await this.pricesRepository.getLatestForProduct(
householdId,
productId,
);
return genericLatest ? genericLatest.price : null;
}
return null;

View file

@ -34,7 +34,7 @@ export class ShoppingListsRepository {
.sort({ createdAt: -1 })
.lean()
.exec();
return lists.map(l => this.sortItems(l));
return lists.map((l) => this.sortItems(l));
}
public async findById(id: string, householdId: string) {
@ -43,19 +43,24 @@ export class ShoppingListsRepository {
}
public async findActiveByHousehold(householdId: string) {
const lists = await ShoppingListModel.find({ householdId, status: { $in: ['active', 'shopping'] } })
const lists = await ShoppingListModel.find({
householdId,
status: { $in: ['active', 'shopping'] },
})
.sort({ updatedAt: -1 })
.lean()
.exec();
return lists.map(l => this.sortItems(l));
return lists.map((l) => this.sortItems(l));
}
public async update(id: string, householdId: string, data: UpdateShoppingListInput) {
const updated = await ShoppingListModel.findOneAndUpdate(
{ _id: id, householdId },
{ $set: data },
{ new: true }
).lean().exec();
{ new: true },
)
.lean()
.exec();
return this.sortItems(updated);
}
@ -69,8 +74,10 @@ export class ShoppingListsRepository {
const updated = await ShoppingListModel.findOneAndUpdate(
{ _id: id, householdId },
{ $push: { items: item } },
{ new: true }
).lean().exec();
{ new: true },
)
.lean()
.exec();
return this.sortItems(updated);
}
@ -78,7 +85,7 @@ export class ShoppingListsRepository {
id: string,
householdId: string,
itemId: string,
updates: Partial<ShoppingItem>
updates: Partial<ShoppingItem>,
) {
const setUpdates: Record<string, unknown> = {};
for (const [key, val] of Object.entries(updates)) {
@ -88,8 +95,10 @@ export class ShoppingListsRepository {
const updated = await ShoppingListModel.findOneAndUpdate(
{ _id: id, householdId, 'items.id': itemId },
{ $set: setUpdates },
{ new: true }
).lean().exec();
{ new: true },
)
.lean()
.exec();
return this.sortItems(updated);
}
@ -97,8 +106,10 @@ export class ShoppingListsRepository {
const updated = await ShoppingListModel.findOneAndUpdate(
{ _id: id, householdId },
{ $pull: { items: { id: itemId } } },
{ new: true }
).lean().exec();
{ new: true },
)
.lean()
.exec();
return this.sortItems(updated);
}
}

View file

@ -68,7 +68,7 @@ export default fp(
fastify.diContainer.register({
shoppingListsRepository: asClass(ShoppingListsRepository, { lifetime: Lifetime.SINGLETON }),
shoppingListsService: asClass(ShoppingListsService, { lifetime: Lifetime.SINGLETON }),
// Cross-domain requirements to service the list workflow orchestrations
shoppingGapService: asClass(ShoppingGapService, { lifetime: Lifetime.SINGLETON }),
pantryService: asClass(PantryService, { lifetime: Lifetime.SINGLETON }),
@ -114,7 +114,7 @@ export default fp(
const list = await service.create(
request.body,
request.params.householdId,
request.user.keycloakId
request.user.keycloakId,
);
return reply.status(201).send(serializeList(list));
},
@ -147,7 +147,7 @@ export default fp(
const list = await service.update(
request.params.id,
request.params.householdId,
request.body
request.body,
);
return reply.send(serializeList(list));
},
@ -181,9 +181,9 @@ export default fp(
const { list, addedItem } = await service.addItem(
request.params.id,
request.params.householdId,
request.body
request.body,
);
// Emit real-time update notification to existing connected viewers
broadcastToList(request.params.id, null as any, {
type: 'ITEM_ADDED',
@ -209,11 +209,13 @@ export default fp(
request.params.householdId,
request.params.itemId,
request.body,
request.user.keycloakId
request.user.keycloakId,
);
// Broadcast the precise item differential state update to sibling websocket listeners
const matchedItem = updatedList.items.find((i: ShoppingItem) => i.id === request.params.itemId);
const matchedItem = updatedList.items.find(
(i: ShoppingItem) => i.id === request.params.itemId,
);
if (matchedItem) {
broadcastToList(request.params.id, null as any, {
type: 'ITEM_UPDATED',
@ -242,7 +244,7 @@ export default fp(
const list = await service.removeItem(
request.params.id,
request.params.householdId,
request.params.itemId
request.params.itemId,
);
broadcastToList(request.params.id, null as any, {
@ -268,7 +270,7 @@ export default fp(
const list = await service.createFromMealPlan(
request.params.mealPlanId,
request.params.householdId,
request.user.keycloakId
request.user.keycloakId,
);
return reply.status(201).send(serializeList(list));
},
@ -286,7 +288,7 @@ export default fp(
const results = await service.syncCheckedToPantry(
request.params.id,
request.params.householdId,
request.user.keycloakId
request.user.keycloakId,
);
return reply.send(results);
},
@ -303,14 +305,14 @@ export default fp(
const service = fastify.diContainer.resolve('shoppingListsService');
const comparison = await service.getStoreComparison(
request.params.id,
request.params.householdId
request.params.householdId,
);
return reply.send(comparison);
},
});
// 4. Persist Collaborative WebSocket Handshakes
/* v8 ignore start */
app.get(
'/api/v1/households/:householdId/shopping-lists/:id/sync',
@ -318,7 +320,7 @@ export default fp(
(connection: any, request: any) => {
const socket = connection.socket;
const listId = request.params.id;
// Setup connection context
if (!activeListSockets.has(listId)) {
activeListSockets.set(listId, new Set());
@ -330,7 +332,7 @@ export default fp(
socket.on('message', async (messageBuffer: any) => {
try {
const payload = JSON.parse(messageBuffer.toString());
// Handlers for inbound events e.g. real-time toggle checks from frontends
if (payload.type === 'TOGGLE_ITEM') {
const service = fastify.diContainer.resolve('shoppingListsService');
@ -339,10 +341,12 @@ export default fp(
request.params.householdId,
payload.itemId,
{ checked: payload.checked },
request.user.keycloakId
request.user.keycloakId,
);
const matched = updatedList.items.find((it: ShoppingItem) => it.id === payload.itemId);
const matched = updatedList.items.find(
(it: ShoppingItem) => it.id === payload.itemId,
);
// Echo back differential confirmation to everyone else on the floor
broadcastToList(listId, socket, {
@ -352,7 +356,7 @@ export default fp(
checked: payload.checked,
checkedAt: matched?.checkedAt?.toISOString(),
checkedBy: matched?.checkedBy,
}
},
});
}
} catch (err) {
@ -370,12 +374,12 @@ export default fp(
}
request.log.info({ listId }, 'Client severed sync handshake connection');
});
}
},
);
/* v8 ignore stop */
},
{
name: 'shopping-lists-routes',
dependencies: ['auth-plugin'],
}
},
);

View file

@ -13,7 +13,7 @@ import type {
ShoppingItem,
} from '@meshitrack/shared';
import { ShoppingListSourceType } from '@meshitrack/shared';
import { StorageLocation, ServingUnit } from '@meshitrack/shared';
import { StorageLocation, type ServingUnit } from '@meshitrack/shared';
import { NotFoundError } from '../../common/errors.js';
import { v4 as uuidv4 } from 'uuid';
@ -158,10 +158,10 @@ export class ShoppingListsService {
householdId: string,
itemId: string,
data: UpdateShoppingItemInput,
userId: string
userId: string,
) {
const updates: Partial<ShoppingItem> = { ...data };
if (data.checked !== undefined) {
updates.checkedAt = data.checked ? new Date() : undefined;
updates.checkedBy = data.checked ? userId : undefined;
@ -188,7 +188,7 @@ export class ShoppingListsService {
if (!plan) throw new NotFoundError('Meal plan not found');
const report = await this.shoppingGapService.calculateGap(householdId, mealPlanId);
const listItems: ShoppingItem[] = [];
let runningTotal = 0;
@ -208,7 +208,10 @@ export class ShoppingListsService {
});
}
const dateStr = new Date((plan as any).weekStartDate).toLocaleDateString('en-US', { month: 'short', day: 'numeric' });
const dateStr = new Date((plan as any).weekStartDate).toLocaleDateString('en-US', {
month: 'short',
day: 'numeric',
});
const name = `Groceries for Week of ${dateStr}`;
const newList = await this.shoppingListsRepository.create({
@ -238,11 +241,13 @@ export class ShoppingListsService {
*/
public async syncCheckedToPantry(id: string, householdId: string, userId: string) {
const list = await this.getById(id, householdId);
let addedCount = 0;
let pricesLogged = 0;
const pendingItems = list.items.filter((it: ShoppingItem) => it.checked && !it.addedToPantry && it.productId);
const pendingItems = list.items.filter(
(it: ShoppingItem) => it.checked && !it.addedToPantry && it.productId,
);
for (const item of pendingItems) {
// 1. Promote item to active pantry
@ -257,7 +262,7 @@ export class ShoppingListsService {
notes: item.notes || undefined,
},
householdId,
userId
userId,
);
addedCount++;
@ -275,7 +280,7 @@ export class ShoppingListsService {
currency: 'USD',
},
householdId,
userId
userId,
);
pricesLogged++;
}
@ -347,7 +352,9 @@ export class ShoppingListsService {
}
// Sort to surface the cheapest/fullest single store options first
singleStoreOptions.sort((a, b) => b.itemsCovered - a.itemsCovered || a.estimatedTotal - b.estimatedTotal);
singleStoreOptions.sort(
(a, b) => b.itemsCovered - a.itemsCovered || a.estimatedTotal - b.estimatedTotal,
);
return { singleStoreOptions };
}

View file

@ -17,7 +17,7 @@ const priceRecordSchema = new mongoose.Schema(
notes: { type: String },
createdBy: { type: String, required: true },
},
{ timestamps: { createdAt: true, updatedAt: false } }
{ timestamps: { createdAt: true, updatedAt: false } },
);
// Performance Indexes for Lookup Speed and Aggregations

View file

@ -17,7 +17,7 @@ const shoppingItemSchema = new mongoose.Schema(
category: { type: String },
addedToPantry: { type: Boolean, required: true, default: false },
},
{ _id: false }
{ _id: false },
);
const shoppingListSourceSchema = new mongoose.Schema(
@ -25,7 +25,7 @@ const shoppingListSourceSchema = new mongoose.Schema(
type: { type: String, required: true }, // values from ShoppingListSourceType
referenceId: { type: String },
},
{ _id: false }
{ _id: false },
);
const shoppingListSchema = new mongoose.Schema(
@ -44,7 +44,7 @@ const shoppingListSchema = new mongoose.Schema(
completedAt: { type: Date },
createdBy: { type: String, required: true },
},
{ timestamps: true }
{ timestamps: true },
);
shoppingListSchema.index({ householdId: 1, status: 1 });

View file

@ -6,7 +6,7 @@ import {
ForbiddenError,
ConflictError,
BadRequestError,
} from './errors.js';
} from '../../src/common/errors.js';
describe(AppError.name, () => {
it('sets statusCode, error, message, and details', () => {

View file

@ -1,5 +1,5 @@
import { describe, it, expect } from 'vitest';
import config from './configuration.js';
import config from '../../src/config/configuration.js';
describe('configuration', () => {
it('exports default config values', () => {

View file

@ -0,0 +1,34 @@
import { vi } from 'vitest';
/**
* Dynamically creates a fully mocked repository from a repository class.
* Recursively walks the prototype chain (inheritance-aware) to gather all methods,
* and assigns them a Vitest mock function (vi.fn()).
*
* @param repoClass The repository class constructor to mock
* @returns An object with all methods mocked as vi.fn()
*
* @example
* const mockRepo = createMockRepository(ProductsRepository);
* mockRepo.findById.mockResolvedValue(mockProduct);
*/
export function createMockRepository<T>(
repoClass: new (...args: any[]) => T
): Record<keyof T, any> {
const mock: Record<string, any> = {};
let proto = repoClass.prototype;
while (proto && proto !== Object.prototype) {
const methods = Object.getOwnPropertyNames(proto).filter(
(name) => name !== 'constructor' && typeof (proto as any)[name] === 'function'
);
for (const method of methods) {
if (!(method in mock)) {
mock[method] = vi.fn();
}
}
proto = Object.getPrototypeOf(proto);
}
return mock as Record<keyof T, any>;
}

View file

@ -63,9 +63,9 @@ vi.mock('jose', () => ({
jwtVerify: vi.fn(),
}));
import { buildApp } from './main.js';
import { buildApp } from '../src/main.js';
import * as jose from 'jose';
import { NotFoundError } from './common/errors.js';
import { NotFoundError } from '../src/common/errors.js';
describe('buildApp', () => {
beforeEach(() => {

View file

@ -7,7 +7,7 @@ const { mockFind, mockSave, mockInsertMany, mockAggregate } = vi.hoisted(() => (
mockAggregate: vi.fn(),
}));
vi.mock('../../schemas/cabinet-event.schema.js', () => {
vi.mock('../../../src/schemas/cabinet-event.schema.js', () => {
const chain = () => ({
sort: vi.fn().mockReturnThis(),
limit: vi.fn().mockReturnThis(),
@ -36,7 +36,7 @@ vi.mock('../../schemas/cabinet-event.schema.js', () => {
return { CabinetEventModel: FakeModel };
});
import { CabinetEventsRepository } from './cabinet-events.repository.js';
import { CabinetEventsRepository } from '../../../src/modules/cabinet-events/cabinet-events.repository.js';
describe(CabinetEventsRepository.name, () => {
let repo: CabinetEventsRepository;

View file

@ -24,7 +24,7 @@ const { mockListEvents, mockGetEventsByItem, mockGetSpendingSummary } = vi.hoist
mockGetSpendingSummary: vi.fn(),
}));
vi.mock('./cabinet-events.repository.js', () => ({
vi.mock('../../../src/modules/cabinet-events/cabinet-events.repository.js', () => ({
CabinetEventsRepository: class {
create = vi.fn();
createMany = vi.fn();
@ -35,7 +35,7 @@ vi.mock('./cabinet-events.repository.js', () => ({
},
}));
vi.mock('./cabinet-events.service.js', () => ({
vi.mock('../../../src/modules/cabinet-events/cabinet-events.service.js', () => ({
CabinetEventsService: class {
logEvent = vi.fn();
logEvents = vi.fn();
@ -46,17 +46,17 @@ vi.mock('./cabinet-events.service.js', () => ({
},
}));
vi.mock('../users/users.repository.js', () => ({
vi.mock('../../../src/modules/users/users.repository.js', () => ({
UsersRepository: class {
findByKeycloakId = vi.fn().mockResolvedValue({ householdIds: ['hh1'] });
upsertFromToken = vi.fn().mockResolvedValue({ householdIds: ['hh1'] });
},
}));
import authPlugin from '../../plugins/auth.plugin.js';
import householdPlugin from '../../plugins/household.plugin.js';
import usersRoutes from '../users/users.routes.js';
import cabinetEventsRoutes from './cabinet-events.routes.js';
import authPlugin from '../../../src/plugins/auth.plugin.js';
import householdPlugin from '../../../src/plugins/household.plugin.js';
import usersRoutes from '../../../src/modules/users/users.routes.js';
import cabinetEventsRoutes from '../../../src/modules/cabinet-events/cabinet-events.routes.js';
function makeFakeEvent(overrides = {}) {
return {

View file

@ -1,5 +1,5 @@
import { describe, it, expect, vi, beforeEach } from 'vitest';
import { CabinetEventsService } from './cabinet-events.service.js';
import { CabinetEventsService } from '../../../src/modules/cabinet-events/cabinet-events.service.js';
describe(CabinetEventsService.name, () => {
const mockCabinetEventsRepo = {

View file

@ -10,7 +10,7 @@ const { mockFind, mockFindOne, mockFindOneAndUpdate, mockSave, mockCountDocument
mockAggregate: vi.fn(),
}));
vi.mock('../../schemas/cabinet-item.schema.js', () => {
vi.mock('../../../src/schemas/cabinet-item.schema.js', () => {
const chain = () => ({
sort: vi.fn().mockReturnThis(),
limit: vi.fn().mockReturnThis(),
@ -54,7 +54,7 @@ vi.mock('../../schemas/cabinet-item.schema.js', () => {
return { CabinetItemModel: FakeModel };
});
import { CabinetRepository } from './cabinet.repository.js';
import { CabinetRepository } from '../../../src/modules/cabinet/cabinet.repository.js';
describe(CabinetRepository.name, () => {
let repo: CabinetRepository;

View file

@ -45,7 +45,7 @@ const {
mockFindActiveByMedicineForFEFO: vi.fn(),
}));
vi.mock('./cabinet.repository.js', () => ({
vi.mock('../../../src/modules/cabinet/cabinet.repository.js', () => ({
CabinetRepository: class {
findByHousehold = mockFindByHousehold;
findById = mockFindById;
@ -65,7 +65,7 @@ const { mockMedicineFindById } = vi.hoisted(() => ({
mockMedicineFindById: vi.fn(),
}));
vi.mock('../medicines/medicines.repository.js', () => ({
vi.mock('../../../src/modules/medicines/medicines.repository.js', () => ({
MedicinesRepository: class {
findById = mockMedicineFindById;
findByHousehold = vi.fn();
@ -80,7 +80,7 @@ const { mockProductFindById } = vi.hoisted(() => ({
mockProductFindById: vi.fn(),
}));
vi.mock('../medicine-products/medicine-products.repository.js', () => ({
vi.mock('../../../src/modules/medicine-products/medicine-products.repository.js', () => ({
MedicineProductsRepository: class {
findById = mockProductFindById;
findByMedicine = vi.fn();
@ -91,7 +91,7 @@ vi.mock('../medicine-products/medicine-products.repository.js', () => ({
},
}));
vi.mock('../medicine-products/medicine-products.service.js', () => ({
vi.mock('../../../src/modules/medicine-products/medicine-products.service.js', () => ({
MedicineProductsService: class {
listByMedicine = vi.fn();
getById = vi.fn();
@ -101,7 +101,7 @@ vi.mock('../medicine-products/medicine-products.service.js', () => ({
},
}));
vi.mock('../medicines/medicines.service.js', () => ({
vi.mock('../../../src/modules/medicines/medicines.service.js', () => ({
MedicinesService: class {
list = vi.fn();
getById = vi.fn();
@ -111,7 +111,7 @@ vi.mock('../medicines/medicines.service.js', () => ({
},
}));
vi.mock('../cabinet-events/cabinet-events.repository.js', () => ({
vi.mock('../../../src/modules/cabinet-events/cabinet-events.repository.js', () => ({
CabinetEventsRepository: class {
create = vi.fn();
createMany = vi.fn();
@ -122,7 +122,7 @@ vi.mock('../cabinet-events/cabinet-events.repository.js', () => ({
},
}));
vi.mock('../cabinet-events/cabinet-events.service.js', () => ({
vi.mock('../../../src/modules/cabinet-events/cabinet-events.service.js', () => ({
CabinetEventsService: class {
logEvent = vi.fn();
logEvents = vi.fn();
@ -133,20 +133,20 @@ vi.mock('../cabinet-events/cabinet-events.service.js', () => ({
},
}));
vi.mock('../users/users.repository.js', () => ({
vi.mock('../../../src/modules/users/users.repository.js', () => ({
UsersRepository: class {
findByKeycloakId = vi.fn().mockResolvedValue({ householdIds: ['hh1'] });
upsertFromToken = vi.fn().mockResolvedValue({ householdIds: ['hh1'] });
},
}));
import authPlugin from '../../plugins/auth.plugin.js';
import householdPlugin from '../../plugins/household.plugin.js';
import usersRoutes from '../users/users.routes.js';
import medicinesRoutes from '../medicines/medicines.routes.js';
import medicineProductsRoutes from '../medicine-products/medicine-products.routes.js';
import cabinetEventsRoutes from '../cabinet-events/cabinet-events.routes.js';
import cabinetRoutes from './cabinet.routes.js';
import authPlugin from '../../../src/plugins/auth.plugin.js';
import householdPlugin from '../../../src/plugins/household.plugin.js';
import usersRoutes from '../../../src/modules/users/users.routes.js';
import medicinesRoutes from '../../../src/modules/medicines/medicines.routes.js';
import medicineProductsRoutes from '../../../src/modules/medicine-products/medicine-products.routes.js';
import cabinetEventsRoutes from '../../../src/modules/cabinet-events/cabinet-events.routes.js';
import cabinetRoutes from '../../../src/modules/cabinet/cabinet.routes.js';
function makeFakeCabinetItem(overrides = {}) {
return {

View file

@ -1,5 +1,5 @@
import { describe, it, expect, vi, beforeEach } from 'vitest';
import { CabinetService } from './cabinet.service.js';
import { CabinetService } from '../../../src/modules/cabinet/cabinet.service.js';
import { CabinetEventType, CabinetEventSourceType } from '@meshitrack/shared';
describe(CabinetService.name, () => {

View file

@ -16,7 +16,7 @@ const {
mockFindById: vi.fn(),
}));
vi.mock('../../schemas/freshness-rule.schema.js', () => {
vi.mock('../../../src/schemas/freshness-rule.schema.js', () => {
const chain = () => ({
sort: vi.fn().mockReturnThis(),
limit: vi.fn().mockReturnThis(),
@ -62,7 +62,7 @@ vi.mock('../../schemas/freshness-rule.schema.js', () => {
return { FreshnessRuleModel: FakeModel };
});
import { FreshnessRulesRepository } from './freshness-rules.repository.js';
import { FreshnessRulesRepository } from '../../../src/modules/freshness-rules/freshness-rules.repository.js';
describe(FreshnessRulesRepository.name, () => {
let repo: FreshnessRulesRepository;

View file

@ -28,7 +28,7 @@ const { mockFindByHousehold, mockFindById, mockCreate, mockUpdate, mockDelete }
}),
);
vi.mock('./freshness-rules.repository.js', () => ({
vi.mock('../../../src/modules/freshness-rules/freshness-rules.repository.js', () => ({
FreshnessRulesRepository: class {
findByHousehold = mockFindByHousehold;
findById = mockFindById;
@ -39,17 +39,17 @@ vi.mock('./freshness-rules.repository.js', () => ({
},
}));
vi.mock('../users/users.repository.js', () => ({
vi.mock('../../../src/modules/users/users.repository.js', () => ({
UsersRepository: class {
findByKeycloakId = vi.fn().mockResolvedValue({ householdIds: ['hh1'] });
upsertFromToken = vi.fn().mockResolvedValue({ householdIds: ['hh1'] });
},
}));
import authPlugin from '../../plugins/auth.plugin.js';
import householdPlugin from '../../plugins/household.plugin.js';
import usersRoutes from '../users/users.routes.js';
import freshnessRulesRoutes from './freshness-rules.routes.js';
import authPlugin from '../../../src/plugins/auth.plugin.js';
import householdPlugin from '../../../src/plugins/household.plugin.js';
import usersRoutes from '../../../src/modules/users/users.routes.js';
import freshnessRulesRoutes from '../../../src/modules/freshness-rules/freshness-rules.routes.js';
function makeRule(overrides: Record<string, unknown> = {}) {
return {

View file

@ -1,6 +1,6 @@
import { describe, it, expect, vi, beforeEach } from 'vitest';
import { FreshnessRulesService } from './freshness-rules.service.js';
import { NotFoundError, BadRequestError } from '../../common/errors.js';
import { FreshnessRulesService } from '../../../src/modules/freshness-rules/freshness-rules.service.js';
import { NotFoundError, BadRequestError } from '../../../src/common/errors.js';
import { FreshnessRuleSource } from '@meshitrack/shared';
const mockRepo = {

View file

@ -1,7 +1,7 @@
import { describe, it, expect } from 'vitest';
import Fastify from 'fastify';
import { serializerCompiler, validatorCompiler } from 'fastify-type-provider-zod';
import healthRoutes from './health.routes.js';
import healthRoutes from '../../../src/modules/health/health.routes.js';
describe('Health Routes', () => {
async function buildTestApp() {

View file

@ -15,7 +15,7 @@ const { _mockLean, mockExec, mockFindById, mockFindOne, mockFindByIdAndUpdate, m
};
});
vi.mock('../../schemas/household.schema.js', () => {
vi.mock('../../../src/schemas/household.schema.js', () => {
class MockHouseholdModel {
_data: Record<string, unknown>;
constructor(data: Record<string, unknown>) {
@ -36,7 +36,7 @@ vi.mock('../../schemas/household.schema.js', () => {
return { HouseholdModel: MockHouseholdModel };
});
import { HouseholdsRepository } from './households.repository.js';
import { HouseholdsRepository } from '../../../src/modules/households/households.repository.js';
describe('HouseholdsRepository', () => {
let repo: HouseholdsRepository;

View file

@ -41,7 +41,7 @@ const {
mockUserUpdate: vi.fn(),
}));
vi.mock('./households.repository.js', () => ({
vi.mock('../../../src/modules/households/households.repository.js', () => ({
HouseholdsRepository: class {
create = mockCreate;
findById = mockFindById;
@ -52,7 +52,7 @@ vi.mock('./households.repository.js', () => ({
},
}));
vi.mock('../users/users.repository.js', () => ({
vi.mock('../../../src/modules/users/users.repository.js', () => ({
UsersRepository: class {
findByKeycloakId = mockFindByKeycloakId;
update = mockUserUpdate;
@ -75,10 +75,10 @@ vi.mock('mongoose', () => ({
},
}));
import authPlugin from '../../plugins/auth.plugin.js';
import householdPlugin from '../../plugins/household.plugin.js';
import usersRoutes from '../users/users.routes.js';
import householdsRoutes from './households.routes.js';
import authPlugin from '../../../src/plugins/auth.plugin.js';
import householdPlugin from '../../../src/plugins/household.plugin.js';
import usersRoutes from '../../../src/modules/users/users.routes.js';
import householdsRoutes from '../../../src/modules/households/households.routes.js';
function makeFakeHousehold(overrides = {}) {
return {

View file

@ -1,6 +1,6 @@
import { describe, it, expect, vi, beforeEach } from 'vitest';
import { HouseholdsService } from './households.service.js';
import { NotFoundError, ForbiddenError, ConflictError } from '../../common/errors.js';
import { HouseholdsService } from '../../../src/modules/households/households.service.js';
import { NotFoundError, ForbiddenError, ConflictError } from '../../../src/common/errors.js';
import { HouseholdRole } from '@meshitrack/shared';
// Mock uuid to return deterministic values

View file

@ -1,6 +1,6 @@
import { describe, it, expect, vi } from 'vitest';
import { NoOpLlmProvider } from './no-op-llm.provider.js';
import { LLM_PROVIDER } from './llm-provider.interface.js';
import { NoOpLlmProvider } from '../../../src/modules/llm/no-op-llm.provider.js';
import { LLM_PROVIDER } from '../../../src/modules/llm/llm-provider.interface.js';
describe(NoOpLlmProvider.name, () => {
const provider = new NoOpLlmProvider();

View file

@ -1,5 +1,5 @@
import { describe, it, expect, vi, beforeEach } from 'vitest';
import { MealPlanRepository } from './meal-plans.repository.js';
import { MealPlanRepository } from '../../../src/modules/meal-plans/meal-plans.repository.js';
import { MealPlanStatus } from '@meshitrack/shared';
const { mockSave, MockMealPlanModel } = vi.hoisted(() => {
@ -17,11 +17,11 @@ const { mockSave, MockMealPlanModel } = vi.hoisted(() => {
return { mockSave, MockMealPlanModel: MockModel };
});
vi.mock('../../schemas/meal-plan.schema.js', () => ({
vi.mock('../../../src/schemas/meal-plan.schema.js', () => ({
MealPlanModel: MockMealPlanModel,
}));
const { MealPlanModel } = await import('../../schemas/meal-plan.schema.js');
const { MealPlanModel } = await import('../../../src/schemas/meal-plan.schema.js');
function makeChain(result: unknown = null) {
return {

View file

@ -37,7 +37,7 @@ const {
mockDelete: vi.fn(),
}));
vi.mock('./meal-plans.repository.js', () => ({
vi.mock('../../../src/modules/meal-plans/meal-plans.repository.js', () => ({
MealPlanRepository: class {
findByHousehold = mockFindByHousehold;
findById = mockFindById;
@ -50,42 +50,42 @@ vi.mock('./meal-plans.repository.js', () => ({
}));
// Mock prerequisite repositories to allow SuggestionEngine/Gap to resolve
vi.mock('../recipes/recipes.repository.js', () => ({
vi.mock('../../../src/modules/recipes/recipes.repository.js', () => ({
RecipesRepository: class {
findByHousehold = vi.fn().mockResolvedValue({ data: [] });
findById = vi.fn();
},
}));
vi.mock('../pantry/pantry.repository.js', () => ({
vi.mock('../../../src/modules/pantry/pantry.repository.js', () => ({
PantryRepository: class {
findActiveByHousehold = vi.fn().mockResolvedValue([]);
},
}));
vi.mock('../nutrition-targets/nutrition-target.repository.js', () => ({
vi.mock('../../../src/modules/nutrition-targets/nutrition-target.repository.js', () => ({
NutritionTargetRepository: class {
findByUser = vi.fn().mockResolvedValue(null);
},
}));
vi.mock('../products/products.repository.js', () => ({
vi.mock('../../../src/modules/products/products.repository.js', () => ({
ProductsRepository: class {
findByIds = vi.fn().mockResolvedValue([]);
},
}));
vi.mock('../users/users.repository.js', () => ({
vi.mock('../../../src/modules/users/users.repository.js', () => ({
UsersRepository: class {
findByKeycloakId = vi.fn().mockResolvedValue({ householdIds: ['hh1'] });
upsertFromToken = vi.fn().mockResolvedValue({ householdIds: ['hh1'] });
},
}));
import authPlugin from '../../plugins/auth.plugin.js';
import householdPlugin from '../../plugins/household.plugin.js';
import usersRoutes from '../users/users.routes.js';
import mealPlanRoutes from './meal-plans.routes.js';
import authPlugin from '../../../src/plugins/auth.plugin.js';
import householdPlugin from '../../../src/plugins/household.plugin.js';
import usersRoutes from '../../../src/modules/users/users.routes.js';
import mealPlanRoutes from '../../../src/modules/meal-plans/meal-plans.routes.js';
const emptyNutrition = { calories: 0, protein: 0, carbs: 0, fat: 0, fiber: 0, sugar: 0, sodium: 0, saturatedFat: 0, cholesterol: 0 };

View file

@ -1,8 +1,8 @@
import { describe, it, expect, vi, beforeEach } from 'vitest';
import { MealPlanService } from './meal-plans.service.js';
import type { MealPlanRepository } from './meal-plans.repository.js';
import { MealPlanService } from '../../../src/modules/meal-plans/meal-plans.service.js';
import type { MealPlanRepository } from '../../../src/modules/meal-plans/meal-plans.repository.js';
import { MealPlanStatus, MealType } from '@meshitrack/shared';
import { BadRequestError, NotFoundError } from '../../common/errors.js';
import { BadRequestError, NotFoundError } from '../../../src/common/errors.js';
describe(MealPlanService.name, () => {
let service: MealPlanService;

View file

@ -1,10 +1,10 @@
import { describe, it, expect, vi, beforeEach } from 'vitest';
import { ShoppingGapService } from './shopping-gap.service.js';
import type { MealPlanRepository } from './meal-plans.repository.js';
import type { RecipesRepository } from '../recipes/recipes.repository.js';
import type { PantryRepository } from '../pantry/pantry.repository.js';
import type { ProductsRepository } from '../products/products.repository.js';
import { NotFoundError } from '../../common/errors.js';
import { ShoppingGapService } from '../../../src/modules/meal-plans/shopping-gap.service.js';
import type { MealPlanRepository } from '../../../src/modules/meal-plans/meal-plans.repository.js';
import type { RecipesRepository } from '../../../src/modules/recipes/recipes.repository.js';
import type { PantryRepository } from '../../../src/modules/pantry/pantry.repository.js';
import type { ProductsRepository } from '../../../src/modules/products/products.repository.js';
import { NotFoundError } from '../../../src/common/errors.js';
describe(ShoppingGapService.name, () => {
let service: ShoppingGapService;

View file

@ -1,9 +1,9 @@
import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
import { SuggestionEngineService } from './suggestion-engine.service.js';
import type { RecipesRepository } from '../recipes/recipes.repository.js';
import type { PantryRepository } from '../pantry/pantry.repository.js';
import type { MealPlanRepository } from './meal-plans.repository.js';
import type { NutritionTargetRepository } from '../nutrition-targets/nutrition-target.repository.js';
import { SuggestionEngineService } from '../../../src/modules/meal-plans/suggestion-engine.service.js';
import type { RecipesRepository } from '../../../src/modules/recipes/recipes.repository.js';
import type { PantryRepository } from '../../../src/modules/pantry/pantry.repository.js';
import type { MealPlanRepository } from '../../../src/modules/meal-plans/meal-plans.repository.js';
import type { NutritionTargetRepository } from '../../../src/modules/nutrition-targets/nutrition-target.repository.js';
describe(SuggestionEngineService.name, () => {
let service: SuggestionEngineService;

View file

@ -7,7 +7,7 @@ const { mockFind, mockFindOne, mockAggregate, mockSave } = vi.hoisted(() => ({
mockSave: vi.fn(),
}));
vi.mock('../../schemas/medicine-price.schema.js', () => {
vi.mock('../../../src/schemas/medicine-price.schema.js', () => {
const chain = () => ({
sort: vi.fn().mockReturnThis(),
limit: vi.fn().mockReturnThis(),
@ -37,7 +37,7 @@ vi.mock('../../schemas/medicine-price.schema.js', () => {
return { MedicinePriceModel: FakeModel };
});
import { MedicinePricesRepository } from './medicine-prices.repository.js';
import { MedicinePricesRepository } from '../../../src/modules/medicine-prices/medicine-prices.repository.js';
describe(MedicinePricesRepository.name, () => {
let repo: MedicinePricesRepository;

View file

@ -27,7 +27,7 @@ const { mockRecordPrice, mockGetPriceHistory, mockCompareStores, mockGetAnalytic
}),
);
vi.mock('./medicine-prices.repository.js', () => ({
vi.mock('../../../src/modules/medicine-prices/medicine-prices.repository.js', () => ({
MedicinePricesRepository: class {
create = vi.fn();
findByMedicine = vi.fn();
@ -37,7 +37,7 @@ vi.mock('./medicine-prices.repository.js', () => ({
},
}));
vi.mock('./medicine-prices.service.js', () => ({
vi.mock('../../../src/modules/medicine-prices/medicine-prices.service.js', () => ({
MedicinePricesService: class {
recordPrice = mockRecordPrice;
getPriceHistory = mockGetPriceHistory;
@ -46,29 +46,29 @@ vi.mock('./medicine-prices.service.js', () => ({
},
}));
vi.mock('../medicine-products/medicine-products.repository.js', () => ({
vi.mock('../../../src/modules/medicine-products/medicine-products.repository.js', () => ({
MedicineProductsRepository: class {
findById = vi.fn();
},
}));
vi.mock('../stores/stores.repository.js', () => ({
vi.mock('../../../src/modules/stores/stores.repository.js', () => ({
StoresRepository: class {
findById = vi.fn();
},
}));
vi.mock('../users/users.repository.js', () => ({
vi.mock('../../../src/modules/users/users.repository.js', () => ({
UsersRepository: class {
findByKeycloakId = vi.fn().mockResolvedValue({ householdIds: ['hh1'] });
upsertFromToken = vi.fn().mockResolvedValue({ householdIds: ['hh1'] });
},
}));
import authPlugin from '../../plugins/auth.plugin.js';
import householdPlugin from '../../plugins/household.plugin.js';
import usersRoutes from '../users/users.routes.js';
import medicinePricesRoutes from './medicine-prices.routes.js';
import authPlugin from '../../../src/plugins/auth.plugin.js';
import householdPlugin from '../../../src/plugins/household.plugin.js';
import usersRoutes from '../../../src/modules/users/users.routes.js';
import medicinePricesRoutes from '../../../src/modules/medicine-prices/medicine-prices.routes.js';
function makeFakePriceRecord(overrides = {}) {
return {

View file

@ -1,5 +1,5 @@
import { describe, it, expect, vi, beforeEach } from 'vitest';
import { MedicinePricesService } from './medicine-prices.service.js';
import { MedicinePricesService } from '../../../src/modules/medicine-prices/medicine-prices.service.js';
describe(MedicinePricesService.name, () => {
const mockPricesRepo = {

View file

@ -30,7 +30,7 @@ const {
};
});
vi.mock('../../schemas/medicine-product.schema.js', () => {
vi.mock('../../../src/schemas/medicine-product.schema.js', () => {
class MockMedicineProductModel {
_data: Record<string, unknown>;
constructor(data: Record<string, unknown>) {
@ -52,7 +52,7 @@ vi.mock('../../schemas/medicine-product.schema.js', () => {
return { MedicineProductModel: MockMedicineProductModel };
});
import { MedicineProductsRepository } from './medicine-products.repository.js';
import { MedicineProductsRepository } from '../../../src/modules/medicine-products/medicine-products.repository.js';
describe(MedicineProductsRepository.name, () => {
let repo: MedicineProductsRepository;

View file

@ -35,7 +35,7 @@ const {
mockMedicineFindById: vi.fn(),
}));
vi.mock('./medicine-products.repository.js', () => ({
vi.mock('../../../src/modules/medicine-products/medicine-products.repository.js', () => ({
MedicineProductsRepository: class {
findByMedicine = mockFindByMedicine;
findById = mockFindById;
@ -45,24 +45,24 @@ vi.mock('./medicine-products.repository.js', () => ({
},
}));
vi.mock('../medicines/medicines.repository.js', () => ({
vi.mock('../../../src/modules/medicines/medicines.repository.js', () => ({
MedicinesRepository: class {
findById = mockMedicineFindById;
},
}));
vi.mock('../users/users.repository.js', () => ({
vi.mock('../../../src/modules/users/users.repository.js', () => ({
UsersRepository: class {
findByKeycloakId = vi.fn().mockResolvedValue({ householdIds: ['hh1'] });
upsertFromToken = vi.fn().mockResolvedValue({ householdIds: ['hh1'] });
},
}));
import authPlugin from '../../plugins/auth.plugin.js';
import householdPlugin from '../../plugins/household.plugin.js';
import usersRoutes from '../users/users.routes.js';
import medicinesRoutes from '../medicines/medicines.routes.js';
import medicineProductsRoutes from './medicine-products.routes.js';
import authPlugin from '../../../src/plugins/auth.plugin.js';
import householdPlugin from '../../../src/plugins/household.plugin.js';
import usersRoutes from '../../../src/modules/users/users.routes.js';
import medicinesRoutes from '../../../src/modules/medicines/medicines.routes.js';
import medicineProductsRoutes from '../../../src/modules/medicine-products/medicine-products.routes.js';
function makeFakeProduct(overrides = {}) {
return {

View file

@ -1,6 +1,6 @@
import { describe, it, expect, vi, beforeEach } from 'vitest';
import { MedicineProductsService } from './medicine-products.service.js';
import { NotFoundError } from '../../common/errors.js';
import { MedicineProductsService } from '../../../src/modules/medicine-products/medicine-products.service.js';
import { NotFoundError } from '../../../src/common/errors.js';
import { DosageUnit, MedicineProductSource } from '@meshitrack/shared';
describe(MedicineProductsService.name, () => {

View file

@ -27,7 +27,7 @@ const {
};
});
vi.mock('../../schemas/medicine.schema.js', () => {
vi.mock('../../../src/schemas/medicine.schema.js', () => {
class MockMedicineModel {
_data: Record<string, unknown>;
constructor(data: Record<string, unknown>) {
@ -48,7 +48,7 @@ vi.mock('../../schemas/medicine.schema.js', () => {
return { MedicineModel: MockMedicineModel };
});
import { MedicinesRepository } from './medicines.repository.js';
import { MedicinesRepository } from '../../../src/modules/medicines/medicines.repository.js';
describe(MedicinesRepository.name, () => {
let repo: MedicinesRepository;

View file

@ -37,7 +37,7 @@ const {
mockCountByMedicineId: vi.fn(),
}));
vi.mock('./medicines.repository.js', () => ({
vi.mock('../../../src/modules/medicines/medicines.repository.js', () => ({
MedicinesRepository: class {
findByHousehold = mockFindByHousehold;
findById = mockFindById;
@ -48,13 +48,13 @@ vi.mock('./medicines.repository.js', () => ({
},
}));
vi.mock('../medicine-products/medicine-products.repository.js', () => ({
vi.mock('../../../src/modules/medicine-products/medicine-products.repository.js', () => ({
MedicineProductsRepository: class {
countByMedicineId = mockCountByMedicineId;
},
}));
vi.mock('../medicine-products/medicine-products.service.js', () => ({
vi.mock('../../../src/modules/medicine-products/medicine-products.service.js', () => ({
MedicineProductsService: class {
listByMedicine = vi.fn();
getById = vi.fn();
@ -64,18 +64,18 @@ vi.mock('../medicine-products/medicine-products.service.js', () => ({
},
}));
vi.mock('../users/users.repository.js', () => ({
vi.mock('../../../src/modules/users/users.repository.js', () => ({
UsersRepository: class {
findByKeycloakId = vi.fn().mockResolvedValue({ householdIds: ['hh1'] });
upsertFromToken = vi.fn().mockResolvedValue({ householdIds: ['hh1'] });
},
}));
import authPlugin from '../../plugins/auth.plugin.js';
import householdPlugin from '../../plugins/household.plugin.js';
import usersRoutes from '../users/users.routes.js';
import medicinesRoutes from './medicines.routes.js';
import medicineProductsRoutes from '../medicine-products/medicine-products.routes.js';
import authPlugin from '../../../src/plugins/auth.plugin.js';
import householdPlugin from '../../../src/plugins/household.plugin.js';
import usersRoutes from '../../../src/modules/users/users.routes.js';
import medicinesRoutes from '../../../src/modules/medicines/medicines.routes.js';
import medicineProductsRoutes from '../../../src/modules/medicine-products/medicine-products.routes.js';
function makeFakeMedicine(overrides = {}) {
return {

View file

@ -1,6 +1,6 @@
import { describe, it, expect, vi, beforeEach } from 'vitest';
import { MedicinesService } from './medicines.service.js';
import { NotFoundError, ConflictError } from '../../common/errors.js';
import { MedicinesService } from '../../../src/modules/medicines/medicines.service.js';
import { NotFoundError, ConflictError } from '../../../src/common/errors.js';
import { MedicineForm, StrengthUnit, MedicineCategory } from '@meshitrack/shared';
describe(MedicinesService.name, () => {

View file

@ -1,5 +1,5 @@
import { describe, it, expect, vi, beforeEach } from 'vitest';
import { NutritionTargetRepository } from './nutrition-target.repository.js';
import { NutritionTargetRepository } from '../../../src/modules/nutrition-targets/nutrition-target.repository.js';
const { mockSave, MockTargetModel } = vi.hoisted(() => {
const mockSave = vi.fn();
@ -16,11 +16,11 @@ const { mockSave, MockTargetModel } = vi.hoisted(() => {
return { mockSave, MockTargetModel: MockModel };
});
vi.mock('../../schemas/nutrition-target.schema.js', () => ({
vi.mock('../../../src/schemas/nutrition-target.schema.js', () => ({
NutritionTargetModel: MockTargetModel,
}));
const { NutritionTargetModel } = await import('../../schemas/nutrition-target.schema.js');
const { NutritionTargetModel } = await import('../../../src/schemas/nutrition-target.schema.js');
function makeChain(result: unknown = null) {
return {

View file

@ -30,7 +30,7 @@ const {
mockCreate: vi.fn(),
}));
vi.mock('./nutrition-target.repository.js', () => ({
vi.mock('../../../src/modules/nutrition-targets/nutrition-target.repository.js', () => ({
NutritionTargetRepository: class {
findByUser = mockFindByUser;
findAllByUser = mockFindAllByUser;
@ -39,17 +39,17 @@ vi.mock('./nutrition-target.repository.js', () => ({
},
}));
vi.mock('../users/users.repository.js', () => ({
vi.mock('../../../src/modules/users/users.repository.js', () => ({
UsersRepository: class {
findByKeycloakId = vi.fn().mockResolvedValue({ householdIds: ['hh1'] });
upsertFromToken = vi.fn().mockResolvedValue({ householdIds: ['hh1'] });
},
}));
import authPlugin from '../../plugins/auth.plugin.js';
import householdPlugin from '../../plugins/household.plugin.js';
import usersRoutes from '../users/users.routes.js';
import nutritionTargetRoutes from './nutrition-target.routes.js';
import authPlugin from '../../../src/plugins/auth.plugin.js';
import householdPlugin from '../../../src/plugins/household.plugin.js';
import usersRoutes from '../../../src/modules/users/users.routes.js';
import nutritionTargetRoutes from '../../../src/modules/nutrition-targets/nutrition-target.routes.js';
function makeTarget(overrides = {}) {
return {

View file

@ -1,6 +1,6 @@
import { describe, it, expect, vi, beforeEach } from 'vitest';
import { NutritionTargetService } from './nutrition-target.service.js';
import type { NutritionTargetRepository } from './nutrition-target.repository.js';
import { NutritionTargetService } from '../../../src/modules/nutrition-targets/nutrition-target.service.js';
import type { NutritionTargetRepository } from '../../../src/modules/nutrition-targets/nutrition-target.repository.js';
describe(NutritionTargetService.name, () => {
let service: NutritionTargetService;

View file

@ -7,7 +7,7 @@ const { mockFind, mockFindOne, mockFindOneAndUpdate, mockSave } = vi.hoisted(()
mockSave: vi.fn(),
}));
vi.mock('../../schemas/organizer-fill.schema.js', () => {
vi.mock('../../../src/schemas/organizer-fill.schema.js', () => {
const chain = () => ({
sort: vi.fn().mockReturnThis(),
limit: vi.fn().mockReturnThis(),
@ -41,7 +41,7 @@ vi.mock('../../schemas/organizer-fill.schema.js', () => {
return { OrganizerFillModel: FakeModel };
});
import { OrganizerRepository } from './organizer.repository.js';
import { OrganizerRepository } from '../../../src/modules/organizer/organizer.repository.js';
describe(OrganizerRepository.name, () => {
let repo: OrganizerRepository;

View file

@ -27,7 +27,7 @@ const { mockListFills, mockGetFillById, mockPreview, mockFill, mockUndoFill } =
mockUndoFill: vi.fn(),
}));
vi.mock('./organizer.repository.js', () => ({
vi.mock('../../../src/modules/organizer/organizer.repository.js', () => ({
OrganizerRepository: class {
create = vi.fn();
update = vi.fn();
@ -37,7 +37,7 @@ vi.mock('./organizer.repository.js', () => ({
},
}));
vi.mock('./organizer.service.js', () => ({
vi.mock('../../../src/modules/organizer/organizer.service.js', () => ({
OrganizerService: class {
listFills = mockListFills;
getFillById = mockGetFillById;
@ -47,17 +47,17 @@ vi.mock('./organizer.service.js', () => ({
},
}));
vi.mock('../users/users.repository.js', () => ({
vi.mock('../../../src/modules/users/users.repository.js', () => ({
UsersRepository: class {
findByKeycloakId = vi.fn().mockResolvedValue({ householdIds: ['hh1'] });
upsertFromToken = vi.fn().mockResolvedValue({ householdIds: ['hh1'] });
},
}));
import authPlugin from '../../plugins/auth.plugin.js';
import householdPlugin from '../../plugins/household.plugin.js';
import usersRoutes from '../users/users.routes.js';
import organizerRoutes from './organizer.routes.js';
import authPlugin from '../../../src/plugins/auth.plugin.js';
import householdPlugin from '../../../src/plugins/household.plugin.js';
import usersRoutes from '../../../src/modules/users/users.routes.js';
import organizerRoutes from '../../../src/modules/organizer/organizer.routes.js';
function makeFakeFill(overrides = {}) {
return {

View file

@ -19,7 +19,7 @@ vi.mock('mongoose', () => {
return { default: { startSession: vi.fn().mockResolvedValue(mockSession) } };
});
import { OrganizerService } from './organizer.service.js';
import { OrganizerService } from '../../../src/modules/organizer/organizer.service.js';
describe(OrganizerService.name, () => {
const mockOrganizerRepo = {

View file

@ -1,5 +1,5 @@
import { describe, it, expect } from 'vitest';
import { FreshnessCalculatorService } from './freshness-calculator.service.js';
import { FreshnessCalculatorService } from '../../../src/modules/pantry/freshness-calculator.service.js';
import { ItemStatus, FreshnessUrgency, FreshnessSource, StorageLocation } from '@meshitrack/shared';
describe(FreshnessCalculatorService.name, () => {

View file

@ -20,7 +20,7 @@ const {
mockFindByIdAndUpdate: vi.fn(),
}));
vi.mock('../../schemas/pantry-item.schema.js', () => {
vi.mock('../../../src/schemas/pantry-item.schema.js', () => {
const chain = () => ({
sort: vi.fn().mockReturnThis(),
limit: vi.fn().mockReturnThis(),
@ -75,7 +75,7 @@ vi.mock('../../schemas/pantry-item.schema.js', () => {
return { PantryItemModel: FakeModel };
});
import { PantryRepository } from './pantry.repository.js';
import { PantryRepository } from '../../../src/modules/pantry/pantry.repository.js';
describe(PantryRepository.name, () => {
let repo: PantryRepository;

View file

@ -54,7 +54,7 @@ const { mockFindApplicableRule } = vi.hoisted(() => ({
mockFindApplicableRule: vi.fn(),
}));
vi.mock('./pantry.repository.js', () => ({
vi.mock('../../../src/modules/pantry/pantry.repository.js', () => ({
PantryRepository: class {
findByHousehold = mockFindByHousehold;
findById = mockFindById;
@ -71,14 +71,14 @@ vi.mock('./pantry.repository.js', () => ({
},
}));
vi.mock('../products/products.repository.js', () => ({
vi.mock('../../../src/modules/products/products.repository.js', () => ({
ProductsRepository: class {
findById = mockProductFindById;
findByIds = vi.fn();
},
}));
vi.mock('../freshness-rules/freshness-rules.repository.js', () => ({
vi.mock('../../../src/modules/freshness-rules/freshness-rules.repository.js', () => ({
FreshnessRulesRepository: class {
findApplicableRule = mockFindApplicableRule;
findByHousehold = vi.fn();
@ -89,17 +89,17 @@ vi.mock('../freshness-rules/freshness-rules.repository.js', () => ({
},
}));
vi.mock('../users/users.repository.js', () => ({
vi.mock('../../../src/modules/users/users.repository.js', () => ({
UsersRepository: class {
findByKeycloakId = vi.fn().mockResolvedValue({ householdIds: ['hh1'] });
upsertFromToken = vi.fn().mockResolvedValue({ householdIds: ['hh1'] });
},
}));
import authPlugin from '../../plugins/auth.plugin.js';
import householdPlugin from '../../plugins/household.plugin.js';
import usersRoutes from '../users/users.routes.js';
import pantryRoutes from './pantry.routes.js';
import authPlugin from '../../../src/plugins/auth.plugin.js';
import householdPlugin from '../../../src/plugins/household.plugin.js';
import usersRoutes from '../../../src/modules/users/users.routes.js';
import pantryRoutes from '../../../src/modules/pantry/pantry.routes.js';
const freshness = {
estimatedExpiryDate: new Date('2024-02-01').toISOString(),

View file

@ -1,6 +1,6 @@
import { describe, it, expect, vi, beforeEach } from 'vitest';
import { PantryService } from './pantry.service.js';
import { NotFoundError, BadRequestError } from '../../common/errors.js';
import { PantryService } from '../../../src/modules/pantry/pantry.service.js';
import { NotFoundError, BadRequestError } from '../../../src/common/errors.js';
import { ItemStatus } from '@meshitrack/shared';
const mockPantryRepo = {

View file

@ -1,5 +1,5 @@
import { describe, it, expect, vi, beforeEach } from 'vitest';
import { PricesRepository } from './prices.repository.js';
import { PricesRepository } from '../../../src/modules/prices/prices.repository.js';
const { mockSave, MockPriceRecordModel } = vi.hoisted(() => {
const mockSave = vi.fn();
@ -17,11 +17,11 @@ const { mockSave, MockPriceRecordModel } = vi.hoisted(() => {
return { mockSave, MockPriceRecordModel: MockModel };
});
vi.mock('../../schemas/price-record.schema.js', () => ({
vi.mock('../../../src/schemas/price-record.schema.js', () => ({
PriceRecordModel: MockPriceRecordModel,
}));
const { PriceRecordModel } = await import('../../schemas/price-record.schema.js');
const { PriceRecordModel } = await import('../../../src/schemas/price-record.schema.js');
function makeChain(result: unknown = null) {
return {

View file

@ -23,7 +23,7 @@ const mockFindByProduct = vi.fn();
const mockCompareStores = vi.fn();
const mockGetAnalytics = vi.fn();
vi.mock('./prices.repository.js', () => ({
vi.mock('../../../src/modules/prices/prices.repository.js', () => ({
PricesRepository: class {
create = mockCreate;
createMany = mockCreateMany;
@ -33,30 +33,30 @@ vi.mock('./prices.repository.js', () => ({
},
}));
vi.mock('../products/products.repository.js', () => ({
vi.mock('../../../src/modules/products/products.repository.js', () => ({
ProductsRepository: class {
findById = vi.fn().mockResolvedValue({ name: 'Mock Product' });
findByIds = vi.fn().mockResolvedValue([{ _id: 'p1', name: 'Mock Product' }]);
},
}));
vi.mock('../stores/stores.repository.js', () => ({
vi.mock('../../../src/modules/stores/stores.repository.js', () => ({
StoresRepository: class {
findById = vi.fn().mockResolvedValue({ name: 'Mock Store' });
},
}));
vi.mock('../users/users.repository.js', () => ({
vi.mock('../../../src/modules/users/users.repository.js', () => ({
UsersRepository: class {
findByKeycloakId = vi.fn().mockResolvedValue({ householdIds: ['hh1'] });
upsertFromToken = vi.fn().mockResolvedValue({ householdIds: ['hh1'] });
},
}));
import authPlugin from '../../plugins/auth.plugin.js';
import householdPlugin from '../../plugins/household.plugin.js';
import usersRoutes from '../users/users.routes.js';
import pricesRoutes from './prices.routes.js';
import authPlugin from '../../../src/plugins/auth.plugin.js';
import householdPlugin from '../../../src/plugins/household.plugin.js';
import usersRoutes from '../../../src/modules/users/users.routes.js';
import pricesRoutes from '../../../src/modules/prices/prices.routes.js';
describe('prices.routes', () => {
let app: any;

View file

@ -1,6 +1,6 @@
import { describe, it, expect, vi, beforeEach } from 'vitest';
import { PricesService } from './prices.service.js';
import { NotFoundError } from '../../common/errors.js';
import { PricesService } from '../../../src/modules/prices/prices.service.js';
import { NotFoundError } from '../../../src/common/errors.js';
describe('PricesService', () => {
let service: PricesService;

View file

@ -1,5 +1,5 @@
import { describe, it, expect, vi, beforeEach } from 'vitest';
import { BarcodeService } from './barcode.service.js';
import { BarcodeService } from '../../../src/modules/products/barcode.service.js';
vi.mock('undici', () => ({
request: vi.fn(),

View file

@ -1,5 +1,5 @@
import { describe, it, expect } from 'vitest';
import { parseCsv, MAX_FILE_SIZE, MAX_ROWS } from './csv-parser.js';
import { parseCsv, MAX_FILE_SIZE, MAX_ROWS } from '../../../src/modules/products/csv-parser.js';
import { ProductCategory, ServingUnit, ProductSource } from '@meshitrack/shared';
describe('parseCsv', () => {

View file

@ -1,5 +1,5 @@
import { describe, it, expect, vi, beforeEach } from 'vitest';
import { ProductsRepository } from './products.repository.js';
import { ProductsRepository } from '../../../src/modules/products/products.repository.js';
const { mockSave, MockProductModel } = vi.hoisted(() => {
const mockSave = vi.fn();
@ -16,11 +16,11 @@ const { mockSave, MockProductModel } = vi.hoisted(() => {
return { mockSave, MockProductModel };
});
vi.mock('../../schemas/product.schema.js', () => ({
vi.mock('../../../src/schemas/product.schema.js', () => ({
ProductModel: MockProductModel,
}));
const { ProductModel } = await import('../../schemas/product.schema.js');
const { ProductModel } = await import('../../../src/schemas/product.schema.js');
function makeChain(result: unknown = null) {
return {

View file

@ -41,7 +41,7 @@ const {
mockBarcodeLookup: vi.fn(),
}));
vi.mock('./products.repository.js', () => ({
vi.mock('../../../src/modules/products/products.repository.js', () => ({
ProductsRepository: class {
findByHousehold = mockFindByHousehold;
findById = mockFindById;
@ -55,23 +55,23 @@ vi.mock('./products.repository.js', () => ({
},
}));
vi.mock('./barcode.service.js', () => ({
vi.mock('../../../src/modules/products/barcode.service.js', () => ({
BarcodeService: class {
lookup = mockBarcodeLookup;
},
}));
vi.mock('../users/users.repository.js', () => ({
vi.mock('../../../src/modules/users/users.repository.js', () => ({
UsersRepository: class {
findByKeycloakId = vi.fn().mockResolvedValue({ householdIds: ['hh1'] });
upsertFromToken = vi.fn().mockResolvedValue({ householdIds: ['hh1'] });
},
}));
import authPlugin from '../../plugins/auth.plugin.js';
import householdPlugin from '../../plugins/household.plugin.js';
import usersRoutes from '../users/users.routes.js';
import productsRoutes from './products.routes.js';
import authPlugin from '../../../src/plugins/auth.plugin.js';
import householdPlugin from '../../../src/plugins/household.plugin.js';
import usersRoutes from '../../../src/modules/users/users.routes.js';
import productsRoutes from '../../../src/modules/products/products.routes.js';
function makeFakeProduct(overrides: Record<string, unknown> = {}) {
return {

View file

@ -1,6 +1,6 @@
import { describe, it, expect, vi, beforeEach } from 'vitest';
import { ProductsService } from './products.service.js';
import { NotFoundError, ConflictError } from '../../common/errors.js';
import { ProductsService } from '../../../src/modules/products/products.service.js';
import { NotFoundError, ConflictError } from '../../../src/common/errors.js';
import { ProductCategory, ServingUnit, ProductSource } from '@meshitrack/shared';
const mockRepo = {

View file

@ -8,7 +8,7 @@ const { mockFind, mockFindOne, mockFindOneAndUpdate, mockAggregate, mockSave } =
mockSave: vi.fn(),
}));
vi.mock('../../schemas/purchase.schema.js', () => {
vi.mock('../../../src/schemas/purchase.schema.js', () => {
const findChain = () => ({
sort: vi.fn().mockReturnThis(),
limit: vi.fn().mockReturnThis(),
@ -36,7 +36,7 @@ vi.mock('../../schemas/purchase.schema.js', () => {
return { PurchaseModel: FakeModel };
});
import { PurchasesRepository } from './purchases.repository.js';
import { PurchasesRepository } from '../../../src/modules/purchases/purchases.repository.js';
const makeItem = (overrides = {}) => ({
medicineProductId: 'mp-1',
@ -101,7 +101,7 @@ describe(PurchasesRepository.name, () => {
it('filters by status when provided', async () => {
mockFind.mockResolvedValue([]);
const { PurchaseModel } = await import('../../schemas/purchase.schema.js');
const { PurchaseModel } = await import('../../../src/schemas/purchase.schema.js');
await repo.findByHousehold('hh1', { limit: 20, status: 'ordered' });
@ -112,7 +112,7 @@ describe(PurchasesRepository.name, () => {
it('filters by storeId when provided', async () => {
mockFind.mockResolvedValue([]);
const { PurchaseModel } = await import('../../schemas/purchase.schema.js');
const { PurchaseModel } = await import('../../../src/schemas/purchase.schema.js');
await repo.findByHousehold('hh1', { limit: 20, storeId: 'st-1' });
@ -122,7 +122,7 @@ describe(PurchasesRepository.name, () => {
it('applies cursor filter when provided', async () => {
mockFind.mockResolvedValue([]);
const cursor = Buffer.from('p-1').toString('base64');
const { PurchaseModel } = await import('../../schemas/purchase.schema.js');
const { PurchaseModel } = await import('../../../src/schemas/purchase.schema.js');
await repo.findByHousehold('hh1', { limit: 20, cursor });
@ -172,7 +172,7 @@ describe(PurchasesRepository.name, () => {
it('includes items in update set when provided', async () => {
const updated = { _id: 'p-1' };
mockFindOneAndUpdate.mockResolvedValue(updated);
const { PurchaseModel } = await import('../../schemas/purchase.schema.js');
const { PurchaseModel } = await import('../../../src/schemas/purchase.schema.js');
const items = [{ name: 'X', quantity: 1, unit: 'tablet', addedToCabinet: false }];
await repo.update('p-1', 'hh1', { items } as never);
@ -189,7 +189,7 @@ describe(PurchasesRepository.name, () => {
it('sets status to in_cabinet and all items addedToCabinet', async () => {
const updated = { _id: 'p-1', status: 'in_cabinet' };
mockFindOneAndUpdate.mockResolvedValue(updated);
const { PurchaseModel } = await import('../../schemas/purchase.schema.js');
const { PurchaseModel } = await import('../../../src/schemas/purchase.schema.js');
const result = await repo.receiveAll('p-1', 'hh1');
@ -208,7 +208,7 @@ describe(PurchasesRepository.name, () => {
it('builds per-index update set and calls findOneAndUpdate', async () => {
const updated = { _id: 'p-1', items: [] };
mockFindOneAndUpdate.mockResolvedValue(updated);
const { PurchaseModel } = await import('../../schemas/purchase.schema.js');
const { PurchaseModel } = await import('../../../src/schemas/purchase.schema.js');
const result = await repo.markItemsAddedToCabinet('p-1', 'hh1', [0, 2]);

View file

@ -29,7 +29,7 @@ const { mockList, mockGetById, mockCreate, mockUpdate, mockReceive, mockDelete }
}),
);
vi.mock('./purchases.repository.js', () => ({
vi.mock('../../../src/modules/purchases/purchases.repository.js', () => ({
PurchasesRepository: class {
create = vi.fn();
findByHousehold = vi.fn();
@ -41,7 +41,7 @@ vi.mock('./purchases.repository.js', () => ({
},
}));
vi.mock('./purchases.service.js', () => ({
vi.mock('../../../src/modules/purchases/purchases.service.js', () => ({
PurchasesService: class {
list = mockList;
getById = mockGetById;
@ -52,17 +52,17 @@ vi.mock('./purchases.service.js', () => ({
},
}));
vi.mock('../users/users.repository.js', () => ({
vi.mock('../../../src/modules/users/users.repository.js', () => ({
UsersRepository: class {
findByKeycloakId = vi.fn().mockResolvedValue({ householdIds: ['hh1'] });
upsertFromToken = vi.fn().mockResolvedValue({ householdIds: ['hh1'] });
},
}));
import authPlugin from '../../plugins/auth.plugin.js';
import householdPlugin from '../../plugins/household.plugin.js';
import usersRoutes from '../users/users.routes.js';
import purchasesRoutes from './purchases.routes.js';
import authPlugin from '../../../src/plugins/auth.plugin.js';
import householdPlugin from '../../../src/plugins/household.plugin.js';
import usersRoutes from '../../../src/modules/users/users.routes.js';
import purchasesRoutes from '../../../src/modules/purchases/purchases.routes.js';
function makeFakePurchase(overrides = {}) {
return {

View file

@ -1,5 +1,5 @@
import { describe, it, expect, vi, beforeEach } from 'vitest';
import { PurchasesService } from './purchases.service.js';
import { PurchasesService } from '../../../src/modules/purchases/purchases.service.js';
describe(PurchasesService.name, () => {
const mockPurchasesRepo = {

View file

@ -1,5 +1,5 @@
import { describe, it, expect } from 'vitest';
import { NutritionCalculatorService } from './nutrition-calculator.service.js';
import { NutritionCalculatorService } from '../../../src/modules/recipes/nutrition-calculator.service.js';
import { NutritionWarning } from '@meshitrack/shared';
const service = new NutritionCalculatorService();

View file

@ -7,7 +7,7 @@ const { mockFind, mockFindOne, mockFindOneAndUpdate, mockSave } = vi.hoisted(()
mockSave: vi.fn(),
}));
vi.mock('../../schemas/recipe.schema.js', () => {
vi.mock('../../../src/schemas/recipe.schema.js', () => {
const chain = () => ({
sort: vi.fn().mockReturnThis(),
limit: vi.fn().mockReturnThis(),
@ -41,7 +41,7 @@ vi.mock('../../schemas/recipe.schema.js', () => {
return { RecipeModel: FakeModel };
});
import { RecipesRepository } from './recipes.repository.js';
import { RecipesRepository } from '../../../src/modules/recipes/recipes.repository.js';
describe(RecipesRepository.name, () => {
let repo: RecipesRepository;

View file

@ -40,7 +40,7 @@ const { mockFindByIds } = vi.hoisted(() => ({
mockFindByIds: vi.fn(),
}));
vi.mock('./recipes.repository.js', () => ({
vi.mock('../../../src/modules/recipes/recipes.repository.js', () => ({
RecipesRepository: class {
findByHousehold = mockFindByHousehold;
findById = mockFindById;
@ -52,24 +52,24 @@ vi.mock('./recipes.repository.js', () => ({
},
}));
vi.mock('../products/products.repository.js', () => ({
vi.mock('../../../src/modules/products/products.repository.js', () => ({
ProductsRepository: class {
findByIds = mockFindByIds;
findById = vi.fn();
},
}));
vi.mock('../users/users.repository.js', () => ({
vi.mock('../../../src/modules/users/users.repository.js', () => ({
UsersRepository: class {
findByKeycloakId = vi.fn().mockResolvedValue({ householdIds: ['hh1'] });
upsertFromToken = vi.fn().mockResolvedValue({ householdIds: ['hh1'] });
},
}));
import authPlugin from '../../plugins/auth.plugin.js';
import householdPlugin from '../../plugins/household.plugin.js';
import usersRoutes from '../users/users.routes.js';
import recipesRoutes from './recipes.routes.js';
import authPlugin from '../../../src/plugins/auth.plugin.js';
import householdPlugin from '../../../src/plugins/household.plugin.js';
import usersRoutes from '../../../src/modules/users/users.routes.js';
import recipesRoutes from '../../../src/modules/recipes/recipes.routes.js';
const nutrition = { calories: 200, protein: 20, carbs: 10, fat: 8 };

View file

@ -1,6 +1,6 @@
import { describe, it, expect, vi, beforeEach } from 'vitest';
import { RecipesService } from './recipes.service.js';
import { NotFoundError, BadRequestError } from '../../common/errors.js';
import { RecipesService } from '../../../src/modules/recipes/recipes.service.js';
import { NotFoundError, BadRequestError } from '../../../src/common/errors.js';
const makeProduct = (id: string, servingUnit = 'g', servingSize = 100) => ({
_id: { toString: () => id },

View file

@ -1,5 +1,5 @@
import { describe, it, expect } from 'vitest';
import { toMetric } from './unit-conversion.service.js';
import { toMetric } from '../../../src/modules/recipes/unit-conversion.service.js';
describe('toMetric', () => {
describe('metric pass-through', () => {

View file

@ -7,7 +7,7 @@ const { mockFind, mockFindOne, mockFindOneAndUpdate, mockSave } = vi.hoisted(()
mockSave: vi.fn(),
}));
vi.mock('../../schemas/refill-list.schema.js', () => {
vi.mock('../../../src/schemas/refill-list.schema.js', () => {
const chain = () => ({
sort: vi.fn().mockReturnThis(),
limit: vi.fn().mockReturnThis(),
@ -33,7 +33,7 @@ vi.mock('../../schemas/refill-list.schema.js', () => {
return { RefillListModel: FakeModel };
});
import { RefillsRepository } from './refills.repository.js';
import { RefillsRepository } from '../../../src/modules/refills/refills.repository.js';
describe(RefillsRepository.name, () => {
let repo: RefillsRepository;

View file

@ -38,7 +38,7 @@ const {
mockGetStoreComparison: vi.fn(),
}));
vi.mock('./refills.repository.js', () => ({
vi.mock('../../../src/modules/refills/refills.repository.js', () => ({
RefillsRepository: class {
create = vi.fn();
findByHousehold = vi.fn();
@ -49,7 +49,7 @@ vi.mock('./refills.repository.js', () => ({
},
}));
vi.mock('./refills.service.js', () => ({
vi.mock('../../../src/modules/refills/refills.service.js', () => ({
RefillsService: class {
getAlerts = mockGetAlerts;
createList = mockCreateList;
@ -62,17 +62,17 @@ vi.mock('./refills.service.js', () => ({
},
}));
vi.mock('../users/users.repository.js', () => ({
vi.mock('../../../src/modules/users/users.repository.js', () => ({
UsersRepository: class {
findByKeycloakId = vi.fn().mockResolvedValue({ householdIds: ['hh1'] });
upsertFromToken = vi.fn().mockResolvedValue({ householdIds: ['hh1'] });
},
}));
import authPlugin from '../../plugins/auth.plugin.js';
import householdPlugin from '../../plugins/household.plugin.js';
import usersRoutes from '../users/users.routes.js';
import refillsRoutes from './refills.routes.js';
import authPlugin from '../../../src/plugins/auth.plugin.js';
import householdPlugin from '../../../src/plugins/household.plugin.js';
import usersRoutes from '../../../src/modules/users/users.routes.js';
import refillsRoutes from '../../../src/modules/refills/refills.routes.js';
function makeFakeRefillList(overrides = {}) {
return {

View file

@ -1,5 +1,5 @@
import { describe, it, expect, vi, beforeEach } from 'vitest';
import { RefillsService } from './refills.service.js';
import { RefillsService } from '../../../src/modules/refills/refills.service.js';
describe(RefillsService.name, () => {
const mockRepo = {

View file

@ -7,7 +7,7 @@ const { mockFind, mockFindOne, mockFindOneAndUpdate, mockSave } = vi.hoisted(()
mockSave: vi.fn(),
}));
vi.mock('../../schemas/regimen.schema.js', () => {
vi.mock('../../../src/schemas/regimen.schema.js', () => {
const chain = () => ({
sort: vi.fn().mockReturnThis(),
limit: vi.fn().mockReturnThis(),
@ -41,7 +41,7 @@ vi.mock('../../schemas/regimen.schema.js', () => {
return { RegimenModel: FakeModel };
});
import { RegimensRepository } from './regimens.repository.js';
import { RegimensRepository } from '../../../src/modules/regimens/regimens.repository.js';
describe(RegimensRepository.name, () => {
let repo: RegimensRepository;

View file

@ -29,7 +29,7 @@ const { mockList, mockGetById, mockCreate, mockUpdate, mockDelete, mockCalculate
mockCalculateBurnRates: vi.fn(),
}));
vi.mock('./regimens.repository.js', () => ({
vi.mock('../../../src/modules/regimens/regimens.repository.js', () => ({
RegimensRepository: class {
findByHousehold = vi.fn();
findById = vi.fn();
@ -41,7 +41,7 @@ vi.mock('./regimens.repository.js', () => ({
},
}));
vi.mock('./regimens.service.js', () => ({
vi.mock('../../../src/modules/regimens/regimens.service.js', () => ({
RegimensService: class {
list = mockList;
getById = mockGetById;
@ -53,17 +53,17 @@ vi.mock('./regimens.service.js', () => ({
},
}));
vi.mock('../users/users.repository.js', () => ({
vi.mock('../../../src/modules/users/users.repository.js', () => ({
UsersRepository: class {
findByKeycloakId = vi.fn().mockResolvedValue({ householdIds: ['hh1'] });
upsertFromToken = vi.fn().mockResolvedValue({ householdIds: ['hh1'] });
},
}));
import authPlugin from '../../plugins/auth.plugin.js';
import householdPlugin from '../../plugins/household.plugin.js';
import usersRoutes from '../users/users.routes.js';
import regimensRoutes from './regimens.routes.js';
import authPlugin from '../../../src/plugins/auth.plugin.js';
import householdPlugin from '../../../src/plugins/household.plugin.js';
import usersRoutes from '../../../src/modules/users/users.routes.js';
import regimensRoutes from '../../../src/modules/regimens/regimens.routes.js';
function makeFakeRegimen(overrides = {}) {
return {

View file

@ -1,5 +1,5 @@
import { describe, it, expect, vi, beforeEach } from 'vitest';
import { RegimensService } from './regimens.service.js';
import { RegimensService } from '../../../src/modules/regimens/regimens.service.js';
import { DosageFrequency } from '@meshitrack/shared';
describe(RegimensService.name, () => {

View file

@ -1,5 +1,5 @@
import { describe, it, expect, vi, beforeEach } from 'vitest';
import { ShoppingListsRepository } from './shopping-lists.repository.js';
import { ShoppingListsRepository } from '../../../src/modules/shopping-lists/shopping-lists.repository.js';
const { mockSave, MockShoppingListModel } = vi.hoisted(() => {
const mockSave = vi.fn();
@ -16,11 +16,11 @@ const { mockSave, MockShoppingListModel } = vi.hoisted(() => {
return { mockSave, MockShoppingListModel: MockModel };
});
vi.mock('../../schemas/shopping-list.schema.js', () => ({
vi.mock('../../../src/schemas/shopping-list.schema.js', () => ({
ShoppingListModel: MockShoppingListModel,
}));
const { ShoppingListModel } = await import('../../schemas/shopping-list.schema.js');
const { ShoppingListModel } = await import('../../../src/schemas/shopping-list.schema.js');
function makeChain(result: unknown = null) {
return {

View file

@ -26,7 +26,7 @@ const mockAddItem = vi.fn();
const mockUpdateItem = vi.fn();
const mockRemoveItem = vi.fn();
vi.mock('./shopping-lists.repository.js', () => ({
vi.mock('../../../src/modules/shopping-lists/shopping-lists.repository.js', () => ({
ShoppingListsRepository: class {
list = mockList;
findById = mockFindById;
@ -39,25 +39,25 @@ vi.mock('./shopping-lists.repository.js', () => ({
},
}));
vi.mock('../meal-plans/shopping-gap.service.js', () => ({
vi.mock('../../../src/modules/meal-plans/shopping-gap.service.js', () => ({
ShoppingGapService: class {
calculateGap = vi.fn().mockResolvedValue({ missingItems: [] });
},
}));
vi.mock('../pantry/pantry.service.js', () => ({
vi.mock('../../../src/modules/pantry/pantry.service.js', () => ({
PantryService: class {
create = vi.fn().mockResolvedValue({ _id: 'pant1' });
},
}));
vi.mock('../products/products.repository.js', () => ({
vi.mock('../../../src/modules/products/products.repository.js', () => ({
ProductsRepository: class {
findById = vi.fn().mockResolvedValue({ category: 'dairy' });
},
}));
vi.mock('../prices/prices.service.js', () => ({
vi.mock('../../../src/modules/prices/prices.service.js', () => ({
PricesService: class {
estimatePrice = vi.fn().mockResolvedValue(5.0);
recordPrice = vi.fn().mockResolvedValue({});
@ -65,24 +65,24 @@ vi.mock('../prices/prices.service.js', () => ({
},
}));
vi.mock('../meal-plans/meal-plans.repository.js', () => ({
vi.mock('../../../src/modules/meal-plans/meal-plans.repository.js', () => ({
MealPlanRepository: class {
findById = vi.fn().mockResolvedValue({ _id: 'mp1', weekStartDate: new Date() });
update = vi.fn().mockResolvedValue({});
},
}));
vi.mock('../users/users.repository.js', () => ({
vi.mock('../../../src/modules/users/users.repository.js', () => ({
UsersRepository: class {
findByKeycloakId = vi.fn().mockResolvedValue({ householdIds: ['hh1'] });
upsertFromToken = vi.fn().mockResolvedValue({ householdIds: ['hh1'] });
},
}));
import authPlugin from '../../plugins/auth.plugin.js';
import householdPlugin from '../../plugins/household.plugin.js';
import usersRoutes from '../users/users.routes.js';
import shoppingListsRoutes from './shopping-lists.routes.js';
import authPlugin from '../../../src/plugins/auth.plugin.js';
import householdPlugin from '../../../src/plugins/household.plugin.js';
import usersRoutes from '../../../src/modules/users/users.routes.js';
import shoppingListsRoutes from '../../../src/modules/shopping-lists/shopping-lists.routes.js';
describe('shopping-lists.routes', () => {
let app: any;

View file

@ -1,6 +1,6 @@
import { describe, it, expect, vi, beforeEach } from 'vitest';
import { ShoppingListsService } from './shopping-lists.service.js';
import { NotFoundError } from '../../common/errors.js';
import { ShoppingListsService } from '../../../src/modules/shopping-lists/shopping-lists.service.js';
import { NotFoundError } from '../../../src/common/errors.js';
describe('ShoppingListsService', () => {
let service: ShoppingListsService;

View file

@ -7,7 +7,7 @@ const { mockFind, mockFindOne, mockFindOneAndUpdate, mockSave } = vi.hoisted(()
mockSave: vi.fn(),
}));
vi.mock('../../schemas/store.schema.js', () => {
vi.mock('../../../src/schemas/store.schema.js', () => {
const chain = () => ({
sort: vi.fn().mockReturnThis(),
limit: vi.fn().mockReturnThis(),
@ -33,7 +33,7 @@ vi.mock('../../schemas/store.schema.js', () => {
return { StoreModel: FakeModel };
});
import { StoresRepository } from './stores.repository.js';
import { StoresRepository } from '../../../src/modules/stores/stores.repository.js';
describe(StoresRepository.name, () => {
let repo: StoresRepository;

View file

@ -26,7 +26,7 @@ const { mockList, mockGetById, mockCreate, mockUpdate, mockDeactivate } = vi.hoi
mockDeactivate: vi.fn(),
}));
vi.mock('./stores.repository.js', () => ({
vi.mock('../../../src/modules/stores/stores.repository.js', () => ({
StoresRepository: class {
findByHousehold = vi.fn();
findById = vi.fn();
@ -36,7 +36,7 @@ vi.mock('./stores.repository.js', () => ({
},
}));
vi.mock('./stores.service.js', () => ({
vi.mock('../../../src/modules/stores/stores.service.js', () => ({
StoresService: class {
list = mockList;
getById = mockGetById;
@ -46,17 +46,17 @@ vi.mock('./stores.service.js', () => ({
},
}));
vi.mock('../users/users.repository.js', () => ({
vi.mock('../../../src/modules/users/users.repository.js', () => ({
UsersRepository: class {
findByKeycloakId = vi.fn().mockResolvedValue({ householdIds: ['hh1'] });
upsertFromToken = vi.fn().mockResolvedValue({ householdIds: ['hh1'] });
},
}));
import authPlugin from '../../plugins/auth.plugin.js';
import householdPlugin from '../../plugins/household.plugin.js';
import usersRoutes from '../users/users.routes.js';
import storesRoutes from './stores.routes.js';
import authPlugin from '../../../src/plugins/auth.plugin.js';
import householdPlugin from '../../../src/plugins/household.plugin.js';
import usersRoutes from '../../../src/modules/users/users.routes.js';
import storesRoutes from '../../../src/modules/stores/stores.routes.js';
function makeFakeStore(overrides = {}) {
return {

View file

@ -1,5 +1,5 @@
import { describe, it, expect, vi, beforeEach } from 'vitest';
import { StoresService } from './stores.service.js';
import { StoresService } from '../../../src/modules/stores/stores.service.js';
describe(StoresService.name, () => {
const mockRepo = {

View file

@ -15,7 +15,7 @@ const { mockLean, mockExec, mockFindOne, mockFindById, mockFindOneAndUpdate, moc
};
});
vi.mock('../../schemas/user.schema.js', () => {
vi.mock('../../../src/schemas/user.schema.js', () => {
class MockUserModel {
_data: Record<string, unknown>;
constructor(data: Record<string, unknown>) {
@ -36,7 +36,7 @@ vi.mock('../../schemas/user.schema.js', () => {
return { UserModel: MockUserModel };
});
import { UsersRepository } from './users.repository.js';
import { UsersRepository } from '../../../src/modules/users/users.repository.js';
describe('UsersRepository', () => {
let repo: UsersRepository;

View file

@ -24,15 +24,15 @@ const { mockUpsertFromToken, mockFindByKeycloakId } = vi.hoisted(() => ({
mockUpsertFromToken: vi.fn(),
mockFindByKeycloakId: vi.fn(),
}));
vi.mock('./users.repository.js', () => ({
vi.mock('../../../src/modules/users/users.repository.js', () => ({
UsersRepository: class MockUsersRepository {
upsertFromToken = mockUpsertFromToken;
findByKeycloakId = mockFindByKeycloakId;
},
}));
import authPlugin from '../../plugins/auth.plugin.js';
import usersRoutes from './users.routes.js';
import authPlugin from '../../../src/plugins/auth.plugin.js';
import usersRoutes from '../../../src/modules/users/users.routes.js';
describe('users.routes', () => {
async function buildTestApp() {

View file

@ -1,6 +1,6 @@
import { describe, it, expect, vi, beforeEach } from 'vitest';
import { UsersService } from './users.service.js';
import { NotFoundError } from '../../common/errors.js';
import { UsersService } from '../../../src/modules/users/users.service.js';
import { NotFoundError } from '../../../src/common/errors.js';
describe('UsersService', () => {
const mockRepo = {

View file

@ -19,7 +19,7 @@ const { mockFindByKeycloakId, mockUpsertFromToken } = vi.hoisted(() => ({
mockUpsertFromToken: vi.fn(),
}));
import authPlugin from './auth.plugin.js';
import authPlugin from '../../src/plugins/auth.plugin.js';
import * as jose from 'jose';
describe('auth.plugin', () => {

View file

@ -22,8 +22,8 @@ const { mockFindByKeycloakId } = vi.hoisted(() => ({
mockFindByKeycloakId: vi.fn(),
}));
import authPlugin from './auth.plugin.js';
import householdPlugin from './household.plugin.js';
import authPlugin from '../../src/plugins/auth.plugin.js';
import householdPlugin from '../../src/plugins/household.plugin.js';
describe('household.plugin', () => {
async function buildApp() {

View file

@ -15,7 +15,7 @@ vi.mock('@fastify/awilix', () => ({
},
}));
import mongoosePlugin from './mongoose.plugin.js';
import mongoosePlugin from '../../src/plugins/mongoose.plugin.js';
import mongoose from 'mongoose';
import { diContainer } from '@fastify/awilix';

View file

@ -1,5 +1,5 @@
import { describe, it, expect } from 'vitest';
import { HouseholdModel } from './household.schema.js';
import { HouseholdModel } from '../../src/schemas/household.schema.js';
describe('HouseholdModel', () => {
it('is a valid mongoose model', () => {

View file

@ -1,5 +1,5 @@
import { describe, it, expect } from 'vitest';
import { MedicineProductModel } from './medicine-product.schema.js';
import { MedicineProductModel } from '../../src/schemas/medicine-product.schema.js';
describe(MedicineProductModel.name, () => {
it('is a valid mongoose model', () => {

View file

@ -1,5 +1,5 @@
import { describe, it, expect } from 'vitest';
import { MedicineModel } from './medicine.schema.js';
import { MedicineModel } from '../../src/schemas/medicine.schema.js';
describe(MedicineModel.name, () => {
it('is a valid mongoose model', () => {

View file

@ -1,5 +1,5 @@
import { describe, it, expect } from 'vitest';
import { ProductModel } from './product.schema.js';
import { ProductModel } from '../../src/schemas/product.schema.js';
describe(ProductModel.name, () => {
it('is a valid mongoose model', () => {

View file

@ -1,5 +1,5 @@
import { describe, it, expect } from 'vitest';
import { UserModel } from './user.schema.js';
import { UserModel } from '../../src/schemas/user.schema.js';
describe('UserModel', () => {
it('is a valid mongoose model', () => {

View file

@ -4,14 +4,13 @@ export default defineConfig({
test: {
globals: true,
environment: 'node',
include: ['src/**/*.test.ts'],
include: ['tests/**/*.test.ts'],
coverage: {
provider: 'v8',
enabled: false, // enable via --coverage flag or test:cov script
all: true,
include: ['src/**/*.ts'],
exclude: [
'src/**/*.test.ts',
'src/scripts/**',
'src/common/types.ts', // declaration merging only — no runtime logic
],