Skip to content

Commit d564e7d

Browse files
indexzeroClaude
andcommitted
[test] coach claude to write decent tests
Co-authored-by: Claude <[email protected]>
1 parent 444b5e4 commit d564e7d

File tree

1 file changed

+221
-27
lines changed

1 file changed

+221
-27
lines changed

packages/client/src/__tests__/sigstore.test.ts

Lines changed: 221 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ limitations under the License.
1515
*/
1616
import type { SerializedBundle } from '@sigstore/bundle';
1717
import { mockFulcio, mockRekor, mockTSA } from '@sigstore/mock';
18-
import { VerificationError } from '@sigstore/verify';
18+
import { VerificationError, Signer } from '@sigstore/verify';
1919
import { fromPartial } from '@total-typescript/shoehorn';
2020
import mocktuf, { Target } from '@tufjs/repo-mock';
2121
import { attest, createVerifier, sign, verify } from '../sigstore';
@@ -180,8 +180,12 @@ describe('#verify', () => {
180180
validBundles.v1.dsse.withSigningCert
181181
);
182182

183-
it('does not throw an error', async () => {
184-
await expect(verify(bundle, tufOptions)).resolves.toBe(undefined);
183+
it('returns a Signer object', async () => {
184+
const result = await verify(bundle, tufOptions);
185+
expect(result).toBeDefined();
186+
expect(result).toHaveProperty('key');
187+
expect(result.key).toBeDefined();
188+
expect(result).toHaveProperty('identity');
185189
}, 10000);
186190
});
187191

@@ -196,8 +200,11 @@ describe('#verify', () => {
196200
timeout: 0,
197201
};
198202

199-
it('does not throw an error', async () => {
200-
await expect(verify(bundle, options)).resolves.toBe(undefined);
203+
it('returns a Signer object', async () => {
204+
const result = await verify(bundle, options);
205+
expect(result).toBeDefined();
206+
expect(result).toHaveProperty('key');
207+
expect(result.key).toBeDefined();
201208
});
202209
});
203210

@@ -207,10 +214,12 @@ describe('#verify', () => {
207214
);
208215
const artifact = validBundles.artifact;
209216

210-
it('does not throw an error', async () => {
211-
await expect(verify(bundle, artifact, tufOptions)).resolves.toBe(
212-
undefined
213-
);
217+
it('returns a Signer object', async () => {
218+
const result = await verify(bundle, artifact, tufOptions);
219+
expect(result).toBeDefined();
220+
expect(result).toHaveProperty('key');
221+
expect(result.key).toBeDefined();
222+
expect(result).toHaveProperty('identity');
214223
});
215224
});
216225

@@ -220,10 +229,12 @@ describe('#verify', () => {
220229
);
221230
const artifact = validBundles.artifact;
222231

223-
it('does not throw an error', async () => {
224-
await expect(verify(bundle, artifact, tufOptions)).resolves.toBe(
225-
undefined
226-
);
232+
it('returns a Signer object', async () => {
233+
const result = await verify(bundle, artifact, tufOptions);
234+
expect(result).toBeDefined();
235+
expect(result).toHaveProperty('key');
236+
expect(result.key).toBeDefined();
237+
expect(result).toHaveProperty('identity');
227238
});
228239
});
229240

@@ -233,10 +244,12 @@ describe('#verify', () => {
233244
);
234245
const artifact = validBundles.artifact;
235246

236-
it('does not throw an error', async () => {
237-
await expect(verify(bundle, artifact, tufOptions)).resolves.toBe(
238-
undefined
239-
);
247+
it('returns a Signer object', async () => {
248+
const result = await verify(bundle, artifact, tufOptions);
249+
expect(result).toBeDefined();
250+
expect(result).toHaveProperty('key');
251+
expect(result.key).toBeDefined();
252+
expect(result).toHaveProperty('identity');
240253
});
241254
});
242255

@@ -246,10 +259,12 @@ describe('#verify', () => {
246259
);
247260
const artifact = validBundles.artifact;
248261

249-
it('does not throw an error', async () => {
250-
await expect(verify(bundle, artifact, tufOptions)).resolves.toBe(
251-
undefined
252-
);
262+
it('returns a Signer object', async () => {
263+
const result = await verify(bundle, artifact, tufOptions);
264+
expect(result).toBeDefined();
265+
expect(result).toHaveProperty('key');
266+
expect(result.key).toBeDefined();
267+
expect(result).toHaveProperty('identity');
253268
});
254269
});
255270

@@ -288,10 +303,107 @@ describe('#verify', () => {
288303

289304
const artifact = validBundles.artifact;
290305

291-
it('does not throw an error', async () => {
292-
await expect(verify(bundle, artifact, tufOptions)).resolves.toBe(
293-
undefined
294-
);
306+
it('returns a Signer object', async () => {
307+
const result = await verify(bundle, artifact, tufOptions);
308+
expect(result).toBeDefined();
309+
expect(result).toHaveProperty('key');
310+
expect(result.key).toBeDefined();
311+
expect(result).toHaveProperty('identity');
312+
});
313+
});
314+
});
315+
316+
describe('#verify - Signer object structure and properties', () => {
317+
let tufRepo: ReturnType<typeof mocktuf> | undefined;
318+
let tufOptions: VerifyOptions | undefined;
319+
320+
const trustedRootJSON = JSON.stringify(trustedRoot);
321+
const target: Target = {
322+
name: 'trusted_root.json',
323+
content: Buffer.from(trustedRootJSON),
324+
};
325+
326+
beforeEach(() => {
327+
tufRepo = mocktuf(target, { metadataPathPrefix: '' });
328+
tufOptions = {
329+
tufMirrorURL: tufRepo.baseURL,
330+
tufCachePath: tufRepo.cachePath,
331+
tufRootPath: path.join(tufRepo.cachePath, 'root.json'),
332+
certificateIssuer: 'https://github.com/login/oauth',
333+
};
334+
});
335+
336+
afterEach(() => tufRepo?.teardown());
337+
338+
describe('when verifying a DSSE bundle with certificate', () => {
339+
const bundle: SerializedBundle = fromPartial(
340+
validBundles.v1.dsse.withSigningCert
341+
);
342+
343+
it('returns a Signer with a valid key object', async () => {
344+
const result = await verify(bundle, tufOptions);
345+
expect(result).toMatchObject({
346+
key: expect.any(Object),
347+
identity: expect.any(Object),
348+
});
349+
350+
// Verify the key is a proper crypto.KeyObject
351+
expect(result.key).toHaveProperty('asymmetricKeyType');
352+
expect(typeof result.key.export).toBe('function');
353+
});
354+
355+
it('returns a Signer with certificate identity information', async () => {
356+
const result = await verify(bundle, tufOptions);
357+
expect(result.identity).toBeDefined();
358+
359+
// The identity should have either subjectAlternativeName or extensions
360+
expect(
361+
result.identity?.subjectAlternativeName ||
362+
result.identity?.extensions
363+
).toBeDefined();
364+
});
365+
366+
});
367+
368+
describe('when verifying a message signature bundle', () => {
369+
const bundle: SerializedBundle = fromPartial(
370+
validBundles.v1.messageSignature.withSigningCert
371+
);
372+
const artifact = validBundles.artifact;
373+
374+
it('returns a Signer object with key and identity', async () => {
375+
const result = await verify(bundle, artifact, tufOptions);
376+
377+
expect(result).toMatchObject({
378+
key: expect.any(Object),
379+
identity: expect.any(Object),
380+
});
381+
});
382+
383+
it('returns a key that can be used for cryptographic operations', async () => {
384+
const result = await verify(bundle, artifact, tufOptions);
385+
386+
// Verify we can export the public key
387+
expect(() => {
388+
result.key.export({ format: 'pem', type: 'spki' });
389+
}).not.toThrow();
390+
});
391+
});
392+
393+
describe('when verifying with public key', () => {
394+
const bundle: SerializedBundle = fromPartial(
395+
validBundles.v1.dsse.withPublicKey
396+
);
397+
const options: VerifyOptions = {
398+
...tufOptions,
399+
keySelector: (hint: string) => validBundles.publicKeys[hint],
400+
};
401+
402+
it('returns a Signer with key', async () => {
403+
const result = await verify(bundle, options);
404+
405+
expect(result).toHaveProperty('key');
406+
expect(result.key).toBeDefined();
295407
});
296408
});
297409
});
@@ -327,9 +439,13 @@ describe('#createVerifier', () => {
327439
validBundles.v1.dsse.withSigningCert
328440
);
329441

330-
it('does not throw an error when invoked', async () => {
442+
it('returns a Signer object when invoked', async () => {
331443
const verifier = await createVerifier(tufOptions!);
332-
expect(verifier.verify(bundle)).toBeUndefined();
444+
const result = verifier.verify(bundle);
445+
expect(result).toBeDefined();
446+
expect(result).toHaveProperty('key');
447+
expect(result.key).toBeDefined();
448+
expect(result).toHaveProperty('identity');
333449
});
334450
});
335451

@@ -345,4 +461,82 @@ describe('#createVerifier', () => {
345461
}).toThrowWithCode(VerificationError, 'TLOG_BODY_ERROR');
346462
});
347463
});
464+
465+
describe('#createVerifier - BundleVerifier.verify Signer return tests', () => {
466+
describe('when verifying valid bundles', () => {
467+
const bundle: SerializedBundle = fromPartial(
468+
validBundles.v1.dsse.withSigningCert
469+
);
470+
471+
it('BundleVerifier.verify returns Signer with proper structure', async () => {
472+
const verifier = await createVerifier(tufOptions!);
473+
const result = verifier.verify(bundle);
474+
475+
expect(result).toMatchObject({
476+
key: expect.any(Object),
477+
identity: expect.any(Object),
478+
});
479+
480+
// Test the Signer type properties
481+
expect(result.key).toHaveProperty('asymmetricKeyType');
482+
});
483+
484+
485+
it('BundleVerifier.verify throws error for invalid bundle but still returns Signer type when valid', async () => {
486+
const validVerifier = await createVerifier(tufOptions!);
487+
const invalidBundle: SerializedBundle = fromPartial(
488+
invalidBundles.v1.dsse.invalidBadSignature
489+
);
490+
491+
// Test that invalid bundles still throw errors
492+
expect(() => {
493+
validVerifier.verify(invalidBundle);
494+
}).toThrowWithCode(VerificationError, 'TLOG_BODY_ERROR');
495+
496+
// But valid bundles should return Signer
497+
const result = validVerifier.verify(bundle);
498+
expect(result).toMatchObject({
499+
key: expect.any(Object),
500+
identity: expect.any(Object),
501+
});
502+
});
503+
});
504+
505+
describe('when verifying with data payload', () => {
506+
const bundle: SerializedBundle = fromPartial(
507+
validBundles.v1.messageSignature.withSigningCert
508+
);
509+
const artifact = validBundles.artifact;
510+
511+
it('BundleVerifier.verify with data returns proper Signer', async () => {
512+
const verifier = await createVerifier(tufOptions!);
513+
const result = verifier.verify(bundle, artifact);
514+
515+
expect(result).toMatchObject({
516+
key: expect.any(Object),
517+
identity: expect.any(Object),
518+
});
519+
520+
// Verify identity contains expected certificate information
521+
expect(result.identity).toBeDefined();
522+
if (result.identity) {
523+
expect(
524+
result.identity.subjectAlternativeName || result.identity.extensions
525+
).toBeDefined();
526+
}
527+
});
528+
529+
it('BundleVerifier.verify returns Signer with working cryptographic key', async () => {
530+
const verifier = await createVerifier(tufOptions!);
531+
const result = verifier.verify(bundle, artifact);
532+
533+
// Ensure the key can be exported and used
534+
expect(() => {
535+
const exported = result.key.export({ format: 'pem', type: 'spki' });
536+
expect(typeof exported).toBe('string');
537+
expect(exported).toContain('-----BEGIN PUBLIC KEY-----');
538+
}).not.toThrow();
539+
});
540+
});
541+
});
348542
});

0 commit comments

Comments
 (0)