Tests refactor
This commit is contained in:
parent
245520fb50
commit
99134d8556
165 changed files with 911 additions and 531 deletions
|
|
@ -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'],
|
||||
}
|
||||
},
|
||||
);
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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';
|
||||
|
|
|
|||
|
|
@ -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[] = [];
|
||||
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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'],
|
||||
}
|
||||
},
|
||||
);
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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 };
|
||||
|
||||
|
|
|
|||
|
|
@ -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'],
|
||||
}
|
||||
},
|
||||
);
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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'],
|
||||
}
|
||||
},
|
||||
);
|
||||
|
|
|
|||
|
|
@ -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 };
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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 });
|
||||
|
|
|
|||
|
|
@ -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', () => {
|
||||
|
|
@ -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', () => {
|
||||
34
packages/api/tests/helpers/mock-repository.ts
Normal file
34
packages/api/tests/helpers/mock-repository.ts
Normal 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>;
|
||||
}
|
||||
|
|
@ -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(() => {
|
||||
|
|
@ -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;
|
||||
|
|
@ -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 {
|
||||
|
|
@ -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 = {
|
||||
|
|
@ -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;
|
||||
|
|
@ -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 {
|
||||
|
|
@ -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, () => {
|
||||
|
|
@ -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;
|
||||
|
|
@ -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 {
|
||||
|
|
@ -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 = {
|
||||
|
|
@ -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() {
|
||||
|
|
@ -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;
|
||||
|
|
@ -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 {
|
||||
|
|
@ -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
|
||||
|
|
@ -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();
|
||||
|
|
@ -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 {
|
||||
|
|
@ -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 };
|
||||
|
||||
|
|
@ -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;
|
||||
|
|
@ -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;
|
||||
|
|
@ -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;
|
||||
|
|
@ -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;
|
||||
|
|
@ -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 {
|
||||
|
|
@ -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 = {
|
||||
|
|
@ -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;
|
||||
|
|
@ -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 {
|
||||
|
|
@ -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, () => {
|
||||
|
|
@ -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;
|
||||
|
|
@ -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 {
|
||||
|
|
@ -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, () => {
|
||||
|
|
@ -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 {
|
||||
|
|
@ -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 {
|
||||
|
|
@ -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;
|
||||
|
|
@ -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;
|
||||
|
|
@ -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 {
|
||||
|
|
@ -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 = {
|
||||
|
|
@ -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, () => {
|
||||
|
|
@ -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;
|
||||
|
|
@ -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(),
|
||||
|
|
@ -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 = {
|
||||
|
|
@ -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 {
|
||||
|
|
@ -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;
|
||||
|
|
@ -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;
|
||||
|
|
@ -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(),
|
||||
|
|
@ -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', () => {
|
||||
|
|
@ -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 {
|
||||
|
|
@ -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 {
|
||||
|
|
@ -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 = {
|
||||
|
|
@ -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]);
|
||||
|
||||
|
|
@ -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 {
|
||||
|
|
@ -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 = {
|
||||
|
|
@ -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();
|
||||
|
|
@ -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;
|
||||
|
|
@ -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 };
|
||||
|
||||
|
|
@ -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 },
|
||||
|
|
@ -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', () => {
|
||||
|
|
@ -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;
|
||||
|
|
@ -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 {
|
||||
|
|
@ -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 = {
|
||||
|
|
@ -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;
|
||||
|
|
@ -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 {
|
||||
|
|
@ -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, () => {
|
||||
|
|
@ -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 {
|
||||
|
|
@ -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;
|
||||
|
|
@ -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;
|
||||
|
|
@ -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;
|
||||
|
|
@ -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 {
|
||||
|
|
@ -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 = {
|
||||
|
|
@ -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;
|
||||
|
|
@ -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() {
|
||||
|
|
@ -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 = {
|
||||
|
|
@ -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', () => {
|
||||
|
|
@ -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() {
|
||||
|
|
@ -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';
|
||||
|
||||
|
|
@ -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', () => {
|
||||
|
|
@ -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', () => {
|
||||
|
|
@ -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', () => {
|
||||
|
|
@ -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', () => {
|
||||
|
|
@ -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', () => {
|
||||
|
|
@ -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
|
||||
],
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@
|
|||
"lint-fix": "eslint src --fix",
|
||||
"typecheck": "tsc --noEmit",
|
||||
"test": "vitest run",
|
||||
"test:watch": "vitest",
|
||||
"test:cov": "vitest run --coverage"
|
||||
},
|
||||
"dependencies": {
|
||||
|
|
|
|||
|
|
@ -32,7 +32,7 @@ vi.mock('next/link', () => ({
|
|||
default: (props: any) => <a href={props.href}>{props.children}</a>,
|
||||
}));
|
||||
|
||||
import DashboardPage from '../page';
|
||||
import DashboardPage from '../../../../src/app/(dashboard)/dashboard/page';
|
||||
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks();
|
||||
|
|
@ -9,7 +9,7 @@ vi.mock('@/components/layout/TopBar', () => ({
|
|||
TopBar: () => <div data-testid="topbar" />,
|
||||
}));
|
||||
|
||||
import DashboardLayout from '../layout';
|
||||
import DashboardLayout from '../../../src/app/(dashboard)/layout';
|
||||
|
||||
describe('DashboardLayout', () => {
|
||||
it('renders sidebar, topbar and children', () => {
|
||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue