174 lines
4.7 KiB
JavaScript
174 lines
4.7 KiB
JavaScript
export class FakeRecipeRepository {
|
|
constructor() {
|
|
this.recipes = new Map();
|
|
}
|
|
|
|
async listByOwner(ownerGoogleSub) {
|
|
return this.listByOwners([ownerGoogleSub]);
|
|
}
|
|
|
|
async listByOwners(ownerGoogleSubs) {
|
|
return [...this.recipes.values()]
|
|
.filter((recipe) => ownerGoogleSubs.includes(recipe.ownerGoogleSub));
|
|
}
|
|
|
|
async findById(ownerGoogleSub, recipeId) {
|
|
const recipe = this.recipes.get(recipeId);
|
|
return recipe?.ownerGoogleSub === ownerGoogleSub ? recipe : null;
|
|
}
|
|
|
|
async findByIdForOwners(ownerGoogleSubs, recipeId) {
|
|
const recipe = this.recipes.get(recipeId);
|
|
return ownerGoogleSubs.includes(recipe?.ownerGoogleSub) ? recipe : null;
|
|
}
|
|
|
|
async findBySource(ownerGoogleSub, platform, sourceId) {
|
|
return [...this.recipes.values()].find((recipe) => (
|
|
recipe.ownerGoogleSub === ownerGoogleSub
|
|
&& recipe.source.platform === platform
|
|
&& recipe.source.sourceId === sourceId
|
|
)) ?? null;
|
|
}
|
|
|
|
async create(document) {
|
|
if (await this.findBySource(
|
|
document.ownerGoogleSub,
|
|
document.source.platform,
|
|
document.source.sourceId,
|
|
)) {
|
|
const error = new Error('duplicate');
|
|
error.code = 11000;
|
|
throw error;
|
|
}
|
|
this.recipes.set(document._id, document);
|
|
return document;
|
|
}
|
|
|
|
async update(ownerGoogleSub, recipeId, changes) {
|
|
const recipe = await this.findById(ownerGoogleSub, recipeId);
|
|
if (!recipe) return null;
|
|
const updated = { ...recipe, ...changes, updatedAt: new Date() };
|
|
this.recipes.set(recipeId, updated);
|
|
return updated;
|
|
}
|
|
|
|
async delete(ownerGoogleSub, recipeId) {
|
|
const recipe = await this.findById(ownerGoogleSub, recipeId);
|
|
if (!recipe) return null;
|
|
this.recipes.delete(recipeId);
|
|
return recipe;
|
|
}
|
|
}
|
|
|
|
export class FakeGroupRepository {
|
|
constructor(groups = []) {
|
|
this.groups = groups;
|
|
}
|
|
|
|
async listMemberGoogleSubs(googleSub) {
|
|
return [...new Set(this.groups
|
|
.filter((group) => group.memberGoogleSubs.includes(googleSub))
|
|
.flatMap((group) => group.memberGoogleSubs))];
|
|
}
|
|
}
|
|
|
|
export class FakeAllowedEmailRepository {
|
|
constructor(accounts = [
|
|
{ email: 'google-user-1@example.com', canManageAccess: true },
|
|
{ email: 'google-user-2@example.com', canManageAccess: false },
|
|
]) {
|
|
this.accounts = new Map(accounts.map((account) => [account.email, { ...account }]));
|
|
}
|
|
|
|
async findByEmail(email) {
|
|
return this.accounts.get(email?.trim().toLowerCase()) ?? null;
|
|
}
|
|
|
|
async list() {
|
|
return [...this.accounts.values()]
|
|
.map(({ email }) => ({ email }))
|
|
.sort((a, b) => a.email.localeCompare(b.email));
|
|
}
|
|
|
|
async add(email, addedBy) {
|
|
const normalizedEmail = email.trim().toLowerCase();
|
|
const account = this.accounts.get(normalizedEmail) ?? {
|
|
email: normalizedEmail,
|
|
canManageAccess: false,
|
|
addedBy,
|
|
};
|
|
this.accounts.set(normalizedEmail, account);
|
|
return account;
|
|
}
|
|
|
|
async remove(email) {
|
|
return this.accounts.delete(email.trim().toLowerCase());
|
|
}
|
|
}
|
|
|
|
export const fakeUserRepository = {
|
|
async upsertGoogleUser(profile) {
|
|
return profile;
|
|
},
|
|
};
|
|
|
|
export function createTestConfig(overrides = {}) {
|
|
return {
|
|
nodeEnv: 'test',
|
|
host: '127.0.0.1',
|
|
port: 3000,
|
|
logLevel: 'silent',
|
|
publicBaseUrl: 'http://localhost:3000',
|
|
mongoUri: 'mongodb://localhost:27017/our_recipe_atlas_test',
|
|
mongoFallbackUri: undefined,
|
|
imageRoot: new URL('../../data/images', import.meta.url).pathname,
|
|
google: {
|
|
clientId: undefined,
|
|
clientSecret: undefined,
|
|
callbackUrl: 'http://localhost:3000/auth/google/callback',
|
|
allowedEmails: new Set(['allowed@example.com']),
|
|
},
|
|
authJwtSecret: 'test-secret-that-is-long-enough-for-tests',
|
|
authSessionTtlHours: 24,
|
|
minimax: {
|
|
apiKey: undefined,
|
|
baseUrl: 'https://api.minimax.io/v1',
|
|
model: 'MiniMax-M2.7',
|
|
},
|
|
instagramSessionCookie: undefined,
|
|
...overrides,
|
|
};
|
|
}
|
|
|
|
export const recipeDraft = {
|
|
title: '김치찌개',
|
|
summary: '간단한 김치찌개',
|
|
servings: '2인분',
|
|
ingredientGroups: [
|
|
{
|
|
name: '재료',
|
|
items: [
|
|
{ name: '김치', amount: '200g' },
|
|
{ name: '물', amount: '500ml' },
|
|
],
|
|
},
|
|
],
|
|
steps: [
|
|
{ order: 1, text: '김치를 볶는다.', timestampSec: null },
|
|
{ order: 2, text: '물을 넣고 끓인다.', timestampSec: null },
|
|
],
|
|
tips: ['신김치를 사용한다.'],
|
|
tags: ['한식'],
|
|
};
|
|
|
|
export const source = {
|
|
platform: 'instagram',
|
|
sourceUrl: 'https://www.instagram.com/reel/Db137BuzUJe/',
|
|
sourceId: 'Db137BuzUJe',
|
|
title: '김치찌개',
|
|
author: 'recipe_author',
|
|
thumbnailUrl: 'https://images.example.com/cover.jpg',
|
|
rawText: '김치 200g과 물 500ml를 넣고 끓입니다.',
|
|
metadata: {},
|
|
};
|