1- import { BadRequestError , ForbiddenError , NotFoundError } from "routing-controllers" ;
1+ import { ForbiddenError , NotFoundError } from "routing-controllers" ;
2+ import { Repository } from "typeorm" ;
23import { Criterion } from "../../src/entities/criterion" ;
34import { Project } from "../../src/entities/project" ;
45import { Rating } from "../../src/entities/rating" ;
56import { Team } from "../../src/entities/team" ;
67import { User } from "../../src/entities/user" ;
7- import { IDatabaseService } from "../../src/services/database-service " ;
8+ import { UserRole } from "../../src/entities/user-role " ;
89import { IRatingService , RatingService } from "../../src/services/rating-service" ;
910import { ISettingsService } from "../../src/services/settings-service" ;
1011import { MockedService } from "./mock" ;
1112import { MockSettingsService } from "./mock/mock-settings-service" ;
13+ import { TestDatabaseService } from "./mock/mock-database-service" ;
1214
1315describe ( "RatingService" , ( ) => {
16+ let database : TestDatabaseService ;
1417 let settingsService : MockedService < ISettingsService > ;
15- let mockRatingsRepo : any ;
16- let mockProjectsRepo : any ;
17- let mockTeamsRepo : any ;
18- let mockUsersRepo : any ;
19- let mockDatabase : IDatabaseService ;
2018 let ratingService : IRatingService ;
2119
22- const mockUser = Object . assign ( new User ( ) , { id : 1 } ) ;
23- const mockTeam = Object . assign ( new Team ( ) , { id : 10 , users : [ "2" , "3" ] } ) ;
24- const mockProject = Object . assign ( new Project ( ) , {
25- id : 100 ,
26- team : mockTeam ,
27- allowRating : true ,
28- } ) ;
29- const mockCriterion = Object . assign ( new Criterion ( ) , { id : 5 } ) ;
30- const mockRating = Object . assign ( new Rating ( ) , {
31- project : mockProject ,
32- user : mockUser ,
33- criterion : mockCriterion ,
20+ let userRepo : Repository < User > ;
21+ let teamRepo : Repository < Team > ;
22+ let projectRepo : Repository < Project > ;
23+ let criterionRepo : Repository < Criterion > ;
24+ let ratingRepo : Repository < Rating > ;
25+
26+ let ratingUser : User ;
27+ let teamMember : User ;
28+ let mockTeam : Team ;
29+ let mockProject : Project ;
30+ let mockCriterion : Criterion ;
31+
32+ beforeAll ( async ( ) => {
33+ database = new TestDatabaseService ( ) ;
34+ await database . bootstrap ( ) ;
3435 } ) ;
3536
3637 beforeEach ( async ( ) => {
38+ await database . nuke ( ) ;
39+
3740 settingsService = new MockSettingsService ( ) ;
3841
39- mockRatingsRepo = {
40- find : jest . fn ( ) ,
41- findOneBy : jest . fn ( ) ,
42- findOne : jest . fn ( ) ,
43- save : jest . fn ( ) ,
44- delete : jest . fn ( )
45- } ;
46- mockProjectsRepo = {
47- find : jest . fn ( ) ,
48- findOneBy : jest . fn ( )
49- } ;
50- mockTeamsRepo = { findOneBy : jest . fn ( ) } ;
51- mockUsersRepo = { findOneBy : jest . fn ( ) } ;
52-
53- mockDatabase = {
54- bootstrap : jest . fn ( ) ,
55- getRepository : jest . fn ( ) . mockImplementation ( ( entity : any ) => {
56- if ( entity === Rating ) return mockRatingsRepo ;
57- if ( entity === Project ) return mockProjectsRepo ;
58- if ( entity === Team ) return mockTeamsRepo ;
59- if ( entity === User ) return mockUsersRepo ;
60- return { } ;
61- } ) ,
62- } as any ;
63-
64- ratingService = new RatingService ( mockDatabase , settingsService . instance ) ;
42+ userRepo = database . getRepository ( User ) ;
43+ teamRepo = database . getRepository ( Team ) ;
44+ projectRepo = database . getRepository ( Project ) ;
45+ criterionRepo = database . getRepository ( Criterion ) ;
46+ ratingRepo = database . getRepository ( Rating ) ;
47+
48+ // A user who will submit ratings (not in the project's team)
49+ ratingUser = new User ( ) ;
50+ ratingUser . firstName = "Rater" ;
51+ ratingUser . lastName = "User" ;
52+ ratingUser . email = "rater@test.com" ;
53+ ratingUser . password = "" ;
54+ ratingUser . role = UserRole . User ;
55+ ratingUser . verifyToken = "" ;
56+ ratingUser . tokenSecret = "" ;
57+ ratingUser . forgotPasswordToken = "" ;
58+
59+ // A user who is a member of the project team
60+ teamMember = new User ( ) ;
61+ teamMember . firstName = "Team" ;
62+ teamMember . lastName = "Member" ;
63+ teamMember . email = "member@test.com" ;
64+ teamMember . password = "" ;
65+ teamMember . role = UserRole . User ;
66+ teamMember . verifyToken = "" ;
67+ teamMember . tokenSecret = "" ;
68+ teamMember . forgotPasswordToken = "" ;
69+
70+ [ ratingUser , teamMember ] = await userRepo . save ( [ ratingUser , teamMember ] ) ;
71+
72+ mockTeam = new Team ( ) ;
73+ mockTeam . title = "Test Team" ;
74+ mockTeam . users = [ teamMember . id . toString ( ) ] ;
75+ mockTeam . teamImg = "" ;
76+ mockTeam . description = "" ;
77+ mockTeam . requests = [ ] ;
78+ mockTeam = await teamRepo . save ( mockTeam ) ;
79+
80+ mockProject = new Project ( ) ;
81+ mockProject . team = mockTeam ;
82+ mockProject . title = "Test Project" ;
83+ mockProject . description = "" ;
84+ mockProject . allowRating = true ;
85+ mockProject = await projectRepo . save ( mockProject ) ;
86+
87+ mockCriterion = new Criterion ( ) ;
88+ mockCriterion . title = "Test Criterion" ;
89+ mockCriterion . description = "" ;
90+ mockCriterion = await criterionRepo . save ( mockCriterion ) ;
91+
92+ ratingService = new RatingService ( database , settingsService . instance ) ;
6593 await ratingService . bootstrap ( ) ;
6694 } ) ;
6795
@@ -71,10 +99,17 @@ describe("RatingService", () => {
7199 expect . assertions ( 1 ) ;
72100
73101 settingsService . mocks . getSettings . mockResolvedValue (
74- { application : { allowRatingProjects : false } } as any
102+ { application : { allowRatingProjects : false } } as any ,
75103 ) ;
76104
77- await expect ( ratingService . upsertRating ( mockRating , mockUser ) ) . rejects . toThrow (
105+ const rating = Object . assign ( new Rating ( ) , {
106+ project : mockProject ,
107+ user : ratingUser ,
108+ criterion : mockCriterion ,
109+ rating : 3 ,
110+ } ) ;
111+
112+ await expect ( ratingService . upsertRating ( rating , ratingUser ) ) . rejects . toThrow (
78113 ForbiddenError ,
79114 ) ;
80115 } ) ;
@@ -83,12 +118,17 @@ describe("RatingService", () => {
83118 expect . assertions ( 1 ) ;
84119
85120 settingsService . mocks . getSettings . mockResolvedValue (
86- { application : { allowRatingProjects : true } } as any
121+ { application : { allowRatingProjects : true } } as any ,
87122 ) ;
88123
89- mockProjectsRepo . findOneBy . mockResolvedValue ( null ) ;
124+ const rating = Object . assign ( new Rating ( ) , {
125+ project : { id : 99999 } ,
126+ user : ratingUser ,
127+ criterion : mockCriterion ,
128+ rating : 3 ,
129+ } ) ;
90130
91- await expect ( ratingService . upsertRating ( mockRating , mockUser ) ) . rejects . toThrow (
131+ await expect ( ratingService . upsertRating ( rating , ratingUser ) ) . rejects . toThrow (
92132 NotFoundError ,
93133 ) ;
94134 } ) ;
@@ -97,54 +137,39 @@ describe("RatingService", () => {
97137 expect . assertions ( 1 ) ;
98138
99139 settingsService . mocks . getSettings . mockResolvedValue (
100- { application : { allowRatingProjects : true } } as any
140+ { application : { allowRatingProjects : true } } as any ,
101141 ) ;
102142
103- mockProjectsRepo . findOneBy . mockResolvedValue (
104- Object . assign ( new Project ( ) , { ...mockProject , allowRating : false } ) ,
105- ) ;
143+ await projectRepo . update ( mockProject . id , { allowRating : false } ) ;
106144
107145 // The backend should not be tricked by an allowRating: true in the payload
108- const payload = {
109- ...mockRating ,
110- project : {
111- ... mockRating . project ,
112- allowRating : true
113- }
114- }
115- await expect ( ratingService . upsertRating ( payload , mockUser ) ) . rejects . toThrow (
146+ const rating = Object . assign ( new Rating ( ) , {
147+ project : { ...mockProject , allowRating : true } ,
148+ user : ratingUser ,
149+ criterion : mockCriterion ,
150+ rating : 3 ,
151+ } ) ;
152+
153+ await expect ( ratingService . upsertRating ( rating , ratingUser ) ) . rejects . toThrow (
116154 ForbiddenError ,
117155 ) ;
118156 } ) ;
119157
120- it ( "throws NotFoundError when team does not exist" , async ( ) => {
121- expect . assertions ( 1 ) ;
122-
123- settingsService . mocks . getSettings . mockResolvedValue (
124- { application : { allowRatingProjects : true } } as any
125- ) ;
126-
127- mockProjectsRepo . findOneBy . mockResolvedValue ( mockProject ) ;
128- mockTeamsRepo . findOneBy . mockResolvedValue ( null ) ;
129-
130- await expect ( ratingService . upsertRating ( mockRating , mockUser ) ) . rejects . toThrow (
131- NotFoundError ,
132- ) ;
133- } ) ;
134-
135158 it ( "throws ForbiddenError when a user tries to rate their own project" , async ( ) => {
136159 expect . assertions ( 1 ) ;
137160
138161 settingsService . mocks . getSettings . mockResolvedValue (
139- { application : { allowRatingProjects : true } } as any
162+ { application : { allowRatingProjects : true } } as any ,
140163 ) ;
141164
142- mockProjectsRepo . findOneBy . mockResolvedValue ( mockProject ) ;
143- mockTeamsRepo . findOneBy . mockResolvedValue (
144- Object . assign ( new Team ( ) , { ...mockTeam , users : [ "1" , "2" , "3" ] } ) ,
145- ) ;
165+ const rating = Object . assign ( new Rating ( ) , {
166+ project : mockProject ,
167+ user : teamMember ,
168+ criterion : mockCriterion ,
169+ rating : 3 ,
170+ } ) ;
146171
147- await expect ( ratingService . upsertRating ( mockRating , mockUser ) ) . rejects . toThrow (
172+ await expect ( ratingService . upsertRating ( rating , teamMember ) ) . rejects . toThrow (
148173 ForbiddenError ,
149174 ) ;
150175 } ) ;
@@ -153,69 +178,92 @@ describe("RatingService", () => {
153178 expect . assertions ( 2 ) ;
154179
155180 settingsService . mocks . getSettings . mockResolvedValue (
156- { application : { allowRatingProjects : true } } as any
181+ { application : { allowRatingProjects : true } } as any ,
157182 ) ;
158183
159- mockProjectsRepo . findOneBy . mockResolvedValue ( mockProject ) ;
160- mockTeamsRepo . findOneBy . mockResolvedValue ( mockTeam ) ;
161- mockRatingsRepo . findOne . mockResolvedValue ( null ) ;
162- const savedRating = Object . assign ( new Rating ( ) , { ...mockRating , id : 42 } ) ;
163- mockRatingsRepo . save . mockResolvedValue ( savedRating ) ;
184+ const rating = Object . assign ( new Rating ( ) , {
185+ project : mockProject ,
186+ user : ratingUser ,
187+ criterion : mockCriterion ,
188+ rating : 4 ,
189+ } ) ;
164190
165- const result = await ratingService . upsertRating ( mockRating , mockUser ) ;
191+ const result = await ratingService . upsertRating ( rating , ratingUser ) ;
166192
167- expect ( result ) . toBe ( savedRating ) ;
168- expect ( mockRatingsRepo . save ) . toHaveBeenCalledWith ( mockRating ) ;
193+ expect ( result . id ) . toBeDefined ( ) ;
194+ expect ( result . rating ) . toBe ( 4 ) ;
169195 } ) ;
170196
171197 it ( "is forbidden to impersonate other users" , async ( ) => {
172198 expect . assertions ( 1 ) ;
173199
174200 settingsService . mocks . getSettings . mockResolvedValue (
175- { application : { allowRatingProjects : true } } as any
201+ { application : { allowRatingProjects : true } } as any ,
176202 ) ;
177203
178- mockProjectsRepo . findOneBy . mockResolvedValue ( mockProject ) ;
179- mockTeamsRepo . findOneBy . mockResolvedValue ( mockTeam ) ;
180- mockRatingsRepo . findOne . mockResolvedValue ( null ) ;
181-
182- mockRating . user = {
183- ...mockUser ,
184- id : 1234
185- } ;
204+ const rating = Object . assign ( new Rating ( ) , {
205+ project : mockProject ,
206+ user : { ...ratingUser , id : teamMember . id } ,
207+ criterion : mockCriterion ,
208+ rating : 3 ,
209+ } ) ;
186210
187- await expect ( ratingService . upsertRating ( mockRating , mockUser ) ) . rejects . toThrow (
211+ await expect ( ratingService . upsertRating ( rating , ratingUser ) ) . rejects . toThrow (
188212 ForbiddenError ,
189213 ) ;
190214 } ) ;
191-
192215 } ) ;
193216 } ) ;
194217
195218 describe ( "getRatingResults" , ( ) => {
196219 it ( "aggregates ratings for two projects with two ratings each" , async ( ) => {
197220 expect . assertions ( 5 ) ;
198221
199- const projectA = Object . assign ( new Project ( ) , { id : 1 , team : mockTeam } ) ;
200- const projectB = Object . assign ( new Project ( ) , { id : 2 , team : mockTeam } ) ;
201-
202- mockProjectsRepo . find . mockResolvedValue ( [ projectA , projectB ] )
203-
204- const criterionA = Object . assign ( new Criterion ( ) , { id : 1 } ) ;
205- const criterionB = Object . assign ( new Criterion ( ) , { id : 2 } ) ;
206-
207- const ratingsFixture = [
208- // Project A
209- Object . assign ( new Rating ( ) , { id : 1 , project : projectA , criterion : criterionA , rating : 2 } ) ,
210- Object . assign ( new Rating ( ) , { id : 2 , project : projectA , criterion : criterionA , rating : 3 } ) ,
211- Object . assign ( new Rating ( ) , { id : 3 , project : projectA , criterion : criterionB , rating : 1 } ) ,
212- // Project B
213- Object . assign ( new Rating ( ) , { id : 4 , project : projectB , criterion : criterionB , rating : 2 } ) ,
214- Object . assign ( new Rating ( ) , { id : 5 , project : projectB , criterion : criterionB , rating : 5 } ) ,
215- Object . assign ( new Rating ( ) , { id : 6 , project : projectB , criterion : criterionB , rating : 5 } ) ,
216- ] ;
217-
218- mockRatingsRepo . find . mockResolvedValue ( ratingsFixture ) ;
222+ const projectA = await projectRepo . save (
223+ Object . assign ( new Project ( ) , {
224+ team : mockTeam ,
225+ title : "Project A" ,
226+ description : "" ,
227+ allowRating : true ,
228+ } ) ,
229+ ) ;
230+ const projectB = await projectRepo . save (
231+ Object . assign ( new Project ( ) , {
232+ team : mockTeam ,
233+ title : "Project B" ,
234+ description : "" ,
235+ allowRating : true ,
236+ } ) ,
237+ ) ;
238+
239+ const criterionA = await criterionRepo . save (
240+ Object . assign ( new Criterion ( ) , { title : "Criterion A" , description : "" } ) ,
241+ ) ;
242+ const criterionB = await criterionRepo . save (
243+ Object . assign ( new Criterion ( ) , { title : "Criterion B" , description : "" } ) ,
244+ ) ;
245+
246+ // Create extra users to submit ratings (not team members)
247+ const [ raterA , raterB , raterC ] = await userRepo . save ( [
248+ Object . assign ( new User ( ) , { firstName : "A" , lastName : "R" , email : "ra@test.com" , password : "" , role : UserRole . User , verifyToken : "" , tokenSecret : "" , forgotPasswordToken : "" } ) ,
249+ Object . assign ( new User ( ) , { firstName : "B" , lastName : "R" , email : "rb@test.com" , password : "" , role : UserRole . User , verifyToken : "" , tokenSecret : "" , forgotPasswordToken : "" } ) ,
250+ Object . assign ( new User ( ) , { firstName : "C" , lastName : "R" , email : "rc@test.com" , password : "" , role : UserRole . User , verifyToken : "" , tokenSecret : "" , forgotPasswordToken : "" } ) ,
251+ ] ) ;
252+
253+ await ratingRepo . save ( [
254+ // Project A, criterionA: avg 2.5
255+ Object . assign ( new Rating ( ) , { project : projectA , criterion : criterionA , user : raterA , rating : 2 } ) ,
256+ Object . assign ( new Rating ( ) , { project : projectA , criterion : criterionA , user : raterB , rating : 3 } ) ,
257+ // Project A, criterionB: avg 1
258+ Object . assign ( new Rating ( ) , { project : projectA , criterion : criterionB , user : raterA , rating : 1 } ) ,
259+ // Project B, criterionB: avg 4
260+ Object . assign ( new Rating ( ) , { project : projectB , criterion : criterionB , user : raterA , rating : 2 } ) ,
261+ Object . assign ( new Rating ( ) , { project : projectB , criterion : criterionB , user : raterB , rating : 5 } ) ,
262+ Object . assign ( new Rating ( ) , { project : projectB , criterion : criterionB , user : raterC , rating : 5 } ) ,
263+ ] ) ;
264+
265+ // getRatingResults only counts projects that exist; exclude the default mockProject
266+ await projectRepo . delete ( mockProject . id ) ;
219267
220268 const results = await ratingService . getRatingResults ( ) ;
221269
0 commit comments