Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import {
PathOutsideMountsError,
ResetNotSupportedError,
} from '../../../../src/lib/container/managed'
import { ContainerNameInUseError } from '../../../../src/lib/vm/errors'
import type {
ContainerInfo,
ContainerSpec,
Expand Down Expand Up @@ -617,9 +618,6 @@ describe('ManagedContainer', () => {
cli.createContainer = async (spec: ContainerSpec) => {
createAttempts += 1
if (createAttempts < 2) {
const { ContainerNameInUseError } = await import(
'../../../../src/lib/vm/errors'
)
throw new ContainerNameInUseError(
spec.name,
'nerdctl create',
Expand Down
3 changes: 2 additions & 1 deletion packages/browseros-agent/packages/shared/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@
"version": "0.0.1",
"type": "module",
"scripts": {
"typecheck": "tsc --noEmit"
"typecheck": "tsc --noEmit",
"test": "bun test"
},
"dependencies": {
"zod": "^3.24.2"
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,277 @@
/**
* @license
* Copyright 2025 BrowserOS
* SPDX-License-Identifier: AGPL-3.0-or-later
*
* Tests for configurable limits constants.
*
* Env-override tests use Bun.spawn() to run a tiny inline script
* in a child process — this guarantees a fresh module cache so that
* process.env is read at import time with the overridden value.
*/

import path from 'node:path'
import { describe, expect, it } from 'bun:test'

import { spawnWithEnv } from './test-utils'

const LIMITS_MODULE_PATH = JSON.stringify(
path.resolve(import.meta.dir, '../../src/constants/limits.ts'),
)

// ---------------------------------------------------------------------------
// Default values
// ---------------------------------------------------------------------------

describe('AGENT_LIMITS defaults', () => {
// eslint-disable-next-line @typescript-eslint/no-require-imports
const { AGENT_LIMITS } = require('../../src/constants/limits.ts') as typeof import('../../src/constants/limits')

it('exports exactly 23 keys', () => {
expect(Object.keys(AGENT_LIMITS)).toHaveLength(23)
})

it('has correct default MAX_TURNS', () => {
expect(AGENT_LIMITS.MAX_TURNS).toBe(100)
})

it('has correct default DEFAULT_CONTEXT_WINDOW', () => {
expect(AGENT_LIMITS.DEFAULT_CONTEXT_WINDOW).toBe(200_000)
})

it('has correct default COMPACTION_SUMMARIZATION_TIMEOUT_MS', () => {
expect(AGENT_LIMITS.COMPACTION_SUMMARIZATION_TIMEOUT_MS).toBe(60_000)
})
})

describe('TOOL_LIMITS defaults', () => {
// eslint-disable-next-line @typescript-eslint/no-require-imports
const { TOOL_LIMITS } = require('../../src/constants/limits.ts') as typeof import('../../src/constants/limits')

it('exports exactly 3 keys', () => {
expect(Object.keys(TOOL_LIMITS)).toHaveLength(3)
})

it('has correct default FILESYSTEM_READ_MAX_LINES', () => {
expect(TOOL_LIMITS.FILESYSTEM_READ_MAX_LINES).toBe(500)
})

it('has correct default FILESYSTEM_READ_MAX_CHARS', () => {
expect(TOOL_LIMITS.FILESYSTEM_READ_MAX_CHARS).toBe(15_000)
})

it('has correct default INLINE_PAGE_CONTENT_MAX_CHARS', () => {
expect(TOOL_LIMITS.INLINE_PAGE_CONTENT_MAX_CHARS).toBe(5_000)
})
})

describe('AGENT_LIMITS non-overridable defaults', () => {
// eslint-disable-next-line @typescript-eslint/no-require-imports
const { AGENT_LIMITS } = require('../../src/constants/limits.ts') as typeof import('../../src/constants/limits')

it('has correct COMPRESSION_MIN_HEADROOM', () => {
expect(AGENT_LIMITS.COMPRESSION_MIN_HEADROOM).toBe(10_000)
})

it('has correct COMPACTION_RESERVE_TOKENS', () => {
expect(AGENT_LIMITS.COMPACTION_RESERVE_TOKENS).toBe(20_000)
})

it('has correct COMPACTION_MAX_SUMMARIZATION_INPUT', () => {
expect(AGENT_LIMITS.COMPACTION_MAX_SUMMARIZATION_INPUT).toBe(100_000)
})

it('has correct COMPACTION_TOOL_OUTPUT_MAX_CHARS', () => {
expect(AGENT_LIMITS.COMPACTION_TOOL_OUTPUT_MAX_CHARS).toBe(15_000)
})

it('has correct COMPACTION_FIXED_OVERHEAD', () => {
expect(AGENT_LIMITS.COMPACTION_FIXED_OVERHEAD).toBe(12_000)
})
})

describe('PAGINATION defaults', () => {
it('has correct DEFAULT_PAGE_SIZE', () => {
// eslint-disable-next-line @typescript-eslint/no-require-imports
const { PAGINATION } = require('../../src/constants/limits.ts')
expect(PAGINATION.DEFAULT_PAGE_SIZE).toBe(20)
})
})

describe('CDP_LIMITS defaults', () => {
// eslint-disable-next-line @typescript-eslint/no-require-imports
const { CDP_LIMITS } = require('../../src/constants/limits.ts')

it('has correct CONNECT_MAX_RETRIES', () => {
expect(CDP_LIMITS.CONNECT_MAX_RETRIES).toBe(3)
})

it('has correct RECONNECT_MAX_RETRIES', () => {
expect(CDP_LIMITS.RECONNECT_MAX_RETRIES).toBe(3)
})
})

describe('CONTENT_LIMITS defaults', () => {
// eslint-disable-next-line @typescript-eslint/no-require-imports
const { CONTENT_LIMITS } = require('../../src/constants/limits.ts')

it('exports exactly 6 keys', () => {
expect(Object.keys(CONTENT_LIMITS)).toHaveLength(6)
})

it('has correct BODY_CONTEXT_SIZE', () => {
expect(CONTENT_LIMITS.BODY_CONTEXT_SIZE).toBe(10_000)
})

it('has correct MAX_QUEUE_SIZE', () => {
expect(CONTENT_LIMITS.MAX_QUEUE_SIZE).toBe(1_000)
})
})

describe('AGENT_HARNESS_LIMITS defaults', () => {
// eslint-disable-next-line @typescript-eslint/no-require-imports
const { AGENT_HARNESS_LIMITS } = require('../../src/constants/limits.ts')

it('has correct AGENT_NAME_MAX_CHARS', () => {
expect(AGENT_HARNESS_LIMITS.AGENT_NAME_MAX_CHARS).toBe(80)
})

it('has correct QUEUE_MAX_LENGTH', () => {
expect(AGENT_HARNESS_LIMITS.QUEUE_MAX_LENGTH).toBe(50)
})

it('has correct QUEUE_MESSAGE_MAX_BYTES', () => {
expect(AGENT_HARNESS_LIMITS.QUEUE_MESSAGE_MAX_BYTES).toBe(64 * 1024)
})
})

// ---------------------------------------------------------------------------
// ENV OVERRIDE TESTS — subprocess isolation
// ---------------------------------------------------------------------------

describe('AGENT_LIMITS env override', () => {
it('overrides MAX_TURNS via BROWSEROS_LIMIT_MAX_TURNS', async () => {
const result = await spawnWithEnv(
{ BROWSEROS_LIMIT_MAX_TURNS: '200' },
`const { AGENT_LIMITS } = require(${LIMITS_MODULE_PATH}); console.log(AGENT_LIMITS.MAX_TURNS)`,
)
expect(Number(result)).toBe(200)
})

it('overrides DEFAULT_CONTEXT_WINDOW via BROWSEROS_LIMIT_DEFAULT_CONTEXT_WINDOW', async () => {
const result = await spawnWithEnv(
{ BROWSEROS_LIMIT_DEFAULT_CONTEXT_WINDOW: '500000' },
`const { AGENT_LIMITS } = require(${LIMITS_MODULE_PATH}); console.log(AGENT_LIMITS.DEFAULT_CONTEXT_WINDOW)`,
)
expect(Number(result)).toBe(500_000)
})

it('overrides FILESYSTEM_READ_MAX_LINES via BROWSEROS_LIMIT_FILESYSTEM_READ_MAX_LINES', async () => {
const result = await spawnWithEnv(
{ BROWSEROS_LIMIT_FILESYSTEM_READ_MAX_LINES: '1000' },
`const { TOOL_LIMITS } = require(${LIMITS_MODULE_PATH}); console.log(TOOL_LIMITS.FILESYSTEM_READ_MAX_LINES)`,
)
expect(Number(result)).toBe(1000)
})
})

describe('AGENT_LIMITS invalid env fallback', () => {
it('falls back to default when BROWSEROS_LIMIT_MAX_TURNS is non-numeric', async () => {
const result = await spawnWithEnv(
{ BROWSEROS_LIMIT_MAX_TURNS: 'abc' },
`const { AGENT_LIMITS } = require(${LIMITS_MODULE_PATH}); console.log(AGENT_LIMITS.MAX_TURNS)`,
)
expect(Number(result)).toBe(100)
})

it('falls back to default when env has trailing chars', async () => {
const result = await spawnWithEnv(
{ BROWSEROS_LIMIT_MAX_TURNS: '100s' },
`const { AGENT_LIMITS } = require(${LIMITS_MODULE_PATH}); console.log(AGENT_LIMITS.MAX_TURNS)`,
)
expect(Number(result)).toBe(100)
})
})

describe('AGENT_LIMITS negative env fallback', () => {
it('falls back to default when BROWSEROS_LIMIT_MAX_TURNS is negative', async () => {
const result = await spawnWithEnv(
{ BROWSEROS_LIMIT_MAX_TURNS: '-1' },
`const { AGENT_LIMITS } = require(${LIMITS_MODULE_PATH}); console.log(AGENT_LIMITS.MAX_TURNS)`,
)
expect(Number(result)).toBe(100)
})
})

// ---------------------------------------------------------------------------
// Edge cases — zero, float, empty, whitespace, overflow
// ---------------------------------------------------------------------------

describe('AGENT_LIMITS edge-case env handling', () => {
it('falls back to default when env is zero', async () => {
const result = await spawnWithEnv(
{ BROWSEROS_LIMIT_MAX_TURNS: '0' },
`const { AGENT_LIMITS } = require(${LIMITS_MODULE_PATH}); console.log(AGENT_LIMITS.MAX_TURNS)`,
)
expect(Number(result)).toBe(100)
})

it('falls back to default when env is a float', async () => {
const result = await spawnWithEnv(
{ BROWSEROS_LIMIT_MAX_TURNS: '1.5' },
`const { AGENT_LIMITS } = require(${LIMITS_MODULE_PATH}); console.log(AGENT_LIMITS.MAX_TURNS)`,
)
expect(Number(result)).toBe(100)
})

it('falls back to default when env is empty string', async () => {
const result = await spawnWithEnv(
{ BROWSEROS_LIMIT_MAX_TURNS: '' },
`const { AGENT_LIMITS } = require(${LIMITS_MODULE_PATH}); console.log(AGENT_LIMITS.MAX_TURNS)`,
)
expect(Number(result)).toBe(100)
})

it('falls back to default when env is whitespace-only', async () => {
const result = await spawnWithEnv(
{ BROWSEROS_LIMIT_MAX_TURNS: ' \t' },
`const { AGENT_LIMITS } = require(${LIMITS_MODULE_PATH}); console.log(AGENT_LIMITS.MAX_TURNS)`,
)
expect(Number(result)).toBe(100)
})

it('falls back to default when env exceeds MAX_SAFE_INTEGER', async () => {
const result = await spawnWithEnv(
{ BROWSEROS_LIMIT_MAX_TURNS: '99999999999999999999' },
`const { AGENT_LIMITS } = require(${LIMITS_MODULE_PATH}); console.log(AGENT_LIMITS.MAX_TURNS)`,
)
expect(Number(result)).toBe(100)
})
})

// ---------------------------------------------------------------------------
// Structural integrity checks
// ---------------------------------------------------------------------------

describe('AGENT_LIMITS structural checks', () => {
// eslint-disable-next-line @typescript-eslint/no-require-imports
const { AGENT_LIMITS } = require('../../src/constants/limits.ts') as typeof import('../../src/constants/limits')

it('all AGENT_LIMITS values are positive finite numbers', () => {
for (const [key, value] of Object.entries(AGENT_LIMITS)) {
expect(typeof value).toBe('number')
expect(value).toBeGreaterThan(0)
expect(Number.isFinite(value)).toBe(true)
}
})

it('all TOOL_LIMITS values are positive finite numbers', () => {
// eslint-disable-next-line @typescript-eslint/no-require-imports
const { TOOL_LIMITS } = require('../../src/constants/limits.ts')
for (const [key, value] of Object.entries(TOOL_LIMITS)) {
expect(typeof value).toBe('number')
expect(value).toBeGreaterThan(0)
expect(Number.isFinite(value)).toBe(true)
}
})
})
Loading
Loading