You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

212 lines
9.5 KiB
TypeScript

import { expect } from 'chai';
import { composeAccumulate } from '../../../src/utils/ramda';
describe('composeAccumulate with property accumulation', () => {
it('accumulates results across functions, retaining all properties', async () => {
const fn1 = async ({ a }: { a: number }) => ({ b: a * 2 });
const fn2 = async ({ a, b }: { a: number; b: number }) => ({ c: b + 1 });
const composedFunction = composeAccumulate(fn1, fn2);
const result = await composedFunction({ a: 5 });
expect(result).to.deep.equal({ a: 5, b: 10, c: 11 });
});
it('accumulates results with async and sync functions mixed', async () => {
const fn1 = async ({ a }: { a: number }) => ({ b: a * 2 });
const fn2 = ({ a, b }: { a: number; b: number }) => ({ c: a + b });
const composedFunction = composeAccumulate(fn1, fn2);
const result = await composedFunction({ a: 3 });
expect(result).to.deep.equal({ a: 3, b: 6, c: 9 });
});
it('handles functions that do not add properties', async () => {
const fn1 = async ({ a }: { a: number }) => ({});
const fn2 = async ({ a }: { a: number }) => ({ b: a * 2 });
const fn3 = async ({ b }: { b: number }) => ({ c: b + 1 });
const composedFunction = composeAccumulate(fn1, fn2, fn3);
const result = await composedFunction({ a: 4 });
expect(result).to.deep.equal({ a: 4, b: 8, c: 9 });
});
it('handles functions that interrupt the process', async () => {
const fn1 = async ({ a }: { a: number }) => ({});
const fn2 = async ({ a }: { a: number }) => ({ b: a * 2 });
const preventsThrow = ({ b }: { b: number }) => undefined;
const throws = async ({ b }: { b: number }) => {
throw new Error("Error should not be raised")
};
const composedFunction = composeAccumulate(fn1, fn2, preventsThrow, throws);
const result = await composedFunction({ a: 4 });
console.log("eeeeeeee", result)
expect(result).to.deep.equal(undefined);
});
it('handles functions that interrupt the process in a promise', async () => {
const fn1 = async ({ a }: { a: number }) => ({});
const fn2 = async ({ a }: { a: number }) => ({ b: a * 2 });
const preventsThrow = async ({ b }: { b: number }) => {
await new Promise(r => setTimeout(r, 800));
return undefined;
};
const throws = async ({ b }: { b: number }) => {
throw new Error("Error should not be raised")
};
const composedFunction = composeAccumulate(fn1, fn2, preventsThrow, throws);
const result = await composedFunction({ a: 4 });
console.log("eeeeeeee", result)
expect(result).to.deep.equal(undefined);
});
it('handles functions that directly interrupt', async () => {
const fn1 = ({ a }: { a: number }) => undefined;
const fn2 = async ({ a }: { a: number }) => ({ b: a * 2 });
const preventsThrow = async ({ b }: { b: number }) => {
await new Promise(r => setTimeout(r, 800));
return undefined;
};
const throws = async ({ b }: { b: number }) => {
throw new Error("Error should not be raised")
};
const composedFunction = composeAccumulate(fn1, fn2, preventsThrow, throws);
const result = await composedFunction({ a: 4 });
console.log("eeeeeeee", result)
expect(result).to.deep.equal(undefined);
});
it('handles functions that directly interrupt on a promise', async () => {
const directlyInterrupts = async ({ a }: { a: number }) => {
await new Promise(r => setTimeout(r, 800));
return undefined;
};
const fn2 = async ({ a }: { a: number }) => ({ b: a * 2 });
const preventsThrow = async ({ b }: { b: number }) => {
await new Promise(r => setTimeout(r, 800));
return undefined;
};
const throws = async ({ b }: { b: number }) => {
throw new Error("Error should not be raised")
};
const composedFunction = composeAccumulate(directlyInterrupts, fn2, preventsThrow, throws);
const result = await composedFunction({ a: 4 });
console.log("eeeeeeee", result)
expect(result).to.deep.equal(undefined);
});
it('accumulates results when intermediate functions conditionally add properties', async () => {
const fn1 = async ({ a }: { a: number }) => a > 5 ? { b: a - 5 } : {};
const fn2 = async ({ a, b }: { a: number; b?: number }) => ({ c: (b || a) + 1 });
const fn3 = async ({ c }: { c: number }) => ({ d: c * 2 });
const composedFunction = composeAccumulate(fn1, fn2, fn3);
const resultForA5 = await composedFunction({ a: 5 });
const resultForA10 = await composedFunction({ a: 10 });
// For a: 5, b should not exist, and c is based on a
expect(resultForA5).to.deep.equal({ a: 5, c: 6, d: 12 });
// For a: 10, b exists and c is based on b
expect(resultForA10).to.deep.equal({ a: 10, b: 5, c: 6, d: 12 });
});
it('accumulates results when they are string properties', async () => {
// Function 1: Takes an object with a 'name' property and returns an object with a 'greeting' property
const f21 = ({ name }: { name: string }) => ({ greeting: `Hello, ${name}` });
// Function 2: Takes an object with a 'greeting' property and optionally a 'name', and returns an object with a 'farewell' property
const f22 = ({ greeting, name }: { greeting: string; name?: string }) => ({ farewell: `${greeting}. Goodbye, ${name ?? "stranger"}` });
const composedFunction = composeAccumulate(f21, f22);
const result = composedFunction({ name: "John" });
expect(result).to.deep.equal({
farewell: "Hello, John. Goodbye, John",
greeting: "Hello, John",
name: "John",
});
});
it('accumulates results across synchronous functions', () => {
const fn1 = ({ a }: { a: number }) => ({ b: a + 10 });
const fn2 = ({ a, b }: { a: number; b: number }) => ({ c: b * 2 });
const composedFunction = composeAccumulate(fn1, fn2);
const result = composedFunction({ a: 5 });
expect(result).to.deep.equal({ a: 5, b: 15, c: 30 });
});
});
describe('composeAccumulate with an intermediate function that produces a new object', () => {
it('properly accumulates results when an intermediate function ignores previous output', async () => {
const function1 = async ({ a }: { a: number }) => ({ b: a * 2 });
// Intermediate function that ignores input and produces a new object
const intermediateFunction = async () => ({ c: 100 });
const function3 = async ({ b, c }: { b?: number; c: number }) => ({ d: (b || 0) + c });
const composedFunction = composeAccumulate(function1, intermediateFunction, function3);
const result = await composedFunction({ a: 5 });
// Expect that the result includes the produced object from the intermediate function,
// and the final output depends on this object.
expect(result).to.deep.equal({ a: 5, b: 10, c: 100, d: 110 });
});
});
describe('composeAccumulate with mixed functions and async delay', () => {
it('handles async function with 500ms delay followed by a sync function', async () => {
const asyncFunctionWithDelay = async ({ a }: { a: number }) => {
await new Promise(resolve => setTimeout(resolve, 500));
return { b: a * 2 };
};
const syncFunction = ({ a, b }: { a: number; b: number }) => {
return { c: b + 3 };
};
const composedFunction = composeAccumulate(asyncFunctionWithDelay, syncFunction);
const result = await composedFunction({ a: 1 });
expect(result).to.deep.equal({ a: 1, b: 2, c: 5 });
});
it('combines multiple async functions with delays', async () => {
const asyncFunction1 = async ({ a }: { a: number }) => {
await new Promise(resolve => setTimeout(resolve, 500));
return { b: a + 1 };
};
const asyncFunction2 = async ({ b }: { b: number }) => {
await new Promise(resolve => setTimeout(resolve, 500));
return { c: b * 2 };
};
const composedFunction = composeAccumulate(asyncFunction1, asyncFunction2);
const startTime = Date.now();
const result = await composedFunction({ a: 2 });
const endTime = Date.now();
const duration = endTime - startTime;
// Verify the results are as expected
expect(result).to.deep.equal({ a: 2, b: 3, c: 6 });
// Verify that it took at least approximately 1000ms due to the async delays
expect(duration).to.be.greaterThan(1000 - 100); // Allowing a 50ms margin for the test environment's timing inaccuracies
});
it('combines multiple properly', async () => {
const functionA = (input: { a: number }) => ({ b: input.a * 2 });
const functionB = (input: { b: string }) => ({ c: `Value: ${input.b}` });
const b = composeAccumulate(functionA, functionB); // TypeScript Error: functionB expects 'b' to be a string, but functionA returns it as a number
/* const b: (initialArg: {
a: number;
}) => Promise<{
b: number;
} & {
c: string;
}> */
const c = await b({ a: 10 });
/*
const c: Promise<{
b: number;
} & {
c: string;
}> */
expect(c).to.deep.equal({ a: 10, b: 20, c: "Value: 20" });
});
});