import { describe, expect, it } from 'vitest'; import { canAllocateOnNode, canStartOnNode, selectNodeForAllocation, } from './index'; import type { NodeSnapshot } from './types'; function node(partial: Partial & Pick): NodeSnapshot { return { status: 'ONLINE', maxServers: 10, maxRamMb: 16384, platformRamMb: 2048, activeServers: 0, lastHeartbeatAt: new Date(), reservedRamMb: 0, activeAllocationCount: 0, ...partial, }; } describe('scheduler capacity', () => { it('rejects maintenance and draining nodes for new allocations', () => { const maintenance = node({ id: 'a', status: 'MAINTENANCE' }); const draining = node({ id: 'b', status: 'DRAINING' }); expect(canAllocateOnNode(maintenance, 1024)).toBe(false); expect(canAllocateOnNode(draining, 1024)).toBe(false); expect(canStartOnNode(draining, 1024)).toBe(true); }); it('picks the least loaded node for placement', () => { const lightlyLoaded = node({ id: 'light', reservedRamMb: 1024 }); const heavilyLoaded = node({ id: 'heavy', reservedRamMb: 8192 }); const selected = selectNodeForAllocation( [heavilyLoaded, lightlyLoaded], 2048, ); expect(selected?.id).toBe('light'); }); it('returns null when no node has enough RAM', () => { const saturated = node({ id: 'full', reservedRamMb: 14000, maxRamMb: 16384, platformRamMb: 2048, }); expect(selectNodeForAllocation([saturated], 4096)).toBeNull(); }); });