~ knotless Search/
↑↓ move↵ open⇧↵ all results

grain.social/grain

photography-focused photo sharing platform

grain.social
1 collaborator
0
2
74 lines · 2.7 KB Raw History
// Resolving the `:actor` route segment. A profile is linked by handle, so the
// loader turns a handle into a DID on every visit, and the result has to agree
// with what the page's own DID-keyed queries already know.

import { beforeEach, expect, test, vi } from "vitest";
import { QueryClient } from "@tanstack/svelte-query";

const ALICE = "did:plc:alice";
const FOLLOW_URI = `at://did:plc:me/social.grain.graph.follow/1`;

let following: string | undefined;
let fetches = 0;

// Mocked by resolved path: the $hatk and $lib aliases resolve before vi.mock matches.
vi.mock("../hatk.generated.client.ts", () => ({
  callXrpc: async (_nsid: string, _input: unknown) => {
    fetches++;
    return {
      did: ALICE,
      cid: "cid-alice",
      handle: "alice.test",
      followersCount: following ? 1 : 0,
      viewer: following ? { following } : {},
    };
  },
}));

const { resolveRouteActor } = await import("../app/lib/actor.ts");
const { actorProfileQuery } = await import("../app/lib/queries.ts");

const profileIn = (qc: QueryClient, actor: string) =>
  qc.getQueryData(actorProfileQuery(actor).queryKey) as any;

beforeEach(() => {
  following = undefined;
  fetches = 0;
});

test("a DID passes through without a request", async () => {
  const qc = new QueryClient();
  expect(await resolveRouteActor(qc, ALICE, "did:plc:me")).toBe(ALICE);
  expect(fetches).toBe(0);
});

test("a handle resolves to a DID and seeds the profile under the DID key", async () => {
  const qc = new QueryClient();
  expect(await resolveRouteActor(qc, "alice.test", "did:plc:me")).toBe(ALICE);
  expect(profileIn(qc, ALICE).handle).toBe("alice.test");
});

test("a revisit resolves from the remembered DID, leaving no second copy", async () => {
  const qc = new QueryClient();
  await resolveRouteActor(qc, "alice.test", "did:plc:me");
  expect(profileIn(qc, "alice.test")).toBeUndefined();
  expect(fetches).toBe(1);

  await resolveRouteActor(qc, "alice.test", "did:plc:me");
  expect(fetches).toBe(1);
});

test("revisiting the route keeps a follow the page already learned about", async () => {
  const qc = new QueryClient();
  const did = await resolveRouteActor(qc, "alice.test", "did:plc:me");

  // The viewer follows: the write is indexed before it returns, and the page's
  // own query refetches its DID-keyed profile.
  following = FOLLOW_URI;
  await qc.fetchQuery({ ...actorProfileQuery(did, "did:plc:me"), staleTime: 0 });
  expect(profileIn(qc, did).viewer.following).toBe(FOLLOW_URI);

  // Off to the feed and back, inside the profile query's stale window.
  expect(await resolveRouteActor(qc, "alice.test", "did:plc:me")).toBe(did);
  expect(profileIn(qc, did).viewer.following).toBe(FOLLOW_URI);
});