diff --git a/.changeset/nip-22-created-at-tests-empty.md b/.changeset/nip-22-created-at-tests-empty.md new file mode 100644 index 00000000..267ed9cc --- /dev/null +++ b/.changeset/nip-22-created-at-tests-empty.md @@ -0,0 +1,4 @@ +--- +--- + +Add empty changeset for NIP-22 created_at integration test coverage (issue #505). diff --git a/test/integration/features/nip-22/nip-22.feature b/test/integration/features/nip-22/nip-22.feature new file mode 100644 index 00000000..53a5df27 --- /dev/null +++ b/test/integration/features/nip-22/nip-22.feature @@ -0,0 +1,29 @@ +@nip-22 +Feature: NIP-22 created_at timestamp limits + Scenario: Event with created_at at current time is accepted + Given someone called Alice + And created_at limits are set to maxPositiveDelta 900 and maxNegativeDelta 0 + When Alice drafts a text_note event with content "test event" and created_at 0 seconds from now + Then Alice sends their last draft event successfully + When Alice subscribes to author Alice + Then Alice receives a text_note event from Alice with content "test event" + + Scenario: Event with created_at above positive delta limit is rejected + Given someone called Alice + And created_at limits are set to maxPositiveDelta 900 and maxNegativeDelta 0 + When Alice drafts a text_note event with content "test event" and created_at 910 seconds from now + Then Alice sends their last draft event unsuccessfully with reason containing "rejected" + + Scenario: Event older than configured negative delta limit is rejected + Given someone called Alice + And created_at limits are set to maxPositiveDelta 900 and maxNegativeDelta 3600 + When Alice drafts a text_note event with content "test event" and created_at -3601 seconds from now + Then Alice sends their last draft event unsuccessfully with reason containing "rejected" + + Scenario: Event within configured negative delta limit is accepted + Given someone called Alice + And created_at limits are set to maxPositiveDelta 900 and maxNegativeDelta 3600 + When Alice drafts a text_note event with content "test event" and created_at -3590 seconds from now + Then Alice sends their last draft event successfully + When Alice subscribes to author Alice + Then Alice receives a text_note event from Alice with content "test event" diff --git a/test/integration/features/nip-22/nip-22.feature.ts b/test/integration/features/nip-22/nip-22.feature.ts new file mode 100644 index 00000000..c89dbf5d --- /dev/null +++ b/test/integration/features/nip-22/nip-22.feature.ts @@ -0,0 +1,82 @@ +import { After, Before, Given, Then, When } from '@cucumber/cucumber' +import { assocPath, pipe } from 'ramda' + +import { CommandResult, MessageType } from '../../../../src/@types/messages' +import { createEvent, sendEvent } from '../helpers' + +import { Event } from '../../../../src/@types/event' +import { expect } from 'chai' +import { isDraft } from '../shared' +import { SettingsStatic } from '../../../../src/utils/settings' +import WebSocket from 'ws' + +const previousSettingsSnapshot = Symbol('nip22PreviousSettingsSnapshot') + +const setCreatedAtLimits = (maxPositiveDelta: number, maxNegativeDelta: number) => { + const settings = SettingsStatic._settings ?? SettingsStatic.createSettings() + + SettingsStatic._settings = pipe( + assocPath(['limits', 'event', 'createdAt', 'maxPositiveDelta'], maxPositiveDelta), + assocPath(['limits', 'event', 'createdAt', 'maxNegativeDelta'], maxNegativeDelta), + )(settings) as any +} + +Before({ tags: '@nip-22' }, function(this: any) { + this[previousSettingsSnapshot] = SettingsStatic._settings +}) + +After({ tags: '@nip-22' }, function(this: any) { + SettingsStatic._settings = this[previousSettingsSnapshot] + delete this[previousSettingsSnapshot] +}) + +Given(/^created_at limits are set to maxPositiveDelta (\d+) and maxNegativeDelta (\d+)$/, function( + maxPositiveDelta: string, + maxNegativeDelta: string, +) { + setCreatedAtLimits(Number(maxPositiveDelta), Number(maxNegativeDelta)) +}) + +When(/^(\w+) drafts a text_note event with content "([^"]+)" and created_at (-?\d+) seconds from now$/, async function( + name: string, + content: string, + offsetSeconds: string, +) { + const { pubkey, privkey } = this.parameters.identities[name] + const createdAt = Math.floor(Date.now() / 1000) + Number(offsetSeconds) + + const event: Event = await createEvent( + { + pubkey, + kind: 1, + content, + created_at: createdAt, + }, + privkey, + ) + + const draftEvent = event as any + draftEvent[isDraft] = true + + this.parameters.events[name].push(event) +}) + +Then(/^(\w+) sends their last draft event unsuccessfully with reason containing "([^"]+)"$/, async function( + name: string, + expectedReason: string, +) { + const ws = this.parameters.clients[name] as WebSocket + + const event = this.parameters.events[name].findLast((lastEvent: Event) => (lastEvent as any)[isDraft]) + if (!event) { + throw new Error(`No draft event found for ${name}`) + } + + delete (event as any)[isDraft] + + const command = await sendEvent(ws, event, false) as CommandResult + + expect(command[0]).to.equal(MessageType.OK) + expect(command[2]).to.equal(false) + expect(command[3].toLowerCase()).to.contain(expectedReason.toLowerCase()) +}) diff --git a/test/unit/handlers/event-message-handler.spec.ts b/test/unit/handlers/event-message-handler.spec.ts index 8c3f4685..10e25611 100644 --- a/test/unit/handlers/event-message-handler.spec.ts +++ b/test/unit/handlers/event-message-handler.spec.ts @@ -291,9 +291,9 @@ describe('EventMessageHandler', () => { eventLimits.createdAt.maxPositiveDelta = 100 event.created_at += 101 - expect((handler as any).canAcceptEvent(event)).to.equal( - 'rejected: created_at is more than 100 seconds in the future', - ) + expect( + (handler as any).canAcceptEvent(event) + ).to.equal('rejected: created_at is more than 100 seconds in the future') }) })