@@ -81,11 +81,11 @@ func newMockStorage(ctrl *gomock.Controller, request entity.Request) (*storagemo
8181}
8282
8383// newMockChangeStore creates a MockChangeStore with default no-overlap behavior.
84- // Tests that need to simulate overlap can override FindOverlapping with their own EXPECT.
84+ // Tests that need to simulate overlap can override GetByURI with their own EXPECT.
8585// Validate is read-only against the change store — it never calls Create.
8686func newMockChangeStore (ctrl * gomock.Controller ) * changemock.MockChangeStore {
8787 cs := changemock .NewMockChangeStore (ctrl )
88- cs .EXPECT ().FindOverlapping (gomock .Any (), gomock .Any (), gomock .Any ()).Return (nil , nil ).AnyTimes ()
88+ cs .EXPECT ().GetByURI (gomock .Any (), gomock .Any (), gomock .Any ()).Return (nil , nil ).AnyTimes ()
8989 return cs
9090}
9191
@@ -278,61 +278,71 @@ func TestController_Process_DuplicateDetection(t *testing.T) {
278278 queueName = "test-queue"
279279 newRequestID = queueName + "/123"
280280 dupRequestID = queueName + "/100"
281- uri = "github://uber/service/pull/1/abc"
281+ uriA = "github://uber/service/pull/1/abc"
282+ uriB = "github://uber/service/pull/2/def"
282283 anotherReqID = queueName + "/050"
283284 orphanReqID = queueName + "/999"
284285 terminalReqID = queueName + "/200"
285286 )
286287
287288 tests := []struct {
288289 name string
289- overlap []entity.ChangeRecord
290+ requestURIs []string // URIs on the new request; defaults to [uriA]
291+ byURI map [string ][]entity.ChangeRecord // GetByURI mock returns
290292 ownerLookup map [string ]entity.Request
291293 ownerNotFound map [string ]bool
292294 ownerErr map [string ]error
293295 wantUserErr bool
294296 wantUnexpected bool
295297 }{
296298 {
297- name : "no overlap proceeds to merge check" ,
298- overlap : nil ,
299+ name : "no overlap proceeds to merge check" ,
300+ byURI : map [ string ][]entity. ChangeRecord { uriA : nil } ,
299301 },
300302 {
301- name : "overlap with live in-flight request returns user error" ,
302- overlap : []entity.ChangeRecord {{URI : uri , RequestID : dupRequestID , Queue : queueName }},
303+ name : "overlap with live in-flight request returns user error" ,
304+ byURI : map [string ][]entity.ChangeRecord {
305+ uriA : {{URI : uriA , RequestID : dupRequestID , Queue : queueName }},
306+ },
303307 ownerLookup : map [string ]entity.Request {
304308 dupRequestID : {ID : dupRequestID , Queue : queueName , State : entity .RequestStateStarted , Version : 1 },
305309 },
306310 wantUserErr : true ,
307311 },
308312 {
309- name : "overlap with terminal owner is skipped" ,
310- overlap : []entity.ChangeRecord {{URI : uri , RequestID : terminalReqID , Queue : queueName }},
313+ name : "overlap with terminal owner is skipped" ,
314+ byURI : map [string ][]entity.ChangeRecord {
315+ uriA : {{URI : uriA , RequestID : terminalReqID , Queue : queueName }},
316+ },
311317 ownerLookup : map [string ]entity.Request {
312318 terminalReqID : {ID : terminalReqID , Queue : queueName , State : entity .RequestStateLanded , Version : 5 },
313319 },
314320 },
315321 {
316- name : "overlap with orphan owner (ErrNotFound) is skipped" ,
317- overlap : []entity.ChangeRecord {{URI : uri , RequestID : orphanReqID , Queue : queueName }},
322+ name : "overlap with orphan owner (ErrNotFound) is skipped" ,
323+ byURI : map [string ][]entity.ChangeRecord {
324+ uriA : {{URI : uriA , RequestID : orphanReqID , Queue : queueName }},
325+ },
318326 ownerNotFound : map [string ]bool {orphanReqID : true },
319327 },
320328 {
321- name : "multiple URIs same owner deduped to single Get call" ,
322- overlap : []entity.ChangeRecord {
323- {URI : uri , RequestID : dupRequestID , Queue : queueName },
324- {URI : "github://uber/service/pull/2/def" , RequestID : dupRequestID , Queue : queueName },
329+ name : "multi-URI same owner deduped to single Get call" ,
330+ requestURIs : []string {uriA , uriB },
331+ byURI : map [string ][]entity.ChangeRecord {
332+ uriA : {{URI : uriA , RequestID : dupRequestID , Queue : queueName }},
333+ uriB : {{URI : uriB , RequestID : dupRequestID , Queue : queueName }},
325334 },
326335 ownerLookup : map [string ]entity.Request {
327336 dupRequestID : {ID : dupRequestID , Queue : queueName , State : entity .RequestStateValidated , Version : 2 },
328337 },
329338 wantUserErr : true ,
330339 },
331340 {
332- name : "first owner terminal then second live picks the live one" ,
333- overlap : []entity.ChangeRecord {
334- {URI : uri , RequestID : terminalReqID , Queue : queueName },
335- {URI : "github://uber/service/pull/2/def" , RequestID : anotherReqID , Queue : queueName },
341+ name : "first URI's owner is terminal, second URI's owner is live" ,
342+ requestURIs : []string {uriA , uriB },
343+ byURI : map [string ][]entity.ChangeRecord {
344+ uriA : {{URI : uriA , RequestID : terminalReqID , Queue : queueName }},
345+ uriB : {{URI : uriB , RequestID : anotherReqID , Queue : queueName }},
336346 },
337347 ownerLookup : map [string ]entity.Request {
338348 terminalReqID : {ID : terminalReqID , State : entity .RequestStateError , Version : 3 },
@@ -342,25 +352,29 @@ func TestController_Process_DuplicateDetection(t *testing.T) {
342352 },
343353 {
344354 // Store doesn't exclude self; controller filters by RequestID and must not look up its own row.
345- name : "self row in overlap is filtered (no Get call)" ,
346- overlap : []entity.ChangeRecord {
347- { URI : uri , RequestID : newRequestID , Queue : queueName },
355+ name : "self row in result is filtered (no Get call)" ,
356+ byURI : map [ string ] []entity.ChangeRecord {
357+ uriA : {{ URI : uriA , RequestID : newRequestID , Queue : queueName } },
348358 },
349359 },
350360 {
351361 name : "self row mixed with live other returns the other" ,
352- overlap : []entity.ChangeRecord {
353- {URI : uri , RequestID : newRequestID , Queue : queueName },
354- {URI : uri , RequestID : dupRequestID , Queue : queueName },
362+ byURI : map [string ][]entity.ChangeRecord {
363+ uriA : {
364+ {URI : uriA , RequestID : newRequestID , Queue : queueName },
365+ {URI : uriA , RequestID : dupRequestID , Queue : queueName },
366+ },
355367 },
356368 ownerLookup : map [string ]entity.Request {
357369 dupRequestID : {ID : dupRequestID , Queue : queueName , State : entity .RequestStateStarted , Version : 1 },
358370 },
359371 wantUserErr : true ,
360372 },
361373 {
362- name : "owner lookup unexpected error propagates" ,
363- overlap : []entity.ChangeRecord {{URI : uri , RequestID : dupRequestID , Queue : queueName }},
374+ name : "owner lookup unexpected error propagates" ,
375+ byURI : map [string ][]entity.ChangeRecord {
376+ uriA : {{URI : uriA , RequestID : dupRequestID , Queue : queueName }},
377+ },
364378 ownerErr : map [string ]error {
365379 dupRequestID : fmt .Errorf ("db down" ),
366380 },
@@ -373,10 +387,15 @@ func TestController_Process_DuplicateDetection(t *testing.T) {
373387 ctrl := gomock .NewController (t )
374388 mc := newMergeableMock (ctrl )
375389
390+ uris := tt .requestURIs
391+ if uris == nil {
392+ uris = []string {uriA }
393+ }
394+
376395 request := entity.Request {
377396 ID : newRequestID ,
378397 Queue : queueName ,
379- Change : entity.Change {URIs : [] string { uri } },
398+ Change : entity.Change {URIs : uris },
380399 LandStrategy : entity .RequestLandStrategyRebase ,
381400 State : entity .RequestStateStarted ,
382401 Version : 1 ,
@@ -397,7 +416,11 @@ func TestController_Process_DuplicateDetection(t *testing.T) {
397416 store .EXPECT ().GetRequestStore ().Return (mockReqStore ).AnyTimes ()
398417
399418 cs := changemock .NewMockChangeStore (ctrl )
400- cs .EXPECT ().FindOverlapping (gomock .Any (), queueName , []string {uri }).Return (tt .overlap , nil )
419+ // One GetByURI per URI on the request, in order. Controller short-circuits on first
420+ // live duplicate, so .AnyTimes() lets unmatched URIs go un-queried.
421+ for _ , u := range uris {
422+ cs .EXPECT ().GetByURI (gomock .Any (), queueName , u ).Return (tt .byURI [u ], nil ).MaxTimes (1 )
423+ }
401424
402425 controller := newTestController (t , ctrl , store , cs , mc , nil )
403426
@@ -437,7 +460,7 @@ func TestController_Process_ChangeStoreQueryFailure(t *testing.T) {
437460 store , _ := newMockStorage (ctrl , request )
438461
439462 cs := changemock .NewMockChangeStore (ctrl )
440- cs .EXPECT ().FindOverlapping (gomock .Any (), gomock .Any (), gomock .Any ()).Return (nil , fmt .Errorf ("change store down" ))
463+ cs .EXPECT ().GetByURI (gomock .Any (), gomock .Any (), gomock .Any ()).Return (nil , fmt .Errorf ("change store down" ))
441464
442465 controller := newTestController (t , ctrl , store , cs , mc , nil )
443466
0 commit comments