@@ -50,6 +50,7 @@ func TestGetProjectStatusByURI(t *testing.T) {
5050 wantNotFound bool
5151 wantRetryable bool
5252 wantInvalid bool
53+ noProjectLookup bool
5354 wantConsistency bool
5455 }{
5556 {name : "in progress without fact" , request : entity.GetProjectStatusByURIRequest {Queue : projectStatusQueue , ChangeURI : projectStatusURI , Projects : []string {"project-a" }}, factErr : storage .ErrNotFound },
@@ -61,7 +62,7 @@ func TestGetProjectStatusByURI(t *testing.T) {
6162 {name : "empty project" , request : entity.GetProjectStatusByURIRequest {Queue : projectStatusQueue , ChangeURI : projectStatusURI , Projects : []string {"" }}, wantInvalid : true },
6263 {name : "duplicate projects" , request : entity.GetProjectStatusByURIRequest {Queue : projectStatusQueue , ChangeURI : projectStatusURI , Projects : []string {"project-a" , "project-a" }}, wantInvalid : true },
6364 {name : "invalid page size" , request : entity.GetProjectStatusByURIRequest {Queue : projectStatusQueue , ChangeURI : projectStatusURI , PageSize : maxProjectStatusPageSize + 1 }, wantInvalid : true },
64- {name : "page token before project results " , request : entity.GetProjectStatusByURIRequest {Queue : projectStatusQueue , ChangeURI : projectStatusURI , PageToken : "token" }, wantInvalid : true },
65+ {name : "malformed page token" , request : entity.GetProjectStatusByURIRequest {Queue : projectStatusQueue , ChangeURI : projectStatusURI , Projects : [] string { "project-a" }, PageToken : "token" }, factErr : storage . ErrNotFound , wantInvalid : true , noProjectLookup : true },
6566 {name : "invalid fact degree" , request : entity.GetProjectStatusByURIRequest {Queue : projectStatusQueue , ChangeURI : projectStatusURI , Projects : []string {"project-a" }}, fact : entity.ValidationFact {URI : projectStatusURI , RequestID : projectStatusID , Degree : math .NaN ()}, wantConsistency : true },
6667 }
6768
@@ -73,7 +74,7 @@ func TestGetProjectStatusByURI(t *testing.T) {
7374 uriStore := storagemock .NewMockRequestURIStore (mockCtrl )
7475 summaryStore := storagemock .NewMockRequestSummaryStore (mockCtrl )
7576 factStore := storagemock .NewMockValidationFactStore (mockCtrl )
76- if ! tt .wantInvalid {
77+ if ! ( tt .wantInvalid && tt . request . PageToken == "" ) {
7778 factory .EXPECT ().For (storage.Config {QueueName : projectStatusQueue }).Return (store , nil )
7879 store .EXPECT ().GetRequestURIStore ().Return (uriStore )
7980 uriStore .EXPECT ().GetIDByURI (gomock .Any (), projectStatusURI ).Return (projectStatusID , tt .uriErr )
@@ -83,6 +84,11 @@ func TestGetProjectStatusByURI(t *testing.T) {
8384 if tt .summaryErr == nil {
8485 store .EXPECT ().GetValidationFactStore ().Return (factStore )
8586 factStore .EXPECT ().Get (gomock .Any (), projectStatusURI , "" ).Return (tt .fact , tt .factErr )
87+ if ! tt .noProjectLookup && ! (tt .factErr == nil && tt .wantConsistency ) {
88+ for _ , project := range tt .request .Projects {
89+ factStore .EXPECT ().Get (gomock .Any (), projectStatusURI , project ).Return (entity.ValidationFact {}, storage .ErrNotFound )
90+ }
91+ }
8692 }
8793 }
8894 }
@@ -126,13 +132,107 @@ func TestGetProjectStatusByURIUsesRequestSummaryTimestamp(t *testing.T) {
126132 summaryStore .EXPECT ().Get (gomock .Any (), projectStatusID ).Return (requestSummary , nil )
127133 store .EXPECT ().GetValidationFactStore ().Return (factStore )
128134 factStore .EXPECT ().Get (gomock .Any (), projectStatusURI , "" ).Return (entity.ValidationFact {URI : projectStatusURI , RequestID : projectStatusID , CreatedAt : 20 }, nil )
135+ factStore .EXPECT ().Get (gomock .Any (), projectStatusURI , "project-a" ).Return (entity.ValidationFact {}, storage .ErrNotFound )
129136
130137 controller := NewGetProjectStatusByURIController (zap .NewNop ().Sugar (), tally .NoopScope , factory )
131138 got , err := controller .GetProjectStatusByURI (context .Background (), entity.GetProjectStatusByURIRequest {Queue : projectStatusQueue , ChangeURI : projectStatusURI , Projects : []string {"project-a" }})
132139 require .NoError (t , err )
133140 assert .Equal (t , int64 (20 ), got .UpdatedAtMs )
134141}
135142
143+ func TestGetProjectStatusByURIReturnsProjectFactsInRequestedOrder (t * testing.T ) {
144+ requestSummary := entity.RequestSummary {RequestID : projectStatusID , Queue : projectStatusQueue , URI : projectStatusURI , StateTimestampMs : 10 }
145+ mockCtrl := gomock .NewController (t )
146+ factory := storagemock .NewMockFactory (mockCtrl )
147+ store := storagemock .NewMockStorage (mockCtrl )
148+ uriStore := storagemock .NewMockRequestURIStore (mockCtrl )
149+ summaryStore := storagemock .NewMockRequestSummaryStore (mockCtrl )
150+ factStore := storagemock .NewMockValidationFactStore (mockCtrl )
151+ factory .EXPECT ().For (storage.Config {QueueName : projectStatusQueue }).Return (store , nil )
152+ store .EXPECT ().GetRequestURIStore ().Return (uriStore )
153+ uriStore .EXPECT ().GetIDByURI (gomock .Any (), projectStatusURI ).Return (projectStatusID , nil )
154+ store .EXPECT ().GetRequestSummaryStore ().Return (summaryStore )
155+ summaryStore .EXPECT ().Get (gomock .Any (), projectStatusID ).Return (requestSummary , nil )
156+ store .EXPECT ().GetValidationFactStore ().Return (factStore )
157+ factStore .EXPECT ().Get (gomock .Any (), projectStatusURI , "" ).Return (entity.ValidationFact {}, storage .ErrNotFound )
158+ factStore .EXPECT ().Get (gomock .Any (), projectStatusURI , "project-b" ).Return (entity.ValidationFact {URI : projectStatusURI , RequestID : projectStatusID , Project : "project-b" , Degree : entity .DegreeBroken , CreatedAt : 20 }, nil )
159+ factStore .EXPECT ().Get (gomock .Any (), projectStatusURI , "missing" ).Return (entity.ValidationFact {}, storage .ErrNotFound )
160+ factStore .EXPECT ().Get (gomock .Any (), projectStatusURI , "project-a" ).Return (entity.ValidationFact {URI : projectStatusURI , RequestID : projectStatusID , Project : "project-a" , Degree : entity .DegreeGreen , CreatedAt : 30 }, nil )
161+
162+ controller := NewGetProjectStatusByURIController (zap .NewNop ().Sugar (), tally .NoopScope , factory )
163+ got , err := controller .GetProjectStatusByURI (context .Background (), entity.GetProjectStatusByURIRequest {Queue : projectStatusQueue , ChangeURI : projectStatusURI , Projects : []string {"project-b" , "missing" , "project-a" }})
164+ require .NoError (t , err )
165+ require .Len (t , got .ProjectValidationFacts , 2 )
166+ assert .Equal (t , []string {"project-b" , "project-a" }, []string {got .ProjectValidationFacts [0 ].Project , got .ProjectValidationFacts [1 ].Project })
167+ assert .Equal (t , int64 (30 ), got .UpdatedAtMs )
168+ assert .Empty (t , got .NextPageToken )
169+ assert .False (t , got .ProjectResultsComplete )
170+ }
171+
172+ func TestGetProjectStatusByURIPaginatesProjectFacts (t * testing.T ) {
173+ requestSummary := entity.RequestSummary {RequestID : projectStatusID , Queue : projectStatusQueue , URI : projectStatusURI , StateTimestampMs : 10 }
174+ projects := []string {"project-a" , "project-b" }
175+
176+ firstPage := func (t * testing.T ) string {
177+ mockCtrl := gomock .NewController (t )
178+ factory := storagemock .NewMockFactory (mockCtrl )
179+ store := storagemock .NewMockStorage (mockCtrl )
180+ uriStore := storagemock .NewMockRequestURIStore (mockCtrl )
181+ summaryStore := storagemock .NewMockRequestSummaryStore (mockCtrl )
182+ factStore := storagemock .NewMockValidationFactStore (mockCtrl )
183+ factory .EXPECT ().For (storage.Config {QueueName : projectStatusQueue }).Return (store , nil )
184+ store .EXPECT ().GetRequestURIStore ().Return (uriStore )
185+ uriStore .EXPECT ().GetIDByURI (gomock .Any (), projectStatusURI ).Return (projectStatusID , nil )
186+ store .EXPECT ().GetRequestSummaryStore ().Return (summaryStore )
187+ summaryStore .EXPECT ().Get (gomock .Any (), projectStatusID ).Return (requestSummary , nil )
188+ store .EXPECT ().GetValidationFactStore ().Return (factStore )
189+ factStore .EXPECT ().Get (gomock .Any (), projectStatusURI , "" ).Return (entity.ValidationFact {}, storage .ErrNotFound )
190+ factStore .EXPECT ().Get (gomock .Any (), projectStatusURI , "project-a" ).Return (entity.ValidationFact {URI : projectStatusURI , RequestID : projectStatusID , Project : "project-a" , Degree : entity .DegreeGreen }, nil )
191+
192+ got , err := NewGetProjectStatusByURIController (zap .NewNop ().Sugar (), tally .NoopScope , factory ).GetProjectStatusByURI (context .Background (), entity.GetProjectStatusByURIRequest {Queue : projectStatusQueue , ChangeURI : projectStatusURI , Projects : projects , PageSize : 1 })
193+ require .NoError (t , err )
194+ require .Len (t , got .ProjectValidationFacts , 1 )
195+ assert .Equal (t , "project-a" , got .ProjectValidationFacts [0 ].Project )
196+ require .NotEmpty (t , got .NextPageToken )
197+ return got .NextPageToken
198+ }(t )
199+
200+ mockCtrl := gomock .NewController (t )
201+ factory := storagemock .NewMockFactory (mockCtrl )
202+ store := storagemock .NewMockStorage (mockCtrl )
203+ uriStore := storagemock .NewMockRequestURIStore (mockCtrl )
204+ summaryStore := storagemock .NewMockRequestSummaryStore (mockCtrl )
205+ factStore := storagemock .NewMockValidationFactStore (mockCtrl )
206+ factory .EXPECT ().For (storage.Config {QueueName : projectStatusQueue }).Return (store , nil )
207+ store .EXPECT ().GetRequestURIStore ().Return (uriStore )
208+ uriStore .EXPECT ().GetIDByURI (gomock .Any (), projectStatusURI ).Return (projectStatusID , nil )
209+ store .EXPECT ().GetRequestSummaryStore ().Return (summaryStore )
210+ summaryStore .EXPECT ().Get (gomock .Any (), projectStatusID ).Return (requestSummary , nil )
211+ store .EXPECT ().GetValidationFactStore ().Return (factStore )
212+ factStore .EXPECT ().Get (gomock .Any (), projectStatusURI , "" ).Return (entity.ValidationFact {}, storage .ErrNotFound )
213+ factStore .EXPECT ().Get (gomock .Any (), projectStatusURI , "project-b" ).Return (entity.ValidationFact {URI : projectStatusURI , RequestID : projectStatusID , Project : "project-b" , Degree : entity .DegreeBroken }, nil )
214+ got , err := NewGetProjectStatusByURIController (zap .NewNop ().Sugar (), tally .NoopScope , factory ).GetProjectStatusByURI (context .Background (), entity.GetProjectStatusByURIRequest {Queue : projectStatusQueue , ChangeURI : projectStatusURI , Projects : projects , PageSize : 1 , PageToken : firstPage })
215+ require .NoError (t , err )
216+ require .Len (t , got .ProjectValidationFacts , 1 )
217+ assert .Equal (t , "project-b" , got .ProjectValidationFacts [0 ].Project )
218+ assert .Empty (t , got .NextPageToken )
219+ }
220+
221+ func TestSelectProjectStatusPageRejectsMismatchedToken (t * testing.T ) {
222+ token , err := encodeProjectStatusPageToken (projectStatusPageToken {RequestID : projectStatusID , Projects : []string {"project-a" , "project-b" }, NextIndex : 1 })
223+ require .NoError (t , err )
224+ _ , err = selectProjectStatusPage (entity.GetProjectStatusByURIRequest {Projects : []string {"project-b" , "project-a" }, PageToken : token }, projectStatusID )
225+ require .Error (t , err )
226+ assert .True (t , IsInvalidRequest (err ))
227+ }
228+
229+ func TestValidateProjectFact (t * testing.T ) {
230+ requestSummary := entity.RequestSummary {RequestID : projectStatusID , URI : projectStatusURI }
231+ assert .NoError (t , validateProjectFact (entity.ValidationFact {URI : projectStatusURI , RequestID : projectStatusID , Project : "project-a" , Degree : entity .DegreeGreen }, requestSummary , "project-a" ))
232+ assert .True (t , IsProjectStatusConsistency (validateProjectFact (entity.ValidationFact {URI : projectStatusURI , RequestID : projectStatusID , Project : "project-b" , Degree : entity .DegreeGreen }, requestSummary , "project-a" )))
233+ assert .True (t , IsProjectStatusConsistency (validateProjectFact (entity.ValidationFact {URI : projectStatusURI , RequestID : projectStatusID , Project : "project-a" , Degree : math .NaN ()}, requestSummary , "project-a" )))
234+ }
235+
136236func TestGetProjectStatusByURIRejectsInconsistentRequest (t * testing.T ) {
137237 mockCtrl := gomock .NewController (t )
138238 factory := storagemock .NewMockFactory (mockCtrl )
0 commit comments