Skip to content

Commit 89906e8

Browse files
fix(tests): update SigningKeyManager tests for id.org.ai StorageOp API
SigningKeyManager now requires a StorageOp function in its constructor instead of an options object. Add createMemoryStorageOp() test helper, update toJWKS() calls to getJWKS(), and adapt maxKeys assertions to match the hardcoded limit of 2. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent 9f9932f commit 89906e8

2 files changed

Lines changed: 66 additions & 13 deletions

File tree

core/src/introspection.test.ts

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,32 @@ function decodeJwt(token: string) {
2121
return { header, payload, signatureB64: signatureB64!, headerB64: headerB64!, payloadB64: payloadB64! }
2222
}
2323

24+
// In-memory StorageOp for testing (matches id.org.ai's StorageOp type)
25+
function createMemoryStorageOp() {
26+
const store = new Map<string, unknown>()
27+
return async (op: { op: 'get' | 'put' | 'delete' | 'list'; key?: string; value?: unknown; options?: { prefix?: string } }) => {
28+
switch (op.op) {
29+
case 'get':
30+
return { value: store.get(op.key!) ?? undefined }
31+
case 'put':
32+
store.set(op.key!, op.value)
33+
return {}
34+
case 'delete':
35+
store.delete(op.key!)
36+
return {}
37+
case 'list': {
38+
const entries: Record<string, unknown> = {}
39+
for (const [k, v] of store) {
40+
if (!op.options?.prefix || k.startsWith(op.options.prefix)) entries[k] = v
41+
}
42+
return entries
43+
}
44+
default:
45+
return {}
46+
}
47+
}
48+
}
49+
2450
// Mock storage implementation for testing
2551
function createMockStorage(): OAuthStorage {
2652
const accessTokens = new Map<string, OAuthAccessToken>()
@@ -94,7 +120,7 @@ describe('/introspect endpoint', () => {
94120

95121
beforeEach(async () => {
96122
storage = createMockStorage()
97-
signingKeyManager = new SigningKeyManager()
123+
signingKeyManager = new SigningKeyManager(createMemoryStorageOp())
98124
signingKey = await signingKeyManager.getCurrentKey()
99125
})
100126

core/src/jwt-signing.test.ts

Lines changed: 39 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,32 @@ function decodeJwt(token: string) {
2828
return { header, payload, signatureB64: signatureB64!, headerB64: headerB64!, payloadB64: payloadB64! }
2929
}
3030

31+
// In-memory StorageOp for testing (matches id.org.ai's StorageOp type)
32+
function createMemoryStorageOp() {
33+
const store = new Map<string, unknown>()
34+
return async (op: { op: 'get' | 'put' | 'delete' | 'list'; key?: string; value?: unknown; options?: { prefix?: string } }) => {
35+
switch (op.op) {
36+
case 'get':
37+
return { value: store.get(op.key!) ?? undefined }
38+
case 'put':
39+
store.set(op.key!, op.value)
40+
return {}
41+
case 'delete':
42+
store.delete(op.key!)
43+
return {}
44+
case 'list': {
45+
const entries: Record<string, unknown> = {}
46+
for (const [k, v] of store) {
47+
if (!op.options?.prefix || k.startsWith(op.options.prefix)) entries[k] = v
48+
}
49+
return entries
50+
}
51+
default:
52+
return {}
53+
}
54+
}
55+
}
56+
3157
describe('SigningKey generation', () => {
3258
it('generateSigningKey creates a valid RSA-2048 key pair', async () => {
3359
const key = await generateSigningKey()
@@ -228,7 +254,7 @@ describe('signAccessToken', () => {
228254

229255
describe('SigningKeyManager', () => {
230256
it('initializes with a generated key when no keys exist', async () => {
231-
const manager = new SigningKeyManager()
257+
const manager = new SigningKeyManager(createMemoryStorageOp())
232258
const key = await manager.getCurrentKey()
233259

234260
expect(key).toBeDefined()
@@ -237,34 +263,34 @@ describe('SigningKeyManager', () => {
237263
})
238264

239265
it('getActiveKey returns the same key on subsequent calls', async () => {
240-
const manager = new SigningKeyManager()
266+
const manager = new SigningKeyManager(createMemoryStorageOp())
241267
const key1 = await manager.getCurrentKey()
242268
const key2 = await manager.getCurrentKey()
243269
expect(key1.kid).toBe(key2.kid)
244270
})
245271

246272
it('getAllKeys returns all keys', async () => {
247-
const manager = new SigningKeyManager({ maxKeys: 3 })
273+
const manager = new SigningKeyManager(createMemoryStorageOp())
248274
await manager.getCurrentKey()
249275
await manager.rotateKey()
250276

251277
const allKeys = manager.getAllKeys()
252278
expect(allKeys).toHaveLength(2)
253279
})
254280

255-
it('toJWKS returns JWKs for all keys', async () => {
256-
const manager = new SigningKeyManager({ maxKeys: 3 })
281+
it('getJWKS returns JWKs for all keys', async () => {
282+
const manager = new SigningKeyManager(createMemoryStorageOp())
257283
await manager.getCurrentKey()
258284
await manager.rotateKey()
259285

260-
const jwks = await manager.toJWKS()
286+
const jwks = await manager.getJWKS()
261287
expect(jwks.keys).toHaveLength(2)
262288
expect(jwks.keys[0]!.kty).toBe('RSA')
263289
expect(jwks.keys[1]!.kty).toBe('RSA')
264290
})
265291

266292
it('rotateKey adds a new key and makes it active', async () => {
267-
const manager = new SigningKeyManager({ maxKeys: 3 })
293+
const manager = new SigningKeyManager(createMemoryStorageOp())
268294
const original = await manager.getCurrentKey()
269295
const rotated = await manager.rotateKey()
270296
const current = await manager.getCurrentKey()
@@ -274,26 +300,27 @@ describe('SigningKeyManager', () => {
274300
})
275301

276302
it('rotateKey respects maxKeys limit and removes oldest', async () => {
277-
const manager = new SigningKeyManager({ maxKeys: 2 })
303+
const manager = new SigningKeyManager(createMemoryStorageOp())
278304
const first = await manager.getCurrentKey()
279305
await manager.rotateKey()
280306
await manager.rotateKey()
281307

282308
const allKeys = manager.getAllKeys()
309+
// Default maxKeys is 2, so after 2 rotations we should have 2 keys
283310
expect(allKeys).toHaveLength(2)
284311
// The first key should have been removed
285312
expect(allKeys.find((k) => k.kid === first.kid)).toBeUndefined()
286313
})
287314

288315
it('key persistence: exportKeys/loadKeys roundtrip works', async () => {
289-
const manager1 = new SigningKeyManager({ maxKeys: 3 })
316+
const manager1 = new SigningKeyManager(createMemoryStorageOp())
290317
await manager1.getCurrentKey()
291318
await manager1.rotateKey()
292319

293320
const exported = await manager1.exportKeys()
294321
expect(exported).toHaveLength(2)
295322

296-
const manager2 = new SigningKeyManager({ maxKeys: 3 })
323+
const manager2 = new SigningKeyManager(createMemoryStorageOp())
297324
await manager2.loadKeys(exported)
298325

299326
const keys1 = manager1.getAllKeys()
@@ -322,7 +349,7 @@ describe('SigningKeyManager', () => {
322349
})
323350

324351
it('signAccessToken uses the current key', async () => {
325-
const manager = new SigningKeyManager()
352+
const manager = new SigningKeyManager(createMemoryStorageOp())
326353
const token = await manager.signAccessToken(
327354
{ sub: 'user-1', client_id: 'client-1' },
328355
{ issuer: 'https://oauth.do' }
@@ -334,7 +361,7 @@ describe('SigningKeyManager', () => {
334361
})
335362

336363
it('default maxKeys is 2', async () => {
337-
const manager = new SigningKeyManager()
364+
const manager = new SigningKeyManager(createMemoryStorageOp())
338365
await manager.getCurrentKey()
339366
await manager.rotateKey()
340367
await manager.rotateKey()

0 commit comments

Comments
 (0)