@wopr-network/platform-core 1.49.3 → 1.49.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
|
@@ -218,6 +218,59 @@ describe("key-server routes", () => {
|
|
|
218
218
|
expect(callCount).toBe(2);
|
|
219
219
|
expect(body.index).toBe(1); // skipped index 0
|
|
220
220
|
});
|
|
221
|
+
it("POST /address retries on Drizzle-wrapped collision error (cause.code)", async () => {
|
|
222
|
+
// Drizzle wraps PG errors: err.code is undefined, err.cause.code has "23505"
|
|
223
|
+
const pgError = Object.assign(new Error("unique_violation"), { code: "23505" });
|
|
224
|
+
const drizzleError = Object.assign(new Error("DrizzleQueryError"), { cause: pgError });
|
|
225
|
+
let callCount = 0;
|
|
226
|
+
const mockMethod = {
|
|
227
|
+
id: "eth",
|
|
228
|
+
type: "native",
|
|
229
|
+
token: "ETH",
|
|
230
|
+
chain: "base",
|
|
231
|
+
xpub: "xpub6CUGRUonZSQ4TWtTMmzXdrXDtypWKiKrhko4egpiMZbpiaQL2jkwSB1icqYh2cfDfVxdx4df189oLKnC5fSwqPfgyP3hooxujYzAu3fDVmz",
|
|
232
|
+
nextIndex: 0,
|
|
233
|
+
decimals: 18,
|
|
234
|
+
addressType: "evm",
|
|
235
|
+
confirmations: 1,
|
|
236
|
+
};
|
|
237
|
+
const db = {
|
|
238
|
+
update: vi.fn().mockImplementation(() => ({
|
|
239
|
+
set: vi.fn().mockReturnValue({
|
|
240
|
+
where: vi.fn().mockReturnValue({
|
|
241
|
+
returning: vi.fn().mockImplementation(() => {
|
|
242
|
+
callCount++;
|
|
243
|
+
return Promise.resolve([{ ...mockMethod, nextIndex: callCount }]);
|
|
244
|
+
}),
|
|
245
|
+
}),
|
|
246
|
+
}),
|
|
247
|
+
})),
|
|
248
|
+
insert: vi.fn().mockImplementation(() => ({
|
|
249
|
+
values: vi.fn().mockImplementation(() => {
|
|
250
|
+
if (callCount <= 1)
|
|
251
|
+
throw drizzleError;
|
|
252
|
+
return { onConflictDoNothing: vi.fn().mockResolvedValue({ rowCount: 1 }) };
|
|
253
|
+
}),
|
|
254
|
+
})),
|
|
255
|
+
select: vi.fn().mockReturnValue({
|
|
256
|
+
from: vi.fn().mockReturnValue({ where: vi.fn().mockResolvedValue([]) }),
|
|
257
|
+
}),
|
|
258
|
+
transaction: vi.fn().mockImplementation(async (fn) => fn(db)),
|
|
259
|
+
};
|
|
260
|
+
const deps = mockDeps();
|
|
261
|
+
deps.db = db;
|
|
262
|
+
const app = createKeyServerApp(deps);
|
|
263
|
+
const res = await app.request("/address", {
|
|
264
|
+
method: "POST",
|
|
265
|
+
headers: { "Content-Type": "application/json" },
|
|
266
|
+
body: JSON.stringify({ chain: "eth" }),
|
|
267
|
+
});
|
|
268
|
+
expect(res.status).toBe(201);
|
|
269
|
+
const body = await res.json();
|
|
270
|
+
expect(body.address).toMatch(/^0x/);
|
|
271
|
+
expect(callCount).toBe(2);
|
|
272
|
+
expect(body.index).toBe(1);
|
|
273
|
+
});
|
|
221
274
|
it("POST /charges creates a charge", async () => {
|
|
222
275
|
const app = createKeyServerApp(mockDeps());
|
|
223
276
|
const res = await app.request("/charges", {
|
|
@@ -71,7 +71,8 @@ async function deriveNextAddress(db, chainId, tenantId) {
|
|
|
71
71
|
return { address, index, chain: method.chain, token: method.token };
|
|
72
72
|
}
|
|
73
73
|
catch (err) {
|
|
74
|
-
|
|
74
|
+
// Drizzle wraps PG errors — check both top-level and cause for the constraint violation code
|
|
75
|
+
const code = err.code ?? err.cause?.code;
|
|
75
76
|
if (code === "23505" && attempt < maxRetries)
|
|
76
77
|
continue; // collision — index already advanced, retry
|
|
77
78
|
throw err;
|
package/package.json
CHANGED
|
@@ -239,6 +239,64 @@ describe("key-server routes", () => {
|
|
|
239
239
|
expect(body.index).toBe(1); // skipped index 0
|
|
240
240
|
});
|
|
241
241
|
|
|
242
|
+
it("POST /address retries on Drizzle-wrapped collision error (cause.code)", async () => {
|
|
243
|
+
// Drizzle wraps PG errors: err.code is undefined, err.cause.code has "23505"
|
|
244
|
+
const pgError = Object.assign(new Error("unique_violation"), { code: "23505" });
|
|
245
|
+
const drizzleError = Object.assign(new Error("DrizzleQueryError"), { cause: pgError });
|
|
246
|
+
let callCount = 0;
|
|
247
|
+
|
|
248
|
+
const mockMethod = {
|
|
249
|
+
id: "eth",
|
|
250
|
+
type: "native",
|
|
251
|
+
token: "ETH",
|
|
252
|
+
chain: "base",
|
|
253
|
+
xpub: "xpub6CUGRUonZSQ4TWtTMmzXdrXDtypWKiKrhko4egpiMZbpiaQL2jkwSB1icqYh2cfDfVxdx4df189oLKnC5fSwqPfgyP3hooxujYzAu3fDVmz",
|
|
254
|
+
nextIndex: 0,
|
|
255
|
+
decimals: 18,
|
|
256
|
+
addressType: "evm",
|
|
257
|
+
confirmations: 1,
|
|
258
|
+
};
|
|
259
|
+
|
|
260
|
+
const db = {
|
|
261
|
+
update: vi.fn().mockImplementation(() => ({
|
|
262
|
+
set: vi.fn().mockReturnValue({
|
|
263
|
+
where: vi.fn().mockReturnValue({
|
|
264
|
+
returning: vi.fn().mockImplementation(() => {
|
|
265
|
+
callCount++;
|
|
266
|
+
return Promise.resolve([{ ...mockMethod, nextIndex: callCount }]);
|
|
267
|
+
}),
|
|
268
|
+
}),
|
|
269
|
+
}),
|
|
270
|
+
})),
|
|
271
|
+
insert: vi.fn().mockImplementation(() => ({
|
|
272
|
+
values: vi.fn().mockImplementation(() => {
|
|
273
|
+
if (callCount <= 1) throw drizzleError;
|
|
274
|
+
return { onConflictDoNothing: vi.fn().mockResolvedValue({ rowCount: 1 }) };
|
|
275
|
+
}),
|
|
276
|
+
})),
|
|
277
|
+
select: vi.fn().mockReturnValue({
|
|
278
|
+
from: vi.fn().mockReturnValue({ where: vi.fn().mockResolvedValue([]) }),
|
|
279
|
+
}),
|
|
280
|
+
transaction: vi.fn().mockImplementation(async (fn: (tx: unknown) => unknown) => fn(db)),
|
|
281
|
+
};
|
|
282
|
+
|
|
283
|
+
const deps = mockDeps();
|
|
284
|
+
(deps as unknown as { db: unknown }).db = db;
|
|
285
|
+
const app = createKeyServerApp(deps);
|
|
286
|
+
|
|
287
|
+
const res = await app.request("/address", {
|
|
288
|
+
method: "POST",
|
|
289
|
+
headers: { "Content-Type": "application/json" },
|
|
290
|
+
body: JSON.stringify({ chain: "eth" }),
|
|
291
|
+
});
|
|
292
|
+
|
|
293
|
+
expect(res.status).toBe(201);
|
|
294
|
+
const body = await res.json();
|
|
295
|
+
expect(body.address).toMatch(/^0x/);
|
|
296
|
+
expect(callCount).toBe(2);
|
|
297
|
+
expect(body.index).toBe(1);
|
|
298
|
+
});
|
|
299
|
+
|
|
242
300
|
it("POST /charges creates a charge", async () => {
|
|
243
301
|
const app = createKeyServerApp(mockDeps());
|
|
244
302
|
const res = await app.request("/charges", {
|
|
@@ -98,7 +98,8 @@ async function deriveNextAddress(
|
|
|
98
98
|
});
|
|
99
99
|
return { address, index, chain: method.chain, token: method.token };
|
|
100
100
|
} catch (err: unknown) {
|
|
101
|
-
|
|
101
|
+
// Drizzle wraps PG errors — check both top-level and cause for the constraint violation code
|
|
102
|
+
const code = (err as { code?: string }).code ?? (err as { cause?: { code?: string } }).cause?.code;
|
|
102
103
|
if (code === "23505" && attempt < maxRetries) continue; // collision — index already advanced, retry
|
|
103
104
|
throw err;
|
|
104
105
|
}
|