test(notion): verify stored image preservation

This commit is contained in:
Taesu
2026-10-16 09:59:09 +09:00
parent 8b5eca434d
commit b245cbcc4a
2 changed files with 89 additions and 2 deletions
+1 -1
View File
@@ -2,4 +2,4 @@
"@better-auth/core": minor
---
Allow partial Notion owner profiles and nullable display fields in `NotionProfile`, and leave the local image unset when no owner avatar is returned.
Allow partial Notion owner profiles and nullable display fields in `NotionProfile`, and leave the local image unset when no owner avatar is returned. Existing user images are preserved when profile updates are enabled and the provider returns no image.
@@ -4,6 +4,7 @@ import type {
GoogleProfile,
} from "@better-auth/core/social-providers";
import { NodeSqliteDialect } from "@better-auth/kysely-adapter/node-sqlite-dialect";
import { getTestInstance } from "better-auth/test";
import { Kysely } from "kysely";
import { HttpResponse, http } from "msw";
import { setupServer } from "msw/node";
@@ -23,7 +24,6 @@ import { createAuthClient } from "../client";
import { parseSetCookieHeader, setCookieToHeader } from "../cookies";
import { signJWT } from "../crypto";
import { getMigrations } from "../db/get-migration";
import { getTestInstance } from "../test-utils/test-instance";
import type { User } from "../types";
import { DEFAULT_SECRET } from "../utils/constants";
@@ -2710,3 +2710,90 @@ describe("oauth2 - account-linking logs use the configured logger", async () =>
);
});
});
/**
* @see https://developers.notion.com/reference/user
* @see https://github.com/better-auth/better-auth/issues/8742
*/
it.each(["sign-in", "link"] as const)(
"preserves the stored image when Notion has no avatar during %s",
async (flow) => {
const storedImage = "https://example.com/stored-avatar.png";
const { auth, client, testUser, signInWithTestUser, cookieSetter } =
await getTestInstance(
{
socialProviders: {
notion: {
clientId: "notion-app",
clientSecret: "notion-secret",
overrideUserInfoOnSignIn: true,
},
},
account: {
accountLinking: {
trustedProviders: ["notion"],
updateUserInfoOnLink: true,
},
},
},
{ testUser: { image: storedImage } },
);
const { user, headers } = await signInWithTestUser();
const ctx = await auth.$context;
if (flow === "sign-in") {
await ctx.internalAdapter.createAccount({
userId: user.id,
providerId: "notion",
accountId: "notion-owner",
});
}
server.use(
http.post("https://api.notion.com/v1/oauth/token", () =>
HttpResponse.json({
access_token: "notion-token",
token_type: "bearer",
}),
),
http.get("https://api.notion.com/v1/users/me", () =>
HttpResponse.json({
bot: {
owner: {
user: {
object: "user",
id: "notion-owner",
type: "person",
name: "Notion User",
avatar_url: null,
person: { email: testUser.email },
},
},
},
}),
),
);
const request = { provider: "notion" as const, callbackURL: "/" };
const options = { headers, onSuccess: cookieSetter(headers) };
const response =
flow === "link"
? await client.linkSocial(request, options)
: await client.signIn.social(request, options);
expect(response.error).toBeNull();
const state = new URL(response.data!.url!).searchParams.get("state");
await client.$fetch("/callback/notion", {
query: { state, code: "notion-code" },
headers,
onError({ response }) {
expect(response.status).toBe(302);
expect(
new URL(
response.headers.get("location")!,
"http://localhost:3000",
).searchParams.get("error"),
).toBeNull();
},
});
const updated = await ctx.internalAdapter.findUserById(user.id);
expect(updated?.name).toBe("Notion User");
expect(updated?.image).toBe(storedImage);
},
);