import { describe, test, expect } from 'bun:test' import { finishEvent, serializeEvent, getEventHash, validateEvent, verifySignature, getSignature, verifiedSymbol, } from './event.ts' import { getPublicKey } from './keys.ts' import { ShortTextNote } from './kinds.ts' describe('Event', () => { describe('finishEvent', () => { test('should create a signed event from a template', () => { const privateKey = 'd217c1ff2f8a65c3e3a1740db3b9f58b8c848bb45e26d00ed4714e4a0f4ceecf' const publicKey = getPublicKey(privateKey) const template = { kind: ShortTextNote, tags: [], content: 'Hello, world!', created_at: 1617932115, } const event = finishEvent(template, privateKey) expect(event.kind).toEqual(template.kind) expect(event.tags).toEqual(template.tags) expect(event.content).toEqual(template.content) expect(event.created_at).toEqual(template.created_at) expect(event.pubkey).toEqual(publicKey) expect(typeof event.id).toEqual('string') expect(typeof event.sig).toEqual('string') }) }) describe('serializeEvent', () => { test('should serialize a valid event object', () => { const privateKey = 'd217c1ff2f8a65c3e3a1740db3b9f58b8c848bb45e26d00ed4714e4a0f4ceecf' const publicKey = getPublicKey(privateKey) const unsignedEvent = { pubkey: publicKey, created_at: 1617932115, kind: ShortTextNote, tags: [], content: 'Hello, world!', } const serializedEvent = serializeEvent(unsignedEvent) expect(serializedEvent).toEqual( JSON.stringify([ 0, publicKey, unsignedEvent.created_at, unsignedEvent.kind, unsignedEvent.tags, unsignedEvent.content, ]), ) }) test('should throw an error for an invalid event object', () => { const privateKey = 'd217c1ff2f8a65c3e3a1740db3b9f58b8c848bb45e26d00ed4714e4a0f4ceecf' const publicKey = getPublicKey(privateKey) const invalidEvent = { kind: ShortTextNote, tags: [], created_at: 1617932115, pubkey: publicKey, // missing content } expect(() => { // @ts-expect-error serializeEvent(invalidEvent) }).toThrow("can't serialize event with wrong or missing properties") }) }) describe('getEventHash', () => { test('should return the correct event hash', () => { const privateKey = 'd217c1ff2f8a65c3e3a1740db3b9f58b8c848bb45e26d00ed4714e4a0f4ceecf' const publicKey = getPublicKey(privateKey) const unsignedEvent = { kind: ShortTextNote, tags: [], content: 'Hello, world!', created_at: 1617932115, pubkey: publicKey, } const eventHash = getEventHash(unsignedEvent) expect(typeof eventHash).toEqual('string') expect(eventHash.length).toEqual(64) }) }) describe('validateEvent', () => { test('should return true for a valid event object', () => { const privateKey = 'd217c1ff2f8a65c3e3a1740db3b9f58b8c848bb45e26d00ed4714e4a0f4ceecf' const publicKey = getPublicKey(privateKey) const unsignedEvent = { kind: ShortTextNote, tags: [], content: 'Hello, world!', created_at: 1617932115, pubkey: publicKey, } const isValid = validateEvent(unsignedEvent) expect(isValid).toEqual(true) }) test('should return false for a non object event', () => { const nonObjectEvent = '' const isValid = validateEvent(nonObjectEvent) expect(isValid).toEqual(false) }) test('should return false for an event object with missing properties', () => { const invalidEvent = { kind: ShortTextNote, tags: [], created_at: 1617932115, // missing content and pubkey } const isValid = validateEvent(invalidEvent) expect(isValid).toEqual(false) }) test('should return false for an empty object', () => { const emptyObj = {} const isValid = validateEvent(emptyObj) expect(isValid).toEqual(false) }) test('should return false for an object with invalid properties', () => { const privateKey = 'd217c1ff2f8a65c3e3a1740db3b9f58b8c848bb45e26d00ed4714e4a0f4ceecf' const publicKey = getPublicKey(privateKey) const invalidEvent = { kind: 1, tags: [], created_at: '1617932115', // should be a number pubkey: publicKey, } const isValid = validateEvent(invalidEvent) expect(isValid).toEqual(false) }) test('should return false for an object with an invalid public key', () => { const invalidEvent = { kind: 1, tags: [], content: 'Hello, world!', created_at: 1617932115, pubkey: 'invalid_pubkey', } const isValid = validateEvent(invalidEvent) expect(isValid).toEqual(false) }) test('should return false for an object with invalid tags', () => { const privateKey = 'd217c1ff2f8a65c3e3a1740db3b9f58b8c848bb45e26d00ed4714e4a0f4ceecf' const publicKey = getPublicKey(privateKey) const invalidEvent = { kind: 1, tags: {}, // should be an array content: 'Hello, world!', created_at: 1617932115, pubkey: publicKey, } const isValid = validateEvent(invalidEvent) expect(isValid).toEqual(false) }) }) describe('verifySignature', () => { test('should return true for a valid event signature', () => { const privateKey = 'd217c1ff2f8a65c3e3a1740db3b9f58b8c848bb45e26d00ed4714e4a0f4ceecf' const event = finishEvent( { kind: ShortTextNote, tags: [], content: 'Hello, world!', created_at: 1617932115, }, privateKey, ) const isValid = verifySignature(event) expect(isValid).toEqual(true) }) test('should return false for an invalid event signature', () => { const privateKey = 'd217c1ff2f8a65c3e3a1740db3b9f58b8c848bb45e26d00ed4714e4a0f4ceecf' const { [verifiedSymbol]: _, ...event } = finishEvent( { kind: ShortTextNote, tags: [], content: 'Hello, world!', created_at: 1617932115, }, privateKey, ) // tamper with the signature event.sig = event.sig.replace(/^.{3}/g, '666') const isValid = verifySignature(event) expect(isValid).toEqual(false) }) test('should return false when verifying an event with a different private key', () => { const privateKey1 = 'd217c1ff2f8a65c3e3a1740db3b9f58b8c848bb45e26d00ed4714e4a0f4ceecf' const privateKey2 = '5b4a34f4e4b23c63ad55a35e3f84a3b53d96dbf266edf521a8358f71d19cbf67' const publicKey2 = getPublicKey(privateKey2) const { [verifiedSymbol]: _, ...event } = finishEvent( { kind: ShortTextNote, tags: [], content: 'Hello, world!', created_at: 1617932115, }, privateKey1, ) // verify with different private key const isValid = verifySignature({ ...event, pubkey: publicKey2, }) expect(isValid).toEqual(false) }) test('should return false for an invalid event id', () => { const privateKey = 'd217c1ff2f8a65c3e3a1740db3b9f58b8c848bb45e26d00ed4714e4a0f4ceecf' const { [verifiedSymbol]: _, ...event } = finishEvent( { kind: 1, tags: [], content: 'Hello, world!', created_at: 1617932115, }, privateKey, ) // tamper with the id event.id = event.id.replace(/^.{3}/g, '666') const isValid = verifySignature(event) expect(isValid).toEqual(false) }) }) describe('getSignature', () => { test('should produce the correct signature for an event object', () => { const privateKey = 'd217c1ff2f8a65c3e3a1740db3b9f58b8c848bb45e26d00ed4714e4a0f4ceecf' const publicKey = getPublicKey(privateKey) const unsignedEvent = { kind: ShortTextNote, tags: [], content: 'Hello, world!', created_at: 1617932115, pubkey: publicKey, } const sig = getSignature(unsignedEvent, privateKey) // verify the signature const isValid = verifySignature({ ...unsignedEvent, id: getEventHash(unsignedEvent), sig, }) expect(typeof sig).toEqual('string') expect(sig.length).toEqual(128) expect(isValid).toEqual(true) }) test('should not sign an event with different private key', () => { const privateKey = 'd217c1ff2f8a65c3e3a1740db3b9f58b8c848bb45e26d00ed4714e4a0f4ceecf' const publicKey = getPublicKey(privateKey) const wrongPrivateKey = 'a91e2a9d9e0f70f0877bea0dbf034e8f95d7392a27a7f07da0d14b9e9d456be7' const unsignedEvent = { kind: ShortTextNote, tags: [], content: 'Hello, world!', created_at: 1617932115, pubkey: publicKey, } const sig = getSignature(unsignedEvent, wrongPrivateKey) // verify the signature // @ts-expect-error const isValid = verifySignature({ ...unsignedEvent, sig, }) expect(typeof sig).toEqual('string') expect(sig.length).toEqual(128) expect(isValid).toEqual(false) }) }) })