diff --git a/.changeset/nip-11-integration-tests.md b/.changeset/nip-11-integration-tests.md new file mode 100644 index 00000000..fde71e52 --- /dev/null +++ b/.changeset/nip-11-integration-tests.md @@ -0,0 +1,5 @@ +--- +"nostream": patch +--- + +Add NIP-11 integration tests and fix max_filters mapping in relay information document. diff --git a/src/handlers/request-handlers/root-request-handler.ts b/src/handlers/request-handlers/root-request-handler.ts index 674d588c..d3502fa3 100644 --- a/src/handlers/request-handlers/root-request-handler.ts +++ b/src/handlers/request-handlers/root-request-handler.ts @@ -36,7 +36,7 @@ export const rootRequestHandler = (request: Request, response: Response, next: N limitation: { max_message_length: settings.network.maxPayloadSize, max_subscriptions: settings.limits?.client?.subscription?.maxSubscriptions, - max_filters: settings.limits?.client?.subscription?.maxFilterValues, + max_filters: settings.limits?.client?.subscription?.maxFilters, max_limit: settings.limits?.client?.subscription?.maxLimit, max_subid_length: settings.limits?.client?.subscription?.maxSubscriptionIdLength, min_prefix: settings.limits?.client?.subscription?.minPrefixLength, diff --git a/test/integration/features/nip-11/nip-11.feature b/test/integration/features/nip-11/nip-11.feature new file mode 100644 index 00000000..6e1bd4c0 --- /dev/null +++ b/test/integration/features/nip-11/nip-11.feature @@ -0,0 +1,25 @@ +Feature: NIP-11 + Scenario: Relay returns information document for NIP-11 request + When a client requests the relay information document + Then the response status is 200 + And the response Content-Type includes "application/nostr+json" + And the relay information document contains the required fields + + Scenario: Relay information document lists supported NIPs from package.json + When a client requests the relay information document + Then the supported_nips field matches the NIPs declared in package.json + + Scenario: Relay does not return information document for a non-NIP-11 Accept header + When a client requests the root path with Accept header "text/html" + Then the response Content-Type does not include "application/nostr+json" + And the response body is not a relay information document + + Scenario: Relay information document reports max_filters from settings + When a client requests the relay information document + Then the limitation object contains a max_filters field + + Scenario: WebSocket connections coexist with HTTP on the same port + Given someone called Alice + When Alice sends a text_note event with content "nostr is great" + And Alice subscribes to author Alice + Then Alice receives a text_note event from Alice with content "nostr is great" diff --git a/test/integration/features/nip-11/nip-11.feature.ts b/test/integration/features/nip-11/nip-11.feature.ts new file mode 100644 index 00000000..79aa1e13 --- /dev/null +++ b/test/integration/features/nip-11/nip-11.feature.ts @@ -0,0 +1,72 @@ +import { Then, When, World } from '@cucumber/cucumber' +import axios, { AxiosResponse } from 'axios' +import chai from 'chai' + +import packageJson from '../../../../package.json' +import { createSettings } from '../../../../src/factories/settings-factory' + +chai.use(require('sinon-chai')) +const { expect } = chai + +const BASE_URL = 'http://localhost:18808' + +When('a client requests the relay information document', async function(this: World>) { + const response: AxiosResponse = await axios.get(BASE_URL, { + headers: { Accept: 'application/nostr+json' }, + validateStatus: () => true, + }) + this.parameters.httpResponse = response +}) + +When('a client requests the root path with Accept header {string}', async function( + this: World>, + acceptHeader: string, +) { + const response: AxiosResponse = await axios.get(BASE_URL, { + headers: { Accept: acceptHeader }, + validateStatus: () => true, + }) + this.parameters.httpResponse = response +}) + +Then('the response status is {int}', function(this: World>, status: number) { + expect(this.parameters.httpResponse.status).to.equal(status) +}) + +Then('the response Content-Type includes {string}', function( + this: World>, + contentType: string, +) { + expect(this.parameters.httpResponse.headers['content-type']).to.include(contentType) +}) + +Then('the response Content-Type does not include {string}', function( + this: World>, + contentType: string, +) { + expect(this.parameters.httpResponse.headers['content-type']).to.not.include(contentType) +}) + +Then('the relay information document contains the required fields', function(this: World>) { + const doc = this.parameters.httpResponse.data + for (const field of ['name', 'description', 'pubkey', 'supported_nips', 'software', 'version']) { + expect(doc, `expected relay info doc to have field "${field}"`).to.have.property(field) + } +}) + +Then('the supported_nips field matches the NIPs declared in package.json', function(this: World>) { + const doc = this.parameters.httpResponse.data + expect(doc.supported_nips).to.deep.equal(packageJson.supportedNips) +}) + +Then('the response body is not a relay information document', function(this: World>) { + const body = this.parameters.httpResponse.data + const isRelayInfoDoc = typeof body === 'object' && body !== null && 'supported_nips' in body + expect(isRelayInfoDoc).to.equal(false) +}) + +Then('the limitation object contains a max_filters field', function(this: World>) { + const doc = this.parameters.httpResponse.data + const expectedMaxFilters = createSettings().limits?.client?.subscription?.maxFilters + expect(doc.limitation.max_filters).to.equal(expectedMaxFilters) +})