Skip to content

Examples

Real-world examples for common test port scenarios.

Integration Test Server

typescript
import { createServer } from 'node:http';
import { PortResolver } from '@tuulbelt/port-resolver';

async function startTestServer() {
  const resolver = new PortResolver();
  const result = await resolver.get({ tag: 'integration-server' });

  if (!result.ok) {
    throw new Error(`Failed to get port: ${result.error.message}`);
  }

  const port = result.value.port;

  const server = createServer((req, res) => {
    res.writeHead(200, { 'Content-Type': 'application/json' });
    res.end(JSON.stringify({ status: 'ok' }));
  });

  return new Promise<{ server: typeof server; port: number; cleanup: () => Promise<void> }>((resolve) => {
    server.listen(port, () => {
      console.log(`Test server running on port ${port}`);
      resolve({
        server,
        port,
        cleanup: async () => {
          server.close();
          await resolver.release(port);
        }
      });
    });
  });
}

// Usage
const { server, port, cleanup } = await startTestServer();
// Run tests against http://localhost:${port}
await cleanup();

Parallel Test Workers

typescript
// test-setup.ts
import { PortResolver } from '@tuulbelt/port-resolver';

const resolver = new PortResolver();

export async function allocateTestPorts(count: number) {
  const result = await resolver.getMultiple(count, {
    tag: `worker-${process.pid}`,
  });

  if (!result.ok) {
    throw result.error;
  }

  return result.value.map(a => a.port);
}

export async function releaseTestPorts() {
  await resolver.releaseAll(`worker-${process.pid}`);
}
typescript
// test-file.test.ts
import { allocateTestPorts, releaseTestPorts } from './test-setup';

let ports: number[];

beforeAll(async () => {
  ports = await allocateTestPorts(3);
  process.env.API_PORT = String(ports[0]);
  process.env.DB_PORT = String(ports[1]);
  process.env.CACHE_PORT = String(ports[2]);
});

afterAll(async () => {
  await releaseTestPorts();
});

test('services communicate correctly', () => {
  // Tests can run in parallel without port conflicts
});

Docker Compose Override

typescript
// generate-docker-ports.ts
import { writeFileSync } from 'node:fs';
import { PortResolver } from '@tuulbelt/port-resolver';

async function generateDockerPorts() {
  const resolver = new PortResolver();

  const services = ['api', 'postgres', 'redis'];
  const ports: Record<string, number> = {};

  for (const service of services) {
    const result = await resolver.get({ tag: `docker-${service}` });
    if (!result.ok) throw result.error;
    ports[service] = result.value.port;
  }

  // Generate docker-compose.override.yml
  const override = `
version: '3.8'
services:
  api:
    ports:
      - "${ports.api}:3000"
  postgres:
    ports:
      - "${ports.postgres}:5432"
  redis:
    ports:
      - "${ports.redis}:6379"
`;

  writeFileSync('docker-compose.override.yml', override);
  console.log('Generated docker-compose.override.yml with dynamic ports');

  return ports;
}

generateDockerPorts();

Express Server Testing

typescript
import express from 'express';
import { PortResolver } from '@tuulbelt/port-resolver';

class TestableServer {
  private resolver = new PortResolver();
  private app = express();
  private server?: ReturnType<typeof express.prototype.listen>;
  private port?: number;

  constructor() {
    this.app.get('/health', (req, res) => {
      res.json({ status: 'healthy' });
    });
  }

  async start(): Promise<string> {
    const result = await this.resolver.get({ tag: 'express-test' });
    if (!result.ok) throw result.error;

    this.port = result.value.port;

    return new Promise((resolve) => {
      this.server = this.app.listen(this.port, () => {
        resolve(`http://localhost:${this.port}`);
      });
    });
  }

  async stop(): Promise<void> {
    if (this.server) {
      this.server.close();
    }
    if (this.port) {
      await this.resolver.release(this.port);
    }
  }
}

// In tests
const server = new TestableServer();
const baseUrl = await server.start();

// Test against baseUrl
const response = await fetch(`${baseUrl}/health`);

await server.stop();

CI Script

bash
#!/bin/bash
# ci-test.sh - Run tests with dynamic port allocation

set -e

# Allocate ports for all services
API_PORT=$(portres get --tag "ci-api")
DB_PORT=$(portres get --tag "ci-db")
REDIS_PORT=$(portres get --tag "ci-redis")

# Export for test processes
export API_PORT DB_PORT REDIS_PORT

echo "Running tests with ports:"
echo "  API: $API_PORT"
echo "  DB: $DB_PORT"
echo "  Redis: $REDIS_PORT"

# Cleanup function
cleanup() {
  echo "Cleaning up allocated ports..."
  portres release-all --tag "ci-api"
  portres release-all --tag "ci-db"
  portres release-all --tag "ci-redis"
}

# Register cleanup on exit
trap cleanup EXIT

# Run tests
npm test

echo "Tests completed successfully!"

WebSocket Server

typescript
import { WebSocketServer } from 'ws';
import { PortResolver } from '@tuulbelt/port-resolver';

async function createTestWebSocket() {
  const resolver = new PortResolver();
  const result = await resolver.get({ tag: 'websocket-test' });

  if (!result.ok) throw result.error;

  const port = result.value.port;
  const wss = new WebSocketServer({ port });

  wss.on('connection', (ws) => {
    ws.on('message', (message) => {
      ws.send(`echo: ${message}`);
    });
  });

  return {
    url: `ws://localhost:${port}`,
    close: async () => {
      wss.close();
      await resolver.release(port);
    }
  };
}

Multiple Services Test Suite

typescript
// multi-service-test.ts
import { PortResolver } from '@tuulbelt/port-resolver';

interface ServicePorts {
  api: number;
  auth: number;
  notification: number;
  analytics: number;
}

class TestEnvironment {
  private resolver = new PortResolver();
  private ports: ServicePorts | null = null;

  async setup(): Promise<ServicePorts> {
    const services = ['api', 'auth', 'notification', 'analytics'] as const;
    const ports: Partial<ServicePorts> = {};

    for (const service of services) {
      const result = await this.resolver.get({
        tag: `multi-service-${service}`,
        minPort: 8000,
        maxPort: 9000,
      });

      if (!result.ok) {
        // Cleanup any allocated ports on failure
        await this.teardown();
        throw result.error;
      }

      ports[service] = result.value.port;
    }

    this.ports = ports as ServicePorts;
    return this.ports;
  }

  async teardown(): Promise<void> {
    const services = ['api', 'auth', 'notification', 'analytics'];
    for (const service of services) {
      await this.resolver.releaseAll(`multi-service-${service}`);
    }
  }

  getUrl(service: keyof ServicePorts): string {
    if (!this.ports) throw new Error('Environment not set up');
    return `http://localhost:${this.ports[service]}`;
  }
}

// Usage
const env = new TestEnvironment();
const ports = await env.setup();

console.log('Service URLs:');
console.log(`  API: ${env.getUrl('api')}`);
console.log(`  Auth: ${env.getUrl('auth')}`);
console.log(`  Notification: ${env.getUrl('notification')}`);
console.log(`  Analytics: ${env.getUrl('analytics')}`);

// Run tests...

await env.teardown();

Resource Pool Pattern

typescript
import { PortResolver } from '@tuulbelt/port-resolver';

class PortPool {
  private resolver = new PortResolver();
  private available: number[] = [];
  private inUse = new Set<number>();

  constructor(private poolSize: number = 10) {}

  async initialize(): Promise<void> {
    const result = await this.resolver.getMultiple(this.poolSize, {
      tag: 'port-pool',
    });

    if (!result.ok) throw result.error;

    this.available = result.value.map(a => a.port);
  }

  acquire(): number {
    const port = this.available.pop();
    if (!port) throw new Error('No ports available in pool');
    this.inUse.add(port);
    return port;
  }

  release(port: number): void {
    if (this.inUse.has(port)) {
      this.inUse.delete(port);
      this.available.push(port);
    }
  }

  async destroy(): Promise<void> {
    await this.resolver.releaseAll('port-pool');
    this.available = [];
    this.inUse.clear();
  }
}

// Usage
const pool = new PortPool(5);
await pool.initialize();

const port1 = pool.acquire();
const port2 = pool.acquire();

// Use ports...

pool.release(port1);
pool.release(port2);

await pool.destroy();

Released under the MIT License.