Back to Repositories

Testing Header Flush Operations in Koa.js Middleware

This test suite examines the flushHeaders functionality in Koa.js, focusing on header management and streaming responses. It verifies header sending behavior, status code handling, and error scenarios in HTTP responses.

Test Coverage Overview

The test suite provides comprehensive coverage of the flushHeaders() method implementation in Koa.js.

  • Header sending verification and timing
  • Response body handling after header flush
  • Status code preservation
  • Header modification restrictions post-flush
  • Streaming response scenarios

Implementation Analysis

The testing approach uses supertest and node:test for HTTP request simulation and assertions. It implements various test cases to verify header management behavior, including streaming responses and error handling patterns specific to Koa.js middleware architecture.

The tests utilize both synchronous and asynchronous patterns, with particular attention to timing-sensitive operations.

Technical Details

  • Testing Framework: node:test with describe/it blocks
  • HTTP Testing: supertest for request simulation
  • Assertion Library: Node.js assert module
  • Stream Testing: PassThrough streams
  • Network Testing: HTTP server setup and teardown

Best Practices Demonstrated

The test suite exemplifies robust testing practices for HTTP middleware components.

  • Isolated test cases with clear assertions
  • Proper error handling and cleanup
  • Stream error management
  • Header manipulation verification
  • Async/await pattern usage

koajs/koa

__tests__/response/flushHeaders.test.js

            
'use strict'

const { describe, it } = require('node:test')
const request = require('supertest')
const assert = require('assert')
const Koa = require('../..')
const http = require('http')

describe('ctx.flushHeaders()', () => {
  it('should set headersSent', () => {
    const app = new Koa()

    app.use((ctx, next) => {
      ctx.body = 'Body'
      ctx.status = 200
      ctx.flushHeaders()
      assert.strictEqual(ctx.res.headersSent, true)
    })

    return request(app.callback())
      .get('/')
      .expect(200)
      .expect('Body')
  })

  it('should allow a response afterwards', () => {
    const app = new Koa()

    app.use((ctx, next) => {
      ctx.status = 200
      ctx.res.setHeader('Content-Type', 'text/plain')
      ctx.flushHeaders()
      ctx.body = 'Body'
    })

    return request(app.callback())
      .get('/')
      .expect(200)
      .expect('Content-Type', 'text/plain')
      .expect('Body')
  })

  it('should send the correct status code', () => {
    const app = new Koa()

    app.use((ctx, next) => {
      ctx.status = 401
      ctx.res.setHeader('Content-Type', 'text/plain')
      ctx.flushHeaders()
      ctx.body = 'Body'
    })

    return request(app.callback())
      .get('/')
      .expect(401)
      .expect('Content-Type', 'text/plain')
      .expect('Body')
  })

  it('should ignore set header after flushHeaders', async () => {
    const app = new Koa()

    app.use((ctx, next) => {
      ctx.status = 401
      ctx.res.setHeader('Content-Type', 'text/plain')
      ctx.flushHeaders()
      ctx.body = 'foo'
      ctx.set('X-Shouldnt-Work', 'Value')
      ctx.remove('Content-Type')
      ctx.vary('Content-Type')
    })

    const res = await request(app.callback())
      .get('/')
      .expect(401)
      .expect('Content-Type', 'text/plain')

    assert.strictEqual(res.headers['x-shouldnt-work'], undefined, 'header set after flushHeaders')
    assert.strictEqual(res.headers.vary, undefined, 'header set after flushHeaders')
  })

  it('should flush headers first and delay to send data', (t, done) => {
    const PassThrough = require('stream').PassThrough
    const app = new Koa()

    app.use(ctx => {
      ctx.type = 'json'
      ctx.status = 200
      ctx.headers.Link = '</css/mycss.css>; as=style; rel=preload, <https://img.craftflair.com>; rel=preconnect; crossorigin'
      const stream = ctx.body = new PassThrough()
      ctx.flushHeaders()
      setTimeout(() => {
        stream.end(JSON.stringify({ message: 'hello!' }))
      })
    })

    app.listen(async function (err) {
      if (err) return done(err)

      const server = this
      const port = this.address().port

      http.request({
        port
      })
        .on('response', res => {
          const onData = () => done(new Error('boom'))
          res.on('data', onData)

          // shouldn't receive any data for a while
          res.removeListener('data', onData)
          res.destroy()
          done()
          server.close()
        })
        .on('error', done)
        .end()
    })
  })

  it('should catch stream error', (t, done) => {
    const PassThrough = require('stream').PassThrough
    const app = new Koa()
    app.once('error', err => {
      assert(err.message === 'mock error')
    })

    app.use(ctx => {
      ctx.type = 'json'
      ctx.status = 200
      ctx.headers.Link = '</css/mycss.css>; as=style; rel=preload, <https://img.craftflair.com>; rel=preconnect; crossorigin'
      ctx.length = 20
      ctx.flushHeaders()
      const stream = ctx.body = new PassThrough()

      setTimeout(() => {
        stream.emit('error', new Error('mock error'))
        stream.end()
      })
    })

    request(app.callback()).get('/').end((err) => {
      assert(err.message === 'aborted')
      done()
    })
  })
})