From 8466f9b621f6aca6271454344f62ba832d636068 Mon Sep 17 00:00:00 2001 From: "M. E. Abdelsalam" Date: Tue, 3 Jun 2025 22:32:41 +0300 Subject: [PATCH] refactor: tests package now is being used by models and server tests. --- .github/pull_request_template.md | 21 +- package.json | 2 +- packages/atlas/package.json | 2 + packages/models/fixtures/db.ts | 558 ---------------- packages/models/package.json | 1 + .../models/tests/availabilitySlot.test.ts | 2 +- .../models/tests/confirmationCodes.test.ts | 2 +- packages/models/tests/interviews.test.ts | 2 +- packages/models/tests/invoices.test.ts | 2 +- packages/models/tests/lessons.test.ts | 4 +- packages/models/tests/messages.test.ts | 2 +- packages/models/tests/plans.test.ts | 2 +- packages/models/tests/ratings.test.ts | 2 +- packages/models/tests/rooms.test.ts | 2 +- packages/models/tests/sessionEvents.test.ts | 9 +- packages/models/tests/subscriptions.test.ts | 2 +- packages/models/tests/topics.test.ts | 2 +- packages/models/tests/transactions.test.ts | 2 +- packages/models/tests/tutors.test.ts | 210 +++--- packages/models/tests/users.test.ts | 3 +- packages/tests/package.json | 22 +- packages/tests/src/db.ts | 205 +++--- packages/tests/src/index.ts | 5 +- packages/tests/src/mocks/api.ts | 7 + packages/tests/tsconfig.cjs.json | 8 + ...{tsconfig.build.json => tsconfig.esm.json} | 2 +- packages/tests/tsconfig.json | 9 +- packages/utils/package.json | 2 + pnpm-lock.yaml | 337 +++++----- services/server/fixtures/api.ts | 78 --- services/server/fixtures/db.ts | 612 ------------------ .../server/fixtures/jest}/kafka.ts | 0 .../server/fixtures/jest}/s3.ts | 0 .../server/fixtures/jest}/telegram.ts | 0 .../server/fixtures/jest}/worker.ts | 0 services/server/fixtures/kafka.ts | 16 - services/server/fixtures/mockApi.ts | 81 --- services/server/fixtures/s3.ts | 28 - services/server/fixtures/telegram.ts | 8 - services/server/fixtures/worker.ts | 8 - services/server/fixtures/wss.ts | 70 -- services/server/jest.config.js | 8 +- services/server/tests/handlers/chat.test.ts | 18 +- .../tests/handlers/confirmationCode.test.ts | 3 +- .../tests/handlers/contactRequest.test.ts | 3 +- .../server/tests/handlers/interview.test.ts | 3 +- .../server/tests/handlers/invoice.test.ts | 3 +- services/server/tests/handlers/lesson.test.ts | 3 +- services/server/tests/handlers/plan.test.ts | 3 +- services/server/tests/handlers/rating.test.ts | 5 +- services/server/tests/handlers/slot.test.ts | 3 +- .../tests/handlers/subscription.test.ts | 3 +- services/server/tests/handlers/topic.test.ts | 3 +- .../server/tests/handlers/transaction.test.ts | 5 +- services/server/tests/handlers/user.test.ts | 4 +- .../server/tests/lib/subscription.test.ts | 2 +- services/server/tests/workers/cache.test.ts | 2 +- services/server/tests/wss/messages.test.ts | 4 +- services/server/tests/wss/session.test.ts | 4 +- services/server/tests/wss/typing.test.ts | 6 +- 60 files changed, 457 insertions(+), 1958 deletions(-) delete mode 100644 packages/models/fixtures/db.ts create mode 100644 packages/tests/tsconfig.cjs.json rename packages/tests/{tsconfig.build.json => tsconfig.esm.json} (79%) delete mode 100644 services/server/fixtures/api.ts delete mode 100644 services/server/fixtures/db.ts rename {packages/tests/src/mocks => services/server/fixtures/jest}/kafka.ts (100%) rename {packages/tests/src/mocks => services/server/fixtures/jest}/s3.ts (100%) rename {packages/tests/src/mocks => services/server/fixtures/jest}/telegram.ts (100%) rename {packages/tests/src/mocks => services/server/fixtures/jest}/worker.ts (100%) delete mode 100644 services/server/fixtures/kafka.ts delete mode 100644 services/server/fixtures/mockApi.ts delete mode 100644 services/server/fixtures/s3.ts delete mode 100644 services/server/fixtures/telegram.ts delete mode 100644 services/server/fixtures/worker.ts delete mode 100644 services/server/fixtures/wss.ts diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md index bc72bb3b1..5073df683 100644 --- a/.github/pull_request_template.md +++ b/.github/pull_request_template.md @@ -8,23 +8,22 @@ - [ ] I ensured that the this pull request satisfies all the requirements in the associated task. - [ ] I performed a self-review. - -### Links +### Links - [ClickUp](#task-link-here) - - [Context Video](#context-video-here) diff --git a/package.json b/package.json index 5767a8fa6..0e36db7b5 100644 --- a/package.json +++ b/package.json @@ -116,5 +116,5 @@ "sharp" ] }, - "packageManager": "pnpm@10.11.0+sha256.a69e9cb077da419d47d18f1dd52e207245b29cac6e076acedbeb8be3b1a67bd7" + "packageManager": "pnpm@10.11.1+sha512.e519b9f7639869dc8d5c3c5dfef73b3f091094b0a006d7317353c72b124e80e1afd429732e28705ad6bfa1ee879c1fce46c128ccebd3192101f43dd67c667912" } diff --git a/packages/atlas/package.json b/packages/atlas/package.json index d35b1c5f8..b2ea4f6ab 100644 --- a/packages/atlas/package.json +++ b/packages/atlas/package.json @@ -2,6 +2,8 @@ "name": "@litespace/atlas", "description": "LiteSpace API SDK", "version": "1.0.0", + "main": "dist/cjs/index.js", + "module": "dist/esm/index.js", "files": [ "dist" ], diff --git a/packages/models/fixtures/db.ts b/packages/models/fixtures/db.ts deleted file mode 100644 index 244c271e7..000000000 --- a/packages/models/fixtures/db.ts +++ /dev/null @@ -1,558 +0,0 @@ -import { - hashPassword, - interviews, - knex, - lessons, - messages, - rooms, - sessionEvents, - topics, - users, - ratings, - tutors, - availabilitySlots, - contactRequests, - invoices, -} from "@/index"; -import { - IInterview, - ILesson, - ITopic, - IUser, - IRating, - IMessage, - ISession, - IAvailabilitySlot, - ITutor, - ITransaction, - ISubscription, - IPlan, -} from "@litespace/types"; -import { faker } from "@faker-js/faker/locale/ar"; -import { entries, first, range, sample } from "lodash"; -import { Knex } from "knex"; -import dayjs from "@/lib/dayjs"; -import { Time } from "@litespace/utils/time"; -import { randomInt, randomUUID } from "crypto"; -import { confirmationCodes } from "@/confirmationCodes"; -import { transactions } from "@/transactions"; -import { subscriptions } from "@/subscriptions"; -import { plans } from "@/plans"; -import { percentage, price } from "@litespace/utils"; - -export async function flush() { - await knex.transaction(async (tx) => { - await subscriptions.builder(tx).del(); - await transactions.builder(tx).del(); - await plans.builder(tx).del(); - await invoices.builder(tx).del(); - await sessionEvents.builder(tx).del(); - await topics.builder(tx).userTopics.del(); - await topics.builder(tx).topics.del(); - await messages.builder(tx).del(); - await rooms.builder(tx).members.del(); - await rooms.builder(tx).rooms.del(); - await interviews.builder(tx).del(); - await lessons.builder(tx).members.del(); - await lessons.builder(tx).lessons.del(); - await interviews.builder(tx).del(); - await ratings.builder(tx).del(); - await tutors.builder(tx).del(); - await availabilitySlots.builder(tx).del(); - await confirmationCodes.builder(tx).del(); - await users.builder(tx).del(); - await contactRequests.builder(tx).del(); - }); -} - -export function gender(): IUser.Gender { - return sample([IUser.Gender.Male, IUser.Gender.Female])!; -} - -export function duration(): number { - return sample([15, 30])!; -} - -export function time() { - const times = range(0, 24).map((hour) => - [hour.toString().padStart(2, "0"), "00"].join(":") - ); - return Time.from(sample(times)!).utc().format(); -} - -export async function user( - payload: Omit, "password"> & { - password?: string; - withPassword?: boolean; - } -) { - return await users.create({ - email: payload.email || faker.internet.email(), - gender: payload.gender || gender(), - name: payload.name || faker.internet.username(), - password: payload.withPassword - ? hashPassword(payload.password || faker.internet.password()) - : undefined, - birthYear: payload.birthYear || faker.number.int({ min: 2000, max: 2024 }), - role: payload.role || IUser.Role.Tutor, - }); -} - -export async function slot(payload?: Partial) { - const start = dayjs.utc(payload?.start || faker.date.future()); - const end = start.add(faker.number.int(8), "hours"); - - const slots = await availabilitySlots.create([ - { - userId: await or.tutorId(payload?.userId), - start: start.toISOString(), - end: end.toISOString(), - }, - ]); - const slot = first(slots); - if (!slot) throw new Error("Slot not found; should never happen"); - return slot; -} - -const or = { - async tutorId(id?: number): Promise { - if (!id) return await tutor().then((tutor) => tutor.id); - return id; - }, - async studentId(id?: number): Promise { - if (!id) return await student().then((student) => student.id); - return id; - }, - async tutorManagerId(id?: number): Promise { - if (!id) - return await tutorManager().then((tutorManager) => tutorManager.id); - return id; - }, - async sessionId(type: ISession.Type): Promise { - return `${type}:${randomUUID()}`; - }, - async roomId(roomId?: number): Promise { - if (!roomId) return await makeRoom(); - return roomId; - }, - async slotId(id?: number): Promise { - if (!id) return await slot().then((slot) => slot.id); - return id; - }, - async planId(id?: number): Promise { - if (!id) return await plan().then((plan) => plan.id); - return id; - }, - planPeriod(period?: IPlan.Period) { - if (!period) - return sample([ - IPlan.Period.Month, - IPlan.Period.Quarter, - IPlan.Period.Year, - ]); - return period; - }, - boolean(cond?: boolean) { - if (cond === undefined) return sample([false, true]); - return cond; - }, - start(start?: string): string { - if (!start) return faker.date.soon().toISOString(); - return start; - }, -} as const; - -type LessonReturn = { - lesson: ILesson.Self; - members: ILesson.Member[]; -}; - -export async function lesson( - payload?: Partial & { - timing?: "future" | "past"; - canceled?: boolean; - } -): Promise { - return await knex.transaction(async (tx: Knex.Transaction) => { - const tutor = await or.tutorId(payload?.tutor); - const student = await or.studentId(payload?.student); - const data = await lessons.create({ - session: await or.sessionId("lesson"), - start: - payload?.timing === "future" - ? faker.date.future().toISOString() - : payload?.timing === "past" - ? faker.date.past().toISOString() - : payload?.start || faker.date.soon().toISOString(), - duration: payload?.duration || sample([15, 30]), - price: payload?.price || faker.number.int(500), - slot: await or.slotId(payload?.slot), - student, - tutor, - tx, - }); - - if (payload?.canceled) - await lessons.cancel({ - canceledBy: tutor, - ids: [data.lesson.id], - tx, - }); - const lesson = await lessons.findById(data.lesson.id); - return { lesson: lesson || data.lesson, members: data.members }; - }); -} - -export async function interview(payload: Partial) { - return await interviews.create({ - interviewer: await or.tutorManagerId(payload.interviewer), - interviewee: await or.tutorId(payload.interviewee), - session: await or.sessionId("interview"), - slot: await or.slotId(payload.slot), - start: or.start(payload.start), - }); -} - -export async function topic(payload?: Partial) { - return await topics.create({ - name: { - ar: payload?.name?.ar || faker.animal.bear(), - en: payload?.name?.en || faker.animal.bird(), - }, - }); -} - -async function tutor( - tutorPayload?: Partial, - userPayload?: Partial, - withPassword?: boolean -) { - const info = await user({ role: IUser.Role.Tutor, withPassword }); - const tutor = await tutors.create(info.id); - await tutors.update(tutor.id, tutorPayload || {}); - await users.update(tutor.id, userPayload || {}); - const data = await tutors.findById(tutor.id); - if (!data) throw new Error("tutor not found"); - return data; -} - -function student() { - return user({ role: IUser.Role.Student }); -} - -async function tutorManager( - tutorPayload?: Partial, - userPayload?: Partial -) { - const info = await user({ role: IUser.Role.TutorManager }); - const tutor = await tutors.create(info.id); - await tutors.update(tutor.id, tutorPayload || {}); - await users.update(tutor.id, userPayload || {}); - const data = await tutors.findById(tutor.id); - if (!data) throw new Error("tutor not found"); - return data; -} - -async function students(count: number) { - return await Promise.all(range(0, count).map(() => student())); -} - -async function makeTutors(count: number) { - return await Promise.all(range(0, count).map(() => tutor())); -} - -export type MakeLessonsReturn = Array<{ - future: LessonReturn[]; - past: LessonReturn[]; - canceled: { - future: LessonReturn[]; - past: LessonReturn[]; - }; - uncanceled: { - future: LessonReturn[]; - past: LessonReturn[]; - }; -}>; - -async function makeLessons({ - tutor, - students, - future, - past, - canceled, - duration, - price, - slot, -}: { - tutor: number; - students: number[]; - future: number[]; - past: number[]; - duration?: ILesson.Duration; - price?: number; - canceled: { - future: number[]; - past: number[]; - }; - slot: number; -}): Promise { - const result: MakeLessonsReturn = []; - - for (const [key, student] of entries(students)) { - const index = Number(key); - const futureLessonCount = future[index]; - const pastLessonCount = past[index]; - const canceledFutureLessonCount = canceled.future[index]; - const canceledPastLessonCount = canceled.past[index]; - - const create = async (start: string) => { - return await lesson({ - slot, - tutor, - student, - start, - duration, - price, - }); - }; - - const futureLessons = await Promise.all( - range(0, futureLessonCount).map((i) => - create( - dayjs - .utc() - .add(i + 1, "days") - .toISOString() - ) - ) - ); - - const pastLessons = await Promise.all( - range(0, pastLessonCount).map((i) => - create( - dayjs - .utc() - .subtract(i + 1, "days") - .toISOString() - ) - ) - ); - - // cancel future lessons - const canceledFutureLessons = await Promise.all( - range(0, canceledFutureLessonCount).map(async (i) => { - const info = futureLessons[i]; - if (!lesson) throw new Error("invalid future lesson index"); - await lessons.cancel({ - canceledBy: tutor, - ids: [info.lesson.id], - }); - return info; - }) - ); - - // cancel past lessons - const canceledPastLessons = await Promise.all( - range(0, canceledPastLessonCount).map(async (i) => { - const info = pastLessons[i]; - if (!info) throw new Error("invalid past lesson index"); - await lessons.cancel({ - canceledBy: tutor, - ids: [info.lesson.id], - }); - return info; - }) - ); - - const canceledFutureLessonIds = canceledFutureLessons.map( - (future) => future.lesson.id - ); - const canceledPastLessonIds = canceledPastLessons.map( - (past) => past.lesson.id - ); - // filter out canceled future lessons - const uncanceledFutureLessons = futureLessons.filter( - (future) => !canceledFutureLessonIds.includes(future.lesson.id) - ); - // filter out canceled past lessons - const uncanceledPastLessons = pastLessons.filter( - (past) => !canceledPastLessonIds.includes(past.lesson.id) - ); - result.push({ - future: futureLessons, - past: pastLessons, - canceled: { - future: canceledFutureLessons, - past: canceledPastLessons, - }, - uncanceled: { - future: uncanceledFutureLessons, - past: uncanceledPastLessons, - }, - }); - } - - return result; -} - -export async function makeRating(payload?: Partial) { - return await ratings.create({ - raterId: await or.studentId(payload?.raterId), - rateeId: await or.tutorId(payload?.rateeId), - value: payload?.value || faker.number.int({ min: 0, max: 5 }), - feedback: payload?.feedback || faker.lorem.words(20), - }); -} - -export async function makeRatings({ - values, - ratee, -}: { - values: number[]; - ratee?: number; -}) { - const rateeId: number = - ratee || (await user({ role: IUser.Role.Tutor }).then((user) => user.id)); - - return Promise.all( - values.map(async (value) => { - const student = await user({ role: IUser.Role.Student }); - return await ratings.create({ - rateeId: rateeId, - raterId: student.id, - value: value, - feedback: "Great Teacher", - }); - }) - ); -} - -async function makeRoom(payload?: [number, number]) { - const [firstUserId, secondUserId]: [number, number] = payload || [ - await tutor().then((user) => user.id), - await student().then((user) => user.id), - ]; - return await knex.transaction(async (tx) => { - return await rooms.create([firstUserId, secondUserId], tx); - }); -} - -async function makeMessage(payload?: Partial) { - const roomId: number = await or.roomId(payload?.roomId); - const userId: number = - payload?.userId || - (await rooms - .findRoomMembers({ roomIds: [roomId] }) - .then((members) => members[0].id)); - - const text = payload?.text || faker.lorem.words(10); - - return await messages.create({ - userId, - roomId, - text, - }); -} - -async function makeInterviews(payload: { - data: [ - { - interviewer: number; - interviewees: number[]; - statuses: IInterview.Status[]; - levels: IInterview.Self["level"][]; - }, - ]; -}) { - for (const { interviewer, interviewees, statuses, levels } of payload.data) { - for (const [key, interviewee] of entries(interviewees)) { - const index = Number(key); - const status = statuses[index]; - const level = levels[index]; - const interviewObj = await interview({ - interviewer, - interviewee, - }); - - if (status) await interviews.update(interviewObj.ids.self, { status }); - - if (level) await interviews.update(interviewObj.ids.self, { level }); - } - } -} - -async function transaction( - payload?: Partial -): Promise { - return await transactions.create({ - userId: payload?.userId || (await user({})).id, - amount: payload?.amount || randomInt(1, 1000), - paymentMethod: payload?.paymentMethod || ITransaction.PaymentMethod.Card, - providerRefNum: payload?.providerRefNum || null, - planId: await or.planId(payload?.planId), - planPeriod: or.planPeriod(payload?.planPeriod), - }); -} - -async function plan( - payload?: Partial -): Promise { - return await plans.create({ - weeklyMinutes: payload?.weeklyMinutes || randomInt(1, 1000), - baseMonthlyPrice: payload?.baseMonthlyPrice || randomPrice(), - monthDiscount: payload?.monthDiscount || randomDiscount(), - quarterDiscount: payload?.quarterDiscount || randomDiscount(), - yearDiscount: payload?.yearDiscount || randomDiscount(), - forInvitesOnly: or.boolean(payload?.forInvitesOnly), - active: or.boolean(payload?.active), - }); -} - -function randomPrice() { - return price.scale(randomInt(1, 5000)); -} - -function randomDiscount() { - return percentage.scale(randomInt(1, 100)); -} - -async function subscription( - payload?: Partial -): Promise { - const userId = await or.studentId(payload?.userId); - return await subscriptions.create({ - userId, - planId: await or.planId(payload?.planId), - txId: payload?.txId || (await transaction({ userId })).id, - period: or.planPeriod(payload?.period), - weeklyMinutes: payload?.weeklyMinutes || randomInt(1, 1000), - start: payload?.start || faker.date.future().toISOString(), - end: payload?.end || faker.date.future().toISOString(), - }); -} - -export default { - user, - tutor, - student, - tutorManager, - students, - interview, - lesson, - flush, - topic, - slot, - plan, - transaction, - subscription, - room: makeRoom, - rating: makeRating, - message: makeMessage, - make: { - lessons: makeLessons, - interviews: makeInterviews, - tutors: makeTutors, - ratings: makeRatings, - room: makeRoom, - }, -}; diff --git a/packages/models/package.json b/packages/models/package.json index 9f39bff01..cd1727693 100644 --- a/packages/models/package.json +++ b/packages/models/package.json @@ -65,6 +65,7 @@ }, "dependencies": { "@litespace/types": "workspace:^", + "@litespace/tests": "workspace:^", "dayjs": "^1.11.13" }, "peerDependencies": { diff --git a/packages/models/tests/availabilitySlot.test.ts b/packages/models/tests/availabilitySlot.test.ts index d58cc1853..b3b1cb604 100644 --- a/packages/models/tests/availabilitySlot.test.ts +++ b/packages/models/tests/availabilitySlot.test.ts @@ -1,5 +1,5 @@ import { availabilitySlots } from "@/availabilitySlots"; -import fixtures from "@fixtures/db"; +import { fixtures } from "@litespace/tests"; import { dayjs, nameof, safe } from "@litespace/utils"; import { expect } from "chai"; import { first } from "lodash"; diff --git a/packages/models/tests/confirmationCodes.test.ts b/packages/models/tests/confirmationCodes.test.ts index 9a6f89e31..54cf7eef7 100644 --- a/packages/models/tests/confirmationCodes.test.ts +++ b/packages/models/tests/confirmationCodes.test.ts @@ -1,5 +1,5 @@ import { confirmationCodes } from "@/confirmationCodes"; -import fixtures from "@fixtures/db"; +import { fixtures } from "@litespace/tests"; import { nameof } from "@litespace/utils/utils"; import { IConfirmationCode } from "@litespace/types"; import { use, expect } from "chai"; diff --git a/packages/models/tests/interviews.test.ts b/packages/models/tests/interviews.test.ts index a8251707f..5188bb78c 100644 --- a/packages/models/tests/interviews.test.ts +++ b/packages/models/tests/interviews.test.ts @@ -1,5 +1,5 @@ import { interviews } from "@/interviews"; -import fixtures from "@fixtures/db"; +import { fixtures } from "@litespace/tests"; import { nameof } from "@litespace/utils/utils"; import { IInterview } from "@litespace/types"; import { expect } from "chai"; diff --git a/packages/models/tests/invoices.test.ts b/packages/models/tests/invoices.test.ts index 30ea8fbfa..a49aaf012 100644 --- a/packages/models/tests/invoices.test.ts +++ b/packages/models/tests/invoices.test.ts @@ -1,4 +1,4 @@ -import fixtures from "@fixtures/db"; +import { fixtures } from "@litespace/tests"; import { nameof } from "@litespace/utils/utils"; import { invoices } from "@/index"; import { IInvoice } from "@litespace/types"; diff --git a/packages/models/tests/lessons.test.ts b/packages/models/tests/lessons.test.ts index e0f7a53c7..1eb6fa78f 100644 --- a/packages/models/tests/lessons.test.ts +++ b/packages/models/tests/lessons.test.ts @@ -1,6 +1,6 @@ import { lessons } from "@/index"; import { expect } from "chai"; -import fixtures, { MakeLessonsReturn } from "@fixtures/db"; +import { fixtures } from "@litespace/tests"; import { ILesson, ITutor } from "@litespace/types"; import { price } from "@litespace/utils/value"; import { nameof } from "@litespace/utils/utils"; @@ -240,7 +240,7 @@ describe("Lessons", () => { describe("Lessons Duration Sum, Lessons Count, Lesson Days, and Price", () => { let tutor: ITutor.Self; - let tutorLessons: MakeLessonsReturn; + let tutorLessons: Awaited>; const futureLessons = 8; const pastLessons = 13; const canceledFutureLessons = 3; diff --git a/packages/models/tests/messages.test.ts b/packages/models/tests/messages.test.ts index cc2278896..894af597e 100644 --- a/packages/models/tests/messages.test.ts +++ b/packages/models/tests/messages.test.ts @@ -1,5 +1,5 @@ import { knex, messages, rooms } from "@/index"; -import fixtures from "@fixtures/db"; +import { fixtures } from "@litespace/tests"; import { nameof } from "@litespace/utils"; import { expect } from "chai"; diff --git a/packages/models/tests/plans.test.ts b/packages/models/tests/plans.test.ts index 83e098111..db339a23b 100644 --- a/packages/models/tests/plans.test.ts +++ b/packages/models/tests/plans.test.ts @@ -1,7 +1,7 @@ -import fixtures from "@fixtures/db"; import { expect } from "chai"; import { plans } from "@/plans"; import { nameof } from "@litespace/utils"; +import { fixtures } from "@litespace/tests"; describe("plans", () => { beforeEach(async () => { diff --git a/packages/models/tests/ratings.test.ts b/packages/models/tests/ratings.test.ts index 3deadc0e1..4aef0ecda 100644 --- a/packages/models/tests/ratings.test.ts +++ b/packages/models/tests/ratings.test.ts @@ -1,4 +1,4 @@ -import fixtures from "@fixtures/db"; +import { fixtures } from "@litespace/tests"; import { nameof } from "@litespace/utils/utils"; import { ratings } from "@/index"; import { expect } from "chai"; diff --git a/packages/models/tests/rooms.test.ts b/packages/models/tests/rooms.test.ts index 9d2813540..ec737a9cf 100644 --- a/packages/models/tests/rooms.test.ts +++ b/packages/models/tests/rooms.test.ts @@ -1,4 +1,4 @@ -import fixtures from "@fixtures/db"; +import { fixtures } from "@litespace/tests"; import { nameof } from "@litespace/utils/utils"; import { knex, rooms } from "@/index"; import { expect } from "chai"; diff --git a/packages/models/tests/sessionEvents.test.ts b/packages/models/tests/sessionEvents.test.ts index ec7e54839..6e171ef46 100644 --- a/packages/models/tests/sessionEvents.test.ts +++ b/packages/models/tests/sessionEvents.test.ts @@ -1,7 +1,6 @@ -import fixtures from "@fixtures/db"; +import { fixtures } from "@litespace/tests"; import { sessionEvents, knex } from "@/index"; import { ISessionEvent, IUser } from "@litespace/types"; -import db from "@fixtures/db"; import { expect } from "chai"; describe("Events", () => { @@ -10,7 +9,7 @@ describe("Events", () => { }); it("should successfully create new event records and retrieve a record with its id", async () => { - const user = await db.user({ role: IUser.Role.Tutor }); + const user = await fixtures.user({ role: IUser.Role.Tutor }); const event = await knex.transaction((tx) => { return sessionEvents.create( @@ -29,8 +28,8 @@ describe("Events", () => { }); it("should find a list of events for a specific userId", async () => { - const user1 = await db.user({ role: IUser.Role.Tutor }); - const user2 = await db.user({ role: IUser.Role.TutorManager }); + const user1 = await fixtures.user({ role: IUser.Role.Tutor }); + const user2 = await fixtures.user({ role: IUser.Role.TutorManager }); await knex.transaction((tx) => { return sessionEvents.createMany( diff --git a/packages/models/tests/subscriptions.test.ts b/packages/models/tests/subscriptions.test.ts index e11a2892f..8726260ef 100644 --- a/packages/models/tests/subscriptions.test.ts +++ b/packages/models/tests/subscriptions.test.ts @@ -1,5 +1,5 @@ import { subscriptions } from "@/subscriptions"; -import fixtures from "@fixtures/db"; +import { fixtures } from "@litespace/tests"; import { nameof } from "@litespace/utils/utils"; import { expect } from "chai"; diff --git a/packages/models/tests/topics.test.ts b/packages/models/tests/topics.test.ts index 7318d4f96..d9d74538c 100644 --- a/packages/models/tests/topics.test.ts +++ b/packages/models/tests/topics.test.ts @@ -1,4 +1,4 @@ -import fixtures from "@fixtures/db"; +import { fixtures } from "@litespace/tests"; import { nameof } from "@litespace/utils/utils"; import { knex, topics } from "@/index"; import { expect } from "chai"; diff --git a/packages/models/tests/transactions.test.ts b/packages/models/tests/transactions.test.ts index 453dc12a4..921c7d966 100644 --- a/packages/models/tests/transactions.test.ts +++ b/packages/models/tests/transactions.test.ts @@ -1,5 +1,5 @@ import { transactions } from "@/transactions"; -import fixtures from "@fixtures/db"; +import { fixtures } from "@litespace/tests"; import { IPlan, ITransaction } from "@litespace/types"; import { nameof } from "@litespace/utils/utils"; import { expect } from "chai"; diff --git a/packages/models/tests/tutors.test.ts b/packages/models/tests/tutors.test.ts index 803968f2a..b78efa631 100644 --- a/packages/models/tests/tutors.test.ts +++ b/packages/models/tests/tutors.test.ts @@ -1,5 +1,5 @@ import { tutors, Tutors } from "@/tutors"; -import fixtures from "@fixtures/db"; +import { fixtures } from "@litespace/tests"; import { expect } from "chai"; import { dayjs, nameof } from "@litespace/utils"; import { range } from "lodash"; @@ -52,6 +52,13 @@ describe(nameof(Tutors), () => { const mockTutors = await Promise.all( range(0, 5).map(() => fixtures.tutor( + { + image: "./photo.jpg", + city: IUser.City.Giza, + phone: "01143759540", + verifiedEmail: true, + verifiedPhone: true, + }, { bio: "empty", about: "empty", @@ -59,13 +66,6 @@ describe(nameof(Tutors), () => { thumbnail: "./image.jpg", studioId: studio.id, activated: true, - }, - { - image: "./photo.jpg", - city: IUser.City.Giza, - phone: "01143759540", - verifiedEmail: true, - verifiedPhone: true, } ) ) @@ -91,10 +91,10 @@ describe(nameof(Tutors), () => { const studio2 = await fixtures.user({ role: Role.Studio }); const tutorsSet1 = await Promise.all( - range(0, 5).map(() => fixtures.tutor({ studioId: studio1.id })) + range(0, 5).map(() => fixtures.tutor({}, { studioId: studio1.id })) ); const tutorsSet2 = await Promise.all( - range(0, 3).map(() => fixtures.tutor({ studioId: studio2.id })) + range(0, 3).map(() => fixtures.tutor({}, { studioId: studio2.id })) ); const res1 = await tutors.findStudioTutors({ studioId: studio1.id }); @@ -114,7 +114,7 @@ describe(nameof(Tutors), () => { describe(nameof(tutors.findStudioTutor), () => { it("should retrieve tutors that subscribed to a specific studioId", async () => { const studio = await fixtures.user({ role: Role.Studio }); - const tutor = await fixtures.tutor({ studioId: studio.id }); + const tutor = await fixtures.tutor({}, { studioId: studio.id }); const res1 = await tutors.findStudioTutor(0); expect(res1).to.eq(null); @@ -137,8 +137,8 @@ describe(nameof(Tutors), () => { }); it("should filter based on cities", async () => { - await fixtures.tutor({}, { city: IUser.City.Alexandria }); - await fixtures.tutor({}, { city: IUser.City.Damietta }); + await fixtures.tutor({ city: IUser.City.Alexandria }, {}); + await fixtures.tutor({ city: IUser.City.Damietta }, {}); await fixtures.tutor(); await fixtures.tutor(); await fixtures.tutor(); @@ -150,11 +150,11 @@ describe(nameof(Tutors), () => { }); it("should filter based on genders", async () => { - await fixtures.tutor({}, { gender: IUser.Gender.Male }); - await fixtures.tutor({}, { gender: IUser.Gender.Male }); - await fixtures.tutor({}, { gender: IUser.Gender.Female }); - await fixtures.tutor({}, { gender: IUser.Gender.Female }); - await fixtures.tutor({}, { gender: IUser.Gender.Female }); + await fixtures.tutor({ gender: IUser.Gender.Male }, {}); + await fixtures.tutor({ gender: IUser.Gender.Male }, {}); + await fixtures.tutor({ gender: IUser.Gender.Female }, {}); + await fixtures.tutor({ gender: IUser.Gender.Female }, {}); + await fixtures.tutor({ gender: IUser.Gender.Female }, {}); const res1 = await tutors.find({}); expect(res1.list.length).to.be.eq(5); @@ -177,24 +177,24 @@ describe(nameof(Tutors), () => { it("should filter based on notification methods", async () => { await fixtures.tutor( - {}, - { notificationMethod: IUser.NotificationMethod.Telegram } + { notificationMethod: IUser.NotificationMethod.Telegram }, + {} ); await fixtures.tutor( - {}, - { notificationMethod: IUser.NotificationMethod.Telegram } + { notificationMethod: IUser.NotificationMethod.Telegram }, + {} ); await fixtures.tutor( - {}, - { notificationMethod: IUser.NotificationMethod.Telegram } + { notificationMethod: IUser.NotificationMethod.Telegram }, + {} ); await fixtures.tutor( - {}, - { notificationMethod: IUser.NotificationMethod.Whatsapp } + { notificationMethod: IUser.NotificationMethod.Whatsapp }, + {} ); await fixtures.tutor( - {}, - { notificationMethod: IUser.NotificationMethod.Whatsapp } + { notificationMethod: IUser.NotificationMethod.Whatsapp }, + {} ); const res1 = await tutors.find({}); @@ -220,11 +220,11 @@ describe(nameof(Tutors), () => { }); it("should filter based on name", async () => { - await fixtures.tutor({}, { name: "Mostafa Kamar" }); - await fixtures.tutor({}, { name: null }); - await fixtures.tutor({}, { name: "Ahmed Ibrahim" }); - await fixtures.tutor({}, { name: "Mahmoud Ehab" }); - await fixtures.tutor({}, { name: "Mohamed Ali" }); + await fixtures.tutor({ name: "Mostafa Kamar" }, {}); + await fixtures.tutor({ name: null }, {}); + await fixtures.tutor({ name: "Ahmed Ibrahim" }, {}); + await fixtures.tutor({ name: "Mahmoud Ehab" }, {}); + await fixtures.tutor({ name: "Mohamed Ali" }, {}); const res1 = await tutors.find({ name: "kamar", @@ -236,9 +236,9 @@ describe(nameof(Tutors), () => { }); it("should filter based on bio", async () => { - await fixtures.tutor({ bio: "Hello, I am here" }); - await fixtures.tutor({ bio: "Hello, I am not here" }); - await fixtures.tutor({ bio: "Hello, i am here" }); + await fixtures.tutor({}, { bio: "Hello, I am here" }); + await fixtures.tutor({}, { bio: "Hello, I am not here" }); + await fixtures.tutor({}, { bio: "Hello, i am here" }); await fixtures.tutor({}); const res1 = await tutors.find({ @@ -256,9 +256,9 @@ describe(nameof(Tutors), () => { }); it("should filter based on about", async () => { - await fixtures.tutor({ about: "Hello, I am here" }); - await fixtures.tutor({ about: "Hello, I am not here" }); - await fixtures.tutor({ about: "Hello, i am here" }); + await fixtures.tutor({}, { about: "Hello, I am here" }); + await fixtures.tutor({}, { about: "Hello, I am not here" }); + await fixtures.tutor({}, { about: "Hello, i am here" }); await fixtures.tutor({}); const res1 = await tutors.find({ about: "not" }); @@ -275,11 +275,11 @@ describe(nameof(Tutors), () => { it("should filter based on phone", async () => { const t1 = await fixtures.tutor( - {}, - { phone: faker.phone.number().slice(0, 12) } + { phone: faker.phone.number().slice(0, 12) }, + {} ); - await fixtures.tutor({}, { phone: faker.phone.number().slice(0, 12) }); - await fixtures.tutor({}, { phone: faker.phone.number().slice(0, 12) }); + await fixtures.tutor({ phone: faker.phone.number().slice(0, 12) }, {}); + await fixtures.tutor({ phone: faker.phone.number().slice(0, 12) }, {}); await fixtures.tutor(); const res1 = await tutors.find({ @@ -292,11 +292,11 @@ describe(nameof(Tutors), () => { }); it("should filter based on email", async () => { - await fixtures.tutor({}, { email: "s1@litespace.org" }); - await fixtures.tutor({}, { email: "s2@litespace.org" }); - await fixtures.tutor({}, { email: "s3@litespace.org" }); - await fixtures.tutor({}, { email: "s4@litespace.org" }); - await fixtures.tutor({}, { email: "s5@litespace.org" }); + await fixtures.tutor({ email: "s1@litespace.org" }, {}); + await fixtures.tutor({ email: "s2@litespace.org" }, {}); + await fixtures.tutor({ email: "s3@litespace.org" }, {}); + await fixtures.tutor({ email: "s4@litespace.org" }, {}); + await fixtures.tutor({ email: "s5@litespace.org" }, {}); const res1 = await tutors.find({ email: "@litespace.org" }); expect(res1.list.length).to.be.eq(5); @@ -306,11 +306,11 @@ describe(nameof(Tutors), () => { }); it("should filter based on activated", async () => { - await fixtures.tutor({ activated: false }); - await fixtures.tutor({ activated: true }); - await fixtures.tutor({ activated: true }); - await fixtures.tutor({ activated: false }); - await fixtures.tutor({ activated: false }); + await fixtures.tutor({}, { activated: false }); + await fixtures.tutor({}, { activated: true }); + await fixtures.tutor({}, { activated: true }); + await fixtures.tutor({}, { activated: false }); + await fixtures.tutor({}, { activated: false }); const res1 = await tutors.find({ activated: true, @@ -324,11 +324,11 @@ describe(nameof(Tutors), () => { }); it("should filter based on verifiedEmail", async () => { - await fixtures.tutor({}, { verifiedEmail: true }); - await fixtures.tutor({}, { verifiedEmail: true }); - await fixtures.tutor({}, { verifiedEmail: false }); - await fixtures.tutor({}, { verifiedEmail: false }); - await fixtures.tutor({}, { verifiedEmail: false }); + await fixtures.tutor({ verifiedEmail: true }, {}); + await fixtures.tutor({ verifiedEmail: true }, {}); + await fixtures.tutor({ verifiedEmail: false }, {}); + await fixtures.tutor({ verifiedEmail: false }, {}); + await fixtures.tutor({ verifiedEmail: false }, {}); const res1 = await tutors.find({ verifiedEmail: true }); expect(res1.list.length).to.be.eq(2); @@ -338,11 +338,11 @@ describe(nameof(Tutors), () => { }); it("should filter based on verifiedPhone", async () => { - await fixtures.tutor({}, { verifiedPhone: true }); - await fixtures.tutor({}, { verifiedPhone: true }); - await fixtures.tutor({}, { verifiedPhone: false }); - await fixtures.tutor({}, { verifiedPhone: false }); - await fixtures.tutor({}, { verifiedPhone: false }); + await fixtures.tutor({ verifiedPhone: true }, {}); + await fixtures.tutor({ verifiedPhone: true }, {}); + await fixtures.tutor({ verifiedPhone: false }, {}); + await fixtures.tutor({ verifiedPhone: false }, {}); + await fixtures.tutor({ verifiedPhone: false }, {}); const res1 = await tutors.find({ verifiedPhone: true }); expect(res1.list.length).to.be.eq(2); @@ -352,11 +352,11 @@ describe(nameof(Tutors), () => { }); it("should filter based on verifiedTelegram", async () => { - await fixtures.tutor({}, { verifiedTelegram: true }); - await fixtures.tutor({}, { verifiedTelegram: true }); - await fixtures.tutor({}, { verifiedTelegram: false }); - await fixtures.tutor({}, { verifiedTelegram: false }); - await fixtures.tutor({}, { verifiedTelegram: false }); + await fixtures.tutor({ verifiedTelegram: true }, {}); + await fixtures.tutor({ verifiedTelegram: true }, {}); + await fixtures.tutor({ verifiedTelegram: false }, {}); + await fixtures.tutor({ verifiedTelegram: false }, {}); + await fixtures.tutor({ verifiedTelegram: false }, {}); const res1 = await tutors.find({ verifiedTelegram: true }); expect(res1.list.length).to.be.eq(2); @@ -366,11 +366,11 @@ describe(nameof(Tutors), () => { }); it("should filter based on verifiedWhatsapp", async () => { - await fixtures.tutor({}, { verifiedWhatsApp: true }); - await fixtures.tutor({}, { verifiedWhatsApp: true }); - await fixtures.tutor({}, { verifiedWhatsApp: false }); - await fixtures.tutor({}, { verifiedWhatsApp: false }); - await fixtures.tutor({}, { verifiedWhatsApp: false }); + await fixtures.tutor({ verifiedWhatsApp: true }, {}); + await fixtures.tutor({ verifiedWhatsApp: true }, {}); + await fixtures.tutor({ verifiedWhatsApp: false }, {}); + await fixtures.tutor({ verifiedWhatsApp: false }, {}); + await fixtures.tutor({ verifiedWhatsApp: false }, {}); const res1 = await tutors.find({ verifiedWhatsapp: true }); expect(res1.list.length).to.be.eq(2); @@ -379,26 +379,12 @@ describe(nameof(Tutors), () => { expect(res2.list.length).to.be.eq(3); }); - it("should filter based on password", async () => { - await fixtures.tutor({}, {}, true); - await fixtures.tutor({}, {}, true); - await fixtures.tutor({}, {}); - await fixtures.tutor({}, {}); - await fixtures.tutor({}, {}); - - const res1 = await tutors.find({ password: true }); - expect(res1.list.length).to.be.eq(2); - - const res2 = await tutors.find({ password: false }); - expect(res2.list.length).to.be.eq(3); - }); - it("should filter based on image", async () => { - await fixtures.tutor({}, { image: "/image" }); - await fixtures.tutor({}, { image: "/image" }); - await fixtures.tutor({}, { image: null }); - await fixtures.tutor({}, { image: null }); - await fixtures.tutor({}, { image: null }); + await fixtures.tutor({ image: "/image" }, {}); + await fixtures.tutor({ image: "/image" }, {}); + await fixtures.tutor({ image: null }, {}); + await fixtures.tutor({ image: null }, {}); + await fixtures.tutor({ image: null }, {}); const res1 = await tutors.find({ image: true }); expect(res1.list.length).to.be.eq(2); @@ -410,11 +396,11 @@ describe(nameof(Tutors), () => { }); it("should filter based on thumbnail", async () => { - await fixtures.tutor({ thumbnail: "/image" }); - await fixtures.tutor({ thumbnail: "/image" }); - await fixtures.tutor({ thumbnail: null }); - await fixtures.tutor({ thumbnail: null }); - await fixtures.tutor({ thumbnail: null }); + await fixtures.tutor({}, { thumbnail: "/image" }); + await fixtures.tutor({}, { thumbnail: "/image" }); + await fixtures.tutor({}, { thumbnail: null }); + await fixtures.tutor({}, { thumbnail: null }); + await fixtures.tutor({}, { thumbnail: null }); const res1 = await tutors.find({ thumbnail: true }); expect(res1.list.length).to.be.eq(2); @@ -424,11 +410,11 @@ describe(nameof(Tutors), () => { }); it("should filter based on video", async () => { - await fixtures.tutor({ video: "/video" }); - await fixtures.tutor({ video: "/video" }); - await fixtures.tutor({ video: null }); - await fixtures.tutor({ video: null }); - await fixtures.tutor({ video: null }); + await fixtures.tutor({}, { video: "/video" }); + await fixtures.tutor({}, { video: "/video" }); + await fixtures.tutor({}, { video: null }); + await fixtures.tutor({}, { video: null }); + await fixtures.tutor({}, { video: null }); const res1 = await tutors.find({ video: true }); expect(res1.list.length).to.be.eq(2); @@ -438,11 +424,11 @@ describe(nameof(Tutors), () => { }); it("should filter based on notice", async () => { - await fixtures.tutor({ notice: 40 }); - await fixtures.tutor({ notice: 30 }); - await fixtures.tutor({ notice: 20 }); - await fixtures.tutor({ notice: 10 }); - await fixtures.tutor({ notice: 5 }); + await fixtures.tutor({}, { notice: 40 }); + await fixtures.tutor({}, { notice: 30 }); + await fixtures.tutor({}, { notice: 20 }); + await fixtures.tutor({}, { notice: 10 }); + await fixtures.tutor({}, { notice: 5 }); const res1 = await tutors.find({ notice: 40 }); expect(res1.list.length).to.be.eq(1); @@ -461,11 +447,11 @@ describe(nameof(Tutors), () => { }); it("should filter based on birthYear", async () => { - await fixtures.tutor({}, { birthYear: 2000 }); - await fixtures.tutor({}, { birthYear: 2020 }); - await fixtures.tutor({}, { birthYear: 2002 }); - await fixtures.tutor({}, { birthYear: 1995 }); - await fixtures.tutor({}, { birthYear: 1998 }); + await fixtures.tutor({ birthYear: 2000 }); + await fixtures.tutor({ birthYear: 2020 }); + await fixtures.tutor({ birthYear: 2002 }); + await fixtures.tutor({ birthYear: 1995 }); + await fixtures.tutor({ birthYear: 1998 }); const res1 = await tutors.find({ birthYear: 2000 }); expect(res1.list.length).to.be.eq(1); diff --git a/packages/models/tests/users.test.ts b/packages/models/tests/users.test.ts index 42230cc54..82884631b 100644 --- a/packages/models/tests/users.test.ts +++ b/packages/models/tests/users.test.ts @@ -1,4 +1,4 @@ -import fixtures from "@fixtures/db"; +import { fixtures } from "@litespace/tests"; import { nameof } from "@litespace/utils/utils"; import { hashPassword, users } from "@/index"; import { expect } from "chai"; @@ -146,7 +146,6 @@ describe("Users", () => { const created = await fixtures.user({ role: IUser.Role.TutorManager, password, - withPassword: true, }); const hash = await users.findUserPasswordHash(created.id); expect(hash).to.be.eq(hashPassword(password)); diff --git a/packages/tests/package.json b/packages/tests/package.json index dc6ec7738..e4857fa7b 100644 --- a/packages/tests/package.json +++ b/packages/tests/package.json @@ -4,18 +4,20 @@ "license": "MIT", "description": "LiteSpace monorepo tests", "main": "dist/cjs/index.js", - "module": "dist/index.js", + "module": "dist/esm/index.js", "files": [ "dist/**" ], "types": "dist/index.d.ts", "scripts": { - "build": "tsc -p tsconfig.build.json", - "build:ts": "tsc", - "prepare": "ts-patch install -s", + "build": "pnpm build:cjs && pnpm build:esm", + "build:cjs": "tsc -p tsconfig.cjs.json", + "build:esm": "tsc -p tsconfig.esm.json", + "check": "tsc --noEmit", "clean": "rm -rf dist/", - "check": "tsc -p tsconfig.json --noEmit", - "watch": "npm-watch build" + "watch": "npm-watch build", + "prepare": "ts-patch install -s", + "test": "jest" }, "watch": { "build": { @@ -35,6 +37,8 @@ "@litespace/types": "workspace:^", "@litespace/utils": "workspace:^", "dayjs": "^1.11.13", + "express": "^4.21.2", + "socket.io": "^4.8.1", "ts-patch": "^3.2.1", "typescript": "^5.4.5", "typescript-transform-paths": "^3.5.2" @@ -42,5 +46,11 @@ "packageManager": "pnpm@10.11.0", "devDependencies": { "chai-as-promised": "^8.0.1" + }, + "exports": { + ".": { + "require": "./dist/cjs/index.js", + "import": "./dist/esm/index.js" + } } } diff --git a/packages/tests/src/db.ts b/packages/tests/src/db.ts index 4b886a282..fda552d4d 100644 --- a/packages/tests/src/db.ts +++ b/packages/tests/src/db.ts @@ -16,6 +16,7 @@ import { confirmationCodes, plans, subscriptions, + invoices, } from "@litespace/models"; import { IInterview, @@ -34,7 +35,7 @@ import { import { faker } from "@faker-js/faker/locale/ar"; import { entries, first, range, sample } from "lodash"; import { Knex } from "knex"; -import { Time } from "@litespace/utils/time"; +import { Time } from "@litespace/utils"; export { faker } from "@faker-js/faker/locale/ar"; import { randomInt, randomUUID } from "crypto"; import { percentage, price } from "@litespace/utils"; @@ -48,6 +49,7 @@ export async function flush() { await subscriptions.builder(tx).del(); await transactions.builder(tx).del(); await plans.builder(tx).del(); + await invoices.builder(tx).del(); await sessionEvents.builder(tx).del(); await topics.builder(tx).userTopics.del(); await topics.builder(tx).topics.del(); @@ -66,6 +68,51 @@ export async function flush() { }); } +export function gender(): IUser.Gender { + return sample([IUser.Gender.Male, IUser.Gender.Female])!; +} + +export function duration(): number { + return sample([15, 30])!; +} + +export function time() { + const times = range(0, 24).map((hour) => + [hour.toString().padStart(2, "0"), "00"].join(":") + ); + return Time.from(sample(times)!).utc().format(); +} + +export async function user(payload?: Partial) { + return await users.create({ + email: payload?.email || faker.internet.email(), + gender: payload?.gender || gender(), + name: payload?.name || faker.internet.username(), + password: hashPassword(payload?.password || "Password@8"), + birthYear: payload?.birthYear || faker.number.int({ min: 2000, max: 2024 }), + role: or.role(payload?.role), + verifiedEmail: payload?.verifiedEmail || false, + }); +} + +export async function slot(payload?: Partial) { + const start = dayjs.utc(payload?.start || faker.date.future()); + const end = payload?.end + ? dayjs.utc(payload.end) + : start.add(faker.number.int(8), "hours"); + + const newSlots = await availabilitySlots.create([ + { + userId: await or.tutorId(payload?.userId), + start: start.toISOString(), + end: end.toISOString(), + }, + ]); + const res = first(newSlots); + if (!res) throw Error("error: couldn't insert new slot."); + return res; +} + const or = { async tutorId(id?: number): Promise { if (!id) return await tutor().then((tutor) => tutor.id); @@ -131,51 +178,6 @@ const or = { }, } as const; -export function gender(): IUser.Gender { - return sample([IUser.Gender.Male, IUser.Gender.Female])!; -} - -export function duration(): number { - return sample([15, 30])!; -} - -export function time() { - const times = range(0, 24).map((hour) => - [hour.toString().padStart(2, "0"), "00"].join(":") - ); - return Time.from(sample(times)!).utc().format(); -} - -export async function user(payload?: Partial) { - return await users.create({ - email: payload?.email || faker.internet.email(), - gender: payload?.gender || gender(), - name: payload?.name || faker.internet.username(), - password: hashPassword(payload?.password || "Password@8"), - birthYear: payload?.birthYear || faker.number.int({ min: 2000, max: 2024 }), - role: or.role(payload?.role), - verifiedEmail: payload?.verifiedEmail || false, - }); -} - -export async function slot(payload?: Partial) { - const start = dayjs.utc(payload?.start || faker.date.future()); - const end = payload?.end - ? dayjs.utc(payload.end) - : start.add(faker.number.int(8), "hours"); - - const newSlots = await availabilitySlots.create([ - { - userId: payload?.userId || 1, - start: start.toISOString(), - end: end.toISOString(), - }, - ]); - const res = first(newSlots); - if (!res) throw Error("error: couldn't insert new slot."); - return res; -} - type LessonReturn = { lesson: ILesson.Self; members: ILesson.Member[]; @@ -222,7 +224,7 @@ export async function interview(payload: Partial) { interviewer: await or.tutorManagerId(payload.interviewer), interviewee: await or.tutorId(payload.interviewee), session: await or.sessionId("interview"), - slot: await or.slotId(payload.slot, payload.interviewer), + slot: await or.slotId(payload.slot), start: or.start(payload.start), }); } @@ -237,48 +239,43 @@ export async function topic(payload?: Partial) { } async function tutor( - userPayload?: Partial, + userPayload?: Partial, tutorPayload?: Partial ) { - const info = await user({ ...userPayload, role: IUser.Role.Tutor }); + const info = await user({ role: IUser.Role.Tutor }); const tutor = await tutors.create(info.id); await tutors.update(tutor.id, tutorPayload || {}); - return tutor; -} - -async function onboardedTutor() { - const newTutor = await tutor(); - - await users.update(newTutor.id, { - phone: "01012345678", - image: "/image.jpg", - verifiedEmail: true, - verifiedPhone: true, - }); - - await tutors.update(newTutor.id, { - about: faker.lorem.paragraphs(), - bio: faker.person.bio(), - activated: true, - video: "/video.mp4", - notice: 10, - }); - - return newTutor; + await users.update(tutor.id, userPayload || {}); + const data = await tutors.findById(tutor.id); + if (!data) throw new Error("tutor not found"); + return data; } function student() { return user({ role: IUser.Role.Student }); } -async function tutorManager( +async function tutorUser( userPayload?: Partial, tutorPayload?: Partial ) { - const info = await user({ ...userPayload, role: IUser.Role.TutorManager }); + const info = await user({ ...userPayload, role: IUser.Role.Tutor }); + const tutor = await tutors.create(info.id); + await tutors.update(tutor.id, tutorPayload || {}); + return info; +} + +async function tutorManager( + tutorPayload?: Partial, + userPayload?: Partial +) { + const info = await user({ role: IUser.Role.TutorManager }); const tutor = await tutors.create(info.id); await tutors.update(tutor.id, tutorPayload || {}); - return tutor; + await users.update(tutor.id, userPayload || {}); + const data = await tutors.findById(tutor.id); + if (!data) throw new Error("tutor not found"); + return data; } async function students(count: number) { @@ -310,6 +307,7 @@ async function makeLessons({ canceled, duration, price, + slot, }: { tutor: number; students: number[]; @@ -321,6 +319,7 @@ async function makeLessons({ future: number[]; past: number[]; }; + slot: number; }): Promise { const result: MakeLessonsReturn = []; @@ -333,6 +332,7 @@ async function makeLessons({ const create = async (start: string) => { return await lesson({ + slot, tutor, student, start, @@ -421,18 +421,11 @@ async function makeLessons({ } export async function makeRating(payload?: Partial) { - const raterId: number = - payload?.raterId || - (await user({ role: IUser.Role.Student }).then((user) => user.id)); - const rateeId: number = - payload?.rateeId || - (await user({ role: IUser.Role.Tutor }).then((user) => user.id)); - return await ratings.create({ - rateeId, - raterId, + raterId: await or.studentId(payload?.raterId), + rateeId: await or.tutorId(payload?.rateeId), value: payload?.value || faker.number.int({ min: 0, max: 5 }), - feedback: faker.lorem.words(20), + feedback: payload?.feedback || faker.lorem.words(20), }); } @@ -460,19 +453,16 @@ export async function makeRatings({ } async function makeRoom(payload?: [number, number]) { + const [firstUserId, secondUserId]: [number, number] = payload || [ + await tutor().then((user) => user.id), + await student().then((user) => user.id), + ]; return await knex.transaction(async (tx) => { - const [firstUserId, secondUserId]: [number, number] = payload || [ - await tutor().then((user) => user.id), - await student().then((user) => user.id), - ]; return await rooms.create([firstUserId, secondUserId], tx); }); } -async function makeMessage( - _: Knex.Transaction, - payload?: Partial -) { +async function makeMessage(payload?: Partial) { const roomId: number = await or.roomId(payload?.roomId); const userId: number = payload?.userId || @@ -509,15 +499,9 @@ async function makeInterviews(payload: { interviewee, }); - if (status) - await interviews.update(interviewObj.ids.self, { - status, - }); + if (status) await interviews.update(interviewObj.ids.self, { status }); - if (level) - await interviews.update(interviewObj.ids.self, { - level, - }); + if (level) await interviews.update(interviewObj.ids.self, { level }); } } } @@ -526,8 +510,8 @@ async function transaction( payload?: Partial ): Promise { return await transactions.create({ - userId: await or.studentId(payload?.userId), - amount: payload?.amount || randomInt(1000), + userId: payload?.userId || (await user({})).id, + amount: payload?.amount || randomInt(1, 1000), paymentMethod: payload?.paymentMethod || ITransaction.PaymentMethod.Card, providerRefNum: payload?.providerRefNum || null, planId: await or.planId(payload?.planId), @@ -539,7 +523,7 @@ async function plan( payload?: Partial ): Promise { return await plans.create({ - weeklyMinutes: payload?.weeklyMinutes || randomInt(1000), + weeklyMinutes: payload?.weeklyMinutes || randomInt(1, 1000), baseMonthlyPrice: payload?.baseMonthlyPrice || randomPrice(), monthDiscount: payload?.monthDiscount || randomDiscount(), quarterDiscount: payload?.quarterDiscount || randomDiscount(), @@ -550,11 +534,11 @@ async function plan( } function randomPrice() { - return price.scale(randomInt(5000)); + return price.scale(randomInt(1, 5000)); } function randomDiscount() { - return percentage.scale(randomInt(100)); + return percentage.scale(randomInt(1, 100)); } async function subscription( @@ -564,9 +548,9 @@ async function subscription( return await subscriptions.create({ userId, planId: await or.planId(payload?.planId), - txId: await or.txId(payload?.txId, userId), + txId: payload?.txId || (await transaction({ userId })).id, period: or.planPeriod(payload?.period), - weeklyMinutes: payload?.weeklyMinutes || sample([120, 150, 180]), + weeklyMinutes: payload?.weeklyMinutes || randomInt(1, 1000), start: payload?.start || faker.date.future().toISOString(), end: payload?.end || faker.date.future().toISOString(), }); @@ -575,7 +559,7 @@ async function subscription( export default { user, tutor, - onboardedTutor, + tutorUser, student, tutorManager, students, @@ -584,16 +568,17 @@ export default { flush, topic, slot, - transaction, plan, + transaction, subscription, room: makeRoom, + rating: makeRating, message: makeMessage, make: { lessons: makeLessons, interviews: makeInterviews, tutors: makeTutors, - rating: makeRating, ratings: makeRatings, + room: makeRoom, }, }; diff --git a/packages/tests/src/index.ts b/packages/tests/src/index.ts index 20e7c089d..283583a18 100644 --- a/packages/tests/src/index.ts +++ b/packages/tests/src/index.ts @@ -1,4 +1,7 @@ export { ClientSocket } from "@/mocks/wss"; export { mockApi, mockApiContext } from "@/mocks/api"; -export * as db from "@/db"; export { api, unexpectedApiSuccess, Api } from "@/api"; +export { faker } from "@/db"; + +import db from "@/db"; +export const fixtures = db; diff --git a/packages/tests/src/mocks/api.ts b/packages/tests/src/mocks/api.ts index d828a44c8..fe6b097a0 100644 --- a/packages/tests/src/mocks/api.ts +++ b/packages/tests/src/mocks/api.ts @@ -13,6 +13,7 @@ type MockRequest = { params?: Params; query?: Query; user?: IUser.Self | IUser.Role; + files?: Record | Express.Multer.File[]; }; class MockResponse { @@ -57,6 +58,12 @@ export function mockApi< return async ( request: MockRequest ): Promise<{ status: number | null; body: Res | null }> => { + // these assigns avoids absurd errors while using + // the mockApi in test suites + if (!request.body) request.body = {} as Body; + if (!request.query) request.query = {} as Query; + if (!request.params) request.params = {} as Params; + if (typeof request.user === "number") request.user = await db.user({ role: request.user }); diff --git a/packages/tests/tsconfig.cjs.json b/packages/tests/tsconfig.cjs.json new file mode 100644 index 000000000..0d4c98efc --- /dev/null +++ b/packages/tests/tsconfig.cjs.json @@ -0,0 +1,8 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "outDir": "./dist/cjs", + "module": "commonjs" + }, + "include": ["src/"] +} diff --git a/packages/tests/tsconfig.build.json b/packages/tests/tsconfig.esm.json similarity index 79% rename from packages/tests/tsconfig.build.json rename to packages/tests/tsconfig.esm.json index b59a28ffd..bda04d2a7 100644 --- a/packages/tests/tsconfig.build.json +++ b/packages/tests/tsconfig.esm.json @@ -1,7 +1,7 @@ { "extends": "./tsconfig.json", "compilerOptions": { - "outDir": "./dist", + "outDir": "./dist/esm", "module": "esnext" }, "include": ["src/"] diff --git a/packages/tests/tsconfig.json b/packages/tests/tsconfig.json index 1da016ce4..b9dad51c9 100644 --- a/packages/tests/tsconfig.json +++ b/packages/tests/tsconfig.json @@ -1,18 +1,18 @@ { "compilerOptions": { - "target": "es2022", + "target": "esnext", "esModuleInterop": true, "forceConsistentCasingInFileNames": true, "strict": true, "skipLibCheck": true, + "jsx": "react", "module": "esnext", "declaration": true, "declarationMap": true, "sourceMap": true, "outDir": "dist", - "moduleResolution": "bundler", + "moduleResolution": "node", "allowSyntheticDefaultImports": true, - "emitDeclarationOnly": false, "paths": { "@/*": ["./src/*"] }, @@ -20,5 +20,6 @@ { "transform": "typescript-transform-paths" }, { "transform": "typescript-transform-paths", "afterDeclarations": true } ] - } + }, + "include": ["src/", "tests/"] } diff --git a/packages/utils/package.json b/packages/utils/package.json index 5642dcaa6..16f748a23 100644 --- a/packages/utils/package.json +++ b/packages/utils/package.json @@ -2,6 +2,8 @@ "name": "@litespace/utils", "description": "LiteSpace shared logic", "version": "1.0.0", + "main": "dist/cjs/index.js", + "module": "dist/esm/index.js", "files": [ "dist/" ], diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 866975310..d571b48b2 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -39,10 +39,10 @@ importers: version: 22.15.17 '@typescript-eslint/eslint-plugin': specifier: ^8.12.2 - version: 8.32.1(@typescript-eslint/parser@8.32.1(eslint@9.26.0(jiti@1.21.7))(typescript@5.8.3))(eslint@9.26.0(jiti@1.21.7))(typescript@5.8.3) + version: 8.32.1(@typescript-eslint/parser@8.32.1(eslint@9.26.0(jiti@1.21.7))(typescript@4.9.5))(eslint@9.26.0(jiti@1.21.7))(typescript@4.9.5) '@typescript-eslint/parser': specifier: ^8.12.2 - version: 8.32.1(eslint@9.26.0(jiti@1.21.7))(typescript@5.8.3) + version: 8.32.1(eslint@9.26.0(jiti@1.21.7))(typescript@4.9.5) autocannon: specifier: ^8.0.0 version: 8.0.0 @@ -72,7 +72,7 @@ importers: version: 0.4.20(eslint@9.26.0(jiti@1.21.7)) eslint-plugin-storybook: specifier: ^0.12.0 - version: 0.12.0(eslint@9.26.0(jiti@1.21.7))(typescript@5.8.3) + version: 0.12.0(eslint@9.26.0(jiti@1.21.7))(typescript@4.9.5) glob: specifier: ^11.0.0 version: 11.0.2 @@ -96,7 +96,7 @@ importers: version: 6.9.0 ts-node: specifier: ^10.9.2 - version: 10.9.2(@swc/core@1.11.24)(@types/node@22.15.17)(typescript@5.8.3) + version: 10.9.2(@swc/core@1.11.24)(@types/node@22.15.17)(typescript@4.9.5) vercel: specifier: ^34.2.7 version: 34.4.0(@swc/core@1.11.24) @@ -1016,6 +1016,9 @@ importers: packages/models: dependencies: + '@litespace/tests': + specifier: workspace:^ + version: link:../tests '@litespace/types': specifier: workspace:^ version: link:../types @@ -1201,6 +1204,12 @@ importers: dayjs: specifier: ^1.11.13 version: 1.11.13(patch_hash=aa81ceb75c492cff223134948ee626fc02bb04b49873a531b674f73099fd868b) + express: + specifier: ^4.21.2 + version: 4.21.2 + socket.io: + specifier: ^4.8.1 + version: 4.8.1(bufferutil@4.0.9)(utf-8-validate@5.0.10) ts-patch: specifier: ^3.2.1 version: 3.3.0 @@ -16628,7 +16637,7 @@ snapshots: '@babel/traverse': 7.27.1 '@babel/types': 7.27.1 convert-source-map: 2.0.0 - debug: 4.4.0(supports-color@5.5.0) + debug: 4.4.0(supports-color@8.1.1) gensync: 1.0.0-beta.2 json5: 2.2.3 semver: 6.3.1 @@ -16648,7 +16657,7 @@ snapshots: '@babel/traverse': 7.27.1 '@babel/types': 7.27.1 convert-source-map: 2.0.0 - debug: 4.4.0(supports-color@5.5.0) + debug: 4.4.0(supports-color@8.1.1) gensync: 1.0.0-beta.2 json5: 2.2.3 semver: 6.3.1 @@ -16727,7 +16736,7 @@ snapshots: '@babel/core': 7.27.1 '@babel/helper-compilation-targets': 7.27.2 '@babel/helper-plugin-utils': 7.27.1 - debug: 4.4.0(supports-color@5.5.0) + debug: 4.4.0(supports-color@8.1.1) lodash.debounce: 4.0.8 resolve: 1.22.10 transitivePeerDependencies: @@ -16738,7 +16747,7 @@ snapshots: '@babel/core': 7.27.1 '@babel/helper-compilation-targets': 7.27.2 '@babel/helper-plugin-utils': 7.27.1 - debug: 4.4.0(supports-color@5.5.0) + debug: 4.4.0(supports-color@8.1.1) lodash.debounce: 4.0.8 resolve: 1.22.10 transitivePeerDependencies: @@ -17657,7 +17666,7 @@ snapshots: '@babel/parser': 7.27.1 '@babel/template': 7.27.1 '@babel/types': 7.27.1 - debug: 4.4.0(supports-color@5.5.0) + debug: 4.4.0(supports-color@8.1.1) globals: 11.12.0 transitivePeerDependencies: - supports-color @@ -18258,7 +18267,7 @@ snapshots: '@eslint/config-array@0.20.0': dependencies: '@eslint/object-schema': 2.1.6 - debug: 4.4.0(supports-color@5.5.0) + debug: 4.4.0(supports-color@8.1.1) minimatch: 3.1.2 transitivePeerDependencies: - supports-color @@ -18272,7 +18281,7 @@ snapshots: '@eslint/eslintrc@3.3.1': dependencies: ajv: 6.12.6 - debug: 4.4.0(supports-color@5.5.0) + debug: 4.4.0(supports-color@8.1.1) espree: 10.3.0 globals: 14.0.0 ignore: 5.3.2 @@ -18326,7 +18335,7 @@ snapshots: chalk: 4.1.2 ci-info: 3.9.0 connect: 3.7.0 - debug: 4.4.0(supports-color@5.5.0) + debug: 4.4.0(supports-color@8.1.1) env-editor: 0.4.2 fast-glob: 3.3.3 find-yarn-workspace-root: 2.0.0 @@ -18394,7 +18403,7 @@ snapshots: '@expo/plist': 0.1.3 '@expo/sdk-runtime-versions': 1.0.0 chalk: 4.1.2 - debug: 4.4.0(supports-color@5.5.0) + debug: 4.4.0(supports-color@8.1.1) find-up: 5.0.0 getenv: 1.0.0 glob: 7.1.6 @@ -18445,7 +18454,7 @@ snapshots: '@expo/env@0.3.0': dependencies: chalk: 4.1.2 - debug: 4.4.0(supports-color@5.5.0) + debug: 4.4.0(supports-color@8.1.1) dotenv: 16.4.7 dotenv-expand: 11.0.7 getenv: 1.0.0 @@ -18490,7 +18499,7 @@ snapshots: '@expo/json-file': 8.3.3 '@expo/spawn-async': 1.7.2 chalk: 4.1.2 - debug: 4.4.0(supports-color@5.5.0) + debug: 4.4.0(supports-color@8.1.1) find-yarn-workspace-root: 2.0.0 fs-extra: 9.1.0 getenv: 1.0.0 @@ -18540,7 +18549,7 @@ snapshots: '@expo/image-utils': 0.5.1 '@expo/json-file': 8.3.3 '@react-native/normalize-colors': 0.74.85 - debug: 4.4.0(supports-color@5.5.0) + debug: 4.4.0(supports-color@8.1.1) expo-modules-autolinking: 1.11.3 fs-extra: 9.1.0 resolve-from: 5.0.0 @@ -18568,7 +18577,7 @@ snapshots: dependencies: '@remix-run/node': 2.15.3(typescript@5.4.5) abort-controller: 3.0.0 - debug: 4.4.0(supports-color@5.5.0) + debug: 4.4.0(supports-color@8.1.1) source-map-support: 0.5.21 transitivePeerDependencies: - supports-color @@ -18894,7 +18903,7 @@ snapshots: '@jest/console@29.7.0': dependencies: '@jest/types': 29.6.3 - '@types/node': 22.15.17 + '@types/node': 20.17.46 chalk: 4.1.2 jest-message-util: 29.7.0 jest-util: 29.7.0 @@ -18996,7 +19005,7 @@ snapshots: dependencies: '@jest/types': 29.6.3 '@sinonjs/fake-timers': 10.3.0 - '@types/node': 22.15.17 + '@types/node': 20.17.46 jest-message-util: 29.7.0 jest-mock: 29.7.0 jest-util: 29.7.0 @@ -19018,7 +19027,7 @@ snapshots: '@jest/transform': 29.7.0 '@jest/types': 29.6.3 '@jridgewell/trace-mapping': 0.3.25 - '@types/node': 22.15.17 + '@types/node': 20.17.46 chalk: 4.1.2 collect-v8-coverage: 1.0.2 exit: 0.1.2 @@ -19093,7 +19102,7 @@ snapshots: dependencies: '@types/istanbul-lib-coverage': 2.0.6 '@types/istanbul-reports': 3.0.4 - '@types/node': 22.15.17 + '@types/node': 20.17.46 '@types/yargs': 15.0.19 chalk: 4.1.2 @@ -19495,7 +19504,7 @@ snapshots: '@pm2/pm2-version-check@1.0.4': dependencies: - debug: 4.4.0(supports-color@5.5.0) + debug: 4.4.0(supports-color@8.1.1) transitivePeerDependencies: - supports-color @@ -21025,7 +21034,7 @@ snapshots: dependencies: '@remotion/streaming': 4.0.301 execa: 5.1.1 - extract-zip: 2.0.1 + extract-zip: 2.0.1(supports-color@8.1.1) react: 18.2.0 react-dom: 18.2.0(react@18.2.0) remotion: 4.0.301(react-dom@18.2.0(react@18.2.0))(react@18.2.0) @@ -22478,7 +22487,7 @@ snapshots: '@types/connect@3.4.38': dependencies: - '@types/node': 22.15.17 + '@types/node': 20.17.46 '@types/cookie@0.4.1': {} @@ -22589,7 +22598,7 @@ snapshots: '@types/jsdom@20.0.1': dependencies: - '@types/node': 22.15.17 + '@types/node': 20.17.46 '@types/tough-cookie': 4.0.5 parse5: 7.2.1 @@ -22725,7 +22734,7 @@ snapshots: '@types/send@0.17.4': dependencies: '@types/mime': 1.3.5 - '@types/node': 22.15.17 + '@types/node': 20.17.46 '@types/serve-static@1.15.7': dependencies: @@ -22794,37 +22803,37 @@ snapshots: transitivePeerDependencies: - supports-color - '@typescript-eslint/eslint-plugin@8.32.1(@typescript-eslint/parser@8.32.1(eslint@9.26.0(jiti@1.21.7))(typescript@5.4.5))(eslint@9.26.0(jiti@1.21.7))(typescript@5.4.5)': + '@typescript-eslint/eslint-plugin@8.32.1(@typescript-eslint/parser@8.32.1(eslint@9.26.0(jiti@1.21.7))(typescript@4.9.5))(eslint@9.26.0(jiti@1.21.7))(typescript@4.9.5)': dependencies: '@eslint-community/regexpp': 4.12.1 - '@typescript-eslint/parser': 8.32.1(eslint@9.26.0(jiti@1.21.7))(typescript@5.4.5) + '@typescript-eslint/parser': 8.32.1(eslint@9.26.0(jiti@1.21.7))(typescript@4.9.5) '@typescript-eslint/scope-manager': 8.32.1 - '@typescript-eslint/type-utils': 8.32.1(eslint@9.26.0(jiti@1.21.7))(typescript@5.4.5) - '@typescript-eslint/utils': 8.32.1(eslint@9.26.0(jiti@1.21.7))(typescript@5.4.5) + '@typescript-eslint/type-utils': 8.32.1(eslint@9.26.0(jiti@1.21.7))(typescript@4.9.5) + '@typescript-eslint/utils': 8.32.1(eslint@9.26.0(jiti@1.21.7))(typescript@4.9.5) '@typescript-eslint/visitor-keys': 8.32.1 eslint: 9.26.0(jiti@1.21.7) graphemer: 1.4.0 ignore: 7.0.4 natural-compare: 1.4.0 - ts-api-utils: 2.1.0(typescript@5.4.5) - typescript: 5.4.5 + ts-api-utils: 2.1.0(typescript@4.9.5) + typescript: 4.9.5 transitivePeerDependencies: - supports-color - '@typescript-eslint/eslint-plugin@8.32.1(@typescript-eslint/parser@8.32.1(eslint@9.26.0(jiti@1.21.7))(typescript@5.8.3))(eslint@9.26.0(jiti@1.21.7))(typescript@5.8.3)': + '@typescript-eslint/eslint-plugin@8.32.1(@typescript-eslint/parser@8.32.1(eslint@9.26.0(jiti@1.21.7))(typescript@5.4.5))(eslint@9.26.0(jiti@1.21.7))(typescript@5.4.5)': dependencies: '@eslint-community/regexpp': 4.12.1 - '@typescript-eslint/parser': 8.32.1(eslint@9.26.0(jiti@1.21.7))(typescript@5.8.3) + '@typescript-eslint/parser': 8.32.1(eslint@9.26.0(jiti@1.21.7))(typescript@5.4.5) '@typescript-eslint/scope-manager': 8.32.1 - '@typescript-eslint/type-utils': 8.32.1(eslint@9.26.0(jiti@1.21.7))(typescript@5.8.3) - '@typescript-eslint/utils': 8.32.1(eslint@9.26.0(jiti@1.21.7))(typescript@5.8.3) + '@typescript-eslint/type-utils': 8.32.1(eslint@9.26.0(jiti@1.21.7))(typescript@5.4.5) + '@typescript-eslint/utils': 8.32.1(eslint@9.26.0(jiti@1.21.7))(typescript@5.4.5) '@typescript-eslint/visitor-keys': 8.32.1 eslint: 9.26.0(jiti@1.21.7) graphemer: 1.4.0 ignore: 7.0.4 natural-compare: 1.4.0 - ts-api-utils: 2.1.0(typescript@5.8.3) - typescript: 5.8.3 + ts-api-utils: 2.1.0(typescript@5.4.5) + typescript: 5.4.5 transitivePeerDependencies: - supports-color @@ -22834,33 +22843,33 @@ snapshots: '@typescript-eslint/types': 8.22.0 '@typescript-eslint/typescript-estree': 8.22.0(typescript@5.4.5) '@typescript-eslint/visitor-keys': 8.22.0 - debug: 4.4.0(supports-color@5.5.0) + debug: 4.4.0(supports-color@8.1.1) eslint: 9.26.0(jiti@1.21.7) typescript: 5.4.5 transitivePeerDependencies: - supports-color - '@typescript-eslint/parser@8.32.1(eslint@9.26.0(jiti@1.21.7))(typescript@5.4.5)': + '@typescript-eslint/parser@8.32.1(eslint@9.26.0(jiti@1.21.7))(typescript@4.9.5)': dependencies: '@typescript-eslint/scope-manager': 8.32.1 '@typescript-eslint/types': 8.32.1 - '@typescript-eslint/typescript-estree': 8.32.1(typescript@5.4.5) + '@typescript-eslint/typescript-estree': 8.32.1(typescript@4.9.5) '@typescript-eslint/visitor-keys': 8.32.1 - debug: 4.4.0(supports-color@5.5.0) + debug: 4.4.0(supports-color@8.1.1) eslint: 9.26.0(jiti@1.21.7) - typescript: 5.4.5 + typescript: 4.9.5 transitivePeerDependencies: - supports-color - '@typescript-eslint/parser@8.32.1(eslint@9.26.0(jiti@1.21.7))(typescript@5.8.3)': + '@typescript-eslint/parser@8.32.1(eslint@9.26.0(jiti@1.21.7))(typescript@5.4.5)': dependencies: '@typescript-eslint/scope-manager': 8.32.1 '@typescript-eslint/types': 8.32.1 - '@typescript-eslint/typescript-estree': 8.32.1(typescript@5.8.3) + '@typescript-eslint/typescript-estree': 8.32.1(typescript@5.4.5) '@typescript-eslint/visitor-keys': 8.32.1 - debug: 4.4.0(supports-color@5.5.0) + debug: 4.4.0(supports-color@8.1.1) eslint: 9.26.0(jiti@1.21.7) - typescript: 5.8.3 + typescript: 5.4.5 transitivePeerDependencies: - supports-color @@ -22878,32 +22887,32 @@ snapshots: dependencies: '@typescript-eslint/typescript-estree': 8.22.0(typescript@5.4.5) '@typescript-eslint/utils': 8.22.0(eslint@9.26.0(jiti@1.21.7))(typescript@5.4.5) - debug: 4.4.0(supports-color@5.5.0) + debug: 4.4.0(supports-color@8.1.1) eslint: 9.26.0(jiti@1.21.7) ts-api-utils: 2.0.1(typescript@5.4.5) typescript: 5.4.5 transitivePeerDependencies: - supports-color - '@typescript-eslint/type-utils@8.32.1(eslint@9.26.0(jiti@1.21.7))(typescript@5.4.5)': + '@typescript-eslint/type-utils@8.32.1(eslint@9.26.0(jiti@1.21.7))(typescript@4.9.5)': dependencies: - '@typescript-eslint/typescript-estree': 8.32.1(typescript@5.4.5) - '@typescript-eslint/utils': 8.32.1(eslint@9.26.0(jiti@1.21.7))(typescript@5.4.5) - debug: 4.4.0(supports-color@5.5.0) + '@typescript-eslint/typescript-estree': 8.32.1(typescript@4.9.5) + '@typescript-eslint/utils': 8.32.1(eslint@9.26.0(jiti@1.21.7))(typescript@4.9.5) + debug: 4.4.0(supports-color@8.1.1) eslint: 9.26.0(jiti@1.21.7) - ts-api-utils: 2.1.0(typescript@5.4.5) - typescript: 5.4.5 + ts-api-utils: 2.1.0(typescript@4.9.5) + typescript: 4.9.5 transitivePeerDependencies: - supports-color - '@typescript-eslint/type-utils@8.32.1(eslint@9.26.0(jiti@1.21.7))(typescript@5.8.3)': + '@typescript-eslint/type-utils@8.32.1(eslint@9.26.0(jiti@1.21.7))(typescript@5.4.5)': dependencies: - '@typescript-eslint/typescript-estree': 8.32.1(typescript@5.8.3) - '@typescript-eslint/utils': 8.32.1(eslint@9.26.0(jiti@1.21.7))(typescript@5.8.3) - debug: 4.4.0(supports-color@5.5.0) + '@typescript-eslint/typescript-estree': 8.32.1(typescript@5.4.5) + '@typescript-eslint/utils': 8.32.1(eslint@9.26.0(jiti@1.21.7))(typescript@5.4.5) + debug: 4.4.0(supports-color@8.1.1) eslint: 9.26.0(jiti@1.21.7) - ts-api-utils: 2.1.0(typescript@5.8.3) - typescript: 5.8.3 + ts-api-utils: 2.1.0(typescript@5.4.5) + typescript: 5.4.5 transitivePeerDependencies: - supports-color @@ -22911,103 +22920,103 @@ snapshots: '@typescript-eslint/types@8.32.1': {} - '@typescript-eslint/typescript-estree@8.22.0(typescript@5.4.5)': + '@typescript-eslint/typescript-estree@8.22.0(typescript@4.9.5)': dependencies: '@typescript-eslint/types': 8.22.0 '@typescript-eslint/visitor-keys': 8.22.0 - debug: 4.4.0(supports-color@5.5.0) + debug: 4.4.0(supports-color@8.1.1) fast-glob: 3.3.3 is-glob: 4.0.3 minimatch: 9.0.5 semver: 7.7.2 - ts-api-utils: 2.1.0(typescript@5.4.5) - typescript: 5.4.5 + ts-api-utils: 2.1.0(typescript@4.9.5) + typescript: 4.9.5 transitivePeerDependencies: - supports-color - '@typescript-eslint/typescript-estree@8.22.0(typescript@5.8.3)': + '@typescript-eslint/typescript-estree@8.22.0(typescript@5.4.5)': dependencies: '@typescript-eslint/types': 8.22.0 '@typescript-eslint/visitor-keys': 8.22.0 - debug: 4.4.0(supports-color@5.5.0) + debug: 4.4.0(supports-color@8.1.1) fast-glob: 3.3.3 is-glob: 4.0.3 minimatch: 9.0.5 semver: 7.7.2 - ts-api-utils: 2.1.0(typescript@5.8.3) - typescript: 5.8.3 + ts-api-utils: 2.1.0(typescript@5.4.5) + typescript: 5.4.5 transitivePeerDependencies: - supports-color - '@typescript-eslint/typescript-estree@8.32.1(typescript@5.4.5)': + '@typescript-eslint/typescript-estree@8.32.1(typescript@4.9.5)': dependencies: '@typescript-eslint/types': 8.32.1 '@typescript-eslint/visitor-keys': 8.32.1 - debug: 4.4.0(supports-color@5.5.0) + debug: 4.4.0(supports-color@8.1.1) fast-glob: 3.3.3 is-glob: 4.0.3 minimatch: 9.0.5 semver: 7.7.2 - ts-api-utils: 2.1.0(typescript@5.4.5) - typescript: 5.4.5 + ts-api-utils: 2.1.0(typescript@4.9.5) + typescript: 4.9.5 transitivePeerDependencies: - supports-color - '@typescript-eslint/typescript-estree@8.32.1(typescript@5.8.3)': + '@typescript-eslint/typescript-estree@8.32.1(typescript@5.4.5)': dependencies: '@typescript-eslint/types': 8.32.1 '@typescript-eslint/visitor-keys': 8.32.1 - debug: 4.4.0(supports-color@5.5.0) + debug: 4.4.0(supports-color@8.1.1) fast-glob: 3.3.3 is-glob: 4.0.3 minimatch: 9.0.5 semver: 7.7.2 - ts-api-utils: 2.1.0(typescript@5.8.3) - typescript: 5.8.3 + ts-api-utils: 2.1.0(typescript@5.4.5) + typescript: 5.4.5 transitivePeerDependencies: - supports-color - '@typescript-eslint/utils@8.22.0(eslint@9.26.0(jiti@1.21.7))(typescript@5.4.5)': + '@typescript-eslint/utils@8.22.0(eslint@9.26.0(jiti@1.21.7))(typescript@4.9.5)': dependencies: '@eslint-community/eslint-utils': 4.4.1(eslint@9.26.0(jiti@1.21.7)) '@typescript-eslint/scope-manager': 8.22.0 '@typescript-eslint/types': 8.22.0 - '@typescript-eslint/typescript-estree': 8.22.0(typescript@5.4.5) + '@typescript-eslint/typescript-estree': 8.22.0(typescript@4.9.5) eslint: 9.26.0(jiti@1.21.7) - typescript: 5.4.5 + typescript: 4.9.5 transitivePeerDependencies: - supports-color - '@typescript-eslint/utils@8.22.0(eslint@9.26.0(jiti@1.21.7))(typescript@5.8.3)': + '@typescript-eslint/utils@8.22.0(eslint@9.26.0(jiti@1.21.7))(typescript@5.4.5)': dependencies: '@eslint-community/eslint-utils': 4.4.1(eslint@9.26.0(jiti@1.21.7)) '@typescript-eslint/scope-manager': 8.22.0 '@typescript-eslint/types': 8.22.0 - '@typescript-eslint/typescript-estree': 8.22.0(typescript@5.8.3) + '@typescript-eslint/typescript-estree': 8.22.0(typescript@5.4.5) eslint: 9.26.0(jiti@1.21.7) - typescript: 5.8.3 + typescript: 5.4.5 transitivePeerDependencies: - supports-color - '@typescript-eslint/utils@8.32.1(eslint@9.26.0(jiti@1.21.7))(typescript@5.4.5)': + '@typescript-eslint/utils@8.32.1(eslint@9.26.0(jiti@1.21.7))(typescript@4.9.5)': dependencies: '@eslint-community/eslint-utils': 4.7.0(eslint@9.26.0(jiti@1.21.7)) '@typescript-eslint/scope-manager': 8.32.1 '@typescript-eslint/types': 8.32.1 - '@typescript-eslint/typescript-estree': 8.32.1(typescript@5.4.5) + '@typescript-eslint/typescript-estree': 8.32.1(typescript@4.9.5) eslint: 9.26.0(jiti@1.21.7) - typescript: 5.4.5 + typescript: 4.9.5 transitivePeerDependencies: - supports-color - '@typescript-eslint/utils@8.32.1(eslint@9.26.0(jiti@1.21.7))(typescript@5.8.3)': + '@typescript-eslint/utils@8.32.1(eslint@9.26.0(jiti@1.21.7))(typescript@5.4.5)': dependencies: '@eslint-community/eslint-utils': 4.7.0(eslint@9.26.0(jiti@1.21.7)) '@typescript-eslint/scope-manager': 8.32.1 '@typescript-eslint/types': 8.32.1 - '@typescript-eslint/typescript-estree': 8.32.1(typescript@5.8.3) + '@typescript-eslint/typescript-estree': 8.32.1(typescript@5.4.5) eslint: 9.26.0(jiti@1.21.7) - typescript: 5.8.3 + typescript: 5.4.5 transitivePeerDependencies: - supports-color @@ -23446,7 +23455,7 @@ snapshots: agent-base@6.0.2: dependencies: - debug: 4.4.0(supports-color@5.5.0) + debug: 4.4.0(supports-color@8.1.1) transitivePeerDependencies: - supports-color @@ -24031,7 +24040,7 @@ snapshots: dependencies: bytes: 3.1.2 content-type: 1.0.5 - debug: 4.4.0(supports-color@5.5.0) + debug: 4.4.0(supports-color@8.1.1) http-errors: 2.0.0 iconv-lite: 0.6.3 on-finished: 2.4.1 @@ -24879,7 +24888,7 @@ snapshots: chalk: 2.4.2 commander: 2.20.3 core-js: 3.40.0 - debug: 4.4.0(supports-color@5.5.0) + debug: 4.4.0(supports-color@8.1.1) fast-json-patch: 3.1.1 get-stdin: 6.0.0 http-proxy-agent: 5.0.0 @@ -25258,7 +25267,7 @@ snapshots: dependencies: '@types/cookie': 0.4.1 '@types/cors': 2.8.17 - '@types/node': 22.15.17 + '@types/node': 20.17.46 accepts: 1.3.8 base64id: 2.0.0 cookie: 0.4.2 @@ -25494,7 +25503,7 @@ snapshots: esbuild-register@3.6.0(esbuild@0.25.3): dependencies: - debug: 4.4.0(supports-color@5.5.0) + debug: 4.4.0(supports-color@8.1.1) esbuild: 0.25.3 transitivePeerDependencies: - supports-color @@ -25706,7 +25715,7 @@ snapshots: eslint-import-resolver-typescript@3.7.0(eslint-plugin-import@2.31.0(@typescript-eslint/parser@8.22.0(eslint@9.26.0(jiti@1.21.7))(typescript@5.4.5))(eslint@9.26.0(jiti@1.21.7)))(eslint@9.26.0(jiti@1.21.7)): dependencies: '@nolyfill/is-core-module': 1.0.39 - debug: 4.4.0(supports-color@5.5.0) + debug: 4.4.0(supports-color@8.1.1) enhanced-resolve: 5.18.0 eslint: 9.26.0(jiti@1.21.7) fast-glob: 3.3.3 @@ -25816,10 +25825,10 @@ snapshots: dependencies: eslint: 9.26.0(jiti@1.21.7) - eslint-plugin-storybook@0.12.0(eslint@9.26.0(jiti@1.21.7))(typescript@5.8.3): + eslint-plugin-storybook@0.12.0(eslint@9.26.0(jiti@1.21.7))(typescript@4.9.5): dependencies: '@storybook/csf': 0.1.13 - '@typescript-eslint/utils': 8.22.0(eslint@9.26.0(jiti@1.21.7))(typescript@5.8.3) + '@typescript-eslint/utils': 8.22.0(eslint@9.26.0(jiti@1.21.7))(typescript@4.9.5) eslint: 9.26.0(jiti@1.21.7) ts-dedent: 2.2.0 transitivePeerDependencies: @@ -25864,7 +25873,7 @@ snapshots: ajv: 6.12.6 chalk: 4.1.2 cross-spawn: 7.0.6 - debug: 4.4.0(supports-color@5.5.0) + debug: 4.4.0(supports-color@8.1.1) escape-string-regexp: 4.0.0 eslint-scope: 8.3.0 eslint-visitor-keys: 4.2.0 @@ -26167,7 +26176,7 @@ snapshots: expo-system-ui@3.0.7(expo@51.0.39(@babel/core@7.27.1)(@babel/preset-env@7.27.2(@babel/core@7.27.1))(bufferutil@4.0.9)(react-native@0.74.5(@babel/core@7.27.1)(@babel/preset-env@7.27.2(@babel/core@7.27.1))(@types/react@18.2.79)(bufferutil@4.0.9)(react@18.2.0)(utf-8-validate@5.0.10))(react@18.2.0)(utf-8-validate@5.0.10)): dependencies: '@react-native/normalize-colors': 0.74.85 - debug: 4.4.0(supports-color@5.5.0) + debug: 4.4.0(supports-color@8.1.1) expo: 51.0.39(@babel/core@7.27.1)(@babel/preset-env@7.27.2(@babel/core@7.27.1))(bufferutil@4.0.9)(react-native@0.74.5(@babel/core@7.27.1)(@babel/preset-env@7.27.2(@babel/core@7.27.1))(@types/react@18.2.79)(bufferutil@4.0.9)(react@18.2.0)(utf-8-validate@5.0.10))(react@18.2.0)(utf-8-validate@5.0.10) transitivePeerDependencies: - supports-color @@ -26276,7 +26285,7 @@ snapshots: content-type: 1.0.5 cookie: 0.7.2 cookie-signature: 1.2.2 - debug: 4.4.0(supports-color@5.5.0) + debug: 4.4.0(supports-color@8.1.1) encodeurl: 2.0.0 escape-html: 1.0.3 etag: 1.8.1 @@ -26310,16 +26319,6 @@ snapshots: extend@3.0.2: {} - extract-zip@2.0.1: - dependencies: - debug: 4.4.0(supports-color@5.5.0) - get-stream: 5.2.0 - yauzl: 2.10.0 - optionalDependencies: - '@types/yauzl': 2.10.3 - transitivePeerDependencies: - - supports-color - extract-zip@2.0.1(supports-color@8.1.1): dependencies: debug: 4.4.0(supports-color@8.1.1) @@ -26478,7 +26477,7 @@ snapshots: finalhandler@2.1.0: dependencies: - debug: 4.4.0(supports-color@5.5.0) + debug: 4.4.0(supports-color@8.1.1) encodeurl: 2.0.0 escape-html: 1.0.3 on-finished: 2.4.1 @@ -26771,7 +26770,7 @@ snapshots: dependencies: basic-ftp: 5.0.5 data-uri-to-buffer: 6.0.2 - debug: 4.4.0(supports-color@5.5.0) + debug: 4.4.0(supports-color@8.1.1) transitivePeerDependencies: - supports-color @@ -27066,14 +27065,14 @@ snapshots: dependencies: '@tootallnate/once': 2.0.0 agent-base: 6.0.2 - debug: 4.4.0(supports-color@5.5.0) + debug: 4.4.0(supports-color@8.1.1) transitivePeerDependencies: - supports-color http-proxy-agent@7.0.2: dependencies: agent-base: 7.1.3 - debug: 4.4.0(supports-color@5.5.0) + debug: 4.4.0(supports-color@8.1.1) transitivePeerDependencies: - supports-color @@ -27086,14 +27085,14 @@ snapshots: https-proxy-agent@5.0.1: dependencies: agent-base: 6.0.2 - debug: 4.4.0(supports-color@5.5.0) + debug: 4.4.0(supports-color@8.1.1) transitivePeerDependencies: - supports-color https-proxy-agent@7.0.6: dependencies: agent-base: 7.1.3 - debug: 4.4.0(supports-color@5.5.0) + debug: 4.4.0(supports-color@8.1.1) transitivePeerDependencies: - supports-color @@ -27255,7 +27254,7 @@ snapshots: is-async-function@2.1.1: dependencies: async-function: 1.0.0 - call-bound: 1.0.3 + call-bound: 1.0.4 get-proto: 1.0.1 has-tostringtag: 1.0.2 safe-regex-test: 1.1.0 @@ -27270,7 +27269,7 @@ snapshots: is-boolean-object@1.2.1: dependencies: - call-bound: 1.0.3 + call-bound: 1.0.4 has-tostringtag: 1.0.2 is-buffer@1.1.6: {} @@ -27293,7 +27292,7 @@ snapshots: is-date-object@1.1.0: dependencies: - call-bound: 1.0.3 + call-bound: 1.0.4 has-tostringtag: 1.0.2 is-directory@0.3.1: {} @@ -27308,7 +27307,7 @@ snapshots: is-finalizationregistry@1.1.1: dependencies: - call-bound: 1.0.3 + call-bound: 1.0.4 is-fullwidth-code-point@2.0.0: {} @@ -27348,7 +27347,7 @@ snapshots: is-number-object@1.1.1: dependencies: - call-bound: 1.0.3 + call-bound: 1.0.4 has-tostringtag: 1.0.2 is-number@7.0.0: {} @@ -27397,7 +27396,7 @@ snapshots: is-symbol@1.1.1: dependencies: - call-bound: 1.0.3 + call-bound: 1.0.4 has-symbols: 1.1.0 safe-regex-test: 1.1.0 @@ -27421,8 +27420,8 @@ snapshots: is-weakset@2.0.4: dependencies: - call-bound: 1.0.3 - get-intrinsic: 1.2.7 + call-bound: 1.0.4 + get-intrinsic: 1.3.0 is-wsl@1.1.0: {} @@ -27474,7 +27473,7 @@ snapshots: istanbul-lib-source-maps@4.0.1: dependencies: - debug: 4.4.0(supports-color@5.5.0) + debug: 4.4.0(supports-color@8.1.1) istanbul-lib-coverage: 3.2.2 source-map: 0.6.1 transitivePeerDependencies: @@ -27752,7 +27751,7 @@ snapshots: dependencies: '@jest/types': 29.6.3 '@types/graceful-fs': 4.1.9 - '@types/node': 22.15.17 + '@types/node': 20.17.46 anymatch: 3.1.3 fb-watchman: 2.0.2 graceful-fs: 4.2.11 @@ -27826,7 +27825,7 @@ snapshots: '@jest/test-result': 29.7.0 '@jest/transform': 29.7.0 '@jest/types': 29.6.3 - '@types/node': 22.15.17 + '@types/node': 20.17.46 chalk: 4.1.2 emittery: 0.13.1 graceful-fs: 4.2.11 @@ -27854,7 +27853,7 @@ snapshots: '@jest/test-result': 29.7.0 '@jest/transform': 29.7.0 '@jest/types': 29.6.3 - '@types/node': 22.15.17 + '@types/node': 20.17.46 chalk: 4.1.2 cjs-module-lexer: 1.4.3 collect-v8-coverage: 1.0.2 @@ -27936,7 +27935,7 @@ snapshots: dependencies: '@jest/test-result': 29.7.0 '@jest/types': 29.6.3 - '@types/node': 22.15.17 + '@types/node': 20.17.46 ansi-escapes: 4.3.2 chalk: 4.1.2 emittery: 0.13.1 @@ -28941,7 +28940,7 @@ snapshots: dependencies: '@tokenizer/token': 0.3.0 content-type: 1.0.5 - debug: 4.4.0(supports-color@5.5.0) + debug: 4.4.0(supports-color@8.1.1) file-type: 16.5.4 media-typer: 1.1.0 strtok3: 6.3.0 @@ -29415,7 +29414,7 @@ snapshots: dependencies: '@tootallnate/quickjs-emscripten': 0.23.0 agent-base: 7.1.3 - debug: 4.4.0(supports-color@5.5.0) + debug: 4.4.0(supports-color@8.1.1) get-uri: 6.0.4 http-proxy-agent: 7.0.2 https-proxy-agent: 7.0.6 @@ -29689,7 +29688,7 @@ snapshots: pm2-axon-rpc@0.7.1: dependencies: - debug: 4.4.0(supports-color@5.5.0) + debug: 4.4.0(supports-color@8.1.1) transitivePeerDependencies: - supports-color @@ -29697,7 +29696,7 @@ snapshots: dependencies: amp: 0.3.1 amp-message: 0.1.2 - debug: 4.4.0(supports-color@5.5.0) + debug: 4.4.0(supports-color@8.1.1) escape-string-regexp: 4.0.0 transitivePeerDependencies: - supports-color @@ -29714,7 +29713,7 @@ snapshots: pm2-sysmonit@1.2.8: dependencies: async: 3.2.6 - debug: 4.4.0(supports-color@5.5.0) + debug: 4.4.0(supports-color@8.1.1) pidusage: 2.0.21 systeminformation: 5.25.11 tx2: 1.0.5 @@ -29736,7 +29735,7 @@ snapshots: commander: 2.15.1 croner: 4.1.97 dayjs: 1.11.13(patch_hash=aa81ceb75c492cff223134948ee626fc02bb04b49873a531b674f73099fd868b) - debug: 4.4.0(supports-color@5.5.0) + debug: 4.4.0(supports-color@8.1.1) enquirer: 2.3.6 eventemitter2: 5.0.1 fclone: 1.0.11 @@ -29908,7 +29907,7 @@ snapshots: yaml: 2.7.0 optionalDependencies: postcss: 8.4.38 - ts-node: 10.9.2(@swc/core@1.11.24(@swc/helpers@0.5.15))(@types/node@22.15.17)(typescript@5.4.5) + ts-node: 10.9.2(@swc/core@1.11.24)(@types/node@22.15.17)(typescript@5.4.5) postcss-load-config@4.0.2(postcss@8.4.47)(ts-node@10.9.2(@swc/core@1.11.24)(@types/node@22.15.17)(typescript@5.4.5)): dependencies: @@ -30272,7 +30271,7 @@ snapshots: '@protobufjs/path': 1.1.2 '@protobufjs/pool': 1.1.0 '@protobufjs/utf8': 1.1.0 - '@types/node': 22.15.17 + '@types/node': 20.17.46 long: 5.3.1 proxy-addr@2.0.7: @@ -30283,7 +30282,7 @@ snapshots: proxy-agent@6.3.1: dependencies: agent-base: 7.1.3 - debug: 4.4.0(supports-color@5.5.0) + debug: 4.4.0(supports-color@8.1.1) http-proxy-agent: 7.0.2 https-proxy-agent: 7.0.6 lru-cache: 7.18.3 @@ -30997,7 +30996,7 @@ snapshots: es-abstract: 1.23.9 es-errors: 1.3.0 es-object-atoms: 1.1.1 - get-intrinsic: 1.2.7 + get-intrinsic: 1.3.0 get-proto: 1.0.1 which-builtin-type: 1.2.1 @@ -31056,7 +31055,7 @@ snapshots: require-in-the-middle@5.2.0: dependencies: - debug: 4.4.0(supports-color@5.5.0) + debug: 4.4.0(supports-color@8.1.1) module-details-from-path: 1.0.3 resolve: 1.22.10 transitivePeerDependencies: @@ -31202,7 +31201,7 @@ snapshots: router@2.2.0: dependencies: - debug: 4.4.0(supports-color@5.5.0) + debug: 4.4.0(supports-color@8.1.1) depd: 2.0.0 is-promise: 4.0.0 parseurl: 1.3.3 @@ -31364,7 +31363,7 @@ snapshots: send@1.2.0: dependencies: - debug: 4.4.0(supports-color@5.5.0) + debug: 4.4.0(supports-color@8.1.1) encodeurl: 2.0.0 escape-html: 1.0.3 etag: 1.8.1 @@ -31493,16 +31492,16 @@ snapshots: side-channel-map@1.0.1: dependencies: - call-bound: 1.0.3 + call-bound: 1.0.4 es-errors: 1.3.0 - get-intrinsic: 1.2.7 + get-intrinsic: 1.3.0 object-inspect: 1.13.3 side-channel-weakmap@1.0.2: dependencies: - call-bound: 1.0.3 + call-bound: 1.0.4 es-errors: 1.3.0 - get-intrinsic: 1.2.7 + get-intrinsic: 1.3.0 object-inspect: 1.13.3 side-channel-map: 1.0.1 @@ -31640,7 +31639,7 @@ snapshots: socks-proxy-agent@8.0.5: dependencies: agent-base: 7.1.3 - debug: 4.4.0(supports-color@5.5.0) + debug: 4.4.0(supports-color@8.1.1) socks: 2.8.3 transitivePeerDependencies: - supports-color @@ -32344,13 +32343,13 @@ snapshots: dependencies: typescript: 5.4.5 - ts-api-utils@2.1.0(typescript@5.4.5): + ts-api-utils@2.1.0(typescript@4.9.5): dependencies: - typescript: 5.4.5 + typescript: 4.9.5 - ts-api-utils@2.1.0(typescript@5.8.3): + ts-api-utils@2.1.0(typescript@5.4.5): dependencies: - typescript: 5.8.3 + typescript: 5.4.5 ts-custom-error@3.3.1: {} @@ -32426,27 +32425,6 @@ snapshots: optionalDependencies: '@swc/core': 1.11.24(@swc/helpers@0.5.15) - ts-node@10.9.2(@swc/core@1.11.24(@swc/helpers@0.5.15))(@types/node@22.15.17)(typescript@5.4.5): - dependencies: - '@cspotcode/source-map-support': 0.8.1 - '@tsconfig/node10': 1.0.11 - '@tsconfig/node12': 1.0.11 - '@tsconfig/node14': 1.0.3 - '@tsconfig/node16': 1.0.4 - '@types/node': 22.15.17 - acorn: 8.14.0 - acorn-walk: 8.3.4 - arg: 4.1.3 - create-require: 1.1.1 - diff: 4.0.2 - make-error: 1.3.6 - typescript: 5.4.5 - v8-compile-cache-lib: 3.0.1 - yn: 3.1.1 - optionalDependencies: - '@swc/core': 1.11.24(@swc/helpers@0.5.15) - optional: true - ts-node@10.9.2(@swc/core@1.11.24)(@types/node@20.17.46)(typescript@5.4.5): dependencies: '@cspotcode/source-map-support': 0.8.1 @@ -32467,7 +32445,7 @@ snapshots: optionalDependencies: '@swc/core': 1.11.24(@swc/helpers@0.5.15) - ts-node@10.9.2(@swc/core@1.11.24)(@types/node@22.15.17)(typescript@5.4.5): + ts-node@10.9.2(@swc/core@1.11.24)(@types/node@22.15.17)(typescript@4.9.5): dependencies: '@cspotcode/source-map-support': 0.8.1 '@tsconfig/node10': 1.0.11 @@ -32481,13 +32459,13 @@ snapshots: create-require: 1.1.1 diff: 4.0.2 make-error: 1.3.6 - typescript: 5.4.5 + typescript: 4.9.5 v8-compile-cache-lib: 3.0.1 yn: 3.1.1 optionalDependencies: '@swc/core': 1.11.24(@swc/helpers@0.5.15) - ts-node@10.9.2(@swc/core@1.11.24)(@types/node@22.15.17)(typescript@5.8.3): + ts-node@10.9.2(@swc/core@1.11.24)(@types/node@22.15.17)(typescript@5.4.5): dependencies: '@cspotcode/source-map-support': 0.8.1 '@tsconfig/node10': 1.0.11 @@ -32501,7 +32479,7 @@ snapshots: create-require: 1.1.1 diff: 4.0.2 make-error: 1.3.6 - typescript: 5.8.3 + typescript: 5.4.5 v8-compile-cache-lib: 3.0.1 yn: 3.1.1 optionalDependencies: @@ -32679,7 +32657,8 @@ snapshots: typescript@5.4.5: {} - typescript@5.8.3: {} + typescript@5.8.3: + optional: true ua-parser-js@1.0.40: {} @@ -32937,7 +32916,7 @@ snapshots: vite-node@2.1.9(@types/node@22.15.17)(terser@5.37.0): dependencies: cac: 6.7.14 - debug: 4.4.0(supports-color@5.5.0) + debug: 4.4.0(supports-color@8.1.1) es-module-lexer: 1.6.0 pathe: 1.1.2 vite: 5.4.19(@types/node@22.15.17)(terser@5.37.0) @@ -32957,7 +32936,7 @@ snapshots: '@microsoft/api-extractor': 7.43.0(@types/node@22.15.17) '@rollup/pluginutils': 5.1.4(rollup@4.40.2) '@vue/language-core': 1.8.27(typescript@5.4.5) - debug: 4.4.0(supports-color@5.5.0) + debug: 4.4.0(supports-color@8.1.1) kolorist: 1.8.0 magic-string: 0.30.17 typescript: 5.4.5 @@ -33176,7 +33155,7 @@ snapshots: which-builtin-type@1.2.1: dependencies: - call-bound: 1.0.3 + call-bound: 1.0.4 function.prototype.name: 1.1.8 has-tostringtag: 1.0.2 is-async-function: 2.1.1 diff --git a/services/server/fixtures/api.ts b/services/server/fixtures/api.ts deleted file mode 100644 index 5c958a56d..000000000 --- a/services/server/fixtures/api.ts +++ /dev/null @@ -1,78 +0,0 @@ -import { Api as AtlasApi, AuthToken, TokenType } from "@litespace/atlas"; -import { IUser } from "@litespace/types"; -import { faker } from "@faker-js/faker/locale/ar"; -import { sample } from "lodash"; -import db from "@fixtures/db"; -import { tutors } from "@litespace/models"; - -function asBearerToken(token?: string): AuthToken | null { - if (!token) return null; - return { - type: TokenType.Bearer, - value: token, - }; -} - -export function api(token?: string): AtlasApi { - return new AtlasApi("local", asBearerToken(token)); -} - -export function unexpectedApiSuccess() { - throw new Error("Unexpected API response; Request should fail"); -} - -export class Api { - token: string; - public readonly api: AtlasApi; - - constructor(token?: string) { - this.api = api(token); - this.token = token || ""; - } - - static async fromCredentials(email: string, password: string) { - const result = await api().auth.password({ email, password }); - return new Api(result.token); - } - - static async forUser(role: IUser.Role) { - const email = faker.internet.email(); - const password = "Password@8"; - await db.user({ role, email, password }); - return await Api.fromCredentials(email, password); - } - - static async forSuperAdmin() { - return await this.forUser(IUser.Role.SuperAdmin); - } - - static async forStudent() { - return await this.forUser(IUser.Role.Student); - } - - static async forTutor() { - const res = await this.forUser(IUser.Role.Tutor); - - // insert row in tutors table - const current = await res.findCurrentUser(); - await tutors.create(current.id); - - return res; - } - - async createUser(payload?: Partial) { - return this.api.user.create({ - email: payload?.email || faker.internet.email(), - password: payload?.password || "Password@8", - role: payload?.role || (sample(Object.values(IUser.Role)) as IUser.Role), - }); - } - - async findCurrentUser() { - return this.api.user.findCurrentUser(); - } -} - -export default { - api, -}; diff --git a/services/server/fixtures/db.ts b/services/server/fixtures/db.ts deleted file mode 100644 index 4270cfdee..000000000 --- a/services/server/fixtures/db.ts +++ /dev/null @@ -1,612 +0,0 @@ -import { - hashPassword, - interviews, - knex, - lessons, - messages, - rooms, - topics, - users, - ratings, - tutors, - availabilitySlots, - contactRequests, - sessionEvents, - transactions, - confirmationCodes, - plans, - subscriptions, - invoices, -} from "@litespace/models"; -import { - IInterview, - ILesson, - ITopic, - IUser, - IRating, - IMessage, - ISession, - IAvailabilitySlot, - ITutor, - ITransaction, - ISubscription, - IPlan, -} from "@litespace/types"; -import { faker } from "@faker-js/faker/locale/ar"; -import { entries, first, range, sample } from "lodash"; -import { Knex } from "knex"; -import { Time } from "@litespace/utils/time"; -export { faker } from "@faker-js/faker/locale/ar"; -import dayjs from "dayjs"; -import utc from "dayjs/plugin/utc"; -import { randomInt, randomUUID } from "crypto"; -import { percentage, price } from "@litespace/utils"; - -dayjs.extend(utc); - -export async function flush() { - await knex.transaction(async (tx) => { - await subscriptions.builder(tx).del(); - await transactions.builder(tx).del(); - await plans.builder(tx).del(); - await invoices.builder(tx).del(); - await sessionEvents.builder(tx).del(); - await topics.builder(tx).userTopics.del(); - await topics.builder(tx).topics.del(); - await messages.builder(tx).del(); - await rooms.builder(tx).members.del(); - await rooms.builder(tx).rooms.del(); - await lessons.builder(tx).members.del(); - await lessons.builder(tx).lessons.del(); - await interviews.builder(tx).del(); - await ratings.builder(tx).del(); - await confirmationCodes.builder(tx).del(); - await tutors.builder(tx).del(); - await availabilitySlots.builder(tx).del(); - await users.builder(tx).del(); - await contactRequests.builder(tx).del(); - }); -} - -const or = { - async tutorId(id?: number): Promise { - if (!id) return await tutor().then((tutor) => tutor.id); - return id; - }, - async studentId(id?: number): Promise { - if (!id) return await student().then((student) => student.id); - return id; - }, - async tutorManagerId(id?: number): Promise { - if (!id) - return await tutorManager().then((tutorManager) => tutorManager.id); - return id; - }, - async sessionId(type: ISession.Type): Promise { - return `${type}:${randomUUID()}`; - }, - async roomId(roomId?: number): Promise { - if (!roomId) return await makeRoom(); - return roomId; - }, - async slotId(id?: number, userId?: number): Promise { - if (!id) return await slot({ userId }).then((slot) => slot.id); - return id; - }, - async planId(id?: number): Promise { - if (!id) return await plan().then((plan) => plan.id); - return id; - }, - async txId(id?: number, userId?: number): Promise { - if (!id) return await transaction({ userId }).then((tx) => tx.id); - return id; - }, - role(role?: IUser.Role) { - if (!role) - return sample([ - IUser.Role.Student, - IUser.Role.Tutor, - IUser.Role.TutorManager, - IUser.Role.SuperAdmin, - IUser.Role.RegularAdmin, - IUser.Role.Studio, - ]); - - return role; - }, - boolean(cond?: boolean) { - if (cond === undefined) return sample([false, true]); - return cond; - }, - start(start?: string): string { - if (!start) return faker.date.soon().toISOString(); - return start; - }, - planPeriod(period?: IPlan.Period) { - if (!period) - return sample([ - IPlan.Period.Month, - IPlan.Period.Quarter, - IPlan.Period.Year, - ]); - return period; - }, -} as const; - -export function gender(): IUser.Gender { - return sample([IUser.Gender.Male, IUser.Gender.Female])!; -} - -export function duration(): number { - return sample([15, 30])!; -} - -export function time() { - const times = range(0, 24).map((hour) => - [hour.toString().padStart(2, "0"), "00"].join(":") - ); - return Time.from(sample(times)!).utc().format(); -} - -export async function user(payload?: Partial) { - return await users.create({ - email: payload?.email || faker.internet.email(), - gender: payload?.gender || gender(), - name: payload?.name || faker.internet.username(), - password: hashPassword(payload?.password || "Password@8"), - birthYear: payload?.birthYear || faker.number.int({ min: 2000, max: 2024 }), - role: or.role(payload?.role), - verifiedEmail: payload?.verifiedEmail || false, - }); -} - -export async function slot(payload?: Partial) { - const start = dayjs.utc(payload?.start || faker.date.future()); - const end = payload?.end - ? dayjs.utc(payload.end) - : start.add(faker.number.int(8), "hours"); - - const newSlots = await availabilitySlots.create([ - { - userId: payload?.userId || 1, - start: start.toISOString(), - end: end.toISOString(), - }, - ]); - const res = first(newSlots); - if (!res) throw Error("error: couldn't insert new slot."); - return res; -} - -type LessonReturn = { - lesson: ILesson.Self; - members: ILesson.Member[]; -}; - -export async function lesson( - payload?: Partial & { - timing?: "future" | "past"; - canceled?: boolean; - } -): Promise { - return await knex.transaction(async (tx: Knex.Transaction) => { - const tutor = await or.tutorId(payload?.tutor); - const student = await or.studentId(payload?.student); - const data = await lessons.create({ - session: await or.sessionId("lesson"), - start: - payload?.timing === "future" - ? faker.date.future().toISOString() - : payload?.timing === "past" - ? faker.date.past().toISOString() - : payload?.start || faker.date.soon().toISOString(), - duration: payload?.duration || sample([15, 30]), - price: payload?.price || faker.number.int(500), - slot: await or.slotId(payload?.slot, tutor), - student, - tutor, - tx, - }); - - if (payload?.canceled) - await lessons.cancel({ - canceledBy: tutor, - ids: [data.lesson.id], - tx, - }); - - return data; - }); -} - -export async function interview(payload: Partial) { - return await interviews.create({ - interviewer: await or.tutorManagerId(payload.interviewer), - interviewee: await or.tutorId(payload.interviewee), - session: await or.sessionId("interview"), - slot: await or.slotId(payload.slot, payload.interviewer), - start: or.start(payload.start), - }); -} - -export async function topic(payload?: Partial) { - return await topics.create({ - name: { - ar: payload?.name?.ar || faker.animal.bear(), - en: payload?.name?.en || faker.animal.bird(), - }, - }); -} - -async function tutor( - userPayload?: Partial, - tutorPayload?: Partial -) { - const info = await user({ ...userPayload, role: IUser.Role.Tutor }); - const tutor = await tutors.create(info.id); - await tutors.update(tutor.id, tutorPayload || {}); - return tutor; -} - -async function tutorUser( - userPayload?: Partial, - tutorPayload?: Partial -) { - const info = await user({ ...userPayload, role: IUser.Role.Tutor }); - const tutor = await tutors.create(info.id); - await tutors.update(tutor.id, tutorPayload || {}); - return info; -} - -async function onboardedTutor() { - const newTutor = await tutor(); - - await users.update(newTutor.id, { - phone: "01012345678", - image: "/image.jpg", - verifiedEmail: true, - verifiedPhone: true, - }); - - await tutors.update(newTutor.id, { - about: faker.lorem.paragraphs(), - bio: faker.person.bio(), - activated: true, - video: "/video.mp4", - notice: 10, - }); - - return newTutor; -} - -function student() { - return user({ role: IUser.Role.Student }); -} - -async function tutorManager( - userPayload?: Partial, - tutorPayload?: Partial -) { - const info = await user({ ...userPayload, role: IUser.Role.TutorManager }); - const tutor = await tutors.create(info.id); - await tutors.update(tutor.id, tutorPayload || {}); - return tutor; -} - -async function students(count: number) { - return await Promise.all(range(0, count).map(() => student())); -} - -async function makeTutors(count: number) { - return await Promise.all(range(0, count).map(() => tutor())); -} - -export type MakeLessonsReturn = Array<{ - future: LessonReturn[]; - past: LessonReturn[]; - canceled: { - future: LessonReturn[]; - past: LessonReturn[]; - }; - uncanceled: { - future: LessonReturn[]; - past: LessonReturn[]; - }; -}>; - -async function makeLessons({ - tutor, - students, - future, - past, - canceled, - duration, - price, -}: { - tutor: number; - students: number[]; - future: number[]; - past: number[]; - duration?: ILesson.Duration; - price?: number; - canceled: { - future: number[]; - past: number[]; - }; -}): Promise { - const result: MakeLessonsReturn = []; - - for (const [key, student] of entries(students)) { - const index = Number(key); - const futureLessonCount = future[index]; - const pastLessonCount = past[index]; - const canceledFutureLessonCount = canceled.future[index]; - const canceledPastLessonCount = canceled.past[index]; - - const create = async (start: string) => { - return await lesson({ - tutor, - student, - start, - duration, - price, - }); - }; - - const futureLessons = await Promise.all( - range(0, futureLessonCount).map((i) => - create( - dayjs - .utc() - .add(i + 1, "days") - .toISOString() - ) - ) - ); - - const pastLessons = await Promise.all( - range(0, pastLessonCount).map((i) => - create( - dayjs - .utc() - .subtract(i + 1, "days") - .toISOString() - ) - ) - ); - - // cancel future lessons - const canceledFutureLessons = await Promise.all( - range(0, canceledFutureLessonCount).map(async (i) => { - const info = futureLessons[i]; - if (!lesson) throw new Error("invalid future lesson index"); - await lessons.cancel({ - canceledBy: tutor, - ids: [info.lesson.id], - }); - return info; - }) - ); - - // cancel past lessons - const canceledPastLessons = await Promise.all( - range(0, canceledPastLessonCount).map(async (i) => { - const info = pastLessons[i]; - if (!info) throw new Error("invalid past lesson index"); - await lessons.cancel({ - canceledBy: tutor, - ids: [info.lesson.id], - }); - return info; - }) - ); - - const canceledFutureLessonIds = canceledFutureLessons.map( - (future) => future.lesson.id - ); - const canceledPastLessonIds = canceledPastLessons.map( - (past) => past.lesson.id - ); - // filter out canceled future lessons - const uncanceledFutureLessons = futureLessons.filter( - (future) => !canceledFutureLessonIds.includes(future.lesson.id) - ); - // filter out canceled past lessons - const uncanceledPastLessons = pastLessons.filter( - (past) => !canceledPastLessonIds.includes(past.lesson.id) - ); - result.push({ - future: futureLessons, - past: pastLessons, - canceled: { - future: canceledFutureLessons, - past: canceledPastLessons, - }, - uncanceled: { - future: uncanceledFutureLessons, - past: uncanceledPastLessons, - }, - }); - } - - return result; -} - -export async function makeRating(payload?: Partial) { - const raterId: number = - payload?.raterId || - (await user({ role: IUser.Role.Student }).then((user) => user.id)); - const rateeId: number = - payload?.rateeId || - (await user({ role: IUser.Role.Tutor }).then((user) => user.id)); - - return await ratings.create({ - rateeId, - raterId, - value: payload?.value || faker.number.int({ min: 0, max: 5 }), - feedback: faker.lorem.words(20), - }); -} - -export async function makeRatings({ - values, - ratee, -}: { - values: number[]; - ratee?: number; -}) { - const rateeId: number = - ratee || (await user({ role: IUser.Role.Tutor }).then((user) => user.id)); - - return Promise.all( - values.map(async (value) => { - const student = await user({ role: IUser.Role.Student }); - return await ratings.create({ - rateeId: rateeId, - raterId: student.id, - value: value, - feedback: "Great Teacher", - }); - }) - ); -} - -async function makeRoom(payload?: [number, number]) { - return await knex.transaction(async (tx) => { - const [firstUserId, secondUserId]: [number, number] = payload || [ - await tutor().then((user) => user.id), - await student().then((user) => user.id), - ]; - return await rooms.create([firstUserId, secondUserId], tx); - }); -} - -async function makeMessage( - _: Knex.Transaction, - payload?: Partial -) { - const roomId: number = await or.roomId(payload?.roomId); - const userId: number = - payload?.userId || - (await rooms - .findRoomMembers({ roomIds: [roomId] }) - .then((members) => members[0].id)); - - const text = payload?.text || faker.lorem.words(10); - - return await messages.create({ - userId, - roomId, - text, - }); -} - -async function makeInterviews(payload: { - data: [ - { - interviewer: number; - interviewees: number[]; - statuses: IInterview.Status[]; - levels: IInterview.Self["level"][]; - }, - ]; -}) { - for (const { interviewer, interviewees, statuses, levels } of payload.data) { - for (const [key, interviewee] of entries(interviewees)) { - const index = Number(key); - const status = statuses[index]; - const level = levels[index]; - const interviewObj = await interview({ - interviewer, - interviewee, - }); - - if (status) - await interviews.update(interviewObj.ids.self, { - status, - }); - - if (level) - await interviews.update(interviewObj.ids.self, { - level, - }); - } - } -} - -async function transaction( - payload?: Partial -): Promise { - return await transactions.create({ - userId: await or.studentId(payload?.userId), - amount: payload?.amount || randomInt(1000), - paymentMethod: payload?.paymentMethod || ITransaction.PaymentMethod.Card, - providerRefNum: payload?.providerRefNum || null, - planId: await or.planId(payload?.planId), - planPeriod: or.planPeriod(payload?.planPeriod), - }); -} - -async function plan( - payload?: Partial -): Promise { - return await plans.create({ - weeklyMinutes: payload?.weeklyMinutes || randomInt(1000), - baseMonthlyPrice: payload?.baseMonthlyPrice || randomPrice(), - monthDiscount: payload?.monthDiscount || randomDiscount(), - quarterDiscount: payload?.quarterDiscount || randomDiscount(), - yearDiscount: payload?.yearDiscount || randomDiscount(), - forInvitesOnly: or.boolean(payload?.forInvitesOnly), - active: or.boolean(payload?.active), - }); -} - -function randomPrice() { - return price.scale(randomInt(5000)); -} - -function randomDiscount() { - return percentage.scale(randomInt(100)); -} - -async function subscription( - payload?: Partial -): Promise { - const userId = await or.studentId(payload?.userId); - return await subscriptions.create({ - userId, - planId: await or.planId(payload?.planId), - txId: await or.txId(payload?.txId, userId), - period: or.planPeriod(payload?.period), - weeklyMinutes: payload?.weeklyMinutes || sample([120, 150, 180]), - start: payload?.start || faker.date.future().toISOString(), - end: payload?.end || faker.date.future().toISOString(), - }); -} - -export default { - user, - tutor, - tutorUser, - onboardedTutor, - student, - tutorManager, - students, - interview, - lesson, - flush, - topic, - slot, - transaction, - plan, - subscription, - room: makeRoom, - message: makeMessage, - make: { - lessons: makeLessons, - interviews: makeInterviews, - tutors: makeTutors, - rating: makeRating, - ratings: makeRatings, - }, -}; diff --git a/packages/tests/src/mocks/kafka.ts b/services/server/fixtures/jest/kafka.ts similarity index 100% rename from packages/tests/src/mocks/kafka.ts rename to services/server/fixtures/jest/kafka.ts diff --git a/packages/tests/src/mocks/s3.ts b/services/server/fixtures/jest/s3.ts similarity index 100% rename from packages/tests/src/mocks/s3.ts rename to services/server/fixtures/jest/s3.ts diff --git a/packages/tests/src/mocks/telegram.ts b/services/server/fixtures/jest/telegram.ts similarity index 100% rename from packages/tests/src/mocks/telegram.ts rename to services/server/fixtures/jest/telegram.ts diff --git a/packages/tests/src/mocks/worker.ts b/services/server/fixtures/jest/worker.ts similarity index 100% rename from packages/tests/src/mocks/worker.ts rename to services/server/fixtures/jest/worker.ts diff --git a/services/server/fixtures/kafka.ts b/services/server/fixtures/kafka.ts deleted file mode 100644 index da9676440..000000000 --- a/services/server/fixtures/kafka.ts +++ /dev/null @@ -1,16 +0,0 @@ -class Producer { - constructor() {} - send() {} - connect() {} -} - -class Consumer { - constructor() {} - subscribe() {} - run() {} -} - -jest.mock("@litespace/kafka", () => ({ - Producer, - Consumer, -})); diff --git a/services/server/fixtures/mockApi.ts b/services/server/fixtures/mockApi.ts deleted file mode 100644 index 8dd9c5bd7..000000000 --- a/services/server/fixtures/mockApi.ts +++ /dev/null @@ -1,81 +0,0 @@ -import { Request, Response, NextFunction } from "express"; -import { IUser } from "@litespace/types"; -import db from "@fixtures/db"; -import { ApiContext } from "@/types/api"; -import { Server } from "socket.io"; - -type MockRequest = { - body?: Body; - params?: Params; - query?: Query; - user?: IUser.Self | IUser.Role; - files?: Record | Express.Multer.File[]; -}; - -class MockResponse { - data: { - status: number | null; - body: T | null; - } = { status: null, body: null }; - - sendStatus(status: number) { - this.data.status = status; - return this; - } - - status(status: number) { - this.data.status = status; - return this; - } - - json(body: T) { - this.data.body = body; - return this; - } - - send(body: T) { - this.data.body = body; - return this; - } -} - -export function mockApi< - Body = object, - Params = object, - Query = object, - Res = unknown, ->( - handler: ( - req: Request, - res: Response, - next: NextFunction - ) => Promise | void -) { - return async ( - request: MockRequest - ): Promise<{ status: number | null; body: Res | null }> => { - // these assigns avoids absurd errors while using - // the mockApi in test suites - if (!request.body) request.body = {} as Body; - if (!request.query) request.query = {} as Query; - if (!request.params) request.params = {} as Params; - - if (typeof request.user === "number") - request.user = await db.user({ role: request.user }); - - const response = new MockResponse(); - return await new Promise((resolve) => { - const result = handler( - request as unknown as Request, - response as unknown as Response, - (next) => resolve(next) - ); - if (result instanceof Promise) result.then(() => resolve(response.data)); - else return resolve(response.data); - }); - }; -} - -export function mockApiContext(): ApiContext { - return { io: new Server() }; -} diff --git a/services/server/fixtures/s3.ts b/services/server/fixtures/s3.ts deleted file mode 100644 index 446971ada..000000000 --- a/services/server/fixtures/s3.ts +++ /dev/null @@ -1,28 +0,0 @@ -class S3Client { - name = "litespace"; - constructor() {} - - send() {} -} - -class PutObjectCommand { - constructor() {} -} - -class GetObjectCommand { - constructor() {} -} - -function getSignedUrl(): string { - return "https://example.com/"; -} - -jest.mock("@aws-sdk/client-s3", () => ({ - S3Client, - PutObjectCommand, - GetObjectCommand, -})); - -jest.mock("@aws-sdk/s3-request-presigner", () => ({ - getSignedUrl, -})); diff --git a/services/server/fixtures/telegram.ts b/services/server/fixtures/telegram.ts deleted file mode 100644 index f453d4e96..000000000 --- a/services/server/fixtures/telegram.ts +++ /dev/null @@ -1,8 +0,0 @@ -class TelegramBot { - sendMessage() {} -} - -jest.mock("@litespace/radio/telegram/bot", () => ({ - ...jest.requireActual("@litespace/radio/telegram/bot"), - TelegramBot, -})); diff --git a/services/server/fixtures/worker.ts b/services/server/fixtures/worker.ts deleted file mode 100644 index 18889524b..000000000 --- a/services/server/fixtures/worker.ts +++ /dev/null @@ -1,8 +0,0 @@ -class MockWorker { - postMessage() {} -} - -jest.mock("node:worker_threads", () => ({ - ...jest.requireActual("node:worker_threads"), - Worker: MockWorker, -})); diff --git a/services/server/fixtures/wss.ts b/services/server/fixtures/wss.ts deleted file mode 100644 index d2b148f13..000000000 --- a/services/server/fixtures/wss.ts +++ /dev/null @@ -1,70 +0,0 @@ -import { io, Socket } from "socket.io-client"; -import { sockets } from "@litespace/atlas"; -import { ISession, Wss } from "@litespace/types"; -import { uniqueId } from "lodash"; - -export class ClientSocket { - public readonly client: Socket; - - constructor(token: string) { - const options = { - extraHeaders: { Authorization: `Bearer ${token}` }, - } as const; - this.client = io(sockets.main.local, options); - } - - userTyping(roomId: number) { - return this.emit(Wss.ClientEvent.UserTyping, { roomId }); - } - - preJoinSession(sessionId: ISession.Id) { - return this.emit(Wss.ClientEvent.PreJoinSession, { sessionId }); - } - - joinSession(sessionId: ISession.Id) { - return this.emit(Wss.ClientEvent.JoinSession, { sessionId }); - } - - leaveSession(sessionId: ISession.Id) { - return this.emit(Wss.ClientEvent.LeaveSession, { sessionId }); - } - - sendMessage(roomId: number, text: string) { - const refId = uniqueId(); - return this.emit(Wss.ClientEvent.SendMessage, { roomId, text, refId }); - } - - deleteMessage(msgId: number) { - return this.emit(Wss.ClientEvent.DeleteMessage, { id: msgId }); - } - - markMessageAsRead(msgId: number) { - return this.emit(Wss.ClientEvent.MarkMessageAsRead, { id: msgId }); - } - - async emit( - event: keyof Wss.ClientEventsMap, - data: Wss.EventPayload - ): Promise { - return new Promise((resolve, _) => { - // eslint-disable-next-line @typescript-eslint/no-explicit-any - this.client.emit(event, data as any, (ack) => ack && resolve(ack)); - setTimeout(() => resolve({ code: Wss.AcknowledgeCode.Ok }), 2_000); - }); - } - - /** - * Wait for event. - * - * Wrapp event callback with promise. - */ - async wait( - event: T - ): Promise> { - return new Promise((resolve, reject) => { - // @ts-expect-error it hard to get the `event` type to match what socket.io expects - this.client.on(event, resolve); - setTimeout(() => reject(new Error("TIMEOUT")), 2_000); - }); - } -} diff --git a/services/server/jest.config.js b/services/server/jest.config.js index e120f59b9..ca4a08069 100644 --- a/services/server/jest.config.js +++ b/services/server/jest.config.js @@ -21,9 +21,9 @@ module.exports = { globalSetup: "/fixtures/setup.ts", globalTeardown: "/fixtures/teardown.ts", setupFiles: [ - "/fixtures/s3.ts", - "/fixtures/worker.ts", - "/fixtures/telegram.ts", - "/fixtures/kafka.ts", + "/fixtures/jest/s3.ts", + "/fixtures/jest/worker.ts", + "/fixtures/jest/telegram.ts", + "/fixtures/jest/kafka.ts", ], }; diff --git a/services/server/tests/handlers/chat.test.ts b/services/server/tests/handlers/chat.test.ts index dd27d8673..df3163a3d 100644 --- a/services/server/tests/handlers/chat.test.ts +++ b/services/server/tests/handlers/chat.test.ts @@ -1,10 +1,9 @@ import { expect } from "chai"; -import db from "@fixtures/db"; -import { knex, messages, rooms } from "@litespace/models"; +import { fixtures as db, mockApi } from "@litespace/tests"; +import { messages, rooms } from "@litespace/models"; import { range } from "lodash"; import { IMessage, IRoom } from "@litespace/types"; import handlers from "@/handlers/chat"; -import { mockApi } from "@fixtures/mockApi"; import { empty, forbidden } from "@/lib/error"; import { cache } from "@/lib/cache"; @@ -166,14 +165,11 @@ describe("/api/v1/chat", () => { for (const name of ["student-1", "student-2", "student-3"]) { const student = await db.student(); const room = await db.room([tutor.id, student.id]); - - await knex.transaction(async (tx) => - db.message(tx, { - roomId: room, - userId: tutor.id, - text: `Hello, ${name}`, - }) - ); + await db.message({ + roomId: room, + userId: tutor.id, + text: `Hello, ${name}`, + }); } const tests = [ diff --git a/services/server/tests/handlers/confirmationCode.test.ts b/services/server/tests/handlers/confirmationCode.test.ts index 999893aca..a7b35a526 100644 --- a/services/server/tests/handlers/confirmationCode.test.ts +++ b/services/server/tests/handlers/confirmationCode.test.ts @@ -1,4 +1,4 @@ -import db from "@fixtures/db"; +import { fixtures as db, mockApi } from "@litespace/tests"; import { confirmationCodes, users } from "@litespace/models"; import { bad, @@ -11,7 +11,6 @@ import { } from "@/lib/error"; import dayjs from "@/lib/dayjs"; import { IConfirmationCode, ITelegram, IUser } from "@litespace/types"; -import { mockApi } from "@fixtures/mockApi"; import handlers from "@/handlers/confirmationCode"; import { safe, safePromise } from "@litespace/utils"; import { expect } from "chai"; diff --git a/services/server/tests/handlers/contactRequest.test.ts b/services/server/tests/handlers/contactRequest.test.ts index c169fa68b..e3ef1f08f 100644 --- a/services/server/tests/handlers/contactRequest.test.ts +++ b/services/server/tests/handlers/contactRequest.test.ts @@ -1,5 +1,4 @@ -import { mockApi } from "@fixtures/mockApi"; -import db from "@fixtures/db"; +import { fixtures as db, mockApi } from "@litespace/tests"; import { safe } from "@litespace/utils"; import { expect } from "chai"; import handlers from "@/handlers/contactRequest"; diff --git a/services/server/tests/handlers/interview.test.ts b/services/server/tests/handlers/interview.test.ts index 4d420deef..52d9d4d49 100644 --- a/services/server/tests/handlers/interview.test.ts +++ b/services/server/tests/handlers/interview.test.ts @@ -1,5 +1,4 @@ -import { mockApi } from "@fixtures/mockApi"; -import db from "@fixtures/db"; +import { fixtures as db, mockApi } from "@litespace/tests"; import handlers from "@/handlers/interview"; import { IInterview, IUser } from "@litespace/types"; import { expect } from "chai"; diff --git a/services/server/tests/handlers/invoice.test.ts b/services/server/tests/handlers/invoice.test.ts index dcfbcc3a3..f2dd5895d 100644 --- a/services/server/tests/handlers/invoice.test.ts +++ b/services/server/tests/handlers/invoice.test.ts @@ -1,5 +1,4 @@ -import { mockApi, mockApiContext } from "@fixtures/mockApi"; -import db from "@fixtures/db"; +import { fixtures as db, mockApi, mockApiContext } from "@litespace/tests"; import handlers from "@/handlers/invoice"; import { FieldError, IInvoice, IUser } from "@litespace/types"; import { expect } from "chai"; diff --git a/services/server/tests/handlers/lesson.test.ts b/services/server/tests/handlers/lesson.test.ts index 209db83e4..a7b19d4a6 100644 --- a/services/server/tests/handlers/lesson.test.ts +++ b/services/server/tests/handlers/lesson.test.ts @@ -1,7 +1,6 @@ -import { mockApi, mockApiContext } from "@fixtures/mockApi"; import { expect } from "chai"; import { cache } from "@/lib/cache"; -import db from "@fixtures/db"; +import { fixtures as db, mockApi, mockApiContext } from "@litespace/tests"; import handlers from "@/handlers/lesson"; import { notfound, diff --git a/services/server/tests/handlers/plan.test.ts b/services/server/tests/handlers/plan.test.ts index 5560c419f..55c4ef395 100644 --- a/services/server/tests/handlers/plan.test.ts +++ b/services/server/tests/handlers/plan.test.ts @@ -1,7 +1,6 @@ -import db from "@fixtures/db"; +import { fixtures as db, mockApi } from "@litespace/tests"; import handlers from "@/handlers/plan"; import { IPlan, IUser } from "@litespace/types"; -import { mockApi } from "@fixtures/mockApi"; import { expect } from "chai"; import { forbidden, notfound } from "@/lib/error"; diff --git a/services/server/tests/handlers/rating.test.ts b/services/server/tests/handlers/rating.test.ts index 63910b148..0606cdd71 100644 --- a/services/server/tests/handlers/rating.test.ts +++ b/services/server/tests/handlers/rating.test.ts @@ -1,10 +1,9 @@ import { exists, forbidden, notfound } from "@/lib/error"; -import db, { flush } from "@fixtures/db"; +import { fixtures as db, mockApi } from "@litespace/tests"; import { ratings } from "@litespace/models"; import { expect } from "chai"; import { first } from "lodash"; import handlers from "@/handlers/rating"; -import { mockApi } from "@fixtures/mockApi"; import { IRating } from "@litespace/types"; const findRaterRatings = mockApi< @@ -55,7 +54,7 @@ const deleteRating = mockApi( describe("/api/v1/rating/", () => { beforeEach(async () => { - await flush(); + await db.flush(); }); describe("GET /api/v1/rating", () => { diff --git a/services/server/tests/handlers/slot.test.ts b/services/server/tests/handlers/slot.test.ts index f8a787391..d7933c6f0 100644 --- a/services/server/tests/handlers/slot.test.ts +++ b/services/server/tests/handlers/slot.test.ts @@ -1,11 +1,10 @@ -import db from "@fixtures/db"; +import { fixtures as db, mockApi } from "@litespace/tests"; import dayjs from "@/lib/dayjs"; import { expect } from "chai"; import { bad, conflictingSchedule, forbidden, notfound } from "@/lib/error"; import { availabilitySlots, lessons } from "@litespace/models"; import { first } from "lodash"; import handlers from "@/handlers/availabilitySlot"; -import { mockApi } from "@fixtures/mockApi"; import { IAvailabilitySlot } from "@litespace/types"; const findSlot = mockApi< diff --git a/services/server/tests/handlers/subscription.test.ts b/services/server/tests/handlers/subscription.test.ts index 8570e2b82..a0b9e000c 100644 --- a/services/server/tests/handlers/subscription.test.ts +++ b/services/server/tests/handlers/subscription.test.ts @@ -1,7 +1,6 @@ -import db from "@fixtures/db"; +import { fixtures as db, mockApi } from "@litespace/tests"; import handlers from "@/handlers/subscription"; import { ISubscription, IUser } from "@litespace/types"; -import { mockApi } from "@fixtures/mockApi"; import { expect } from "chai"; import { forbidden, notfound } from "@/lib/error"; diff --git a/services/server/tests/handlers/topic.test.ts b/services/server/tests/handlers/topic.test.ts index e731d11bd..1bc42d21e 100644 --- a/services/server/tests/handlers/topic.test.ts +++ b/services/server/tests/handlers/topic.test.ts @@ -1,6 +1,5 @@ import { empty, forbidden, notfound } from "@/lib/error"; -import { mockApi } from "@fixtures/mockApi"; -import db, { faker } from "@fixtures/db"; +import { fixtures as db, faker, mockApi } from "@litespace/tests"; import { topics } from "@litespace/models"; import { MAX_TOPICS_COUNT, safe } from "@litespace/utils"; import { expect } from "chai"; diff --git a/services/server/tests/handlers/transaction.test.ts b/services/server/tests/handlers/transaction.test.ts index d1d32fdf8..58a75b855 100644 --- a/services/server/tests/handlers/transaction.test.ts +++ b/services/server/tests/handlers/transaction.test.ts @@ -1,9 +1,8 @@ -import db from "@fixtures/db"; import handlers from "@/handlers/transaction"; import { ITransaction, IUser } from "@litespace/types"; -import { mockApi } from "@fixtures/mockApi"; import { expect } from "chai"; import { forbidden, notfound } from "@/lib/error"; +import { fixtures as db, mockApi } from "@litespace/tests"; const find = mockApi(handlers.find); const findById = mockApi(handlers.findById); @@ -52,7 +51,7 @@ describe("/api/v1/tx/", () => { const s1 = await db.user({ role: IUser.Role.Student }); const tx = await db.transaction({ userId: s1.id }); - const res = await findById({ + const res = await findById({ user: s1, params: { id: tx.id }, }); diff --git a/services/server/tests/handlers/user.test.ts b/services/server/tests/handlers/user.test.ts index d618a1ea4..bf64cb41d 100644 --- a/services/server/tests/handlers/user.test.ts +++ b/services/server/tests/handlers/user.test.ts @@ -1,8 +1,6 @@ import { ITutor, IUser } from "@litespace/types"; -import { Api } from "@fixtures/api"; -import db, { faker } from "@fixtures/db"; +import { fixtures as db, Api, faker, mockApi } from "@litespace/tests"; import { expect } from "chai"; -import { mockApi } from "@fixtures/mockApi"; import { safe } from "@litespace/utils/error"; import { cacheTutors } from "@/lib/tutor"; import dayjs from "@/lib/dayjs"; diff --git a/services/server/tests/lib/subscription.test.ts b/services/server/tests/lib/subscription.test.ts index c0452005d..370de3b97 100644 --- a/services/server/tests/lib/subscription.test.ts +++ b/services/server/tests/lib/subscription.test.ts @@ -2,7 +2,7 @@ import { calculateRemainingWeeklyMinutesOfCurrentWeekByUserId } from "@/lib/subs import time from "@fixtures/time"; import { nameof } from "@litespace/utils"; import { expect } from "@fixtures/chai"; -import db from "@fixtures/db"; +import { fixtures as db } from "@litespace/tests"; import { subscriptions } from "@litespace/models"; describe(nameof(calculateRemainingWeeklyMinutesOfCurrentWeekByUserId), () => { diff --git a/services/server/tests/workers/cache.test.ts b/services/server/tests/workers/cache.test.ts index e3774e0bf..5dfe74f8f 100644 --- a/services/server/tests/workers/cache.test.ts +++ b/services/server/tests/workers/cache.test.ts @@ -1,4 +1,4 @@ -import db, { faker } from "@fixtures/db"; +import { fixtures as db, faker } from "@litespace/tests"; import { expect } from "chai"; import { cache } from "@/lib/cache"; import { tutors, users } from "@litespace/models"; diff --git a/services/server/tests/wss/messages.test.ts b/services/server/tests/wss/messages.test.ts index 76d85a484..fdc1039b1 100644 --- a/services/server/tests/wss/messages.test.ts +++ b/services/server/tests/wss/messages.test.ts @@ -1,6 +1,4 @@ -import { Api } from "@fixtures/api"; -import db from "@fixtures/db"; -import { ClientSocket } from "@fixtures/wss"; +import { fixtures as db, ClientSocket, Api } from "@litespace/tests"; import { knex, messages, rooms } from "@litespace/models"; import { Wss } from "@litespace/types"; import { expect } from "chai"; diff --git a/services/server/tests/wss/session.test.ts b/services/server/tests/wss/session.test.ts index ecd884300..a74f341b5 100644 --- a/services/server/tests/wss/session.test.ts +++ b/services/server/tests/wss/session.test.ts @@ -1,6 +1,4 @@ -import { Api } from "@fixtures/api"; -import db from "@fixtures/db"; -import { ClientSocket } from "@fixtures/wss"; +import { fixtures as db, ClientSocket, Api } from "@litespace/tests"; import { IUser, Wss } from "@litespace/types"; import dayjs from "@/lib/dayjs"; import { expect } from "chai"; diff --git a/services/server/tests/wss/typing.test.ts b/services/server/tests/wss/typing.test.ts index 96da50341..b5531a3e1 100644 --- a/services/server/tests/wss/typing.test.ts +++ b/services/server/tests/wss/typing.test.ts @@ -1,6 +1,4 @@ -import { Api } from "@fixtures/api"; -import db, { flush } from "@fixtures/db"; -import { ClientSocket } from "@fixtures/wss"; +import { fixtures as db, ClientSocket, Api } from "@litespace/tests"; import { IUser, Wss } from "@litespace/types"; describe("Typing", () => { @@ -11,7 +9,7 @@ describe("Typing", () => { let studentSocket: ClientSocket; beforeEach(async () => { - await flush(); + await db.flush(); const tutorApi = await Api.forTutor(); tutor = await tutorApi.findCurrentUser();