@@ -3,6 +3,7 @@ package handler
33import (
44 "context"
55 "errors"
6+ "strings"
67 "testing"
78
89 "github.com/indexdata/crosslink/broker/common"
@@ -155,3 +156,211 @@ type MockIllRepositoryNoSelectedSupplier struct {
155156func (r * MockIllRepositoryNoSelectedSupplier ) GetSelectedSupplierForIllTransaction (ctx common.ExtendedContext , illTransId string ) (ill_db.LocatedSupplier , error ) {
156157 return ill_db.LocatedSupplier {}, pgx .ErrNoRows
157158}
159+
160+ // mockDuplicateCheckRepo overrides FindDuplicateIllTransaction to return
161+ // configurable results for duplicate-check testing.
162+ type mockDuplicateCheckRepo struct {
163+ mocks.MockIllRepositorySuccess
164+ duplicate bool
165+ err error
166+ called bool
167+ cql string
168+ }
169+
170+ func (r * mockDuplicateCheckRepo ) ListIllTransactions (ctx common.ExtendedContext , params ill_db.ListIllTransactionsParams , cql * string , symbols []string ) ([]ill_db.IllTransaction , int64 , error ) {
171+ if cql != nil {
172+ r .cql = * cql
173+ }
174+ r .called = true
175+ if r .duplicate {
176+ return []ill_db.IllTransaction {{ID : "duplicate-id" }}, 1 , nil
177+ }
178+ return []ill_db.IllTransaction {}, 0 , r .err
179+ }
180+
181+ func TestCheckDuplicateRequest (t * testing.T ) {
182+ window1 := 1
183+ window0 := 0
184+ windowNeg := - 1
185+
186+ baseRequest := & iso18626.Request {
187+ BibliographicInfo : iso18626.BibliographicInfo {
188+ SupplierUniqueRecordId : "rec-1" ,
189+ Title : "Test Title" ,
190+ },
191+ ServiceInfo : & iso18626.ServiceInfo {
192+ ServiceType : iso18626 .TypeServiceTypeLoan ,
193+ },
194+ PatronInfo : & iso18626.PatronInfo {
195+ PatronId : "patron-1" ,
196+ },
197+ }
198+
199+ isbnRequest := & iso18626.Request {
200+ BibliographicInfo : iso18626.BibliographicInfo {
201+ BibliographicItemId : []iso18626.BibliographicItemId {
202+ {
203+ BibliographicItemIdentifier : "978-1234" ,
204+ BibliographicItemIdentifierCode : iso18626.TypeSchemeValuePair {Text : "ISBN" },
205+ },
206+ },
207+ },
208+ ServiceInfo : & iso18626.ServiceInfo {ServiceType : iso18626 .TypeServiceTypeCopy },
209+ PatronInfo : & iso18626.PatronInfo {PatronId : "patron-2" },
210+ }
211+
212+ tests := []struct {
213+ name string
214+ request * iso18626.Request
215+ peer ill_db.Peer
216+ duplicate bool
217+ repoErr error
218+ wantErr error
219+ wantRepoCalled bool
220+ wantPatronId string
221+ wantIdentifier string
222+ wantIsbn string
223+ wantIssn string
224+ wantTitle string
225+ wantSvcType string
226+ }{
227+ {
228+ name : "no DuplicateCheckWindowHours configured - skips check" ,
229+ request : baseRequest ,
230+ peer : ill_db.Peer {},
231+ wantErr : nil ,
232+ wantRepoCalled : false ,
233+ },
234+ {
235+ name : "window is zero - skips check" ,
236+ request : baseRequest ,
237+ peer : ill_db.Peer {CustomData : directory.Entry {DuplicateCheckWindowHours : & window0 }},
238+ wantErr : nil ,
239+ wantRepoCalled : false ,
240+ },
241+ {
242+ name : "window is negative - skips check" ,
243+ request : baseRequest ,
244+ peer : ill_db.Peer {CustomData : directory.Entry {DuplicateCheckWindowHours : & windowNeg }},
245+ wantErr : nil ,
246+ wantRepoCalled : false ,
247+ },
248+ {
249+ name : "db error - fails open, allows request through" ,
250+ request : baseRequest ,
251+ peer : ill_db.Peer {CustomData : directory.Entry {DuplicateCheckWindowHours : & window1 }},
252+ repoErr : errors .New ("db connection error" ),
253+ wantErr : nil ,
254+ wantRepoCalled : true ,
255+ wantPatronId : "patron-1" ,
256+ wantIdentifier : "rec-1" ,
257+ wantTitle : "Test Title" ,
258+ wantSvcType : "Loan" ,
259+ },
260+ {
261+ name : "no duplicate found (ErrNoRows) - not a duplicate" ,
262+ request : baseRequest ,
263+ peer : ill_db.Peer {CustomData : directory.Entry {DuplicateCheckWindowHours : & window1 }},
264+ repoErr : pgx .ErrNoRows ,
265+ wantErr : nil ,
266+ wantRepoCalled : true ,
267+ wantPatronId : "patron-1" ,
268+ wantIdentifier : "rec-1" ,
269+ wantTitle : "Test Title" ,
270+ wantSvcType : "Loan" ,
271+ },
272+ {
273+ name : "duplicate found - returns ErrDuplicateRequest" ,
274+ request : baseRequest ,
275+ peer : ill_db.Peer {CustomData : directory.Entry {DuplicateCheckWindowHours : & window1 }},
276+ duplicate : true ,
277+ wantErr : ErrDuplicateRequest ,
278+ wantRepoCalled : true ,
279+ wantPatronId : "patron-1" ,
280+ wantIdentifier : "rec-1" ,
281+ wantTitle : "Test Title" ,
282+ wantSvcType : "Loan" ,
283+ },
284+ {
285+ name : "nil PatronInfo - skips duplicate check (can't verify same patron)" ,
286+ request : & iso18626.Request {
287+ BibliographicInfo : iso18626.BibliographicInfo {SupplierUniqueRecordId : "rec-1" },
288+ ServiceInfo : & iso18626.ServiceInfo {ServiceType : iso18626 .TypeServiceTypeLoan },
289+ },
290+ peer : ill_db.Peer {CustomData : directory.Entry {DuplicateCheckWindowHours : & window1 }},
291+ repoErr : pgx .ErrNoRows ,
292+ wantErr : nil ,
293+ wantRepoCalled : false ,
294+ },
295+ {
296+ name : "no service level - skips duplicate check (can't verify same patron)" ,
297+ request : & iso18626.Request {
298+ BibliographicInfo : iso18626.BibliographicInfo {
299+ SupplierUniqueRecordId : "rec-1" ,
300+ Title : "Test Title" ,
301+ },
302+ PatronInfo : & iso18626.PatronInfo {
303+ PatronId : "patron-1" ,
304+ },
305+ },
306+ peer : ill_db.Peer {CustomData : directory.Entry {DuplicateCheckWindowHours : & window1 }},
307+ repoErr : pgx .ErrNoRows ,
308+ wantErr : nil ,
309+ wantRepoCalled : false ,
310+ },
311+ {
312+ name : "isbn passed as parameter to DB query" ,
313+ request : isbnRequest ,
314+ peer : ill_db.Peer {CustomData : directory.Entry {DuplicateCheckWindowHours : & window1 }},
315+ repoErr : pgx .ErrNoRows ,
316+ wantErr : nil ,
317+ wantRepoCalled : true ,
318+ wantPatronId : "patron-2" ,
319+ wantIsbn : "978-1234" ,
320+ wantSvcType : "Copy" ,
321+ },
322+ {
323+ name : "duplicate found via isbn - returns ErrDuplicateRequest" ,
324+ request : isbnRequest ,
325+ peer : ill_db.Peer {CustomData : directory.Entry {DuplicateCheckWindowHours : & window1 }},
326+ duplicate : true ,
327+ wantErr : ErrDuplicateRequest ,
328+ wantRepoCalled : true ,
329+ wantPatronId : "patron-2" ,
330+ wantIsbn : "978-1234" ,
331+ wantSvcType : "Copy" ,
332+ },
333+ {
334+ name : "no duplicate - returns nil" ,
335+ request : isbnRequest ,
336+ peer : ill_db.Peer {CustomData : directory.Entry {DuplicateCheckWindowHours : & window1 }},
337+ duplicate : false ,
338+ wantErr : nil ,
339+ wantRepoCalled : true ,
340+ wantPatronId : "patron-2" ,
341+ wantIsbn : "978-1234" ,
342+ wantSvcType : "Copy" ,
343+ },
344+ }
345+
346+ for _ , tt := range tests {
347+ t .Run (tt .name , func (t * testing.T ) {
348+ appCtx := common .CreateExtCtxWithArgs (context .Background (), nil )
349+ mockRepo := & mockDuplicateCheckRepo {
350+ duplicate : tt .duplicate ,
351+ err : tt .repoErr ,
352+ }
353+ err := checkDuplicateRequest (appCtx , tt .request , mockRepo , "ISIL:REQ1" , tt .peer )
354+ assert .Equal (t , tt .wantErr , err )
355+ assert .Equal (t , tt .wantRepoCalled , mockRepo .called )
356+ if tt .wantRepoCalled {
357+ assert .True (t , tt .wantPatronId == "" || strings .Contains (mockRepo .cql , tt .wantPatronId ))
358+ assert .True (t , tt .wantIdentifier == "" || strings .Contains (mockRepo .cql , tt .wantIdentifier ))
359+ assert .True (t , tt .wantIsbn == "" || strings .Contains (mockRepo .cql , tt .wantIsbn ))
360+ assert .True (t , tt .wantIssn == "" || strings .Contains (mockRepo .cql , tt .wantIssn ))
361+ assert .True (t , tt .wantTitle == "" || strings .Contains (mockRepo .cql , tt .wantTitle ))
362+ assert .True (t , tt .wantSvcType == "" || strings .Contains (mockRepo .cql , tt .wantSvcType ))
363+ }
364+ })
365+ }
366+ }
0 commit comments