From 57b1364cc056af00d54eb243eee5a3f93873259a Mon Sep 17 00:00:00 2001 From: ABHAY PANDEY Date: Mon, 25 May 2026 09:33:38 +0530 Subject: [PATCH] test(integration): verify response content-types across HTTP routes Signed-off-by: ABHAY PANDEY --- .../response-types-integration-tests.md | 5 ++ .../response-types/response-types.feature | 39 +++++++++++++ .../response-types/response-types.feature.ts | 58 +++++++++++++++++++ 3 files changed, 102 insertions(+) create mode 100644 .changeset/response-types-integration-tests.md create mode 100644 test/integration/features/response-types/response-types.feature create mode 100644 test/integration/features/response-types/response-types.feature.ts diff --git a/.changeset/response-types-integration-tests.md b/.changeset/response-types-integration-tests.md new file mode 100644 index 00000000..70c5126e --- /dev/null +++ b/.changeset/response-types-integration-tests.md @@ -0,0 +1,5 @@ +--- +"nostream": patch +--- + +test(integration): verify response content-type across core HTTP paths diff --git a/test/integration/features/response-types/response-types.feature b/test/integration/features/response-types/response-types.feature new file mode 100644 index 00000000..fc0d8733 --- /dev/null +++ b/test/integration/features/response-types/response-types.feature @@ -0,0 +1,39 @@ +Feature: HTTP response types + Scenario Outline: GET path returns expected response Content-Type + When a client requests path "" with Accept header "" + Then the HTTP response status is + And the HTTP response Content-Type includes "" + + Examples: + | path | acceptHeader | statusCode | contentType | + | / | application/nostr+json | 200 | application/nostr+json | + | / | text/html | 200 | text/html | + | /healthz | */* | 200 | text/plain | + | /terms | */* | 200 | text/html | + | /privacy | */* | 200 | text/html | + | /.well-known/nodeinfo | */* | 200 | application/json | + | /nodeinfo/2.1 | */* | 200 | application/json | + | /nodeinfo/2.0 | */* | 200 | application/json | + + Scenario Outline: dynamic GET path returns expected response Content-Type + When a client requests dynamic path "" + Then the HTTP response status is + And the HTTP response Content-Type includes "" + + Examples: + | path | statusCode | contentType | + | /admissions/check/0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef | 200 | application/json | + | /invoices/non-existent-invoice/status | 404 | application/json | + + Scenario Outline: POST path returns expected response Content-Type + When a client posts "" to path "" with Content-Type "" + Then the HTTP response status is + And the HTTP response Content-Type includes "" + + Examples: + | path | contentTypeHeader | body | statusCode | contentType | + | /invoices | application/x-www-form-urlencoded | | 400 | text/plain | + | /callbacks/zebedee | application/json | {} | 403 | text/html | + | /callbacks/lnbits | application/json | {} | 403 | text/html | + | /callbacks/opennode | application/x-www-form-urlencoded | id=test&status=paid | 403 | text/html | + | /callbacks/nodeless | application/json | {} | 403 | text/html | diff --git a/test/integration/features/response-types/response-types.feature.ts b/test/integration/features/response-types/response-types.feature.ts new file mode 100644 index 00000000..3ac9c800 --- /dev/null +++ b/test/integration/features/response-types/response-types.feature.ts @@ -0,0 +1,58 @@ +import { Then, When, World } from '@cucumber/cucumber' +import { expect } from 'chai' +import axios, { AxiosResponse } from 'axios' + +const BASE_URL = 'http://localhost:18808' + +When( + 'a client requests path {string} with Accept header {string}', + async function (this: World>, requestPath: string, acceptHeader: string) { + const response: AxiosResponse = await axios.get(`${BASE_URL}${requestPath}`, { + headers: { Accept: acceptHeader }, + validateStatus: () => true, + }) + + this.parameters.httpResponse = response + }, +) + +When('a client requests dynamic path {string}', async function (this: World>, requestPath: string) { + const response: AxiosResponse = await axios.get(`${BASE_URL}${requestPath}`, { + validateStatus: () => true, + }) + + this.parameters.httpResponse = response +}) + +When( + 'a client posts {string} to path {string} with Content-Type {string}', + async function ( + this: World>, + body: string, + requestPath: string, + contentTypeHeader: string, + ) { + const response: AxiosResponse = await axios.post(`${BASE_URL}${requestPath}`, body, { + headers: { 'content-type': contentTypeHeader }, + validateStatus: () => true, + }) + + this.parameters.httpResponse = response + }, +) + +Then('the HTTP response status is {int}', function (this: World>, statusCode: number) { + expect(this.parameters.httpResponse.status).to.equal(statusCode) +}) + +Then( + 'the HTTP response Content-Type includes {string}', + function (this: World>, contentType: string) { + // Normalize header value to plain string and compare case-insensitively + // so assertions are robust to header casing/charset formatting variations. + const contentTypeHeader = this.parameters.httpResponse.headers['content-type'] + const headerValue = Array.isArray(contentTypeHeader) ? contentTypeHeader.join(';') : contentTypeHeader + const normalizedHeader = typeof headerValue === 'string' ? headerValue.toLowerCase() : '' + expect(normalizedHeader).to.include(contentType.toLowerCase()) + }, +)