Files
2026-07-13 13:23:39 +08:00

190 lines
6.9 KiB
TypeScript

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);
});
});
});