Skip to content
Draft
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
4 changes: 4 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -30,3 +30,7 @@ NEXT_PUBLIC_POSTHOG_KEY=

# Dev (optional)
PORTLESS_URL=

# Perf harness test user (see docs/perf/PLAN.md)
PERF_TEST_EMAIL=
PERF_TEST_PASSWORD=
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -91,3 +91,7 @@ AGENTS.md
.waypoint/docs/code-guide.md
.env
.env.local

# perf harness output & auth state
perf-results/
apps/web/e2e/perf/.auth/
3 changes: 3 additions & 0 deletions apps/web/__tests__/api/contributions.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ describe("GET /api/users/[username]/contributions", () => {

const client: Record<string, any> = {
auth: {
getClaims: vi.fn().mockResolvedValue({ data: null, error: null }),
getUser: vi.fn().mockResolvedValue({
data: { user: null },
error: null,
Expand Down Expand Up @@ -113,6 +114,7 @@ describe("GET /api/users/[username]/contributions", () => {
it("returns 404 for non-existent user", async () => {
const client: Record<string, any> = {
auth: {
getClaims: vi.fn().mockResolvedValue({ data: null, error: null }),
getUser: vi.fn().mockResolvedValue({
data: { user: null },
error: null,
Expand Down Expand Up @@ -151,6 +153,7 @@ describe("GET /api/users/[username]/contributions", () => {
it("returns empty data when user has no usage", async () => {
const client: Record<string, any> = {
auth: {
getClaims: vi.fn().mockResolvedValue({ data: null, error: null }),
getUser: vi.fn().mockResolvedValue({
data: { user: null },
error: null,
Expand Down
47 changes: 44 additions & 3 deletions apps/web/__tests__/api/leaderboard.test.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,21 @@
import { describe, it, expect, vi, beforeEach } from "vitest";

const leaderboardMocks = vi.hoisted(() => ({
loadEntries: vi.fn(),
loadRank: vi.fn(),
getAuthIdentity: vi.fn(),
}));

vi.mock("@/lib/supabase/auth", () => ({
getAuthIdentity: leaderboardMocks.getAuthIdentity,
}));

vi.mock("@/lib/data/leaderboard", () => ({
LEADERBOARD_PERIODS: ["day", "week", "month", "all_time"],
loadLeaderboardEntries: leaderboardMocks.loadEntries,
loadLeaderboardRank: leaderboardMocks.loadRank,
}));

vi.mock("@/lib/supabase/server", () => ({
createClient: vi.fn(),
}));
Expand Down Expand Up @@ -106,6 +122,9 @@ function mockSupabase(opts: {

beforeEach(() => {
vi.clearAllMocks();
leaderboardMocks.loadEntries.mockResolvedValue([]);
leaderboardMocks.loadRank.mockResolvedValue(null);
leaderboardMocks.getAuthIdentity.mockResolvedValue(null);
});

describe("GET /api/leaderboard", () => {
Expand All @@ -114,6 +133,7 @@ describe("GET /api/leaderboard", () => {
{ user_id: "u1", total_cost: 100, username: "alice" },
{ user_id: "u2", total_cost: 50, username: "bob" },
];
leaderboardMocks.loadEntries.mockResolvedValue(entries);

// Use a simple mock where the main query returns entries
const client: Record<string, any> = {
Expand Down Expand Up @@ -175,8 +195,12 @@ describe("GET /api/leaderboard", () => {

await GET(makeRequest());

// The from() call should use leaderboard_weekly view
expect(client.from).toHaveBeenCalledWith("leaderboard_weekly");
expect(leaderboardMocks.loadEntries).toHaveBeenCalledWith({
period: "week",
region: null,
cursor: null,
limit: 50,
});
});

it("filters by period", async () => {
Expand All @@ -200,7 +224,12 @@ describe("GET /api/leaderboard", () => {
(getServiceClient as any).mockReturnValue(client);

await GET(makeRequest({ period: "month" }));
expect(client.from).toHaveBeenCalledWith("leaderboard_monthly");
expect(leaderboardMocks.loadEntries).toHaveBeenCalledWith({
period: "month",
region: null,
cursor: null,
limit: 50,
});
});

it("rejects invalid period", async () => {
Expand Down Expand Up @@ -256,13 +285,24 @@ describe("GET /api/leaderboard", () => {
const json = await res.json();

expect(res.status).toBe(200);
expect(leaderboardMocks.loadEntries).toHaveBeenCalledWith({
period: "week",
region: "north_america",
cursor: null,
limit: 50,
});
});

it("includes user_rank for current user in page", async () => {
const entries = [
{ user_id: "u1", total_cost: 100 },
{ user_id: "current-user", total_cost: 50 },
];
leaderboardMocks.loadEntries.mockResolvedValue(entries);
leaderboardMocks.getAuthIdentity.mockResolvedValue({
id: "current-user",
email: null,
});

const client: Record<string, any> = {
auth: {
Expand Down Expand Up @@ -298,6 +338,7 @@ describe("GET /api/leaderboard", () => {
user_id: `u${i}`,
total_cost: 100 - i,
}));
leaderboardMocks.loadEntries.mockResolvedValue(entries);

const client: Record<string, any> = {
auth: {
Expand Down
20 changes: 14 additions & 6 deletions apps/web/__tests__/api/messages.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -510,8 +510,12 @@ describe("message attachments", () => {
}),
storage: {
from: vi.fn().mockReturnValue({
createSignedUrl: vi.fn().mockResolvedValue({
data: { signedUrl: "https://example.supabase.co/storage/v1/object/sign/dm-attachments/user-2/file.png?token=abc" },
createSignedUrls: vi.fn().mockResolvedValue({
data: [{
path: "user-2/file.png",
signedUrl: "https://example.supabase.co/storage/v1/object/sign/dm-attachments/user-2/file.png?token=abc",
error: null,
}],
error: null,
}),
}),
Expand Down Expand Up @@ -539,8 +543,12 @@ describe("message attachments", () => {
}),
},
};
const createSignedUrl = vi.fn().mockResolvedValue({
data: { signedUrl: "https://example.supabase.co/storage/v1/object/sign/dm-attachments/user-2/file.png?token=abc" },
const createSignedUrls = vi.fn().mockResolvedValue({
data: [{
path: "user-2/file.png",
signedUrl: "https://example.supabase.co/storage/v1/object/sign/dm-attachments/user-2/file.png?token=abc",
error: null,
}],
error: null,
});
const serviceClient: Record<string, any> = {
Expand Down Expand Up @@ -619,7 +627,7 @@ describe("message attachments", () => {
}),
storage: {
from: vi.fn().mockReturnValue({
createSignedUrl,
createSignedUrls,
}),
},
};
Expand All @@ -634,7 +642,7 @@ describe("message attachments", () => {

expect(response.status).toBe(200);
expect(json.messages[0].attachments).toEqual([]);
expect(createSignedUrl).not.toHaveBeenCalled();
expect(createSignedUrls).not.toHaveBeenCalled();
});
});

Expand Down
5 changes: 5 additions & 0 deletions apps/web/__tests__/api/profile.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,10 @@ describe("GET /api/users/[username]", () => {

const client: Record<string, any> = {
auth: {
getClaims: vi.fn().mockResolvedValue({
data: { claims: { sub: "viewer-1" } },
error: null,
}),
getUser: vi.fn().mockResolvedValue({
data: { user: { id: "viewer-1" } },
error: null,
Expand Down Expand Up @@ -192,6 +196,7 @@ describe("GET /api/users/[username]", () => {
it("returns 404 for non-existent username", async () => {
const client: Record<string, any> = {
auth: {
getClaims: vi.fn().mockResolvedValue({ data: null, error: null }),
getUser: vi.fn().mockResolvedValue({
data: { user: null },
error: null,
Expand Down
110 changes: 110 additions & 0 deletions apps/web/__tests__/api/right-sidebar.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
import { readFileSync } from "node:fs";
import { join } from "node:path";
import { beforeEach, describe, expect, it, vi } from "vitest";

const mocks = vi.hoisted(() => ({
createClient: vi.fn(),
loadUsageTotals: vi.fn(),
loadPublicData: vi.fn(),
getAuthIdentity: vi.fn(),
}));

vi.mock("@/lib/supabase/server", () => ({
createClient: mocks.createClient,
}));

vi.mock("@/lib/supabase/auth", () => ({
getAuthIdentity: mocks.getAuthIdentity,
}));

vi.mock("@/lib/data/usage-totals", () => ({
loadUsageTotals: mocks.loadUsageTotals,
}));

vi.mock("@/lib/data/right-sidebar", () => ({
loadRightSidebarPublicData: mocks.loadPublicData,
}));

import { GET } from "@/app/api/app/right-sidebar/route";

function clientFor(userId: string, followingIds: string[]) {
return {
auth: {
getUser: vi.fn().mockResolvedValue({
data: { user: { id: userId } },
error: null,
}),
},
from: vi.fn(() => ({
select: vi.fn(() => ({
eq: vi.fn().mockResolvedValue({
data: followingIds.map((following_id) => ({ following_id })),
error: null,
}),
})),
})),
};
}

describe("GET /api/app/right-sidebar", () => {
beforeEach(() => {
vi.clearAllMocks();
mocks.loadUsageTotals.mockImplementation(async (_client: unknown, userId: string) => ({
totalTokens: userId === "viewer-1" ? 100 : 200,
totalCost: 0,
}));
mocks.loadPublicData.mockResolvedValue({
activeUsers: [
{ id: "a", username: "a", avatar_url: null, bio: null },
{ id: "b", username: "b", avatar_url: null, bio: null },
{ id: "c", username: "c", avatar_url: null, bio: null },
],
newSignups: [],
pinnedUsers: [],
topUsers: [],
});
});

it("keeps follows and usage totals request-scoped across users", async () => {
mocks.createClient
.mockResolvedValueOnce(clientFor("viewer-1", ["a"]))
.mockResolvedValueOnce(clientFor("viewer-2", ["b"]));
mocks.getAuthIdentity
.mockResolvedValueOnce({ id: "viewer-1", email: null })
.mockResolvedValueOnce({ id: "viewer-2", email: null });

const first = await GET();
const second = await GET();
const firstBody = await first.json();
const secondBody = await second.json();

expect(firstBody.suggested.map((user: { id: string }) => user.id)).toEqual([
"b",
"c",
]);
expect(secondBody.suggested.map((user: { id: string }) => user.id)).toEqual([
"a",
"c",
]);
expect(firstBody.totalOutputTokens).toBe(100);
expect(secondBody.totalOutputTokens).toBe(200);
expect(mocks.loadPublicData).toHaveBeenCalledTimes(2);
expect(mocks.loadUsageTotals.mock.calls.map((call) => call[1])).toEqual([
"viewer-1",
"viewer-2",
]);
});

it("only admits public pinned users into the shared candidate cache", () => {
const source = readFileSync(
join(process.cwd(), "lib/data/right-sidebar.ts"),
"utf8"
);

expect(source).toMatch(
/from\("users"\)[\s\S]*?\.eq\("is_public", true\)[\s\S]*?\.eq\("is_pinned_suggestion", true\)/
);
expect(source).not.toContain("follower_id");
expect(source).not.toContain("loadUsageTotals");
});
});
3 changes: 2 additions & 1 deletion apps/web/__tests__/api/usage-submit.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,8 @@ vi.mock("@supabase/supabase-js", () => ({
createClient: vi.fn(),
}));

import { POST, aggregateDeviceRows } from "@/app/api/usage/submit/route";
import { POST } from "@/app/api/usage/submit/route";
import { aggregateDeviceRows } from "@/lib/usage/aggregate-device-rows";
import { captureServerActivationEvent } from "@/lib/analytics/server";
import { createClient } from "@/lib/supabase/server";
import { verifyCliToken, verifyCliTokenWithRefresh } from "@/lib/api/cli-auth";
Expand Down
Loading
Loading