Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 12 additions & 4 deletions src/components/pm-venues-leaderboard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -131,18 +131,25 @@ export function PmVenuesLeaderboard({ rows }: { rows: PmVenueRow[] }) {
</tr>
</thead>
<tbody>
{filtered.map((r, i) => (
{filtered.map((r, i) => {
const href = r.benched === false && r.externalUrl
? r.externalUrl
: `/products/${r.slug}`;
const isExternal = r.benched === false;
return (
<tr
key={r.slug}
onClick={() => router.push(`/products/${r.slug}`)}
onClick={() => isExternal ? window.open(href, "_blank") : router.push(href)}
className="border-t border-ink/5 hover:bg-paper-soft/40 transition-colors cursor-pointer"
>
<Td muted mono>
{i + 1}
</Td>
<Td>
<Link
href={`/products/${r.slug}`}
href={href}
target={isExternal ? "_blank" : undefined}
rel={isExternal ? "noopener noreferrer" : undefined}
className="flex items-center gap-2 min-w-0 group"
onClick={(e) => e.stopPropagation()}
>
Expand Down Expand Up @@ -182,7 +189,8 @@ export function PmVenuesLeaderboard({ rows }: { rows: PmVenueRow[] }) {
<Td mono>{fmtMs(r.p50ApiLatencyMs)}</Td>
<Td mono>{fmtCount(r.marketsAbove1m)}</Td>
</tr>
))}
);
})}
{filtered.length === 0 && (
<tr>
<td
Expand Down
158 changes: 158 additions & 0 deletions src/lib/compare-compute.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,158 @@
import { describe, expect, test } from "bun:test";
import { latestIso, fmtTs, decideWinner, parseAdHocSlug, canonicalisationTarget } from "./compare-compute";

describe("latestIso", () => {
test("returns the most recent ISO string from a list", () => {
const items = [
{ ts: "2026-01-01T00:00:00.000Z" },
{ ts: "2026-06-15T12:00:00.000Z" },
{ ts: "2026-03-10T00:00:00.000Z" },
];
expect(latestIso(items, (x) => x.ts)).toBe("2026-06-15T12:00:00.000Z");
});

test("returns null for empty list", () => {
expect(latestIso([], (x: { ts: string }) => x.ts)).toBeNull();
});

test("skips null/undefined picks", () => {
const items = [
{ ts: null as string | null },
{ ts: "2026-05-01T00:00:00.000Z" },
{ ts: undefined as string | undefined },
];
expect(latestIso(items, (x) => x.ts)).toBe("2026-05-01T00:00:00.000Z");
});

test("returns null when all picks are null", () => {
const items = [{ ts: null }, { ts: null }];
expect(latestIso(items, (x) => x.ts)).toBeNull();
});

test("single item returns that item's timestamp", () => {
expect(
latestIso([{ ts: "2026-07-01T00:00:00.000Z" }], (x) => x.ts),
).toBe("2026-07-01T00:00:00.000Z");
});

test("works with plain string array via identity pick", () => {
const strs = [
"2025-12-31T23:59:59.000Z",
"2026-01-01T00:00:00.000Z",
"2025-06-15T00:00:00.000Z",
];
expect(latestIso(strs, (s) => s)).toBe("2026-01-01T00:00:00.000Z");
});

test("two equal timestamps returns either (both same)", () => {
const ts = "2026-07-15T10:00:00.000Z";
expect(latestIso([{ ts }, { ts }], (x) => x.ts)).toBe(ts);
});
});

describe("fmtTs", () => {
test("formats a valid ISO string as UTC", () => {
const out = fmtTs("2026-07-15T14:30:00.000Z");
expect(out).not.toBeNull();
expect(out).toContain("UTC");
expect(out).toContain("2026");
});

test("returns null for null input", () => {
expect(fmtTs(null)).toBeNull();
});

test("returns null for undefined input", () => {
expect(fmtTs(undefined)).toBeNull();
});

test("returns null for empty string", () => {
expect(fmtTs("")).toBeNull();
});

test("output replaces GMT with UTC", () => {
const out = fmtTs("2026-01-01T00:00:00.000Z");
expect(out).not.toContain("GMT");
expect(out).toContain("UTC");
});
});

describe("decideWinner", () => {
test("tie when p50s are equal", () => {
expect(decideWinner(100, 100, false)).toBe("tie");
expect(decideWinner(100, 100, true)).toBe("tie");
});

test("lower-is-better: lower p50 wins", () => {
expect(decideWinner(50, 100, false)).toBe("a");
expect(decideWinner(100, 50, false)).toBe("b");
});

test("higher-is-better: higher p50 wins", () => {
expect(decideWinner(200, 100, true)).toBe("a");
expect(decideWinner(100, 200, true)).toBe("b");
});

test("zero vs positive: zero loses in lower-is-better", () => {
expect(decideWinner(0, 100, false)).toBe("a");
});

test("zero vs positive: zero loses in higher-is-better", () => {
expect(decideWinner(0, 100, true)).toBe("b");
});
});

describe("parseAdHocSlug", () => {
test("parses a simple a-vs-b slug", () => {
expect(parseAdHocSlug("ethereum-vs-solana")).toEqual({ a: "ethereum", b: "solana" });
});

test("handles hyphenated provider names", () => {
expect(parseAdHocSlug("helius-rpc-vs-alchemy")).toEqual({
a: "helius-rpc",
b: "alchemy",
});
});

test("returns null when delimiter is absent", () => {
expect(parseAdHocSlug("no-delimiter-here")).toBeNull();
});

test("returns null when delimiter is at position 0 (empty a)", () => {
expect(parseAdHocSlug("-vs-something")).toBeNull();
});

test("returns null when b is empty after delimiter", () => {
expect(parseAdHocSlug("something-vs-")).toBeNull();
});

test("returns null when a and b are identical", () => {
expect(parseAdHocSlug("ethereum-vs-ethereum")).toBeNull();
});

test("returns null for empty string", () => {
expect(parseAdHocSlug("")).toBeNull();
});
});

describe("canonicalisationTarget", () => {
test("returns canonical (sorted) slug when out of order", () => {
expect(canonicalisationTarget("solana-vs-ethereum")).toBe("ethereum-vs-solana");
});

test("returns null when slug is already canonical", () => {
expect(canonicalisationTarget("ethereum-vs-solana")).toBeNull();
});

test("returns null for invalid pair slug", () => {
expect(canonicalisationTarget("not-a-pair")).toBeNull();
});

test("returns null for same-provider slug", () => {
expect(canonicalisationTarget("eth-vs-eth")).toBeNull();
});

test("alphabetical ordering determines canonical form", () => {
expect(canonicalisationTarget("zcash-vs-aptos")).toBe("aptos-vs-zcash");
});
});
89 changes: 89 additions & 0 deletions src/lib/downsample.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
import { describe, expect, test } from "bun:test";
import { downsample, MINI_CHART_POINTS } from "./downsample";

describe("downsample", () => {
test("returns input as-is (filtered) when length <= target", () => {
expect(downsample([1, 2, 3], 10)).toEqual([1, 2, 3]);
expect(downsample([1, 2, 3], 3)).toEqual([1, 2, 3]);
});

test("filters nulls out when no downsampling needed", () => {
expect(downsample([1, null, 3, null, 5], 10)).toEqual([1, 3, 5]);
});

test("reduces to target number of points when no nulls", () => {
const input = Array.from({ length: 100 }, (_, i) => i + 1);
const out = downsample(input, 10);
expect(out.length).toBe(10);
});

test("output may be less than target when buckets are all-null", () => {
// 4 values → 2 buckets; second all-null → 1 output point
const out = downsample([10, 20, null, null], 2);
expect(out.length).toBeLessThan(2);
});

test("target=1 returns mean of all values", () => {
const out = downsample([1, 2, 3, 4, 5], 1);
expect(out).toEqual([3]);
});

test("target=0 returns empty array", () => {
expect(downsample([1, 2, 3], 0)).toEqual([]);
});

test("bucket mean: each output point is the mean of its bucket", () => {
// 4 values → 2 buckets of 2: [1,2]=1.5, [3,4]=3.5
const out = downsample([1, 2, 3, 4], 2);
expect(out).toEqual([1.5, 3.5]);
});

test("nulls inside a bucket are excluded from the mean", () => {
// 6 values, target 2 → bucketSize=3
// bucket 0: [10, null, 20] → mean of [10,20] = 15
// bucket 1: [30, null, 60] → mean of [30,60] = 45
const out = downsample([10, null, 20, 30, null, 60], 2);
expect(out[0]).toBe(15);
expect(out[1]).toBe(45);
});

test("bucket with all nulls is omitted from output", () => {
// 4 values → 2 buckets; second bucket all null → only 1 output
const out = downsample([10, 20, null, null], 2);
expect(out).toHaveLength(1);
expect(out[0]).toBe(15);
});

test("empty array returns empty", () => {
expect(downsample([], 10)).toEqual([]);
});

test("all-null array returns empty", () => {
expect(downsample([null, null, null], 10)).toEqual([]);
});

test("single value passthrough", () => {
expect(downsample([42], 5)).toEqual([42]);
});

test("preserves shape: monotone series stays monotone after downsampling", () => {
const input = Array.from({ length: 200 }, (_, i) => i * 2);
const out = downsample(input, 20);
for (let i = 1; i < out.length; i++) {
expect(out[i]).toBeGreaterThanOrEqual(out[i - 1]);
}
});

test("uniform series: all output points equal the same value", () => {
const input = Array.from({ length: 50 }, () => 7);
const out = downsample(input, 10);
expect(out.every((v) => v === 7)).toBe(true);
});
});

describe("MINI_CHART_POINTS constant", () => {
test("is a positive integer", () => {
expect(MINI_CHART_POINTS).toBeGreaterThan(0);
expect(Number.isInteger(MINI_CHART_POINTS)).toBe(true);
});
});
Loading
Loading