test: add opt-in e2e suite
This commit is contained in:
parent
5d96853cff
commit
de907c9dbe
122
e2e/clawdhub.e2e.test.ts
Normal file
122
e2e/clawdhub.e2e.test.ts
Normal file
@ -0,0 +1,122 @@
|
||||
/* @vitest-environment node */
|
||||
|
||||
import { spawnSync } from 'node:child_process'
|
||||
import { mkdtemp, rm, writeFile } from 'node:fs/promises'
|
||||
import { tmpdir } from 'node:os'
|
||||
import { join } from 'node:path'
|
||||
import {
|
||||
ApiCliWhoamiResponseSchema,
|
||||
ApiRoutes,
|
||||
ApiSearchResponseSchema,
|
||||
parseArk,
|
||||
} from '@clawdhub/schema'
|
||||
import { describe, expect, it } from 'vitest'
|
||||
import { readGlobalConfig } from '../packages/clawdhub/src/config'
|
||||
|
||||
function mustGetToken() {
|
||||
const fromEnv = process.env.CLAWDHUB_E2E_TOKEN?.trim()
|
||||
if (fromEnv) return fromEnv
|
||||
return null
|
||||
}
|
||||
|
||||
async function makeTempConfig(registry: string, token: string | null) {
|
||||
const dir = await mkdtemp(join(tmpdir(), 'clawdhub-e2e-'))
|
||||
const path = join(dir, 'config.json')
|
||||
await writeFile(
|
||||
path,
|
||||
`${JSON.stringify({ registry, token: token || undefined }, null, 2)}\n`,
|
||||
'utf8',
|
||||
)
|
||||
return { dir, path }
|
||||
}
|
||||
|
||||
describe('clawdhub e2e', () => {
|
||||
it('search endpoint returns a results array (schema parse)', async () => {
|
||||
const registry = process.env.CLAWDHUB_REGISTRY?.trim() || 'https://clawdhub.com'
|
||||
const url = new URL(ApiRoutes.search, registry)
|
||||
url.searchParams.set('q', 'gif')
|
||||
url.searchParams.set('limit', '5')
|
||||
|
||||
const response = await fetch(url.toString(), { headers: { Accept: 'application/json' } })
|
||||
expect(response.ok).toBe(true)
|
||||
const json = (await response.json()) as unknown
|
||||
const parsed = parseArk(ApiSearchResponseSchema, json, 'API response')
|
||||
expect(Array.isArray(parsed.results)).toBe(true)
|
||||
})
|
||||
|
||||
it('cli search does not error on multi-result responses', async () => {
|
||||
const registry = process.env.CLAWDHUB_REGISTRY?.trim() || 'https://clawdhub.com'
|
||||
const site = process.env.CLAWDHUB_SITE?.trim() || 'https://clawdhub.com'
|
||||
const token = mustGetToken() ?? (await readGlobalConfig())?.token ?? null
|
||||
|
||||
const cfg = await makeTempConfig(registry, token)
|
||||
try {
|
||||
const workdir = await mkdtemp(join(tmpdir(), 'clawdhub-e2e-workdir-'))
|
||||
const result = spawnSync(
|
||||
'bun',
|
||||
[
|
||||
'clawdhub',
|
||||
'search',
|
||||
'gif',
|
||||
'--limit',
|
||||
'5',
|
||||
'--site',
|
||||
site,
|
||||
'--registry',
|
||||
registry,
|
||||
'--workdir',
|
||||
workdir,
|
||||
],
|
||||
{
|
||||
cwd: process.cwd(),
|
||||
env: { ...process.env, CLAWDHUB_CONFIG_PATH: cfg.path },
|
||||
encoding: 'utf8',
|
||||
},
|
||||
)
|
||||
await rm(workdir, { recursive: true, force: true })
|
||||
|
||||
expect(result.status).toBe(0)
|
||||
expect(result.stderr).not.toMatch(/API response:/)
|
||||
} finally {
|
||||
await rm(cfg.dir, { recursive: true, force: true })
|
||||
}
|
||||
})
|
||||
|
||||
it('assumes a logged-in user (whoami succeeds)', async () => {
|
||||
const registry = process.env.CLAWDHUB_REGISTRY?.trim() || 'https://clawdhub.com'
|
||||
const site = process.env.CLAWDHUB_SITE?.trim() || 'https://clawdhub.com'
|
||||
const token = mustGetToken() ?? (await readGlobalConfig())?.token ?? null
|
||||
if (!token) {
|
||||
throw new Error('Missing token. Set CLAWDHUB_E2E_TOKEN or run: bun clawdhub auth login')
|
||||
}
|
||||
|
||||
const cfg = await makeTempConfig(registry, token)
|
||||
try {
|
||||
const whoamiUrl = new URL(ApiRoutes.cliWhoami, registry)
|
||||
const whoamiRes = await fetch(whoamiUrl.toString(), {
|
||||
headers: { Accept: 'application/json', Authorization: `Bearer ${token}` },
|
||||
})
|
||||
expect(whoamiRes.ok).toBe(true)
|
||||
const whoami = parseArk(
|
||||
ApiCliWhoamiResponseSchema,
|
||||
(await whoamiRes.json()) as unknown,
|
||||
'Whoami',
|
||||
)
|
||||
expect(whoami.user).toBeTruthy()
|
||||
|
||||
const result = spawnSync(
|
||||
'bun',
|
||||
['clawdhub', 'whoami', '--site', site, '--registry', registry],
|
||||
{
|
||||
cwd: process.cwd(),
|
||||
env: { ...process.env, CLAWDHUB_CONFIG_PATH: cfg.path },
|
||||
encoding: 'utf8',
|
||||
},
|
||||
)
|
||||
expect(result.status).toBe(0)
|
||||
expect(result.stderr).not.toMatch(/not logged in|unauthorized|error:/i)
|
||||
} finally {
|
||||
await rm(cfg.dir, { recursive: true, force: true })
|
||||
}
|
||||
})
|
||||
})
|
||||
@ -11,6 +11,7 @@
|
||||
"preview": "bun --bun vite preview",
|
||||
"test": "vitest run",
|
||||
"test:watch": "vitest",
|
||||
"test:e2e": "vitest run -c vitest.e2e.config.ts",
|
||||
"coverage": "vitest run --coverage",
|
||||
"convex:deploy": "bunx convex deploy --typecheck=disable --yes",
|
||||
"lint": "bun run lint:biome && bun run lint:oxlint",
|
||||
|
||||
@ -1,9 +1,11 @@
|
||||
import { mkdir, readFile, writeFile } from 'node:fs/promises'
|
||||
import { homedir } from 'node:os'
|
||||
import { dirname, join } from 'node:path'
|
||||
import { dirname, join, resolve } from 'node:path'
|
||||
import { type GlobalConfig, GlobalConfigSchema, parseArk } from '@clawdhub/schema'
|
||||
|
||||
export function getGlobalConfigPath() {
|
||||
const override = process.env.CLAWDHUB_CONFIG_PATH?.trim()
|
||||
if (override) return resolve(override)
|
||||
const home = homedir()
|
||||
if (process.platform === 'darwin') {
|
||||
return join(home, 'Library', 'Application Support', 'clawdhub', 'config.json')
|
||||
|
||||
@ -5,7 +5,14 @@ export default defineConfig({
|
||||
environment: 'jsdom',
|
||||
globals: true,
|
||||
setupFiles: ['./vitest.setup.ts'],
|
||||
exclude: ['**/node_modules/**', '**/dist/**', '**/coverage/**', '**/convex/_generated/**'],
|
||||
exclude: [
|
||||
'**/node_modules/**',
|
||||
'**/dist/**',
|
||||
'**/coverage/**',
|
||||
'**/convex/_generated/**',
|
||||
'e2e/**',
|
||||
'**/*.e2e.test.ts',
|
||||
],
|
||||
coverage: {
|
||||
provider: 'v8',
|
||||
reporter: ['text', 'html', 'lcov'],
|
||||
@ -33,6 +40,7 @@ export default defineConfig({
|
||||
'packages/clawdhub/src/config.ts',
|
||||
'packages/clawdhub/src/types.ts',
|
||||
'packages/schema/dist/',
|
||||
'e2e/**',
|
||||
],
|
||||
},
|
||||
},
|
||||
|
||||
11
vitest.e2e.config.ts
Normal file
11
vitest.e2e.config.ts
Normal file
@ -0,0 +1,11 @@
|
||||
import { defineConfig } from 'vitest/config'
|
||||
|
||||
export default defineConfig({
|
||||
test: {
|
||||
environment: 'node',
|
||||
include: ['e2e/**/*.e2e.test.ts'],
|
||||
exclude: ['**/node_modules/**', '**/dist/**', '**/coverage/**', '**/convex/_generated/**'],
|
||||
testTimeout: 60_000,
|
||||
hookTimeout: 60_000,
|
||||
},
|
||||
})
|
||||
Loading…
Reference in New Issue
Block a user