Skip to content
This repository was archived by the owner on Aug 5, 2026. It is now read-only.
Closed
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
20 changes: 19 additions & 1 deletion apps/ensapi/src/omnigraph-api/schema/query.integration.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -286,6 +286,17 @@
}),
).rejects.toThrow();
});

it.each(DEVNET_ENSV1_NAMES.filter((entry) => !entry.reserved))(
"in returns empty for an unreserved ENSv1-only name",
async ({ name }) => {
const result = await request<QueryDomainsResult>(QueryDomains, {
name: { eq: name },
});
const domains = flattenConnection(result.domains);
expect(domains).toHaveLength(0);

Check failure on line 297 in apps/ensapi/src/omnigraph-api/schema/query.integration.test.ts

View workflow job for this annotation

GitHub Actions / Integration Tests

[ensapi] src/omnigraph-api/schema/query.integration.test.ts > Query.domains > name: { eq | in } > in returns empty for an unreserved ENSv1-only name

AssertionError: expected [ { __typename: 'ENSv1Domain', …(5) } ] to have a length of +0 but got 1 - Expected + Received - 0 + 1 ❯ src/omnigraph-api/schema/query.integration.test.ts:297:25
},
);
Comment on lines +290 to +299

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Exclude unreserved fixtures from the earlier wrapped-name case.

This new expectation conflicts with the existing wrapped-name test above on Line 132: DEVNET_ENSV1_NAMES.filter((entry) => entry.wrapped) now includes legacy-v1-wrapped-unreserved.eth, so the suite asserts that the same name is both returned and hidden from domains(where: { name: { eq } }). Tighten the earlier test to entry.wrapped && entry.reserved (or otherwise exclude unreserved entries), then keep this new hidden-case assertion for the unreserved set.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@apps/ensapi/src/omnigraph-api/schema/query.integration.test.ts` around lines
290 - 299, The wrapped-name test currently uses
DEVNET_ENSV1_NAMES.filter((entry) => entry.wrapped) and therefore includes
unreserved entries like legacy-v1-wrapped-unreserved.eth which conflicts with
the new unreserved test; update the earlier test's filter to only include
reserved wrapped entries (e.g., DEVNET_ENSV1_NAMES.filter((entry) =>
entry.wrapped && entry.reserved) or otherwise exclude unreserved entries) so the
wrapped-name test and the new "in returns empty for an unreserved ENSv1-only
name" test no longer assert opposite expectations for the same fixture.

});
});

Expand All @@ -309,7 +320,7 @@
// migration. The walk prefers the ENSv2 Domain, and the ENSv2 registration emits the literal label,
// so each name resolves to an ENSv2 Domain whose canonical is the full literal name — including the
// legacy-unwrapped name whose ENSv1 canonical is an Encoded LabelHash.
it.each(DEVNET_ENSV1_NAMES)(
it.each(DEVNET_ENSV1_NAMES.filter((entry) => !!entry.reserved))(
"resolves ENSv1-only name $name to its reserved ENSv2 Domain via domain(by: name)",
async ({ name, label, canonical }) => {
const id = makeENSv2DomainId(
Expand All @@ -322,6 +333,13 @@
},
);

it.each(DEVNET_ENSV1_NAMES.filter((entry) => !entry.reserved))(
"unreserved ENSv1-only name is hidden in API",
async ({ name }) => {
await expect(request(DomainByName, { name })).resolves.toMatchObject({ domain: null });
},
);

it("returns null for a nonexistent name", async () => {
await expect(
request(DomainByName, { name: "this-name-definitely-does-not-exist-xyz123.eth" }),
Expand Down
1 change: 1 addition & 0 deletions apps/ensapi/src/test/integration/devnet-names.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ export const DEVNET_ENSV1_NAMES = additionallyRegisteredNames
label: entry.label,
canonical: entry.name,
wrapped: entry.wrapped,
reserved: entry.reserved,
}));

export const DEVNET_NAMES = [...STATIC_DEVNET_NAMES, ...SEEDED_ENSV2_NAMES];
Expand Down
10 changes: 10 additions & 0 deletions packages/integration-test-env/src/devnet/fixtures.ts
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,7 @@ type ENSv1RegisteredName = {
*/
wrapped: boolean;
records?: NameRecords;
reserved: boolean;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The reserved: boolean field on the ENSv1RegisteredName type lacks JSDoc documentation, unlike the wrapped field which has detailed documentation explaining its purpose and behavior.

Fix on Vercel

};

type ENSv2RegisteredName = {
Expand Down Expand Up @@ -195,12 +196,21 @@ export const additionallyRegisteredNames = [
name: "legacy-v1-wrapped.eth",
label: "legacy-v1-wrapped",
wrapped: true,
reserved: true,
},
{
type: "ENSv1",
name: "legacy-v1-unwrapped.eth",
label: "legacy-v1-unwrapped",
wrapped: false,
reserved: true,
},
{
type: "ENSv1",
name: "legacy-v1-wrapped-unreserved.eth",
label: "legacy-v1-wrapped-unreserved",
wrapped: true,
reserved: false,
},
{
type: "ENSv2",
Expand Down
22 changes: 12 additions & 10 deletions packages/integration-test-env/src/seed/registered-names.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ async function seedNameRecords(
async function seedEnsV1Name(
client: DevnetWalletClient,
resolver: Address,
entry: Extract<RegisteredName, { type: "ENSv1" }>,
entry: Extract<RegisteredName, { type: "ENSv1"; reserved: boolean }>,
): Promise<void> {
const wrapped = entry.wrapped !== false;

Expand All @@ -48,15 +48,17 @@ async function seedEnsV1Name(
await seedNameRecords(client, resolver, namehash(entry.name) as Hex, entry.records);
}

// Mirror the ENSv2 migration's reserved-entry state: also register the name in the ENSv2
// ETHRegistry pointing at the ENSV1Resolver (the v1 mirror resolver). This is how a name that
// still lives in ENSv1 stays resolvable under UR2, and makes the preferred ENSv2 Domain the one
// returned by the namegraph walk — matching mainnet, where every v1 name is reserved in v2.
await registerEthName(client, {
label: entry.label,
resolver: contracts.ENSV1Resolver,
subregistry: zeroAddress,
});
if (entry.reserved) {
// Mirror the ENSv2 migration's reserved-entry state: also register the name in the ENSv2
// ETHRegistry pointing at the ENSV1Resolver (the v1 mirror resolver). This is how a name that
// still lives in ENSv1 stays resolvable under UR2, and makes the preferred ENSv2 Domain the one
// returned by the namegraph walk — matching mainnet, where every v1 name is reserved in v2.
await registerEthName(client, {
label: entry.label,
resolver: contracts.ENSV1Resolver,
subregistry: zeroAddress,
});
}
}

async function seedEnsV2Name(
Expand Down
Loading