From 9fd46d2fa6e47adf99c7501f5482f7f32a676bd5 Mon Sep 17 00:00:00 2001 From: Priyanshubhartistm Date: Mon, 20 Apr 2026 05:32:11 +0530 Subject: [PATCH 1/4] test: add NIP-22 created_at integration coverage --- src/handlers/event-message-handler.ts | 2 +- .../features/nip-22/nip-22.feature | 37 +++++++++ .../features/nip-22/nip-22.feature.ts | 75 +++++++++++++++++++ .../handlers/event-message-handler.spec.ts | 6 +- 4 files changed, 116 insertions(+), 4 deletions(-) create mode 100644 test/integration/features/nip-22/nip-22.feature create mode 100644 test/integration/features/nip-22/nip-22.feature.ts diff --git a/src/handlers/event-message-handler.ts b/src/handlers/event-message-handler.ts index 235b1c13..6a846846 100644 --- a/src/handlers/event-message-handler.ts +++ b/src/handlers/event-message-handler.ts @@ -165,7 +165,7 @@ export class EventMessageHandler implements IMessageHandler { limits.createdAt.maxPositiveDelta > 0 && event.created_at > now + limits.createdAt.maxPositiveDelta ) { - return `rejected: created_at is more than ${limits.createdAt.maxPositiveDelta} seconds in the future` + return `invalid: created_at is more than ${limits.createdAt.maxPositiveDelta} seconds in the future` } if ( 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..aed23022 --- /dev/null +++ b/test/integration/features/nip-22/nip-22.feature @@ -0,0 +1,37 @@ +@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 at positive delta limit 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 900 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 901 seconds from now + Then Alice sends their last draft event unsuccessfully with reason containing "invalid" + + 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..05f99c23 --- /dev/null +++ b/test/integration/features/nip-22/nip-22.feature.ts @@ -0,0 +1,75 @@ +import { After, 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 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 +} + +After({ tags: '@nip-22' }, function() { + setCreatedAtLimits(900, 0) +}) + +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..5bd7f649 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('invalid: created_at is more than 100 seconds in the future') }) }) From 37669d281a16007da535548ea5c28a9369cce624 Mon Sep 17 00:00:00 2001 From: Priyanshubhartistm Date: Mon, 20 Apr 2026 05:42:01 +0530 Subject: [PATCH 2/4] chore: add empty changeset for nip-22 tests --- .changeset/nip-22-created-at-tests-empty.md | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 .changeset/nip-22-created-at-tests-empty.md 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). From cbec93c7d2ac6e067723ef08ceca6eb149cb96fe Mon Sep 17 00:00:00 2001 From: Priyanshubhartistm Date: Mon, 20 Apr 2026 19:04:42 +0530 Subject: [PATCH 3/4] fix: address nip-22 review feedback --- src/handlers/event-message-handler.ts | 2 +- test/integration/features/nip-22/nip-22.feature | 12 ++---------- test/integration/features/nip-22/nip-22.feature.ts | 13 ++++++++++--- 3 files changed, 13 insertions(+), 14 deletions(-) diff --git a/src/handlers/event-message-handler.ts b/src/handlers/event-message-handler.ts index 6a846846..235b1c13 100644 --- a/src/handlers/event-message-handler.ts +++ b/src/handlers/event-message-handler.ts @@ -165,7 +165,7 @@ export class EventMessageHandler implements IMessageHandler { limits.createdAt.maxPositiveDelta > 0 && event.created_at > now + limits.createdAt.maxPositiveDelta ) { - return `invalid: created_at is more than ${limits.createdAt.maxPositiveDelta} seconds in the future` + return `rejected: created_at is more than ${limits.createdAt.maxPositiveDelta} seconds in the future` } if ( diff --git a/test/integration/features/nip-22/nip-22.feature b/test/integration/features/nip-22/nip-22.feature index aed23022..53a5df27 100644 --- a/test/integration/features/nip-22/nip-22.feature +++ b/test/integration/features/nip-22/nip-22.feature @@ -8,19 +8,11 @@ Feature: NIP-22 created_at timestamp limits When Alice subscribes to author Alice Then Alice receives a text_note event from Alice with content "test event" - Scenario: Event with created_at at positive delta limit 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 900 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 901 seconds from now - Then Alice sends their last draft event unsuccessfully with reason containing "invalid" + 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 diff --git a/test/integration/features/nip-22/nip-22.feature.ts b/test/integration/features/nip-22/nip-22.feature.ts index 05f99c23..c89dbf5d 100644 --- a/test/integration/features/nip-22/nip-22.feature.ts +++ b/test/integration/features/nip-22/nip-22.feature.ts @@ -1,4 +1,4 @@ -import { After, Given, Then, When } from '@cucumber/cucumber' +import { After, Before, Given, Then, When } from '@cucumber/cucumber' import { assocPath, pipe } from 'ramda' import { CommandResult, MessageType } from '../../../../src/@types/messages' @@ -10,6 +10,8 @@ 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() @@ -19,8 +21,13 @@ const setCreatedAtLimits = (maxPositiveDelta: number, maxNegativeDelta: number) )(settings) as any } -After({ tags: '@nip-22' }, function() { - setCreatedAtLimits(900, 0) +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( From 901b58997cadb6e91b521278039329e0a4d31f26 Mon Sep 17 00:00:00 2001 From: Priyanshubhartistm Date: Mon, 20 Apr 2026 19:32:13 +0530 Subject: [PATCH 4/4] test: align created_at reason expectation --- test/unit/handlers/event-message-handler.spec.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/unit/handlers/event-message-handler.spec.ts b/test/unit/handlers/event-message-handler.spec.ts index 5bd7f649..10e25611 100644 --- a/test/unit/handlers/event-message-handler.spec.ts +++ b/test/unit/handlers/event-message-handler.spec.ts @@ -293,7 +293,7 @@ describe('EventMessageHandler', () => { expect( (handler as any).canAcceptEvent(event) - ).to.equal('invalid: created_at is more than 100 seconds in the future') + ).to.equal('rejected: created_at is more than 100 seconds in the future') }) })