44
55import MockRedis from "ioredis-mock" ;
66import { type Redis } from "ioredis" ;
7- import { BreachDataService } from "./BreachDataService" ;
7+ import { createBreachDataService } from "./BreachDataService" ;
88import { REDIS_ALL_BREACHES_KEY } from "../db/redis/client" ;
99import { seeds } from "../test/db" ;
10- import { IBreachSyncService } from "./BreachSyncService" ;
1110import { mockLogger } from "../test/helpers/mockLogger" ;
11+ import { BreachSyncService } from "./BreachSyncService" ;
1212
13- describe ( "BreachService " , ( ) => {
13+ describe ( "BreachDataService factory " , ( ) => {
1414 const redis = new MockRedis ( ) as unknown as Redis ;
1515 const logger = mockLogger ( ) ;
1616
@@ -20,107 +20,85 @@ describe("BreachService", () => {
2020 afterEach ( async ( ) => {
2121 await redis . flushall ( ) ;
2222 } ) ;
23- describe ( "constructor" , ( ) => {
24- it ( "opts overwrites default values" , ( ) => {
25- const mockSync : IBreachSyncService = {
26- syncBreaches : jest . fn ( ) . mockResolvedValue ( undefined ) ,
27- } ;
28- const repo = {
29- getBreaches : jest . fn ( ) . mockResolvedValue ( [ ] ) ,
30- } ;
31- const service = new BreachDataService ( redis , mockSync , repo , logger , {
32- negTtlSec : 199 ,
33- } ) ;
34- expect ( service . opts ) . toEqual ( { negTtlSec : 199 } ) ;
35- } ) ;
36- it ( "fills default opts values if not passed" , ( ) => {
37- const mockSync : IBreachSyncService = {
38- syncBreaches : jest . fn ( ) . mockResolvedValue ( undefined ) ,
39- } ;
40- const repo = {
41- getBreaches : jest . fn ( ) . mockResolvedValue ( [ ] ) ,
42- } ;
43- const service = new BreachDataService ( redis , mockSync , repo , logger ) ;
44- expect ( service . opts ) . toEqual ( { negTtlSec : 300 } ) ;
45- } ) ;
46- it ( "fills default opts values if key is missing" , ( ) => {
47- const mockSync : IBreachSyncService = {
48- syncBreaches : jest . fn ( ) . mockResolvedValue ( undefined ) ,
49- } ;
50- const repo = {
51- getBreaches : jest . fn ( ) . mockResolvedValue ( [ ] ) ,
52- } ;
53- const service = new BreachDataService ( redis , mockSync , repo , logger , { } ) ;
54- expect ( service . opts ) . toEqual ( { negTtlSec : 300 } ) ;
55- } ) ;
56- } ) ;
5723 describe ( "getBreach" , ( ) => {
5824 it ( "caches negatively if breach is missing after sync" , async ( ) => {
59- const mockSync : IBreachSyncService = {
25+ const mockSync : BreachSyncService = {
6026 syncBreaches : jest . fn ( ) . mockResolvedValue ( undefined ) ,
6127 } ;
62- const repo = {
63- getBreaches : jest . fn ( ) . mockResolvedValue ( [ ] ) ,
64- } ;
65- const service = new BreachDataService ( redis , mockSync , repo , logger ) ;
28+ const getBreachesFromDb = jest . fn ( ) . mockResolvedValue ( [ ] ) ;
29+ const service = createBreachDataService ( {
30+ redis,
31+ sync : mockSync ,
32+ getBreachesFromDb,
33+ logger,
34+ } ) ;
6635 const result = await service . getBreach ( "SomeBreach" ) ;
6736 expect ( result ) . toBeUndefined ( ) ;
6837 expect ( await redis . get ( "breach:neg:somebreach" ) ) . toEqual ( "1" ) ;
6938 } ) ;
7039 it ( "short-circuits if negatively cached and does not fetch or sync" , async ( ) => {
71- const mockSync : IBreachSyncService = {
40+ const mockSync : BreachSyncService = {
7241 syncBreaches : jest . fn ( ) . mockResolvedValue ( undefined ) ,
7342 } ;
74- const repo = {
75- getBreaches : jest . fn ( ) . mockResolvedValue ( [ ] ) ,
76- } ;
43+ const getBreachesFromDb = jest . fn ( ) . mockResolvedValue ( [ ] ) ;
7744 await redis . set ( "breach:neg:somebreach" , "1" , "EX" , 60 ) ;
78- const service = new BreachDataService ( redis , mockSync , repo , logger ) ;
45+ const service = createBreachDataService ( {
46+ redis,
47+ sync : mockSync ,
48+ getBreachesFromDb,
49+ logger,
50+ } ) ;
7951 const result = await service . getBreach ( "SomeBreach" ) ;
8052 expect ( result ) . toBeUndefined ( ) ;
8153 expect ( mockSync . syncBreaches ) . not . toHaveBeenCalled ( ) ;
82- expect ( repo . getBreaches ) . not . toHaveBeenCalled ( ) ;
54+ expect ( getBreachesFromDb ) . not . toHaveBeenCalled ( ) ;
8355 } ) ;
8456 it ( "does not query db if cache contains the expected breach" , async ( ) => {
8557 const fakeBreach = seeds . breaches ( ) ;
8658 await redis . set ( REDIS_ALL_BREACHES_KEY , JSON . stringify ( [ fakeBreach ] ) ) ;
87- const mockSync : IBreachSyncService = {
59+ const mockSync : BreachSyncService = {
8860 syncBreaches : jest . fn ( ) . mockResolvedValue ( undefined ) ,
8961 } ;
90- const repo = {
91- getBreaches : jest . fn ( ) . mockResolvedValue ( [ ] ) ,
92- } ;
93- const service = new BreachDataService ( redis , mockSync , repo , logger ) ;
62+ const getBreachesFromDb = jest . fn ( ) . mockResolvedValue ( [ ] ) ;
63+ const service = createBreachDataService ( {
64+ redis,
65+ sync : mockSync ,
66+ getBreachesFromDb,
67+ logger,
68+ } ) ;
9469 const result = await service . getBreach ( fakeBreach . name ) ;
9570 expect ( result ) . not . toBeUndefined ( ) ;
9671 expect ( result ! . Name ) . toEqual ( fakeBreach . name ) ;
9772 expect ( result ! . Domain ) . toEqual ( fakeBreach . domain ) ;
9873 expect ( mockSync . syncBreaches ) . not . toHaveBeenCalled ( ) ;
99- expect ( repo . getBreaches ) . not . toHaveBeenCalled ( ) ;
74+ expect ( getBreachesFromDb ) . not . toHaveBeenCalled ( ) ;
10075 } ) ;
10176 it ( "reads from db and updates cache if cache miss; no sync if key found" , async ( ) => {
10277 const fakeBreach = seeds . breaches ( ) ;
103- const mockSync : IBreachSyncService = {
78+ const mockSync : BreachSyncService = {
10479 syncBreaches : jest . fn ( ) . mockResolvedValue ( undefined ) ,
10580 } ;
106- const repo = {
107- getBreaches : jest . fn ( ) . mockResolvedValue ( [ fakeBreach ] ) ,
108- } ;
109- const service = new BreachDataService ( redis , mockSync , repo , logger ) ;
81+ const getBreachesFromDb = jest . fn ( ) . mockResolvedValue ( [ fakeBreach ] ) ;
82+ const service = createBreachDataService ( {
83+ redis,
84+ sync : mockSync ,
85+ getBreachesFromDb,
86+ logger,
87+ } ) ;
11088 const result = await service . getBreach ( fakeBreach . name ) ;
11189 expect ( result ) . not . toBeUndefined ( ) ;
11290 expect ( result ! . Name ) . toEqual ( fakeBreach . name ) ;
11391 expect ( result ! . Domain ) . toEqual ( fakeBreach . domain ) ;
11492 expect ( mockSync . syncBreaches ) . not . toHaveBeenCalled ( ) ;
115- expect ( repo . getBreaches ) . toHaveBeenCalledTimes ( 1 ) ;
93+ expect ( getBreachesFromDb ) . toHaveBeenCalledTimes ( 1 ) ;
11694 const cached = await redis . get ( REDIS_ALL_BREACHES_KEY ) ;
11795 expect ( cached ) . toStrictEqual ( JSON . stringify ( [ fakeBreach ] ) ) ;
11896 } ) ;
11997 it ( "requests sync if breach not found and reads from cache" , async ( ) => {
12098 const existingBreach = seeds . breaches ( ) ;
12199 const newBreach = seeds . breaches ( ) ;
122100 await redis . set ( REDIS_ALL_BREACHES_KEY , JSON . stringify ( [ existingBreach ] ) ) ;
123- const mockSync : IBreachSyncService = {
101+ const mockSync : BreachSyncService = {
124102 syncBreaches : jest
125103 . fn ( )
126104 . mockImplementationOnce ( async ( ) => {
@@ -131,28 +109,34 @@ describe("BreachService", () => {
131109 } )
132110 . mockResolvedValue ( undefined ) ,
133111 } ;
134- const repo = {
135- getBreaches : jest . fn ( ) . mockResolvedValue ( [ ] ) ,
136- } ;
137- const service = new BreachDataService ( redis , mockSync , repo , logger ) ;
112+ const getBreachesFromDb = jest . fn ( ) . mockResolvedValue ( [ ] ) ;
113+ const service = createBreachDataService ( {
114+ redis,
115+ sync : mockSync ,
116+ getBreachesFromDb,
117+ logger,
118+ } ) ;
138119 const result = await service . getBreach ( newBreach . name ) ;
139120 expect ( result ) . not . toBeUndefined ( ) ;
140121 expect ( result ! . Name ) . toEqual ( newBreach . name ) ;
141122 expect ( result ! . Domain ) . toEqual ( newBreach . domain ) ;
142123 expect ( mockSync . syncBreaches ) . toHaveBeenCalledTimes ( 1 ) ;
143- expect ( repo . getBreaches ) . not . toHaveBeenCalled ( ) ;
124+ expect ( getBreachesFromDb ) . not . toHaveBeenCalled ( ) ;
144125 } ) ;
145126 it ( "deletes key and rereads from db if cache has unparseable data" , async ( ) => {
146127 const fakeBreach = seeds . breaches ( ) ;
147- const mockSync : IBreachSyncService = {
128+ const mockSync : BreachSyncService = {
148129 syncBreaches : jest . fn ( ) . mockResolvedValue ( undefined ) ,
149130 } ;
150- const repo = {
151- getBreaches : jest . fn ( ) . mockResolvedValue ( [ fakeBreach ] ) ,
152- } ;
131+ const getBreachesFromDb = jest . fn ( ) . mockResolvedValue ( [ fakeBreach ] ) ;
153132 // Seed an invalid cache entry
154133 await redis . set ( REDIS_ALL_BREACHES_KEY , "{" ) ;
155- const service = new BreachDataService ( redis , mockSync , repo , logger ) ;
134+ const service = createBreachDataService ( {
135+ redis,
136+ sync : mockSync ,
137+ getBreachesFromDb,
138+ logger,
139+ } ) ;
156140 const result = await service . getBreach ( "WhateverBreach" ) ;
157141 expect ( result ) . toEqual ( undefined ) ;
158142 const cache = await redis . get ( REDIS_ALL_BREACHES_KEY ) ;
0 commit comments