import asyncFs from 'node:fs/promises'; import { getCgroupsVersion, isDocker, isLambda, sleep, snakeCaseToCamelCase, weightedAvg } from '@crawlee/utils'; describe('isDocker()', () => { test('works for dockerenv && cgroup', async () => { const statMock = vitest.spyOn(asyncFs, 'stat').mockImplementationOnce(async () => null as any); const readMock = vitest .spyOn(asyncFs, 'readFile') .mockImplementationOnce(async () => Promise.resolve('something ... docker ... something')); const is = await isDocker(true); expect(is).toBe(true); }); test('works for dockerenv', async () => { const statMock = vitest.spyOn(asyncFs, 'stat').mockImplementationOnce(async () => null as any); const readMock = vitest .spyOn(asyncFs, 'readFile') .mockImplementationOnce(async () => Promise.resolve('something ... ... something')); const is = await isDocker(true); expect(is).toBe(true); }); test('works for cgroup', async () => { const statMock = vitest .spyOn(asyncFs, 'stat') .mockImplementationOnce(async () => Promise.reject(new Error('no.'))); const readMock = vitest .spyOn(asyncFs, 'readFile') .mockImplementationOnce(async () => Promise.resolve('something ... docker ... something')); const is = await isDocker(true); expect(is).toBe(true); }); test('works for nothing', async () => { const statMock = vitest .spyOn(asyncFs, 'stat') .mockImplementationOnce(async () => Promise.reject(new Error('no.'))); const readMock = vitest .spyOn(asyncFs, 'readFile') .mockImplementationOnce(async () => Promise.resolve('something ... ... something')); const is = await isDocker(true); expect(is).toBe(false); }); }); describe('isContainerized()', () => { afterEach(() => { delete process.env.KUBERNETES_SERVICE_HOST; delete process.env.CRAWLEE_CONTAINERIZED; // resets the `isContainerizedResult` module scoped variable vi.resetModules(); }); test('returns true when a "/.dockerenv" file exists', async () => { // @ts-ignore flaky linting of dynamic import. Some environments throw ts(2307), others not. const { isContainerized } = await import('@crawlee/utils'); const statMock = vitest.spyOn(asyncFs, 'stat').mockImplementationOnce(async () => null as any); const result = await isContainerized(); expect(result).toBe(true); }); test('returns false when isLambda is true', async () => { // @ts-ignore flaky linting of dynamic import. Some environments throw ts(2307), others not. const utils = await import('@crawlee/utils'); const lambdaMock = vitest.spyOn(utils, 'isLambda').mockReturnValue(true); const result = await utils.isContainerized(); expect(result).toBe(false); }); test('returns true when a "/proc/stat/cgroup" file contains "docker"', async () => { // @ts-ignore flaky linting of dynamic import. Some environments throw ts(2307), others not. const { isContainerized } = await import('@crawlee/utils'); const readFileMock = vitest .spyOn(asyncFs, 'readFile') .mockResolvedValue("'something ... docker ... something'"); const result = await isContainerized(); expect(result).toBe(true); }); test('returns true when KUBERNETES_SERVICE_HOST environment variable is set', async () => { // @ts-ignore flaky linting of dynamic import. Some environments throw ts(2307), others not. const { isContainerized } = await import('@crawlee/utils'); process.env.KUBERNETES_SERVICE_HOST = 'some-host'; const result = await isContainerized(); expect(result).toBe(true); }); test('returns false when no other conditions are met', async () => { // @ts-ignore flaky linting of dynamic import. Some environments throw ts(2307), others not. const { isContainerized } = await import('@crawlee/utils'); const result = await isContainerized(); expect(result).toBe(false); }); }); describe('isLambda()', () => { afterEach(() => { delete process.env.AWS_LAMBDA_FUNCTION_MEMORY_SIZE; }); test('returns true when AWS_LAMBDA_FUNCTION_MEMORY_SIZE environment variable is set', () => { process.env.AWS_LAMBDA_FUNCTION_MEMORY_SIZE = '12345'; const result = isLambda(); expect(result).toBe(true); }); }); describe('getCgroupsVersion()', () => { // Reset the module cache so that _cgroupsVersion is not retained across tests. beforeEach(() => { vitest.resetModules(); }); test('returns null when access to /sys/fs/cgroup/ fails', async () => { vitest.spyOn(asyncFs, 'access').mockRejectedValue(new Error('not found')); const version = await getCgroupsVersion(true); expect(version).toBe(null); }); test('returns V2 when access to /sys/fs/cgroup/memory/ fails', async () => { vitest.spyOn(asyncFs, 'access').mockImplementation(async (path) => { if (path === '/sys/fs/cgroup/') { return; } throw new Error('not found'); }); const version = await getCgroupsVersion(true); expect(version).toBe('V2'); }); test('returns V1 when access to /sys/fs/cgroup/memory/ succeeds', async () => { vitest.spyOn(asyncFs, 'access').mockResolvedValue(); const version = await getCgroupsVersion(true); expect(version).toBe('V1'); }); }); describe('weightedAvg()', () => { test('works', () => { expect(weightedAvg([10, 10, 10], [1, 1, 1])).toBe(10); expect(weightedAvg([5, 10, 15], [1, 1, 1])).toBe(10); expect(weightedAvg([10, 10, 10], [0.5, 1, 1.5])).toBe(10); expect(weightedAvg([29, 35, 89], [13, 91, 3])).toEqual((29 * 13 + 35 * 91 + 89 * 3) / (13 + 91 + 3)); expect(weightedAvg([], [])).toEqual(NaN); expect(weightedAvg([1], [0])).toEqual(NaN); expect(weightedAvg([], [1])).toEqual(NaN); }); }); describe('sleep()', () => { test('works', async () => { await Promise.resolve(); await sleep(0); await sleep(); // @ts-expect-error invalid input type await sleep(null); await sleep(-1); const timeBefore = Date.now(); await sleep(100); const timeAfter = Date.now(); expect(timeAfter - timeBefore).toBeGreaterThanOrEqual(95); }); }); describe('snakeCaseToCamelCase()', () => { test('should camel case all sneaky cases of snake case', () => { const tests = { 'aaa_bbb_': 'aaaBbb', '': '', 'AaA_bBb_cCc': 'aaaBbbCcc', 'a_1_b_1a': 'a1B1a', }; Object.entries(tests).forEach(([snakeCase, camelCase]) => { expect(snakeCaseToCamelCase(snakeCase)).toEqual(camelCase); }); }); });