egregoros compat

This commit is contained in:
Moon.eth
2026-07-29 20:59:52 +09:00
parent 081460c7f6
commit 5b7b4360d2
16 changed files with 713 additions and 137 deletions
+74
View File
@@ -0,0 +1,74 @@
import { afterEach, describe, expect, it, vi } from 'vitest'
import { ApiClient } from './client'
function response(body: unknown, status = 200, headers: Record<string, string> = {}): Response {
return new Response(JSON.stringify(body), {
status,
headers: { 'Content-Type': 'application/json', ...headers },
})
}
afterEach(() => {
vi.unstubAllGlobals()
})
describe('ApiClient Egregoros compatibility', () => {
it('refreshes an expired bearer token once and retries the request', async () => {
const refreshToken = vi.fn().mockResolvedValue('fresh-token')
const fetchMock = vi
.fn()
.mockResolvedValueOnce(response({ error: 'Invalid or expired token' }, 401))
.mockResolvedValueOnce(response({ id: 'alice' }))
vi.stubGlobal('fetch', fetchMock)
const client = new ApiClient('egregoros.example', 'expired-token', refreshToken)
await expect(client.get('/api/v1/accounts/verify_credentials')).resolves.toEqual({
id: 'alice',
})
expect(refreshToken).toHaveBeenCalledOnce()
expect((fetchMock.mock.calls[0][1].headers as Headers).get('Authorization')).toBe(
'Bearer expired-token',
)
expect((fetchMock.mock.calls[1][1].headers as Headers).get('Authorization')).toBe(
'Bearer fresh-token',
)
})
it('does not invent a next page when the server supplied only a prev link', async () => {
vi.stubGlobal(
'fetch',
vi.fn().mockResolvedValue(
response(
[{ id: 'newest' }, { id: 'oldest' }],
200,
{
Link: '<https://egregoros.example/api/v1/timelines/public?since_id=newest>; rel="prev"',
},
),
),
)
const page = await new ApiClient('egregoros.example').page<{ id: string }>(
'/api/v1/timelines/public',
{ limit: 20 },
)
expect(page.links.sinceId).toBe('newest')
expect(page.links.maxId).toBeUndefined()
})
it('derives a next cursor only for a full page when Link was stripped entirely', async () => {
vi.stubGlobal(
'fetch',
vi.fn().mockResolvedValue(response([{ id: 'newest' }, { id: 'oldest' }])),
)
const page = await new ApiClient('egregoros.example').page<{ id: string }>(
'/api/v1/timelines/public',
{ limit: 2 },
)
expect(page.links.maxId).toBe('oldest')
})
})