test: add auth routes test suite with 13 cases
Tests check-name validation, login pubkey validation, register name/pubkey validation, register-human validation, NIP-98 session (valid + expired). Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.6
parent
6144fa7910
commit
34a83fb0fc
@@ -0,0 +1,159 @@
|
||||
import { describe, it, expect, vi } from 'vitest'
|
||||
import { Hono } from 'hono'
|
||||
import { authRouter } from './auth.js'
|
||||
import { generateSecretKey, getPublicKey, finalizeEvent } from 'nostr-tools'
|
||||
|
||||
const app = new Hono()
|
||||
app.route('/api/auth', authRouter)
|
||||
|
||||
function makeNip98Header(sk: Uint8Array, url: string, method: string) {
|
||||
const event = finalizeEvent({
|
||||
kind: 27235,
|
||||
created_at: Math.floor(Date.now() / 1000),
|
||||
tags: [['u', url], ['method', method]],
|
||||
content: '',
|
||||
}, sk)
|
||||
return `Nostr ${Buffer.from(JSON.stringify(event)).toString('base64')}`
|
||||
}
|
||||
|
||||
describe('auth routes', () => {
|
||||
// --- check-name ---
|
||||
it('check-name: rejects short name', async () => {
|
||||
const res = await app.request('/api/auth/check-name/x')
|
||||
const body = await res.json() as { available: boolean; error?: string }
|
||||
expect(body.available).toBe(false)
|
||||
expect(body.error).toContain('2-12')
|
||||
})
|
||||
|
||||
it('check-name: rejects long name (>12)', async () => {
|
||||
const res = await app.request('/api/auth/check-name/verylongbotnamehere')
|
||||
const body = await res.json() as { available: boolean; error?: string }
|
||||
expect(body.available).toBe(false)
|
||||
})
|
||||
|
||||
it('check-name: available name returns true', async () => {
|
||||
const name = `t${Date.now().toString(36).slice(-6)}`
|
||||
const res = await app.request(`/api/auth/check-name/${name}`)
|
||||
const body = await res.json() as { available: boolean }
|
||||
expect(body.available).toBe(true)
|
||||
})
|
||||
|
||||
// --- login ---
|
||||
it('login: rejects invalid pubkey (too short)', async () => {
|
||||
const res = await app.request('/api/auth/login', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ pubkey: 'tooshort' }),
|
||||
})
|
||||
expect(res.status).toBe(400)
|
||||
const body = await res.json() as { error: string }
|
||||
expect(body.error).toContain('Invalid pubkey')
|
||||
})
|
||||
|
||||
it('login: rejects missing pubkey', async () => {
|
||||
const res = await app.request('/api/auth/login', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({}),
|
||||
})
|
||||
expect(res.status).toBe(400)
|
||||
})
|
||||
|
||||
it('login: returns exists=false for unknown pubkey', async () => {
|
||||
const pk = '0'.repeat(64)
|
||||
const res = await app.request('/api/auth/login', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ pubkey: pk }),
|
||||
})
|
||||
expect(res.status).toBe(200)
|
||||
const body = await res.json() as { exists: boolean; pubkey?: string }
|
||||
expect(body.exists).toBe(false)
|
||||
expect(body.pubkey).toBe(pk)
|
||||
})
|
||||
|
||||
// --- register ---
|
||||
it('register: rejects invalid pubkey', async () => {
|
||||
const res = await app.request('/api/auth/register', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ pubkey: 'bad', name: 'test-bot' }),
|
||||
})
|
||||
expect(res.status).toBe(400)
|
||||
const body = await res.json() as { error: string }
|
||||
expect(body.error).toContain('Invalid pubkey')
|
||||
})
|
||||
|
||||
it('register: rejects invalid name (special chars)', async () => {
|
||||
const res = await app.request('/api/auth/register', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ pubkey: 'a'.repeat(64), name: 'hello world!' }),
|
||||
})
|
||||
expect(res.status).toBe(400)
|
||||
const body = await res.json() as { error: string }
|
||||
expect(body.error).toContain('alphanumeric')
|
||||
})
|
||||
|
||||
it('register: rejects name too short', async () => {
|
||||
const res = await app.request('/api/auth/register', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ pubkey: 'b'.repeat(64), name: 'x' }),
|
||||
})
|
||||
expect(res.status).toBe(400)
|
||||
const body = await res.json() as { error: string }
|
||||
expect(body.error).toContain('2-12')
|
||||
})
|
||||
|
||||
// --- register-human ---
|
||||
it('register-human: rejects invalid pubkey', async () => {
|
||||
const res = await app.request('/api/auth/register-human', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ pubkey: 'short', name: 'human1' }),
|
||||
})
|
||||
expect(res.status).toBe(400)
|
||||
})
|
||||
|
||||
// --- nostr/session ---
|
||||
it('nostr/session: rejects missing auth header', async () => {
|
||||
const res = await app.request('/api/auth/nostr/session', {
|
||||
method: 'POST',
|
||||
})
|
||||
expect(res.status).toBe(401)
|
||||
})
|
||||
|
||||
it('nostr/session: accepts valid NIP-98 token', async () => {
|
||||
const sk = generateSecretKey()
|
||||
const pk = getPublicKey(sk)
|
||||
const header = makeNip98Header(sk, 'https://localhost/api/auth/nostr/session', 'POST')
|
||||
|
||||
const res = await app.request('/api/auth/nostr/session', {
|
||||
method: 'POST',
|
||||
headers: { Authorization: header },
|
||||
})
|
||||
expect(res.status).toBe(200)
|
||||
const body = await res.json() as { token: string; pubkey: string; exists: boolean }
|
||||
expect(body.token).toBeDefined()
|
||||
expect(body.pubkey).toBe(pk)
|
||||
expect(body.exists).toBe(false)
|
||||
})
|
||||
|
||||
it('nostr/session: rejects expired NIP-98 token', async () => {
|
||||
const sk = generateSecretKey()
|
||||
const event = finalizeEvent({
|
||||
kind: 27235,
|
||||
created_at: Math.floor(Date.now() / 1000) - 300,
|
||||
tags: [['u', 'https://localhost/api/auth/nostr/session'], ['method', 'POST']],
|
||||
content: '',
|
||||
}, sk)
|
||||
const header = `Nostr ${Buffer.from(JSON.stringify(event)).toString('base64')}`
|
||||
|
||||
const res = await app.request('/api/auth/nostr/session', {
|
||||
method: 'POST',
|
||||
headers: { Authorization: header },
|
||||
})
|
||||
expect(res.status).toBe(401)
|
||||
})
|
||||
})
|
||||
Reference in New Issue
Block a user