Files
hanzo.ai/e2e/gates/google-connect.spec.ts
zeekay b0fe332a88 auth: a Google consent is kept the one way @hanzo/app keeps it
The callback wrote Workspace tokens into storage by hand, with no expiry.
It calls @hanzo/app 0.2.28's keep() now, which records the expiry Google
gave, so gmail() can tell a live token from a spent one when the beta
invites send from the member's own Gmail. A gate holds it.

Claude-Session: https://claude.ai/code/session_017vrVus6PPBgP5YRzKV1iqW
2026-10-25 19:05:23 -07:00

44 lines
1.9 KiB
TypeScript

import { expect, test, type Page } from '@playwright/test'
import { serveExport } from './export'
/**
* Google Workspace's consent returns to /auth/callback with the token in the
* fragment. The page keeps it the one way @hanzo/app keeps a Google token
* (`@hanzo/app/lib/gmail` keep): under the account's address, with its scope
* and an expiry, so `gmail()` can hand it to the beta invites.
*/
let base: string
let stop: () => Promise<void>
test.beforeAll(async () => {
const served = await serveExport()
base = served.url
stop = served.close
})
test.afterAll(async () => stop())
const local = (page: Page) =>
page.route('**/*', (route) => {
const u = new URL(route.request().url())
if (u.origin === base) return route.continue()
if (u.href.startsWith('https://www.googleapis.com/oauth2/v2/userinfo')) return route.fulfill({ json: { email: '[email protected]' } })
return route.abort()
})
test('the Google consent fragment is kept under the address, with its scope and expiry', async ({ page }) => {
await local(page)
const before = Date.now()
const scope = 'https://www.googleapis.com/auth/gmail.modify openid email'
await page.goto(`${base}/auth/callback#access_token=tok_1&token_type=Bearer&expires_in=3599&scope=${encodeURIComponent(scope)}&state=gws_x`)
await expect
.poll(async () => page.evaluate(() => localStorage.getItem('hanzo.onboarding.google.tokens')), { timeout: 20_000 })
.not.toBeNull()
const tokens = JSON.parse((await page.evaluate(() => localStorage.getItem('hanzo.onboarding.google.tokens'))) ?? '{}')
const kept = tokens['[email protected]']
expect(kept).toMatchObject({ accessToken: 'tok_1', scope })
const expires = Date.parse(kept.expiresAt)
expect(expires).toBeGreaterThan(before + 3_500_000)
expect(expires).toBeLessThan(Date.now() + 3_700_000)
expect(JSON.parse((await page.evaluate(() => localStorage.getItem('hanzo.onboarding.google.accounts'))) ?? '[]')).toContain('[email protected]')
})