Files
sub2api/frontend/src/api/__tests__/auth-oauth-adoption.spec.ts
2026-04-20 19:30:19 +08:00

193 lines
5.8 KiB
TypeScript

import { beforeEach, describe, expect, it, vi } from 'vitest'
const post = vi.fn()
vi.mock('@/api/client', () => ({
apiClient: {
post
}
}))
describe('oauth adoption auth api', () => {
beforeEach(() => {
post.mockReset()
post.mockResolvedValue({ data: {} })
localStorage.clear()
document.cookie = 'oauth_bind_access_token=; Max-Age=0; path=/'
})
it('posts adoption decisions when exchanging pending oauth completion', async () => {
const { exchangePendingOAuthCompletion } = await import('@/api/auth')
await exchangePendingOAuthCompletion({
adoptDisplayName: false,
adoptAvatar: true
})
expect(post).toHaveBeenCalledWith('/auth/oauth/pending/exchange', {
adopt_display_name: false,
adopt_avatar: true
})
})
it('posts bind-login decisions when finalizing pending oauth bind flow', async () => {
const { completePendingOAuthBindLogin } = await import('@/api/auth')
await completePendingOAuthBindLogin({
adoptDisplayName: true,
adoptAvatar: false
})
expect(post).toHaveBeenCalledWith('/auth/oauth/pending/exchange', {
adopt_display_name: true,
adopt_avatar: false
})
})
it('posts linuxdo invitation completion with adoption decisions', async () => {
const { completeLinuxDoOAuthRegistration } = await import('@/api/auth')
await completeLinuxDoOAuthRegistration('invite-code', {
adoptDisplayName: true,
adoptAvatar: false
})
expect(post).toHaveBeenCalledWith('/auth/oauth/linuxdo/complete-registration', {
invitation_code: 'invite-code',
adopt_display_name: true,
adopt_avatar: false
})
})
it('posts linuxdo create-account completion with adoption decisions', async () => {
const { createPendingLinuxDoOAuthAccount } = await import('@/api/auth')
await createPendingLinuxDoOAuthAccount('invite-code', {
adoptDisplayName: false,
adoptAvatar: true
})
expect(post).toHaveBeenCalledWith('/auth/oauth/linuxdo/complete-registration', {
invitation_code: 'invite-code',
adopt_display_name: false,
adopt_avatar: true
})
})
it('posts oidc invitation completion with adoption decisions', async () => {
const { completeOIDCOAuthRegistration } = await import('@/api/auth')
await completeOIDCOAuthRegistration('invite-code', {
adoptDisplayName: false,
adoptAvatar: true
})
expect(post).toHaveBeenCalledWith('/auth/oauth/oidc/complete-registration', {
invitation_code: 'invite-code',
adopt_display_name: false,
adopt_avatar: true
})
})
it('posts oidc create-account completion with adoption decisions', async () => {
const { createPendingOIDCOAuthAccount } = await import('@/api/auth')
await createPendingOIDCOAuthAccount('invite-code', {
adoptDisplayName: true,
adoptAvatar: false
})
expect(post).toHaveBeenCalledWith('/auth/oauth/oidc/complete-registration', {
invitation_code: 'invite-code',
adopt_display_name: true,
adopt_avatar: false
})
})
it('posts wechat invitation completion with adoption decisions', async () => {
const { completeWeChatOAuthRegistration } = await import('@/api/auth')
await completeWeChatOAuthRegistration('invite-code', {
adoptDisplayName: true,
adoptAvatar: true
})
expect(post).toHaveBeenCalledWith('/auth/oauth/wechat/complete-registration', {
invitation_code: 'invite-code',
adopt_display_name: true,
adopt_avatar: true
})
})
it('posts wechat create-account completion with adoption decisions', async () => {
const { createPendingWeChatOAuthAccount } = await import('@/api/auth')
await createPendingWeChatOAuthAccount('invite-code', {
adoptDisplayName: false,
adoptAvatar: false
})
expect(post).toHaveBeenCalledWith('/auth/oauth/wechat/complete-registration', {
invitation_code: 'invite-code',
adopt_display_name: false,
adopt_avatar: false
})
})
it('classifies oauth completion results as login or bind', async () => {
const { getOAuthCompletionKind } = await import('@/api/auth')
expect(getOAuthCompletionKind({ access_token: 'access-token' })).toBe('login')
expect(getOAuthCompletionKind({ redirect: '/profile' })).toBe('bind')
})
it('provides bind-login utility helpers for invitation and suggested profile states', async () => {
const {
getPendingOAuthBindLoginKind,
hasPendingOAuthSuggestedProfile,
isPendingOAuthCreateAccountRequired
} = await import('@/api/auth')
expect(getPendingOAuthBindLoginKind({ access_token: 'access-token' })).toBe('login')
expect(getPendingOAuthBindLoginKind({ redirect: '/profile' })).toBe('bind')
expect(
isPendingOAuthCreateAccountRequired({
error: 'invitation_required'
})
).toBe(true)
expect(
isPendingOAuthCreateAccountRequired({
error: 'other'
})
).toBe(false)
expect(
hasPendingOAuthSuggestedProfile({
suggested_display_name: 'OAuth Nick'
})
).toBe(true)
expect(
hasPendingOAuthSuggestedProfile({
suggested_avatar_url: 'https://cdn.example/avatar.png'
})
).toBe(true)
expect(hasPendingOAuthSuggestedProfile({})).toBe(false)
})
it('prepares an oauth bind access token cookie before redirect binding', async () => {
localStorage.setItem('auth_token', 'access-token-value')
const setCookie = vi.fn()
Object.defineProperty(document, 'cookie', {
configurable: true,
get: () => '',
set: setCookie
})
const { prepareOAuthBindAccessTokenCookie } = await import('@/api/auth')
prepareOAuthBindAccessTokenCookie()
expect(setCookie).toHaveBeenCalledTimes(1)
expect(setCookie.mock.calls[0]?.[0]).toContain('oauth_bind_access_token=access-token-value')
})
})