Skip to main content

Setup

Install testing dependencies:
npm install --save-dev jest @types/jest ts-jest
Configure Jest:
// jest.config.js
export default {
  preset: 'ts-jest/presets/default-esm',
  testEnvironment: 'node',
  testTimeout: 60000,
  extensionsToTreatAsEsm: ['.ts'],
  moduleNameMapper: {
    '^(\\.{1,2}/.*)\\.js$': '$1'
  }
};

Unit Tests

Test SDK initialization and configuration:
import { In10ntClient, Instance, In10ntError } from '@in10nt/sdk';

describe('In10nt SDK', () => {
  test('should initialize client', () => {
    const client = new In10ntClient({
      apiKey: 'test-key'
    });

    expect(client).toBeDefined();
    expect(client.instances).toBeDefined();
    expect(client.templates).toBeDefined();
    expect(client.savedTemplates).toBeDefined();
    expect(client.usage).toBeDefined();
  });

  test('should throw without API key', () => {
    const original = process.env.IN10NT_API_KEY;
    delete process.env.IN10NT_API_KEY;

    expect(() => new In10ntClient()).toThrow(In10ntError);

    process.env.IN10NT_API_KEY = original;
  });
});

Integration Tests

Test actual API interactions:
import { Instance, In10ntClient } from '@in10nt/sdk';

describe('Instance Management', () => {
  let instance: Instance | null = null;

  afterEach(async () => {
    if (instance) {
      try {
        await instance.destroy();
      } catch {
        // already destroyed
      }
      instance = null;
    }
  });

  test('should create instance (static)', async () => {
    instance = await Instance.create({
      name: 'test-static',
      description: 'Static factory test'
    });

    expect(instance.instanceId).toBeDefined();
    expect(instance.status).toBeDefined();
    expect(instance.name).toBe('test-static');
  });

  test('should create instance (client)', async () => {
    const client = new In10ntClient();
    instance = await client.instances.create({
      name: 'test-client',
      description: 'Client path test'
    });

    expect(instance.instanceId).toBeDefined();
  });

  test('should connect to instance', async () => {
    instance = await Instance.create({
      name: 'test-connect',
      description: 'Connect test'
    });

    const same = await Instance.connect(instance.instanceId);
    expect(same.instanceId).toBe(instance.instanceId);
  });

  test('should list instances', async () => {
    const instances = await Instance.list();
    expect(Array.isArray(instances)).toBe(true);
  });

  test('should run task', async () => {
    instance = await Instance.create({
      name: 'test-run',
      description: 'Run test'
    });

    const result = await instance.run('Echo "Hello World"', {
      timeout: 60_000
    });

    expect(result.response).toBeDefined();
    expect(result.tokensUsed).toBeGreaterThan(0);
  });

  test('should use namespaced methods', async () => {
    instance = await Instance.create({
      name: 'test-namespaces',
      description: 'Namespace test'
    });

    // Tasks namespace
    const tasks = await instance.tasks.list();
    expect(Array.isArray(tasks)).toBe(true);

    // Ports namespace
    const ports = await instance.ports.list();
    expect(Array.isArray(ports)).toBe(true);

    // Files namespace
    const changes = await instance.files.getChanges();
    expect(changes).toBeDefined();

    // Proxy URL (sync)
    const url = instance.getProxyUrl(3000);
    expect(url).toContain(instance.instanceId);
    expect(url).toContain('3000');
  });

  test('should destroy instance', async () => {
    instance = await Instance.create({
      name: 'test-destroy',
      description: 'Destroy test'
    });

    await instance.destroy();
    instance = null;
  });
});

Template Tests

Test container template creation:
import { In10ntClient } from '@in10nt/sdk';

describe('Container Templates', () => {
  const client = new In10ntClient();
  let template: Awaited<ReturnType<typeof client.templates.create>> | null = null;

  afterEach(async () => {
    if (template) {
      try {
        await template.delete();
      } catch {
        // already deleted
      }
      template = null;
    }
  });

  test('should create template', async () => {
    template = await client.templates.create({
      name: 'test-template',
      image: 'python:3.11-slim',
      registryType: 'dockerhub'
    });

    expect(template.templateId).toBeDefined();
    expect(template.name).toBe('test-template');
  });

  test('should wait until ready', async () => {
    template = await client.templates.create({
      name: 'test-ready-template',
      image: 'python:3.11-slim',
      registryType: 'dockerhub'
    });

    await template.waitUntilReady({ timeout: 300_000 });
    expect(template.status).toBe('ready');
  }, 310_000);

  test('should use template with instance', async () => {
    template = await client.templates.create({
      name: 'test-use-template',
      image: 'python:3.11-slim',
      registryType: 'dockerhub'
    });

    await template.waitUntilReady();

    const instance = await client.instances.create({
      name: 'test-with-template',
      description: 'Template test',
      containerTemplateId: template.templateId
    });

    expect(instance.instanceId).toBeDefined();
    await instance.destroy();
  }, 310_000);
});

Error Handling Tests

Test error scenarios:
import { Instance, In10ntClient, In10ntError, TimeoutError } from '@in10nt/sdk';

describe('Error Handling', () => {
  test('should handle invalid instance ID', async () => {
    await expect(
      Instance.connect('invalid-id')
    ).rejects.toThrow(In10ntError);
  });

  test('should handle missing required fields', async () => {
    await expect(
      Instance.create({ name: '', description: '' } as any)
    ).rejects.toThrow();
  });

  test('should handle timeout', async () => {
    const instance = await Instance.create({
      name: 'timeout-test',
      description: 'Timeout test'
    });

    try {
      await expect(
        instance.run('Long task', {
          timeout: 1000,
          waitForHealth: false
        })
      ).rejects.toThrow();
    } finally {
      await instance.destroy();
    }
  });
});

Usage Tests

Test usage tracking:
import { In10ntClient, Instance } from '@in10nt/sdk';

describe('Usage Tracking', () => {
  const client = new In10ntClient();

  test('should get token usage', async () => {
    const usage = await client.usage.getTokenUsage();

    expect(usage.monthlyTokens).toBeGreaterThanOrEqual(0);
    expect(usage.totalTokens).toBeGreaterThanOrEqual(0);
    expect(usage.monthlyCost).toBeGreaterThanOrEqual(0);
    expect(usage.totalCost).toBeGreaterThanOrEqual(0);
  });

  test('should track task usage', async () => {
    const instance = await Instance.create({
      name: 'usage-test',
      description: 'Usage test'
    });

    try {
      const result = await instance.run('Simple task');
      expect(result.tokensUsed).toBeGreaterThan(0);
    } finally {
      await instance.destroy();
    }
  });
});

Background Mode Tests

import { Instance } from '@in10nt/sdk';

describe('Background Mode', () => {
  test('should return taskId in background mode', async () => {
    const instance = await Instance.create({
      name: 'bg-test',
      description: 'Background test'
    });

    try {
      const result = await instance.run('Build something', {
        background: true
      });

      expect(result.taskId).toBeDefined();
      expect(result.status).toBe('processing');

      // Poll for completion
      const final = await instance.tasks.poll(result.taskId!);
      expect(final.response).toBeDefined();
    } finally {
      await instance.destroy();
    }
  });
});

Test Utilities

Create reusable test utilities:
// test-utils.ts
import { Instance, In10ntClient } from '@in10nt/sdk';
import type { ContainerTemplate } from '@in10nt/sdk';

export class TestHelper {
  private client: In10ntClient;
  private instances: Instance[] = [];
  private templates: ContainerTemplate[] = [];

  constructor() {
    this.client = new In10ntClient();
  }

  async createInstance(name: string, description: string): Promise<Instance> {
    const instance = await this.client.instances.create({ name, description });
    this.instances.push(instance);
    return instance;
  }

  async createTemplate(name: string, image: string): Promise<ContainerTemplate> {
    const template = await this.client.templates.create({
      name,
      image,
      registryType: 'dockerhub'
    });
    this.templates.push(template);
    return template;
  }

  async cleanup(): Promise<void> {
    for (const instance of this.instances) {
      try { await instance.destroy(); } catch {}
    }
    for (const template of this.templates) {
      try { await template.delete(); } catch {}
    }
    this.instances = [];
    this.templates = [];
  }
}

// Usage in tests
describe('With Test Helper', () => {
  const helper = new TestHelper();

  afterEach(async () => {
    await helper.cleanup();
  });

  test('should create and cleanup automatically', async () => {
    const instance = await helper.createInstance('test', 'Test instance');
    expect(instance.instanceId).toBeDefined();
    // Cleanup happens automatically in afterEach
  });
});

Run Tests

# Run all tests
npm test

# Run specific test file
npm test -- instances.test.ts

# Run with coverage
npm test -- --coverage

# Run in watch mode
npm test -- --watch

CI/CD Integration

# .github/workflows/test.yml
name: Tests

on: [push, pull_request]

jobs:
  test:
    runs-on: ubuntu-latest

    steps:
      - uses: actions/checkout@v4

      - name: Setup Node.js
        uses: actions/setup-node@v4
        with:
          node-version: '20'

      - name: Install dependencies
        run: npm install

      - name: Run tests
        env:
          IN10NT_API_KEY: ${{ secrets.TEST_API_KEY }}
        run: npm test