oauth.test.js 2.28 KB
import { describe, it, expect } from 'vitest'
import { createAuth, createFakeUserRepo } from '../src/index.js'
import { createFakeHasher, createFakeSigner, createFakeOauth, createFakeEmail } from './fakes.js'

function build(oauth) {
  const userRepo = createFakeUserRepo()
  const auth = createAuth({
    userRepo,
    hasher: createFakeHasher(),
    tokenSigner: createFakeSigner(),
    oauth,
    email: createFakeEmail(),
  })
  return { userRepo, auth }
}

describe('oauthLogin', () => {
  it('first login auto-creates user (is_new_user true)', async () => {
    const oauth = createFakeOauth({
      'google:CODE1': { provider_user_id: 'g-1', email: 'g@x.com', email_verified: true },
    })
    const { auth, userRepo } = build(oauth)
    const res = await auth.oauthLogin({ provider: 'google', code: 'CODE1', redirect_uri: 'https://app/cb' })
    expect(res.is_new_user).toBe(true)
    expect(res.user.email).toBe('g@x.com')
    expect(res.access_token).toBeTruthy()
    const stored = await userRepo.findByEmail('g@x.com')
    expect(stored.oauth_providers).toContainEqual({ provider: 'google', provider_user_id: 'g-1' })
  })

  it('existing email merges into same user (is_new_user false)', async () => {
    const oauth = createFakeOauth({
      'google:CODE1': { provider_user_id: 'g-1', email: 'merge@x.com', email_verified: true },
    })
    const { auth, userRepo } = build(oauth)
    // pre-existing password user with same email
    await userRepo.create({ email: 'merge@x.com', password_hash: 'h$pw' })
    const res = await auth.oauthLogin({ provider: 'google', code: 'CODE1' })
    expect(res.is_new_user).toBe(false)
    const stored = await userRepo.findByEmail('merge@x.com')
    expect(stored.oauth_providers).toContainEqual({ provider: 'google', provider_user_id: 'g-1' })
  })

  it('verify failure -> OAUTH_VERIFY_FAILED', async () => {
    const oauth = createFakeOauth({}, { throwOn: 'BAD' })
    const { auth } = build(oauth)
    await expect(auth.oauthLogin({ provider: 'apple', code: 'BAD' }))
      .rejects.toMatchObject({ code: 'OAUTH_VERIFY_FAILED' })
  })

  it('unsupported provider rejected', async () => {
    const { auth } = build(createFakeOauth())
    await expect(auth.oauthLogin({ provider: 'wechat', code: 'x' }))
      .rejects.toMatchObject({ code: 'UNSUPPORTED_PROVIDER' })
  })
})