Skip to content

Commit 698ff28

Browse files
committed
chore: refactor classes into factory functions
Fix casing and types from pr feedback
1 parent d562f66 commit 698ff28

File tree

5 files changed

+244
-252
lines changed

5 files changed

+244
-252
lines changed

src/services/BreachDataService.test.ts

Lines changed: 55 additions & 71 deletions
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,13 @@
44

55
import MockRedis from "ioredis-mock";
66
import { type Redis } from "ioredis";
7-
import { BreachDataService } from "./BreachDataService";
7+
import { createBreachDataService } from "./BreachDataService";
88
import { REDIS_ALL_BREACHES_KEY } from "../db/redis/client";
99
import { seeds } from "../test/db";
10-
import { IBreachSyncService } from "./BreachSyncService";
1110
import { 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

Comments
 (0)