11import { createProject , getProjectById , updateProjects , deleteProjects
22 , getMembersByProjectId , addMembers , removeMembers } from '../src/controllers/project.controller' ;
3+
34import * as projectService from '../src/services/project.service' ;
45import { ApiError } from '../src/utils/apiError' ;
56import { Response , Request } from 'express' ;
67import * as imageUtils from '../src/utils/imageUtils' ;
78
89
9- jest . mock ( '../src/routes/projects' , ( ) => {
10- return {
11- __esModule : true ,
12- default : jest . fn ( ( ) => require ( 'express' ) . Router ( ) ) ,
13- } ;
14- } ) ;
15-
16-
17- // modify the respose Object
18-
19- const mockRes = ( ) => {
20- const res = { } as Response ;
21- res . status = jest . fn ( ) . mockReturnValue ( res ) ;
22- res . json = jest . fn ( ) . mockReturnValue ( res ) ;
23- return res ;
24- } ;
25-
26- // test for the createProject routes
10+ jest . mock ( '../src/app' , ( ) => ( {
11+ supabase : {
12+ storage : {
13+ from : jest . fn ( ( ) => ( {
14+ upload : jest . fn ( ) . mockResolvedValue ( {
15+ data : { path : 'projects/image.png' } ,
16+ error : null
17+ } ) ,
18+ } ) ) ,
19+ } ,
20+ } ,
21+ } ) ) ;
22+
23+ // Mock image upload utility
24+ jest . mock ( '../src/utils/imageUtils' , ( ) => ( {
25+ uploadImage : jest . fn ( ) . mockResolvedValue ( 'https://fake-url.com/projects/image.png' ) ,
26+ } ) ) ;
27+
28+ // Reusable mock response object
29+ const mockRes = ( ) => {
30+ const res = { } as Response ;
31+ res . status = jest . fn ( ) . mockReturnValue ( res ) ;
32+ res . json = jest . fn ( ) . mockReturnValue ( res ) ;
33+ return res ;
34+ } ;
2735
2836describe ( 'createProjectHandler' , ( ) => {
2937 const adminId = '4037653b-a434-460f-8a81-ca8cb46375aa' ;
@@ -34,33 +42,34 @@ describe('createProjectHandler', () => {
3442
3543 it ( 'should return 200 and created project with valid input' , async ( ) => {
3644 const req = {
37- body :{
38- projectData : {
45+ body : {
46+ projectData : {
3947 name : 'EventHub' ,
4048 githubUrl : 'https://github.com/example/eventhub' ,
41- deployUrl : 'https://eventhub.example.com' ,
4249 adminId,
50+ } ,
51+ deployUrl : 'https://eventhub.example.com' ,
4352 } ,
44- } ,
4553 file : {
4654 mimetype : 'image/png' ,
4755 buffer : Buffer . from ( 'fake-image-data' ) ,
4856 } ,
49- } as unknown as Request ;
57+ } as unknown as Request ;
5058
5159 const res = mockRes ( ) ;
5260
53- const mockProject = {
54- id : 1 ,
55- name : 'EventHub' ,
56- imageUrl : 'https://fake-image-url.com/image.png' ,
57- githubUrl : req . body . projectData . githubUrl ,
58- deployUrl : req . body . projectData . deployUrl ,
59- createdById : adminId ,
60- createdAt : new Date ( ) ,
61- updatedById : null ,
62- updatedAt : new Date ( ) ,
63- } ;
61+ const mockProject = {
62+ id : 1 ,
63+ name : 'EventHub' ,
64+ imageUrl : 'https://fake-image-url.com/image.png' ,
65+ githubUrl : req . body . projectData . githubUrl ,
66+ deployUrl : req . body . projectData . deployUrl ,
67+ createdById : adminId ,
68+ createdAt : new Date ( ) ,
69+ updatedById : null ,
70+ updatedAt : new Date ( ) ,
71+ } ;
72+
6473
6574 jest . spyOn ( projectService , 'createProject' ) . mockResolvedValue ( mockProject ) ;
6675
@@ -70,29 +79,69 @@ describe('createProjectHandler', () => {
7079 expect ( res . json ) . toHaveBeenCalledWith ( mockProject ) ;
7180 } ) ;
7281
73- it ( 'should throw 400 if any required field is missing' , async ( ) => {
74- const req = {
75- body : {
76- projectData : {
77- githubUrl : 'https://github.com/example/eventhub' ,
78- deployUrl : 'https://eventhub.example.com' ,
79- adminId,
80- // name is missing
81- } ,
82- } ,
83- file : {
84- mimetype : 'image/png' ,
85- buffer : Buffer . from ( 'fake-image-data' ) ,
86- } ,
87- } as unknown as Request ;
82+ it ( 'should throw ApiError if image file is missing' , async ( ) => {
83+ const req = {
84+ body : {
85+ projectData : {
86+ name : 'EventHub' ,
87+ githubUrl : 'https://github.com/example/eventhub' ,
88+ adminId,
89+ } ,
90+ deployUrl : 'https://eventhub.example.com' ,
91+ } ,
92+ file : undefined , // <-- No file
93+ } as unknown as Request ;
8894
89- const res = mockRes ( ) ;
95+ const res = mockRes ( ) ;
9096
91- await expect ( createProject ( req , res ) ) . rejects . toThrow ( ApiError ) ;
92- } ) ;
97+ await expect ( createProject ( req , res ) ) . rejects . toThrow ( new ApiError ( 'Image file not found' , 400 ) ) ;
98+ } ) ;
9399
94- } ) ;
100+ it ( 'should throw ApiError if required project fields are missing' , async ( ) => {
101+ const req = {
102+ body : {
103+ projectData : {
104+ githubUrl : 'https://github.com/example/eventhub' ,
105+ adminId,
106+ // name is missing
107+ } ,
108+ deployUrl : 'https://eventhub.example.com' ,
109+ } ,
110+ file : {
111+ mimetype : 'image/png' ,
112+ buffer : Buffer . from ( 'fake-image-data' ) ,
113+ } ,
114+ } as unknown as Request ;
115+
116+ const res = mockRes ( ) ;
117+
118+ await expect ( createProject ( req , res ) ) . rejects . toThrow ( new ApiError ( ' fiels is missing ' , 400 ) ) ;
119+ } ) ;
120+
121+ it ( 'should throw ApiError if imageUrl is missing from upload' , async ( ) => {
122+ // Force uploadImage to return null
123+ ( imageUtils . uploadImage as jest . Mock ) . mockResolvedValueOnce ( null ) ;
124+
125+ const req = {
126+ body : {
127+ projectData : {
128+ name : 'EventHub' ,
129+ githubUrl : 'https://github.com/example/eventhub' ,
130+ adminId,
131+ } ,
132+ deployUrl : 'https://eventhub.example.com' ,
133+ } ,
134+ file : {
135+ mimetype : 'image/png' ,
136+ buffer : Buffer . from ( 'fake-image-data' ) ,
137+ } ,
138+ } as unknown as Request ;
95139
140+ const res = mockRes ( ) ;
141+
142+ await expect ( createProject ( req , res ) ) . rejects . toThrow ( new ApiError ( 'Image url is missing' , 400 ) ) ;
143+ } ) ;
144+ } ) ;
96145
97146// test for get project based on projecId
98147
@@ -340,7 +389,7 @@ describe(' addMembers ' , () => {
340389 it ( 'should return 200 if member added ' , async ( ) => {
341390 const req : any = {
342391 body : {
343- memberId : [ "725f05b6-8053-4610-80c2-9e48c9d27b9e" ]
392+ memberId : [ "725f05b6-8053-4610-80c2-9e48c9d27b9e" , "725f05b6-8053-4610-80c2-9e48c9d27b9e" ]
344393 } ,
345394 params :{
346395 projectId : "1"
@@ -350,7 +399,7 @@ describe(' addMembers ' , () => {
350399 const res = mockRes ( ) ;
351400
352401 const mockBatchPayload = {
353- count : 1 , // number of members added
402+ count : 2 , // number of members added
354403} ;
355404
356405
@@ -385,7 +434,7 @@ describe(' addMembers ' , () => {
385434
386435 const req : any = {
387436 body :{
388- memberId : [ "725f05b6-8053-4610-80c2-9e48c9d27b9e" ]
437+ memberId : [ "725f05b6-8053-4610-80c2-9e48c9d27b9e" , "725f05b6-8053-4610-80c2-9e48c9d27b9r" ]
389438 } ,
390439 params :{
391440
@@ -414,7 +463,7 @@ describe(' removeMembersFromProject ' , () => {
414463
415464 params :{
416465 projectId : "1" ,
417- memberId : [ "725f05b6-8053-4610-80c2-9e48c9d27b9e" ]
466+ memberId : "725f05b6-8053-4610-80c2-9e48c9d27b9e"
418467 }
419468 } ;
420469
@@ -455,7 +504,7 @@ describe(' removeMembersFromProject ' , () => {
455504
456505 const req : any = {
457506 params :{
458- memberId : [ "725f05b6-8053-4610-80c2-9e48c9d27b9e" ]
507+ memberId : "725f05b6-8053-4610-80c2-9e48c9d27b9e"
459508 }
460509 } ;
461510
0 commit comments