← Public packages
@danj/audible-auth
Locked Audible credential gateway: Connect, token refresh, and narrow Audible calls for other Kody packages.
src/__tests__/app.test.ts
62 lines · 3.8 KB · TypeScriptimport assert from 'node:assert/strict'
import { registerHooks } from 'node:module'
import { test } from 'node:test'
// The Connect page with its Kody shell and Audible calls faked.
const fake: any = globalThis
fake.connect = { started: 0, completed: [] as unknown[], disconnected: 0 }
const mocks: Record<string, string> = {
'kody:runtime': `export const packageContext={appBasePath:'/packages/audible-auth',hostedUrl:'https://danj.kody.run/packages/audible-auth'}; export const kody={}; export const packageSecrets={}; export const packageStorage=()=>({})`,
'./status.ts': `export default async()=>({ok:true,connected:false,ready:false,locale:null,hostApprovals:[],archiveEnabled:false})`,
'./disconnect.ts': `export const disconnect=async()=>{globalThis.connect.disconnected++;return{wasConnected:true,deviceRemoved:true,secretsDeleted:true}}`,
'./start-auth.ts': `export default async()=>{globalThis.connect.started++;return{loginUrl:'https://www.amazon.ca/ap/signin?x=1&y=2',authSessionId:'abc123'}}`,
'./complete-auth.ts': `export default async(input)=>{globalThis.connect.completed.push(input);return{ok:true,previousDevice:'removed',message:'Audible connected.',status:{ok:true,connected:true,ready:true,locale:'ca',hostApprovals:[],archiveEnabled:false}}}`,
}
registerHooks({
resolve(specifier, context, next) {
return mocks[specifier] && (specifier.startsWith('kody:') || context.parentURL?.endsWith('/app.ts'))
? { url: `mock:${specifier}`, shortCircuit: true }
: next(specifier, context)
},
load(url, context, next) {
return url.startsWith('mock:') ? { format: 'module', shortCircuit: true, source: mocks[url.slice(5)] } : next(url, context)
},
})
const app = (await import('../app.ts')).default
const base = 'https://danj.kody.run/packages/audible-auth'
const post = (path: string, fields: Record<string, string>, headers: Record<string, string> = {}) =>
app.fetch(new Request(`${base}${path}`, { method: 'POST', body: new URLSearchParams(fields), headers }))
test('the page offers Connect and keeps a same-host return link only', async () => {
const ok = await (await app.fetch(new Request(`${base}/?return=${encodeURIComponent('https://danj.kody.run/packages/audible/classic')}`))).text()
assert.match(ok, /Connect Audible/)
assert.match(ok, /Back to the app/)
const evil = await (await app.fetch(new Request(`${base}/?return=${encodeURIComponent('https://evil.example/packages/x')}`))).text()
assert.doesNotMatch(evil, /Back to the app/)
})
test('start shows the login link from this package, then complete finishes Connect', async () => {
const started = await (await post('/start', { locale: 'ca' })).text()
assert.match(started, /href="https:\/\/www\.amazon\.ca\/ap\/signin\?x=1&y=2"/)
assert.match(started, /name="authSessionId" value="abc123"/)
const done = await (await post('/complete', { redirectUrl: 'https://www.amazon.ca/ap/maplanding?code=1', authSessionId: 'abc123' })).text()
assert.match(done, /previous virtual device was removed/)
assert.deepEqual(fake.connect.completed.at(-1), { redirectUrl: 'https://www.amazon.ca/ap/maplanding?code=1', authSessionId: 'abc123' })
})
test('a cross-site form cannot start Connect', async () => {
const before = fake.connect.started
const response = await post('/start', { locale: 'ca' }, { origin: 'https://evil.example', 'sec-fetch-site': 'cross-site' })
assert.equal(response.status, 403)
assert.equal(fake.connect.started, before)
})
test('disconnect removes the device and reports it; a cross-site form cannot', async () => {
const denied = await post('/disconnect', {}, { origin: 'https://evil.example', 'sec-fetch-site': 'cross-site' })
assert.equal(denied.status, 403)
assert.equal(fake.connect.disconnected, 0)
const done = await (await post('/disconnect', {})).text()
assert.equal(fake.connect.disconnected, 1)
assert.match(done, /virtual device was removed/)
assert.match(done, /Open your Amazon devices/)
})