Skip to content

Commit c01dd6d

Browse files
committed
feat: Update imports to use InMemoryDB and enhance mock data typing
1 parent 3f24583 commit c01dd6d

File tree

6 files changed

+344
-136
lines changed

6 files changed

+344
-136
lines changed

cloudflare-worker/package.json

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,7 @@
99
"dev:env": "cp ../.env .dev.vars && wrangler dev",
1010
"lint": "eslint --fix",
1111
"start": "wrangler dev",
12-
"test": "vitest",
13-
"test:debug": "vitest --inspect-brk --no-file-parallelism",
14-
"test:api": "set -a && source ../.env && set +a && node --experimental-vm-modules ./node_modules/.bin/jest",
12+
"test": "set -a && source ../.env && set +a && node --experimental-vm-modules ./node_modules/.bin/jest",
1513
"cf-typegen": "wrangler types"
1614
},
1715
"devDependencies": {
Lines changed: 308 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,308 @@
1+
/**
2+
* MIT License
3+
*
4+
* Copyright (c) 2025 Ronan LE MEILLAT
5+
*
6+
* Permission is hereby granted, free of charge, to any person obtaining a copy
7+
* of this software and associated documentation files (the "Software"), to deal
8+
* in the Software without restriction, including without limitation the rights
9+
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10+
* copies of the Software, and to permit persons to whom the Software is
11+
* furnished to do so, subject to the following conditions:
12+
*
13+
* The above copyright notice and this permission notice shall be included in all
14+
* copies or substantial portions of the Software.
15+
*
16+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21+
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22+
* SOFTWARE.
23+
*/
24+
25+
import {
26+
Feedback,
27+
IdMapping,
28+
Publication,
29+
Purchase,
30+
Refund,
31+
Tester,
32+
} from "../types/data";
33+
34+
/**
35+
* Interface for database structure
36+
*/
37+
export interface DATABASESCHEMA {
38+
ids: IdMapping[];
39+
testers: Tester[];
40+
purchases: Purchase[];
41+
feedbacks: Feedback[];
42+
publications: Publication[];
43+
refunds: Refund[];
44+
}
45+
46+
/**
47+
* IdMappings repository interface
48+
*/
49+
export interface IdMappingsRepository {
50+
/**
51+
* Check if an ID exists in the database
52+
* @param id The OAuth ID to check
53+
*/
54+
exists(id: string): boolean;
55+
56+
/**
57+
* Check if multiple IDs exist in the database
58+
* @param ids Array of OAuth IDs to check
59+
*/
60+
existsMultiple(ids: string[]): string[];
61+
62+
/**
63+
* Get the tester UUID associated with an ID
64+
* @param id The OAuth ID to look up
65+
*/
66+
getTesterUuid(id: string): string | undefined;
67+
68+
/**
69+
* Add a new ID to tester mapping
70+
* @param id The OAuth ID
71+
* @param testerUuid The associated tester UUID
72+
*/
73+
put(id: string, testerUuid: string): boolean;
74+
75+
/**
76+
* Add multiple ID to tester mappings
77+
* @param ids Array of OAuth IDs
78+
* @param testerUuid The associated tester UUID
79+
*/
80+
putMultiple(ids: string[], testerUuid: string): string[];
81+
82+
/**
83+
* Delete an ID mapping
84+
* @param id The OAuth ID to delete
85+
*/
86+
delete(id: string): boolean;
87+
88+
/**
89+
* Get all ID mappings
90+
*/
91+
getAll(): IdMapping[];
92+
}
93+
94+
/**
95+
* Testers repository interface
96+
*/
97+
export interface TestersRepository {
98+
/**
99+
* Find a tester that matches the provided condition
100+
* @param fn Predicate function to filter testers
101+
*/
102+
find(fn: (tester: Tester) => boolean): Tester | undefined;
103+
104+
/**
105+
* Filter testers based on the provided condition
106+
* @param fn Predicate function to filter testers
107+
*/
108+
filter(fn: (tester: Tester) => boolean): Tester[];
109+
110+
/**
111+
* Add or update a tester in the database
112+
* @param newTester The tester object to add or update
113+
*/
114+
put(newTester: Tester): string[];
115+
116+
/**
117+
* Get all testers from the database
118+
*/
119+
getAll(): Tester[];
120+
121+
/**
122+
* Find a tester by their authentication ID
123+
* @param id Authentication ID to search for
124+
*/
125+
getTesterWithId(id: string): Tester | undefined;
126+
127+
/**
128+
* Find a tester by their UUID
129+
* @param uuid UUID to search for
130+
*/
131+
getTesterWithUuid(uuid: string): Tester | undefined;
132+
133+
/**
134+
* Add IDs to an existing tester
135+
* @param uuid UUID of the tester to update
136+
* @param ids IDs to add to the tester
137+
*/
138+
addIds(uuid: string, ids: string[]): string[] | undefined;
139+
}
140+
141+
/**
142+
* Purchases repository interface
143+
*/
144+
export interface PurchasesRepository {
145+
/**
146+
* Find a purchase that matches the provided condition
147+
* @param fn Predicate function to filter purchases
148+
*/
149+
find(fn: (purchase: Purchase) => boolean): Purchase | undefined;
150+
151+
/**
152+
* Filter purchases based on the provided condition
153+
* @param fn Predicate function to filter purchases
154+
*/
155+
filter(fn: (purchase: Purchase) => boolean): Purchase[];
156+
157+
/**
158+
* Add a new purchase to the database
159+
* @param testerUuid UUID of the tester making the purchase
160+
* @param newPurchase The purchase object to add
161+
*/
162+
put(testerUuid: string, newPurchase: Purchase): string;
163+
164+
/**
165+
* Update an existing purchase in the database
166+
* @param id ID of the purchase to update
167+
* @param updates Fields to update
168+
*/
169+
update(id: string, updates: Partial<Purchase>): boolean;
170+
171+
/**
172+
* Get all purchases from the database
173+
*/
174+
getAll(): Purchase[];
175+
}
176+
177+
/**
178+
* Feedbacks repository interface
179+
*/
180+
export interface FeedbacksRepository {
181+
/**
182+
* Find feedback that matches the provided condition
183+
* @param fn Predicate function to filter feedback
184+
*/
185+
find(fn: (feedback: Feedback) => boolean): Feedback | undefined;
186+
187+
/**
188+
* Filter feedback based on the provided condition
189+
* @param fn Predicate function to filter feedback
190+
*/
191+
filter(fn: (feedback: Feedback) => boolean): Feedback[];
192+
193+
/**
194+
* Add new feedback to the database
195+
* @param testerId ID of the tester providing the feedback
196+
* @param newFeedback The feedback object to add
197+
*/
198+
put(testerId: string, newFeedback: Feedback): string;
199+
200+
/**
201+
* Get all feedback from the database
202+
*/
203+
getAll(): Feedback[];
204+
}
205+
206+
/**
207+
* Publications repository interface
208+
*/
209+
export interface PublicationsRepository {
210+
/**
211+
* Find a publication that matches the provided condition
212+
* @param fn Predicate function to filter publications
213+
*/
214+
find(fn: (publication: Publication) => boolean): Publication | undefined;
215+
216+
/**
217+
* Filter publications based on the provided condition
218+
* @param fn Predicate function to filter publications
219+
*/
220+
filter(fn: (publication: Publication) => boolean): Publication[];
221+
222+
/**
223+
* Add a new publication to the database
224+
* @param testerId ID of the tester creating the publication
225+
* @param newPublication The publication object to add
226+
*/
227+
put(testerId: string, newPublication: Publication): string;
228+
229+
/**
230+
* Get all publications from the database
231+
*/
232+
getAll(): Publication[];
233+
}
234+
235+
/**
236+
* Refunds repository interface
237+
*/
238+
export interface RefundsRepository {
239+
/**
240+
* Find a refund that matches the provided condition
241+
* @param fn Predicate function to filter refunds
242+
*/
243+
find(fn: (refund: Refund) => boolean): Refund | undefined;
244+
245+
/**
246+
* Filter refunds based on the provided condition
247+
* @param fn Predicate function to filter refunds
248+
*/
249+
filter(fn: (refund: Refund) => boolean): Refund[];
250+
251+
/**
252+
* Add a new refund to the database and mark the associated purchase as refunded
253+
* @param testerId ID of the tester receiving the refund
254+
* @param newRefund The refund object to add
255+
*/
256+
put(testerId: string, newRefund: Refund): string;
257+
258+
/**
259+
* Get all refunds from the database
260+
*/
261+
getAll(): Refund[];
262+
}
263+
264+
/**
265+
* Abstract database class that defines the interface for all database implementations
266+
*/
267+
export abstract class FeedbackFlowDB {
268+
/**
269+
* ID mappings operations
270+
*/
271+
abstract idMappings: IdMappingsRepository;
272+
273+
/**
274+
* Tester-related database operations
275+
*/
276+
abstract testers: TestersRepository;
277+
278+
/**
279+
* Purchase-related database operations
280+
*/
281+
abstract purchases: PurchasesRepository;
282+
283+
/**
284+
* Feedback-related database operations
285+
*/
286+
abstract feedbacks: FeedbacksRepository;
287+
288+
/**
289+
* Publication-related database operations
290+
*/
291+
abstract publications: PublicationsRepository;
292+
293+
/**
294+
* Refund-related database operations
295+
*/
296+
abstract refunds: RefundsRepository;
297+
298+
/**
299+
* Reset the database with new data (optional for implementations)
300+
* @param newData Data to reset the database with
301+
*/
302+
abstract reset?(newData: DATABASESCHEMA): void;
303+
304+
/**
305+
* Get a copy of the raw database data (optional for implementations)
306+
*/
307+
abstract getRawData?(): DATABASESCHEMA;
308+
}

cloudflare-worker/src/routes/in-memory-db.ts renamed to cloudflare-worker/src/db/in-memory-db.ts

Lines changed: 9 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -26,37 +26,23 @@
2626

2727
import { v4 as uuidv4 } from "uuid";
2828

29-
import {
30-
Feedback,
31-
IdMapping,
32-
Publication,
33-
Purchase,
34-
Refund,
35-
Tester,
36-
} from "../types/data";
37-
38-
export interface DB {
39-
ids: IdMapping[];
40-
testers: Tester[];
41-
purchases: Purchase[];
42-
feedbacks: Feedback[];
43-
publications: Publication[];
44-
refunds: Refund[];
45-
}
29+
import { Feedback, Publication, Purchase, Refund, Tester } from "../types/data";
30+
31+
import { DATABASESCHEMA, FeedbackFlowDB } from "./db-type";
4632

4733
/**
4834
* In-memory database class for testing purposes
4935
* Provides CRUD-like operations for all data types
5036
*/
51-
export class InMemoryDB {
52-
private data: DB;
37+
export class InMemoryDB implements FeedbackFlowDB {
38+
private data: DATABASESCHEMA;
5339

5440
/**
5541
* Create a new instance of the in-memory database
5642
* @param initialData Initial data to populate the database with
5743
*/
5844
constructor(
59-
initialData: DB = {
45+
initialData: DATABASESCHEMA = {
6046
ids: [],
6147
testers: [],
6248
purchases: [],
@@ -66,22 +52,23 @@ export class InMemoryDB {
6652
},
6753
) {
6854
// Clone the initial data to avoid modifications to the original object
55+
// by converting it to JSON and back
6956
this.data = JSON.parse(JSON.stringify(initialData));
7057
}
7158

7259
/**
7360
* Reset the database with new data
7461
* @param newData Data to reset the database with
7562
*/
76-
reset(newData: DB) {
63+
reset(newData: DATABASESCHEMA) {
7764
this.data = JSON.parse(JSON.stringify(newData));
7865
}
7966

8067
/**
8168
* Get a copy of the raw database data
8269
* @returns A deep copy of the current database state
8370
*/
84-
getRawData(): DB {
71+
getRawData(): DATABASESCHEMA {
8572
return JSON.parse(JSON.stringify(this.data));
8673
}
8774

0 commit comments

Comments
 (0)