CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-loopback--testlab

A collection of test utilities specifically designed for LoopBack 4 applications and TypeScript testing

Pending
Quality

Pending

Does it follow best practices?

Impact

Pending

No eval scenarios have been run

SecuritybySnyk

Pending

The risk profile of this skill

Overview
Eval results
Files

http-utilities.mddocs/

HTTP Utilities

Async HTTP/HTTPS request utilities and server configuration helpers for test environments.

Capabilities

HTTP Request Utilities

Async wrappers for making HTTP and HTTPS requests.

/**
 * Async wrapper for making HTTP GET requests
 * @param urlString - URL to request
 * @param agent - Optional HTTP agent for connection pooling
 * @returns Promise resolving to IncomingMessage response
 */
function httpGetAsync(
  urlString: string,
  agent?: http.Agent
): Promise<IncomingMessage>;

/**
 * Async wrapper for making HTTPS GET requests
 * Uses a default agent that accepts self-signed certificates
 * @param urlString - HTTPS URL to request
 * @param agent - Optional HTTPS agent, defaults to permissive agent
 * @returns Promise resolving to IncomingMessage response
 */
function httpsGetAsync(
  urlString: string,
  agent?: https.Agent
): Promise<IncomingMessage>;

Usage Examples:

import { httpGetAsync, httpsGetAsync, expect } from "@loopback/testlab";
import http from "http";
import https from "https";

// Basic HTTP request
const response = await httpGetAsync("http://localhost:3000/api/status");
expect(response.statusCode).to.equal(200);

// Read response body
let body = "";
response.on("data", (chunk) => {
  body += chunk;
});
response.on("end", () => {
  const data = JSON.parse(body);
  expect(data.status).to.equal("ok");
});

// HTTPS request (accepts self-signed certificates by default)
const httpsResponse = await httpsGetAsync("https://localhost:3443/secure-endpoint");
expect(httpsResponse.statusCode).to.equal(200);

// Custom HTTP agent for connection pooling
const customAgent = new http.Agent({
  keepAlive: true,
  maxSockets: 5
});

const pooledResponse = await httpGetAsync("http://api.example.com/data", customAgent);

// Custom HTTPS agent with specific SSL options
const customHttpsAgent = new https.Agent({
  rejectUnauthorized: true, // Require valid certificates
  keepAlive: true
});

const secureResponse = await httpsGetAsync("https://api.example.com/secure", customHttpsAgent);

HTTP Server Configuration

Helper functions for creating HTTP/HTTPS server configurations suitable for testing.

/**
 * Create an HTTP-server configuration that works well in test environments
 * - Assigns ephemeral port (port 0)
 * - Uses IPv4 localhost (127.0.0.1) to avoid IPv6 issues
 * - Provides default TLS configuration for HTTPS
 * @param customConfig - Additional configuration options
 * @returns Complete server configuration with host and port
 */
function givenHttpServerConfig<T extends HttpOptions | HttpsOptions>(
  customConfig?: T
): HostPort & T;

/**
 * HTTP server configuration options
 */
interface HttpOptions extends ListenOptions {
  protocol?: 'http';
}

/**
 * HTTPS server configuration options
 */
interface HttpsOptions extends ListenOptions, HttpsServerOptions {
  protocol: 'https';
}

/**
 * Interface requiring host and port properties
 */
interface HostPort {
  host: string;
  port: number;
}

Usage Examples:

import { givenHttpServerConfig, expect } from "@loopback/testlab";
import http from "http";
import https from "https";

// Basic HTTP server configuration
const httpConfig = givenHttpServerConfig();
expect(httpConfig.host).to.equal("127.0.0.1");
expect(httpConfig.port).to.equal(0); // Ephemeral port

// Create HTTP server with test config
const httpServer = http.createServer((req, res) => {
  res.writeHead(200, {"Content-Type": "application/json"});
  res.end(JSON.stringify({message: "Hello"}));
});

httpServer.listen(httpConfig, () => {
  const address = httpServer.address() as any;
  console.log(`HTTP server listening on ${address.address}:${address.port}`);
});

// HTTPS server configuration with defaults
const httpsConfig = givenHttpServerConfig({protocol: "https"});
expect(httpsConfig.protocol).to.equal("https");
expect(httpsConfig.host).to.equal("127.0.0.1");
expect(httpsConfig.port).to.equal(0);
expect(httpsConfig).to.have.property("key"); // Default TLS key
expect(httpsConfig).to.have.property("cert"); // Default TLS cert

// Create HTTPS server with test config
const httpsServer = https.createServer(httpsConfig, (req, res) => {
  res.writeHead(200, {"Content-Type": "application/json"});
  res.end(JSON.stringify({secure: true}));
});

httpsServer.listen(httpsConfig, () => {
  const address = httpsServer.address() as any;
  console.log(`HTTPS server listening on ${address.address}:${address.port}`);
});

// Custom HTTP configuration
const customHttpConfig = givenHttpServerConfig({
  host: "0.0.0.0", // Accept from any interface
  port: 8080       // Specific port
});

// Custom HTTPS configuration with own certificates
const customHttpsConfig = givenHttpServerConfig({
  protocol: "https",
  key: fs.readFileSync("/path/to/private-key.pem"),
  cert: fs.readFileSync("/path/to/certificate.pem"),
  host: "localhost",
  port: 8443
});

Error Logging

HTTP error logging utilities for debugging test failures.

/**
 * Creates a logger that logs HTTP errors when status code is unexpected
 * @param expectedStatusCode - Status code that should not be logged
 * @returns Error logging function
 */
function createUnexpectedHttpErrorLogger(
  expectedStatusCode?: number
): LogError;

/**
 * Error logging function type
 */
type LogError = (err: Error, statusCode: number, request: Request) => void;

Usage Examples:

import { createUnexpectedHttpErrorLogger, expect } from "@loopback/testlab";
import { Request } from "express";

// Create logger that ignores 404 errors
const logger = createUnexpectedHttpErrorLogger(404);

// Simulate different error scenarios
const mockRequest = {method: "GET", url: "/api/test"} as Request;

// This will be logged (unexpected error)
logger(new Error("Database connection failed"), 500, mockRequest);
// Output: "Unhandled error in GET /api/test: 500 Error: Database connection failed..."

// This will NOT be logged (expected 404)
logger(new Error("Not found"), 404, mockRequest);
// No output

// Logger for successful requests (ignore 200)
const successLogger = createUnexpectedHttpErrorLogger(200);

// This will be logged (unexpected error)
successLogger(new Error("Validation failed"), 400, mockRequest);

// This will NOT be logged (expected success)
successLogger(new Error("Should not happen"), 200, mockRequest);

// Use in Express error handler
function errorHandler(err: Error, req: Request, res: Response, next: NextFunction) {
  const logger = createUnexpectedHttpErrorLogger();
  logger(err, res.statusCode || 500, req);
  
  res.status(500).json({error: "Internal server error"});
}

Integration with Test Servers

Complete examples of using HTTP utilities in test scenarios.

Usage Examples:

import { 
  givenHttpServerConfig, 
  httpGetAsync, 
  httpsGetAsync,
  expect 
} from "@loopback/testlab";
import http from "http";
import https from "https";

// Test HTTP server lifecycle
describe("HTTP Server Tests", () => {
  let server: http.Server;
  let serverUrl: string;

  beforeEach(async () => {
    const config = givenHttpServerConfig();
    
    server = http.createServer((req, res) => {
      if (req.url === "/ping") {
        res.writeHead(200, {"Content-Type": "application/json"});
        res.end(JSON.stringify({pong: true}));
      } else {
        res.writeHead(404);
        res.end("Not found");
      }
    });

    await new Promise<void>((resolve) => {
      server.listen(config, () => {
        const address = server.address() as any;
        serverUrl = `http://${address.address}:${address.port}`;
        resolve();
      });
    });
  });

  afterEach(async () => {
    await new Promise<void>((resolve) => {
      server.close(() => resolve());
    });
  });

  it("should respond to ping", async () => {
    const response = await httpGetAsync(`${serverUrl}/ping`);
    expect(response.statusCode).to.equal(200);
    
    let body = "";
    response.on("data", chunk => body += chunk);
    await new Promise(resolve => response.on("end", resolve));
    
    expect(JSON.parse(body)).to.eql({pong: true});
  });

  it("should return 404 for unknown routes", async () => {
    const response = await httpGetAsync(`${serverUrl}/unknown`);
    expect(response.statusCode).to.equal(404);
  });
});

// Test HTTPS server with custom certificates
describe("HTTPS Server Tests", () => {
  let server: https.Server;
  let serverUrl: string;

  beforeEach(async () => {
    const config = givenHttpServerConfig({protocol: "https"});
    
    server = https.createServer(config, (req, res) => {
      res.writeHead(200, {"Content-Type": "application/json"});
      res.end(JSON.stringify({secure: true, url: req.url}));
    });

    await new Promise<void>((resolve) => {
      server.listen(config, () => {
        const address = server.address() as any;
        serverUrl = `https://${address.address}:${address.port}`;
        resolve();
      });
    });
  });

  afterEach(async () => {
    await new Promise<void>((resolve) => {
      server.close(() => resolve());
    });
  });

  it("should handle HTTPS requests", async () => {
    // Uses default permissive agent for self-signed certificates
    const response = await httpsGetAsync(`${serverUrl}/secure`);
    expect(response.statusCode).to.equal(200);
    
    let body = "";
    response.on("data", chunk => body += chunk);
    await new Promise(resolve => response.on("end", resolve));
    
    const data = JSON.parse(body);
    expect(data.secure).to.be.true();
    expect(data.url).to.equal("/secure");
  });
});

// Test external API integration
describe("External API Tests", () => {
  it("should handle API responses", async () => {
    // Mock external service (you would replace with actual service in tests)
    const mockServer = http.createServer((req, res) => {
      res.writeHead(200, {"Content-Type": "application/json"});
      res.end(JSON.stringify({data: "mock response"}));
    });

    const config = givenHttpServerConfig();
    await new Promise<void>(resolve => mockServer.listen(config, resolve));
    
    const address = mockServer.address() as any;
    const mockUrl = `http://${address.address}:${address.port}`;

    // Test API call
    const response = await httpGetAsync(`${mockUrl}/api/data`);
    expect(response.statusCode).to.equal(200);

    await new Promise<void>(resolve => mockServer.close(() => resolve()));
  });
});

docs

assertions.md

http-client.md

http-utilities.md

index.md

request-response-mocking.md

test-doubles.md

test-sandbox.md

validation-helpers.md

tile.json