390 lines
12 KiB
TypeScript
390 lines
12 KiB
TypeScript
import { describe, it, expect } from 'vitest';
|
|
import { Query, OkSome, OkNone, ErrNone, okSome, okNone, errNone } from '../';
|
|
|
|
describe('Query constructors and factories - Runtime Tests', () => {
|
|
describe('OkSome constructor', () => {
|
|
it('should create OkSome instance with provided value', () => {
|
|
// Arrange
|
|
const value = 42;
|
|
|
|
// Act
|
|
const result = new OkSome(value);
|
|
|
|
// Assert
|
|
expect(result).toBeInstanceOf(OkSome);
|
|
expect(result.value).toBe(value);
|
|
});
|
|
|
|
it('should store different types of values correctly', () => {
|
|
// Arrange
|
|
const numberValue = 42;
|
|
const stringValue = 'hello';
|
|
const objectValue = { id: 1, name: 'test' };
|
|
const arrayValue = [1, 2, 3];
|
|
const functionValue = (x: number) => x * 2;
|
|
|
|
// Act
|
|
const numberOkSome = new OkSome(numberValue);
|
|
const stringOkSome = new OkSome(stringValue);
|
|
const objectOkSome = new OkSome(objectValue);
|
|
const arrayOkSome = new OkSome(arrayValue);
|
|
const functionOkSome = new OkSome(functionValue);
|
|
|
|
// Assert
|
|
expect(numberOkSome.value).toBe(numberValue);
|
|
expect(stringOkSome.value).toBe(stringValue);
|
|
expect(objectOkSome.value).toBe(objectValue);
|
|
expect(arrayOkSome.value).toBe(arrayValue);
|
|
expect(functionOkSome.value).toBe(functionValue);
|
|
});
|
|
|
|
it('should implement QueryInterface methods', () => {
|
|
// Arrange
|
|
const instance = new OkSome(42);
|
|
|
|
// Assert
|
|
expect(typeof instance.isSome).toBe('function');
|
|
expect(typeof instance.isNone).toBe('function');
|
|
expect(typeof instance.isErr).toBe('function');
|
|
expect(typeof instance.map).toBe('function');
|
|
expect(typeof instance.mapErr).toBe('function');
|
|
expect(typeof instance.flatMap).toBe('function');
|
|
expect(typeof instance.flatMapErr).toBe('function');
|
|
expect(typeof instance.match).toBe('function');
|
|
expect(typeof instance.filter).toBe('function');
|
|
expect(typeof instance.toNullable).toBe('function');
|
|
expect(typeof instance.toUndefined).toBe('function');
|
|
expect(typeof instance.toString).toBe('function');
|
|
});
|
|
|
|
it('should correctly identify as OkSome', () => {
|
|
// Arrange
|
|
const instance = new OkSome(42);
|
|
|
|
// Act & Assert
|
|
expect(instance.isSome()).toBe(true);
|
|
expect(instance.isNone()).toBe(false);
|
|
expect(instance.isErr()).toBe(false);
|
|
});
|
|
});
|
|
|
|
describe('OkNone constructor', () => {
|
|
it('should create OkNone instance', () => {
|
|
// Act
|
|
const result = new OkNone();
|
|
|
|
// Assert
|
|
expect(result).toBeInstanceOf(OkNone);
|
|
});
|
|
|
|
it('should implement QueryInterface methods', () => {
|
|
// Arrange
|
|
const instance = new OkNone();
|
|
|
|
// Assert
|
|
expect(typeof instance.isSome).toBe('function');
|
|
expect(typeof instance.isNone).toBe('function');
|
|
expect(typeof instance.isErr).toBe('function');
|
|
expect(typeof instance.map).toBe('function');
|
|
expect(typeof instance.mapErr).toBe('function');
|
|
expect(typeof instance.flatMap).toBe('function');
|
|
expect(typeof instance.flatMapErr).toBe('function');
|
|
expect(typeof instance.match).toBe('function');
|
|
expect(typeof instance.filter).toBe('function');
|
|
expect(typeof instance.toNullable).toBe('function');
|
|
expect(typeof instance.toUndefined).toBe('function');
|
|
expect(typeof instance.toString).toBe('function');
|
|
});
|
|
|
|
it('should correctly identify as OkNone', () => {
|
|
// Arrange
|
|
const instance = new OkNone();
|
|
|
|
// Act & Assert
|
|
expect(instance.isSome()).toBe(false);
|
|
expect(instance.isNone()).toBe(true);
|
|
expect(instance.isErr()).toBe(false);
|
|
});
|
|
});
|
|
|
|
describe('ErrNone constructor', () => {
|
|
it('should create ErrNone instance with provided error', () => {
|
|
// Arrange
|
|
const error = 'something went wrong';
|
|
|
|
// Act
|
|
const result = new ErrNone(error);
|
|
|
|
// Assert
|
|
expect(result).toBeInstanceOf(ErrNone);
|
|
expect(result.err).toBe(error);
|
|
});
|
|
|
|
it('should store different types of errors correctly', () => {
|
|
// Arrange
|
|
const stringError = 'error';
|
|
const numberError = 404;
|
|
const objectError = { code: 'NOT_FOUND', message: 'not found' };
|
|
const errorInstance = new Error('fail');
|
|
|
|
// Act
|
|
const stringErrNone = new ErrNone(stringError);
|
|
const numberErrNone = new ErrNone(numberError);
|
|
const objectErrNone = new ErrNone(objectError);
|
|
const errorErrNone = new ErrNone(errorInstance);
|
|
|
|
// Assert
|
|
expect(stringErrNone.err).toBe(stringError);
|
|
expect(numberErrNone.err).toBe(numberError);
|
|
expect(objectErrNone.err).toBe(objectError);
|
|
expect(errorErrNone.err).toBe(errorInstance);
|
|
});
|
|
|
|
it('should implement QueryInterface methods', () => {
|
|
// Arrange
|
|
const instance = new ErrNone('error');
|
|
|
|
// Assert
|
|
expect(typeof instance.isSome).toBe('function');
|
|
expect(typeof instance.isNone).toBe('function');
|
|
expect(typeof instance.isErr).toBe('function');
|
|
expect(typeof instance.map).toBe('function');
|
|
expect(typeof instance.mapErr).toBe('function');
|
|
expect(typeof instance.flatMap).toBe('function');
|
|
expect(typeof instance.flatMapErr).toBe('function');
|
|
expect(typeof instance.match).toBe('function');
|
|
expect(typeof instance.filter).toBe('function');
|
|
expect(typeof instance.toNullable).toBe('function');
|
|
expect(typeof instance.toUndefined).toBe('function');
|
|
expect(typeof instance.toString).toBe('function');
|
|
});
|
|
|
|
it('should correctly identify as ErrNone', () => {
|
|
// Arrange
|
|
const instance = new ErrNone('error');
|
|
|
|
// Act & Assert
|
|
expect(instance.isSome()).toBe(false);
|
|
expect(instance.isNone()).toBe(false);
|
|
expect(instance.isErr()).toBe(true);
|
|
});
|
|
});
|
|
|
|
describe('okSome() factory function', () => {
|
|
it('should create OkSome instance with provided value', () => {
|
|
// Arrange
|
|
const value = 42;
|
|
|
|
// Act
|
|
const result = okSome(value);
|
|
|
|
// Assert
|
|
expect(result).toBeInstanceOf(OkSome);
|
|
expect(result.value).toBe(value);
|
|
});
|
|
|
|
it('should work with various data types', () => {
|
|
// Arrange
|
|
const testCases = [
|
|
42,
|
|
'hello',
|
|
true,
|
|
{ id: 1, name: 'test' },
|
|
[1, 2, 3],
|
|
(x: number) => x * 2,
|
|
];
|
|
|
|
testCases.forEach((testValue) => {
|
|
// Act
|
|
const result = okSome(testValue);
|
|
|
|
// Assert
|
|
expect(result).toBeInstanceOf(OkSome);
|
|
expect(result.value).toBe(testValue);
|
|
expect(result.isSome()).toBe(true);
|
|
expect(result.isNone()).toBe(false);
|
|
expect(result.isErr()).toBe(false);
|
|
});
|
|
});
|
|
|
|
it('should create new instances for each call', () => {
|
|
// Arrange
|
|
const value = 42;
|
|
|
|
// Act
|
|
const first = okSome(value);
|
|
const second = okSome(value);
|
|
|
|
// Assert
|
|
expect(first).toBeInstanceOf(OkSome);
|
|
expect(second).toBeInstanceOf(OkSome);
|
|
expect(first).not.toBe(second);
|
|
expect(first.value).toBe(second.value);
|
|
});
|
|
});
|
|
|
|
describe('okNone() factory function', () => {
|
|
it('should return OkNone instance', () => {
|
|
// Act
|
|
const result = okNone();
|
|
|
|
// Assert
|
|
expect(result).toBeInstanceOf(OkNone);
|
|
expect(result.isSome()).toBe(false);
|
|
expect(result.isNone()).toBe(true);
|
|
expect(result.isErr()).toBe(false);
|
|
});
|
|
|
|
it('should return the same singleton instance', () => {
|
|
// Act
|
|
const first = okNone();
|
|
const second = okNone();
|
|
const third = okNone();
|
|
|
|
// Assert
|
|
expect(first).toBe(second);
|
|
expect(second).toBe(third);
|
|
expect(first).toBe(third);
|
|
});
|
|
|
|
it('should return a frozen instance', () => {
|
|
// Act
|
|
const instance = okNone();
|
|
|
|
// Assert
|
|
expect(Object.isFrozen(instance)).toBe(true);
|
|
});
|
|
|
|
it('should not accept any arguments', () => {
|
|
// Act & Assert
|
|
expect(() => okNone()).not.toThrow();
|
|
|
|
// @ts-expect-error Testing runtime behavior when called incorrectly
|
|
expect(() => okNone(42)).not.toThrow();
|
|
});
|
|
});
|
|
|
|
describe('errNone() factory function', () => {
|
|
it('should create ErrNone instance with provided error', () => {
|
|
// Arrange
|
|
const error = 'something failed';
|
|
|
|
// Act
|
|
const result = errNone(error);
|
|
|
|
// Assert
|
|
expect(result).toBeInstanceOf(ErrNone);
|
|
expect(result.err).toBe(error);
|
|
});
|
|
|
|
it('should work with various error types', () => {
|
|
// Arrange
|
|
const testCases = [
|
|
'error string',
|
|
42,
|
|
new Error('fail'),
|
|
{ code: 'ERR', message: 'failed' },
|
|
['err1', 'err2'],
|
|
];
|
|
|
|
testCases.forEach((testError) => {
|
|
// Act
|
|
const result = errNone(testError);
|
|
|
|
// Assert
|
|
expect(result).toBeInstanceOf(ErrNone);
|
|
expect(result.err).toBe(testError);
|
|
expect(result.isSome()).toBe(false);
|
|
expect(result.isNone()).toBe(false);
|
|
expect(result.isErr()).toBe(true);
|
|
});
|
|
});
|
|
|
|
it('should create new instances for each call', () => {
|
|
// Arrange
|
|
const error = 'error';
|
|
|
|
// Act
|
|
const first = errNone(error);
|
|
const second = errNone(error);
|
|
|
|
// Assert
|
|
expect(first).not.toBe(second);
|
|
expect(first.err).toBe(second.err);
|
|
});
|
|
});
|
|
|
|
describe('toString()', () => {
|
|
it('should return correct string for OkSome', () => {
|
|
// Arrange & Act & Assert
|
|
expect(okSome(42).toString()).toBe('OkSome(42)');
|
|
expect(okSome('hello').toString()).toBe('OkSome(hello)');
|
|
expect(okSome(true).toString()).toBe('OkSome(true)');
|
|
});
|
|
|
|
it('should return correct string for OkNone', () => {
|
|
// Arrange & Act & Assert
|
|
expect(okNone().toString()).toBe('OkNone()');
|
|
});
|
|
|
|
it('should return correct string for ErrNone', () => {
|
|
// Arrange & Act & Assert
|
|
expect(errNone('error').toString()).toBe('ErrNone(error)');
|
|
expect(errNone(404).toString()).toBe('ErrNone(404)');
|
|
});
|
|
});
|
|
|
|
describe('Integration tests', () => {
|
|
it('should allow creating mixed arrays of Queries', () => {
|
|
// Arrange & Act
|
|
const queries: Query<number, string>[] = [
|
|
okSome(1) as Query<number, string>,
|
|
okSome(2) as Query<number, string>,
|
|
okNone() as Query<number, string>,
|
|
errNone('error') as Query<number, string>,
|
|
okSome(3) as Query<number, string>,
|
|
];
|
|
|
|
// Assert
|
|
expect(queries).toHaveLength(5);
|
|
expect(queries[0].isSome()).toBe(true);
|
|
expect(queries[1].isSome()).toBe(true);
|
|
expect(queries[2].isNone()).toBe(true);
|
|
expect(queries[3].isErr()).toBe(true);
|
|
expect(queries[4].isSome()).toBe(true);
|
|
});
|
|
|
|
it('should enable type narrowing in runtime', () => {
|
|
// Arrange
|
|
const query: Query<string, number> = okSome('hello') as Query<string, number>;
|
|
|
|
// Act & Assert
|
|
if (query.isSome()) {
|
|
expect(query.value).toBe('hello');
|
|
expect(typeof query.value).toBe('string');
|
|
} else {
|
|
fail('Expected query to be OkSome');
|
|
}
|
|
});
|
|
|
|
it('should handle edge cases correctly', () => {
|
|
// Arrange & Act
|
|
const zeroOkSome = okSome(0);
|
|
const falseOkSome = okSome(false);
|
|
const emptyOkSome = okSome('');
|
|
const emptyArrayOkSome = okSome([]);
|
|
|
|
// Assert - All should be OkSome instances even with "falsy" values
|
|
expect(zeroOkSome.isSome()).toBe(true);
|
|
expect(falseOkSome.isSome()).toBe(true);
|
|
expect(emptyOkSome.isSome()).toBe(true);
|
|
expect(emptyArrayOkSome.isSome()).toBe(true);
|
|
|
|
expect(zeroOkSome.value).toBe(0);
|
|
expect(falseOkSome.value).toBe(false);
|
|
expect(emptyOkSome.value).toBe('');
|
|
expect(emptyArrayOkSome.value).toEqual([]);
|
|
});
|
|
});
|
|
});
|