@blackcode_sa/metaestetics-api 1.14.77 → 1.14.79

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (27) hide show
  1. package/dist/admin/index.d.mts +5 -0
  2. package/dist/admin/index.d.ts +5 -0
  3. package/dist/admin/index.js +48 -6
  4. package/dist/admin/index.mjs +48 -6
  5. package/dist/index.js +7 -5
  6. package/dist/index.mjs +7 -5
  7. package/package.json +1 -1
  8. package/src/admin/aggregation/appointment/appointment.aggregation.service.ts +59 -6
  9. package/src/admin/mailing/clinicWelcome/clinicWelcome.mailing.ts +1 -1
  10. package/src/services/__tests__/auth/auth.mock.test.ts +2 -2
  11. package/src/services/__tests__/auth/auth.setup.ts +6 -1
  12. package/src/services/__tests__/auth.service.test.ts +8 -44
  13. package/src/services/__tests__/base.service.test.ts +4 -45
  14. package/src/services/__tests__/user.service.test.ts +6 -4
  15. package/src/services/appointment/utils/appointment.utils.ts +0 -3
  16. package/src/services/appointment/utils/extended-procedure.utils.ts +1 -0
  17. package/src/services/auth/auth.v2.service.ts +7 -7
  18. package/src/services/clinic/__tests__/clinic-admin.service.test.ts +11 -33
  19. package/src/services/clinic/__tests__/clinic-group.service.test.ts +21 -151
  20. package/src/services/clinic/__tests__/clinic.service.test.ts +17 -69
  21. package/src/services/clinic/utils/clinic-group.utils.ts +2 -2
  22. package/src/services/clinic/utils/clinic.utils.ts +28 -22
  23. package/src/services/clinic/utils/index.ts +0 -1
  24. package/src/services/notifications/__tests__/notification.service.test.ts +5 -5
  25. package/src/services/patient/__tests__/patient.service.test.ts +17 -25
  26. package/src/services/patient/utils/docs.utils.ts +1 -1
  27. package/src/services/user/user.v2.service.ts +4 -3
@@ -3606,6 +3606,11 @@ declare class AppointmentAggregationService {
3606
3606
  * @returns {Promise<void>} A promise that resolves when the operation is complete.
3607
3607
  */
3608
3608
  private createPreAppointmentRequirementInstances;
3609
+ /**
3610
+ * Resolves an array of requirement items that may be string IDs or full Requirement objects.
3611
+ * String IDs are batch-fetched from the backoffice_requirements collection.
3612
+ */
3613
+ private resolveRequirements;
3609
3614
  /**
3610
3615
  * Fetches post-requirements from a procedure document
3611
3616
  * @param procedureId - The procedure ID to fetch requirements from
@@ -3606,6 +3606,11 @@ declare class AppointmentAggregationService {
3606
3606
  * @returns {Promise<void>} A promise that resolves when the operation is complete.
3607
3607
  */
3608
3608
  private createPreAppointmentRequirementInstances;
3609
+ /**
3610
+ * Resolves an array of requirement items that may be string IDs or full Requirement objects.
3611
+ * String IDs are batch-fetched from the backoffice_requirements collection.
3612
+ */
3613
+ private resolveRequirements;
3609
3614
  /**
3610
3615
  * Fetches post-requirements from a procedure document
3611
3616
  * @param procedureId - The procedure ID to fetch requirements from
@@ -603,6 +603,9 @@ var PATIENT_REQUIREMENTS_SUBCOLLECTION_NAME = "patientRequirements";
603
603
  // src/admin/aggregation/appointment/appointment.aggregation.service.ts
604
604
  var admin6 = __toESM(require("firebase-admin"));
605
605
 
606
+ // src/backoffice/types/requirement.types.ts
607
+ var REQUIREMENTS_COLLECTION = "backoffice_requirements";
608
+
606
609
  // src/types/procedure/index.ts
607
610
  var PROCEDURES_COLLECTION = "procedures";
608
611
 
@@ -3605,9 +3608,12 @@ var AppointmentAggregationService = class {
3605
3608
  const batch = this.db.batch();
3606
3609
  let instancesCreatedCount = 0;
3607
3610
  let createdInstances = [];
3611
+ const resolvedPreRequirements = await this.resolveRequirements(
3612
+ appointment.preProcedureRequirements
3613
+ );
3608
3614
  Logger.info(
3609
- `[AggService] Found ${appointment.preProcedureRequirements.length} pre-requirements to process: ${JSON.stringify(
3610
- appointment.preProcedureRequirements.map((r) => {
3615
+ `[AggService] Found ${resolvedPreRequirements.length} pre-requirements to process: ${JSON.stringify(
3616
+ resolvedPreRequirements.map((r) => {
3611
3617
  var _a2, _b;
3612
3618
  return {
3613
3619
  id: r.id,
@@ -3620,7 +3626,7 @@ var AppointmentAggregationService = class {
3620
3626
  })
3621
3627
  )}`
3622
3628
  );
3623
- for (const template of appointment.preProcedureRequirements) {
3629
+ for (const template of resolvedPreRequirements) {
3624
3630
  if (!template) {
3625
3631
  Logger.warn(
3626
3632
  `[AggService] Found null/undefined template in preProcedureRequirements array`
@@ -3818,6 +3824,41 @@ var AppointmentAggregationService = class {
3818
3824
  throw error;
3819
3825
  }
3820
3826
  }
3827
+ /**
3828
+ * Resolves an array of requirement items that may be string IDs or full Requirement objects.
3829
+ * String IDs are batch-fetched from the backoffice_requirements collection.
3830
+ */
3831
+ async resolveRequirements(items) {
3832
+ if (items.length === 0) return [];
3833
+ const resolved = [];
3834
+ const idsToFetch = [];
3835
+ for (const item of items) {
3836
+ if (typeof item === "string") {
3837
+ idsToFetch.push(item);
3838
+ } else if (item && typeof item === "object" && item.id) {
3839
+ resolved.push(item);
3840
+ }
3841
+ }
3842
+ if (idsToFetch.length > 0) {
3843
+ Logger.info(
3844
+ `[AggService] Resolving ${idsToFetch.length} requirement ID(s) from ${REQUIREMENTS_COLLECTION}`
3845
+ );
3846
+ const refs = idsToFetch.map(
3847
+ (id) => this.db.collection(REQUIREMENTS_COLLECTION).doc(id)
3848
+ );
3849
+ const snapshots = await this.db.getAll(...refs);
3850
+ for (const snap of snapshots) {
3851
+ if (snap.exists) {
3852
+ resolved.push({ id: snap.id, ...snap.data() });
3853
+ } else {
3854
+ Logger.warn(
3855
+ `[AggService] Requirement template '${snap.id}' not found in ${REQUIREMENTS_COLLECTION}`
3856
+ );
3857
+ }
3858
+ }
3859
+ }
3860
+ return resolved;
3861
+ }
3821
3862
  /**
3822
3863
  * Fetches post-requirements from a procedure document
3823
3864
  * @param procedureId - The procedure ID to fetch requirements from
@@ -3831,10 +3872,11 @@ var AppointmentAggregationService = class {
3831
3872
  return [];
3832
3873
  }
3833
3874
  const procedure = procedureDoc.data();
3834
- const postRequirements = procedure.postRequirements || [];
3835
- if (postRequirements.length === 0) {
3875
+ const rawPostRequirements = procedure.postRequirements || [];
3876
+ if (rawPostRequirements.length === 0) {
3836
3877
  return [];
3837
3878
  }
3879
+ const postRequirements = await this.resolveRequirements(rawPostRequirements);
3838
3880
  return postRequirements.map((req) => ({
3839
3881
  requirement: req,
3840
3882
  sourceProcedures: [
@@ -14325,7 +14367,7 @@ var ClinicWelcomeMailingService = class extends BaseMailingService {
14325
14367
  */
14326
14368
  constructor(firestore19, mailgunClient) {
14327
14369
  super(firestore19, mailgunClient);
14328
- this.DEFAULT_DASHBOARD_URL = "https://clinic.metaesthetics.net/dashboard";
14370
+ this.DEFAULT_DASHBOARD_URL = "https://app.metaesthetics.net/dashboard";
14329
14371
  this.DEFAULT_SUPPORT_EMAIL = "support@metaesthetics.net";
14330
14372
  this.DEFAULT_SUBJECT = "Welcome to MetaEsthetics - Your Clinic Registration is Complete";
14331
14373
  this.DEFAULT_MAILGUN_DOMAIN = "mg.metaesthetics.net";
@@ -526,6 +526,9 @@ var PATIENT_REQUIREMENTS_SUBCOLLECTION_NAME = "patientRequirements";
526
526
  // src/admin/aggregation/appointment/appointment.aggregation.service.ts
527
527
  import * as admin6 from "firebase-admin";
528
528
 
529
+ // src/backoffice/types/requirement.types.ts
530
+ var REQUIREMENTS_COLLECTION = "backoffice_requirements";
531
+
529
532
  // src/types/procedure/index.ts
530
533
  var PROCEDURES_COLLECTION = "procedures";
531
534
 
@@ -3528,9 +3531,12 @@ var AppointmentAggregationService = class {
3528
3531
  const batch = this.db.batch();
3529
3532
  let instancesCreatedCount = 0;
3530
3533
  let createdInstances = [];
3534
+ const resolvedPreRequirements = await this.resolveRequirements(
3535
+ appointment.preProcedureRequirements
3536
+ );
3531
3537
  Logger.info(
3532
- `[AggService] Found ${appointment.preProcedureRequirements.length} pre-requirements to process: ${JSON.stringify(
3533
- appointment.preProcedureRequirements.map((r) => {
3538
+ `[AggService] Found ${resolvedPreRequirements.length} pre-requirements to process: ${JSON.stringify(
3539
+ resolvedPreRequirements.map((r) => {
3534
3540
  var _a2, _b;
3535
3541
  return {
3536
3542
  id: r.id,
@@ -3543,7 +3549,7 @@ var AppointmentAggregationService = class {
3543
3549
  })
3544
3550
  )}`
3545
3551
  );
3546
- for (const template of appointment.preProcedureRequirements) {
3552
+ for (const template of resolvedPreRequirements) {
3547
3553
  if (!template) {
3548
3554
  Logger.warn(
3549
3555
  `[AggService] Found null/undefined template in preProcedureRequirements array`
@@ -3741,6 +3747,41 @@ var AppointmentAggregationService = class {
3741
3747
  throw error;
3742
3748
  }
3743
3749
  }
3750
+ /**
3751
+ * Resolves an array of requirement items that may be string IDs or full Requirement objects.
3752
+ * String IDs are batch-fetched from the backoffice_requirements collection.
3753
+ */
3754
+ async resolveRequirements(items) {
3755
+ if (items.length === 0) return [];
3756
+ const resolved = [];
3757
+ const idsToFetch = [];
3758
+ for (const item of items) {
3759
+ if (typeof item === "string") {
3760
+ idsToFetch.push(item);
3761
+ } else if (item && typeof item === "object" && item.id) {
3762
+ resolved.push(item);
3763
+ }
3764
+ }
3765
+ if (idsToFetch.length > 0) {
3766
+ Logger.info(
3767
+ `[AggService] Resolving ${idsToFetch.length} requirement ID(s) from ${REQUIREMENTS_COLLECTION}`
3768
+ );
3769
+ const refs = idsToFetch.map(
3770
+ (id) => this.db.collection(REQUIREMENTS_COLLECTION).doc(id)
3771
+ );
3772
+ const snapshots = await this.db.getAll(...refs);
3773
+ for (const snap of snapshots) {
3774
+ if (snap.exists) {
3775
+ resolved.push({ id: snap.id, ...snap.data() });
3776
+ } else {
3777
+ Logger.warn(
3778
+ `[AggService] Requirement template '${snap.id}' not found in ${REQUIREMENTS_COLLECTION}`
3779
+ );
3780
+ }
3781
+ }
3782
+ }
3783
+ return resolved;
3784
+ }
3744
3785
  /**
3745
3786
  * Fetches post-requirements from a procedure document
3746
3787
  * @param procedureId - The procedure ID to fetch requirements from
@@ -3754,10 +3795,11 @@ var AppointmentAggregationService = class {
3754
3795
  return [];
3755
3796
  }
3756
3797
  const procedure = procedureDoc.data();
3757
- const postRequirements = procedure.postRequirements || [];
3758
- if (postRequirements.length === 0) {
3798
+ const rawPostRequirements = procedure.postRequirements || [];
3799
+ if (rawPostRequirements.length === 0) {
3759
3800
  return [];
3760
3801
  }
3802
+ const postRequirements = await this.resolveRequirements(rawPostRequirements);
3761
3803
  return postRequirements.map((req) => ({
3762
3804
  requirement: req,
3763
3805
  sourceProcedures: [
@@ -14248,7 +14290,7 @@ var ClinicWelcomeMailingService = class extends BaseMailingService {
14248
14290
  */
14249
14291
  constructor(firestore19, mailgunClient) {
14250
14292
  super(firestore19, mailgunClient);
14251
- this.DEFAULT_DASHBOARD_URL = "https://clinic.metaesthetics.net/dashboard";
14293
+ this.DEFAULT_DASHBOARD_URL = "https://app.metaesthetics.net/dashboard";
14252
14294
  this.DEFAULT_SUPPORT_EMAIL = "support@metaesthetics.net";
14253
14295
  this.DEFAULT_SUBJECT = "Welcome to MetaEsthetics - Your Clinic Registration is Complete";
14254
14296
  this.DEFAULT_MAILGUN_DOMAIN = "mg.metaesthetics.net";
package/dist/index.js CHANGED
@@ -9779,7 +9779,7 @@ var initSensitiveInfoDocIfNotExists = async (db, patientId, userRef) => {
9779
9779
  (key, value) => value && typeof value === "object" && value.constructor && value.constructor.name === "Object" ? "[serverTimestamp]" : value
9780
9780
  )
9781
9781
  );
9782
- await createSensitiveInfoUtil(db, defaultSensitiveInfo, userRef);
9782
+ await createSensitiveInfoUtil(db, defaultSensitiveInfo, userRef, []);
9783
9783
  const verifyDoc = await (0, import_firestore21.getDoc)(sensitiveInfoRef);
9784
9784
  console.log(
9785
9785
  `[initSensitiveInfoDocIfNotExists] Verification - document exists: ${verifyDoc.exists()}`
@@ -14773,10 +14773,11 @@ async function updateClinic(db, clinicId, data, adminId, clinicAdminService, app
14773
14773
  if (data.featuredPhotos && data.featuredPhotos.length > 0) {
14774
14774
  console.log("[CLINIC] Processing featured photos update");
14775
14775
  try {
14776
- const dataUrlPhotos = data.featuredPhotos.filter(
14776
+ const allPhotos = data.featuredPhotos;
14777
+ const dataUrlPhotos = allPhotos.filter(
14777
14778
  (photo) => typeof photo === "string" && photo.startsWith("data:")
14778
14779
  );
14779
- const existingPhotos = data.featuredPhotos.filter(
14780
+ const existingPhotos = allPhotos.filter(
14780
14781
  (photo) => typeof photo === "string" && !photo.startsWith("data:")
14781
14782
  );
14782
14783
  if (dataUrlPhotos.length > 0) {
@@ -14809,9 +14810,10 @@ async function updateClinic(db, clinicId, data, adminId, clinicAdminService, app
14809
14810
  try {
14810
14811
  const updatedPhotosWithTags = [];
14811
14812
  for (const photoWithTag of data.photosWithTags) {
14812
- if (photoWithTag.url && photoWithTag.url.startsWith("data:")) {
14813
+ const photoUrl = photoWithTag.url;
14814
+ if (photoUrl && photoUrl.startsWith("data:")) {
14813
14815
  const uploadedUrl = await uploadPhoto(
14814
- photoWithTag.url,
14816
+ photoUrl,
14815
14817
  "clinics",
14816
14818
  clinicId,
14817
14819
  `tagged-${photoWithTag.tag}`,
package/dist/index.mjs CHANGED
@@ -9742,7 +9742,7 @@ var initSensitiveInfoDocIfNotExists = async (db, patientId, userRef) => {
9742
9742
  (key, value) => value && typeof value === "object" && value.constructor && value.constructor.name === "Object" ? "[serverTimestamp]" : value
9743
9743
  )
9744
9744
  );
9745
- await createSensitiveInfoUtil(db, defaultSensitiveInfo, userRef);
9745
+ await createSensitiveInfoUtil(db, defaultSensitiveInfo, userRef, []);
9746
9746
  const verifyDoc = await getDoc13(sensitiveInfoRef);
9747
9747
  console.log(
9748
9748
  `[initSensitiveInfoDocIfNotExists] Verification - document exists: ${verifyDoc.exists()}`
@@ -14849,10 +14849,11 @@ async function updateClinic(db, clinicId, data, adminId, clinicAdminService, app
14849
14849
  if (data.featuredPhotos && data.featuredPhotos.length > 0) {
14850
14850
  console.log("[CLINIC] Processing featured photos update");
14851
14851
  try {
14852
- const dataUrlPhotos = data.featuredPhotos.filter(
14852
+ const allPhotos = data.featuredPhotos;
14853
+ const dataUrlPhotos = allPhotos.filter(
14853
14854
  (photo) => typeof photo === "string" && photo.startsWith("data:")
14854
14855
  );
14855
- const existingPhotos = data.featuredPhotos.filter(
14856
+ const existingPhotos = allPhotos.filter(
14856
14857
  (photo) => typeof photo === "string" && !photo.startsWith("data:")
14857
14858
  );
14858
14859
  if (dataUrlPhotos.length > 0) {
@@ -14885,9 +14886,10 @@ async function updateClinic(db, clinicId, data, adminId, clinicAdminService, app
14885
14886
  try {
14886
14887
  const updatedPhotosWithTags = [];
14887
14888
  for (const photoWithTag of data.photosWithTags) {
14888
- if (photoWithTag.url && photoWithTag.url.startsWith("data:")) {
14889
+ const photoUrl = photoWithTag.url;
14890
+ if (photoUrl && photoUrl.startsWith("data:")) {
14889
14891
  const uploadedUrl = await uploadPhoto(
14890
- photoWithTag.url,
14892
+ photoUrl,
14891
14893
  "clinics",
14892
14894
  clinicId,
14893
14895
  `tagged-${photoWithTag.tag}`,
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@blackcode_sa/metaestetics-api",
3
3
  "private": false,
4
- "version": "1.14.77",
4
+ "version": "1.14.79",
5
5
  "description": "Firebase authentication service with anonymous upgrade support",
6
6
  "main": "dist/index.js",
7
7
  "module": "dist/index.mjs",
@@ -13,7 +13,7 @@ import {
13
13
  } from '../../../types/patient/patient-requirements';
14
14
  import {
15
15
  Requirement as RequirementTemplate,
16
- // REQUIREMENTS_COLLECTION as REQUIREMENTS_TEMPLATES_COLLECTION, // Not used directly after refactor
16
+ REQUIREMENTS_COLLECTION,
17
17
  RequirementType,
18
18
  TimeUnit, // Added import
19
19
  } from '../../../backoffice/types/requirement.types';
@@ -671,12 +671,17 @@ export class AppointmentAggregationService {
671
671
  // Store created instances for fallback direct creation if needed
672
672
  let createdInstances = [];
673
673
 
674
+ // Resolve string IDs to full RequirementTemplate objects
675
+ const resolvedPreRequirements = await this.resolveRequirements(
676
+ appointment.preProcedureRequirements as any,
677
+ );
678
+
674
679
  // Log more details about the pre-requirements
675
680
  Logger.info(
676
681
  `[AggService] Found ${
677
- appointment.preProcedureRequirements.length
682
+ resolvedPreRequirements.length
678
683
  } pre-requirements to process: ${JSON.stringify(
679
- appointment.preProcedureRequirements.map(r => ({
684
+ resolvedPreRequirements.map(r => ({
680
685
  id: r.id,
681
686
  name: r.name,
682
687
  type: r.type,
@@ -687,7 +692,7 @@ export class AppointmentAggregationService {
687
692
  )}`,
688
693
  );
689
694
 
690
- for (const template of appointment.preProcedureRequirements) {
695
+ for (const template of resolvedPreRequirements) {
691
696
  if (!template) {
692
697
  Logger.warn(
693
698
  `[AggService] Found null/undefined template in preProcedureRequirements array`,
@@ -933,6 +938,51 @@ export class AppointmentAggregationService {
933
938
  }
934
939
  }
935
940
 
941
+ /**
942
+ * Resolves an array of requirement items that may be string IDs or full Requirement objects.
943
+ * String IDs are batch-fetched from the backoffice_requirements collection.
944
+ */
945
+ private async resolveRequirements(
946
+ items: (string | RequirementTemplate)[],
947
+ ): Promise<RequirementTemplate[]> {
948
+ if (items.length === 0) return [];
949
+
950
+ const resolved: RequirementTemplate[] = [];
951
+ const idsToFetch: string[] = [];
952
+
953
+ for (const item of items) {
954
+ if (typeof item === 'string') {
955
+ idsToFetch.push(item);
956
+ } else if (item && typeof item === 'object' && item.id) {
957
+ resolved.push(item);
958
+ }
959
+ }
960
+
961
+ if (idsToFetch.length > 0) {
962
+ Logger.info(
963
+ `[AggService] Resolving ${idsToFetch.length} requirement ID(s) from ${REQUIREMENTS_COLLECTION}`,
964
+ );
965
+
966
+ // Firestore getAll supports up to 500 docs per call
967
+ const refs = idsToFetch.map(id =>
968
+ this.db.collection(REQUIREMENTS_COLLECTION).doc(id),
969
+ );
970
+ const snapshots = await this.db.getAll(...refs);
971
+
972
+ for (const snap of snapshots) {
973
+ if (snap.exists) {
974
+ resolved.push({ id: snap.id, ...snap.data() } as RequirementTemplate);
975
+ } else {
976
+ Logger.warn(
977
+ `[AggService] Requirement template '${snap.id}' not found in ${REQUIREMENTS_COLLECTION}`,
978
+ );
979
+ }
980
+ }
981
+ }
982
+
983
+ return resolved;
984
+ }
985
+
936
986
  /**
937
987
  * Fetches post-requirements from a procedure document
938
988
  * @param procedureId - The procedure ID to fetch requirements from
@@ -949,12 +999,15 @@ export class AppointmentAggregationService {
949
999
  }
950
1000
 
951
1001
  const procedure = procedureDoc.data() as Procedure;
952
- const postRequirements = procedure.postRequirements || [];
1002
+ const rawPostRequirements = procedure.postRequirements || [];
953
1003
 
954
- if (postRequirements.length === 0) {
1004
+ if (rawPostRequirements.length === 0) {
955
1005
  return [];
956
1006
  }
957
1007
 
1008
+ // Resolve string IDs to full Requirement objects if needed
1009
+ const postRequirements = await this.resolveRequirements(rawPostRequirements as any);
1010
+
958
1011
  return postRequirements.map(req => ({
959
1012
  requirement: req,
960
1013
  sourceProcedures: [
@@ -45,7 +45,7 @@ export interface ClinicWelcomeEmailData {
45
45
  */
46
46
  export class ClinicWelcomeMailingService extends BaseMailingService {
47
47
  private readonly DEFAULT_DASHBOARD_URL =
48
- "https://clinic.metaesthetics.net/dashboard";
48
+ "https://app.metaesthetics.net/dashboard";
49
49
  private readonly DEFAULT_SUPPORT_EMAIL = "support@metaesthetics.net";
50
50
  private readonly DEFAULT_SUBJECT =
51
51
  "Welcome to MetaEsthetics - Your Clinic Registration is Complete";
@@ -1,8 +1,8 @@
1
- import { setupMocks, mockFirebaseUser } from "./auth.setup";
1
+ import { setupService, mockFirebaseUser } from "./auth.setup";
2
2
 
3
3
  describe("Firebase Mock Setup", () => {
4
4
  beforeEach(() => {
5
- setupMocks();
5
+ setupService();
6
6
  });
7
7
 
8
8
  it("should properly initialize mock Firebase instance", async () => {
@@ -288,6 +288,11 @@ export const getMockUser = () => ({
288
288
 
289
289
  // Setup service
290
290
  export const setupService = () => {
291
- const service = new AuthService();
291
+ const service = new AuthService(
292
+ mockDb as unknown as Firestore,
293
+ mockAuth as unknown as Auth,
294
+ {} as FirebaseApp,
295
+ mockUserService
296
+ );
292
297
  return service;
293
298
  };
@@ -121,6 +121,10 @@ jest.mock("../../config/firebase", () => ({
121
121
 
122
122
  import { AuthService } from "../auth/auth.service";
123
123
  import { User as FirebaseUser } from "firebase/auth";
124
+ import { Firestore } from "firebase/firestore";
125
+ import { Auth } from "firebase/auth";
126
+ import { FirebaseApp } from "firebase/app";
127
+ import { UserService } from "../user/user.service";
124
128
  import { UserRole } from "../../types";
125
129
 
126
130
  const mockFacebookUser = {
@@ -150,8 +154,10 @@ describe("AuthService", () => {
150
154
  let service: AuthService;
151
155
 
152
156
  beforeEach(async () => {
153
- service = new AuthService();
154
- await service["initialized"]; // Čekamo da se inicijalizacija završi
157
+ const mockDb = {} as Firestore;
158
+ const mockApp = {} as FirebaseApp;
159
+ const mockUserService = {} as UserService;
160
+ service = new AuthService(mockDb, mockAuth as unknown as Auth, mockApp, mockUserService);
155
161
  });
156
162
 
157
163
  afterEach(() => {
@@ -243,48 +249,6 @@ describe("AuthService", () => {
243
249
  });
244
250
  });
245
251
 
246
- describe("signInWithFacebook", () => {
247
- it("should sign in user with Facebook", async () => {
248
- const expectedUser = {
249
- uid: "facebook-uid",
250
- email: "facebook@example.com",
251
- roles: [UserRole.PATIENT],
252
- isAnonymous: false,
253
- createdAt: expect.any(Date),
254
- updatedAt: expect.any(Date),
255
- lastLoginAt: expect.any(Date),
256
- patientProfile: null,
257
- practitionerProfile: null,
258
- adminProfile: null,
259
- };
260
-
261
- const result = await service.signInWithFacebook();
262
-
263
- expect(result).toEqual(expectedUser);
264
- });
265
- });
266
-
267
- describe("signInWithApple", () => {
268
- it("should sign in user with Apple", async () => {
269
- const expectedUser = {
270
- uid: "apple-uid",
271
- email: "apple@example.com",
272
- roles: [UserRole.PATIENT],
273
- isAnonymous: false,
274
- createdAt: expect.any(Date),
275
- updatedAt: expect.any(Date),
276
- lastLoginAt: expect.any(Date),
277
- patientProfile: null,
278
- practitionerProfile: null,
279
- adminProfile: null,
280
- };
281
-
282
- const result = await service.signInWithApple();
283
-
284
- expect(result).toEqual(expectedUser);
285
- });
286
- });
287
-
288
252
  describe("signInAnonymously", () => {
289
253
  it("should sign in user anonymously", async () => {
290
254
  const expectedUser = {
@@ -1,10 +1,7 @@
1
1
  import { BaseService } from "../base.service";
2
- import { getFirebaseInstance } from "../../config/firebase";
3
2
  import { Auth } from "firebase/auth";
4
3
  import { Firestore } from "firebase/firestore";
5
-
6
- // Mock Firebase config
7
- jest.mock("../../config/firebase");
4
+ import { FirebaseApp } from "firebase/app";
8
5
 
9
6
  // Test implementacija BaseService klase
10
7
  class TestService extends BaseService {
@@ -16,62 +13,24 @@ class TestService extends BaseService {
16
13
  public getAuth(): Auth {
17
14
  return this.auth;
18
15
  }
19
-
20
- // Expose initialize method for testing
21
- public async testEnsureInitialized() {
22
- await this.ensureInitialized();
23
- }
24
16
  }
25
17
 
26
18
  describe("BaseService", () => {
27
19
  let service: TestService;
28
20
  const mockDb = {} as Firestore;
29
21
  const mockAuth = {} as Auth;
22
+ const mockApp = {} as FirebaseApp;
30
23
 
31
24
  beforeEach(() => {
32
25
  jest.clearAllMocks();
33
- (getFirebaseInstance as jest.Mock).mockResolvedValue({
34
- db: mockDb,
35
- auth: mockAuth,
36
- });
37
26
  });
38
27
 
39
28
  describe("initialization", () => {
40
- it("treba da inicijalizuje Firebase servise", async () => {
41
- service = new TestService();
42
- await service.testEnsureInitialized();
29
+ it("treba da inicijalizuje Firebase servise", () => {
30
+ service = new TestService(mockDb, mockAuth, mockApp);
43
31
 
44
- expect(getFirebaseInstance).toHaveBeenCalled();
45
32
  expect(service.getDb()).toBe(mockDb);
46
33
  expect(service.getAuth()).toBe(mockAuth);
47
34
  });
48
-
49
- it("treba da hendla grešku pri inicijalizaciji", async () => {
50
- const mockError = new Error("Firebase init error");
51
- (getFirebaseInstance as jest.Mock).mockRejectedValue(mockError);
52
-
53
- service = new TestService();
54
-
55
- await expect(service.testEnsureInitialized()).rejects.toThrow(mockError);
56
- });
57
-
58
- it("treba da zadrži grešku inicijalizacije i baca je pri svakom pozivu", async () => {
59
- const mockError = new Error("Firebase init error");
60
- (getFirebaseInstance as jest.Mock).mockRejectedValue(mockError);
61
-
62
- service = new TestService();
63
-
64
- // Prvi pokušaj
65
- await expect(service.testEnsureInitialized()).rejects.toThrow(mockError);
66
-
67
- // Firebase je sada uspešno inicijalizovan
68
- (getFirebaseInstance as jest.Mock).mockResolvedValue({
69
- db: mockDb,
70
- auth: mockAuth,
71
- });
72
-
73
- // Ali i dalje treba da dobijemo originalnu grešku
74
- await expect(service.testEnsureInitialized()).rejects.toThrow(mockError);
75
- });
76
35
  });
77
36
  });
@@ -131,9 +131,11 @@ describe("UserService", () => {
131
131
  });
132
132
 
133
133
  userService = new UserService(
134
+ mockDb,
134
135
  mockAuth,
135
- new PatientService(),
136
- new ClinicAdminService()
136
+ {} as any,
137
+ new PatientService(mockDb, mockAuth, {} as any),
138
+ new ClinicAdminService(mockDb, mockAuth, {} as any)
137
139
  );
138
140
  });
139
141
 
@@ -460,7 +462,7 @@ describe("UserService", () => {
460
462
  });
461
463
  });
462
464
 
463
- describe("removeRole", () => {
465
+ describe("removeRoleAndProfile", () => {
464
466
  it("treba da ukloni ulogu korisniku", async () => {
465
467
  const userWithMultipleRoles = {
466
468
  ...mockUser,
@@ -487,7 +489,7 @@ describe("UserService", () => {
487
489
  data: () => updatedUser,
488
490
  });
489
491
 
490
- await userService.removeRole("test-uid", UserRole.PATIENT);
492
+ await userService.removeRoleAndProfile("test-uid", UserRole.PATIENT);
491
493
 
492
494
  // Provera da li je pozvan updateDoc sa ispravnim parametrima
493
495
  expect(updateDoc).toHaveBeenCalledWith(
@@ -33,9 +33,6 @@ import { PATIENTS_COLLECTION } from '../../../types/patient';
33
33
  import { CLINICS_COLLECTION } from '../../../types/clinic';
34
34
  import { BlockingCondition } from '../../../backoffice/types/static/blocking-condition.types';
35
35
  import { Requirement } from '../../../backoffice/types/requirement.types';
36
- import { PRACTITIONERS_COLLECTION } from '../../../types/practitioner';
37
- import { CLINICS_COLLECTION } from '../../../types/clinic';
38
- import { PATIENTS_COLLECTION } from '../../../types/patient';
39
36
  import { PROCEDURES_COLLECTION } from '../../../types/procedure';
40
37
  import { Technology, TECHNOLOGIES_COLLECTION } from '../../../backoffice/types/technology.types';
41
38
  import type { ContraindicationDynamic } from '../../../backoffice';
@@ -4,6 +4,7 @@ import {
4
4
  ExtendedProcedureInfo,
5
5
  AppointmentProductMetadata,
6
6
  APPOINTMENTS_COLLECTION,
7
+ LinkedFormInfo,
7
8
  } from '../../../types/appointment';
8
9
  import { getAppointmentOrThrow, initializeMetadata } from './zone-management.utils';
9
10
  import { PROCEDURES_COLLECTION } from '../../../types/procedure';