You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
vore-rpg/src/testing/Assertions.test.ts

174 lines
7.8 KiB

import {AssertionConfig, AssertionMode, Assertions} from "./Assertions";
describe("Assertion", () => {
describe("check", () => {
const defaults = {
value: Symbol("test value"),
config: AssertionConfig.Skip,
conditionImpl: () => true,
messageImpl: () => "Mockery",
} as const
function run<T>({value, config, conditionImpl, messageImpl}: {value: T, config: typeof AssertionConfig.Skip, conditionImpl: (value: T) => boolean, messageImpl: (value: T) => string}): {value: T, condition: jest.Mock<boolean, [T]>, message: jest.Mock<string, [T]>, retval: T}
function run<T>({value, config, conditionImpl, messageImpl}: {value: T, config: typeof AssertionConfig.Throw, conditionImpl: (value: T) => boolean, messageImpl: (value: T) => string}): {value: T, condition: jest.Mock<boolean, [T]>, message: jest.Mock<string, [T]>, retval?: T, error?: Error}
function run<T>({value, config, conditionImpl, messageImpl}: {value: T, config: ReturnType<typeof AssertionConfig.Log>, conditionImpl: (value: T) => boolean, messageImpl: (value: T) => string}): {value: T, log: jest.Mock<void, any[]>, condition: jest.Mock<boolean, [T]>, message: jest.Mock<string, [T]>, retval: T}
function run<T>({value, config, conditionImpl, messageImpl}: {value: T, config: AssertionConfig.Type, conditionImpl: (value: T) => boolean, messageImpl: (value: T) => string}): {value: T, log?: jest.Mock<void, any[]>, condition: jest.Mock<boolean, [T]>, message: jest.Mock<string, [T]>, retval?: T, error?: Error}
function run<T>({value, config, conditionImpl, messageImpl}: {value: T, config: AssertionConfig.Type, conditionImpl: (value: T) => boolean, messageImpl: (value: T) => string}): {value: T, log?: jest.Mock<void, any[]>, condition: jest.Mock<boolean, [T]>, message: jest.Mock<string, [T]>, retval?: T, error?: Error} {
const assertion = new Assertions()
assertion.config = config
let log: jest.Mock<void, any[]>|undefined = undefined
if (config.mode === AssertionMode.LOG) {
log = jest.fn(config.logger)
assertion.config = AssertionConfig.Log(log)
}
const condition = jest.fn(conditionImpl)
const message = jest.fn(messageImpl)
let error: Error|undefined = undefined
let retval: T|undefined = undefined
try {
retval = assertion.check(value, condition, message)
} catch (e) {
if (e instanceof Error && config.mode === AssertionMode.THROW) {
error = e
} else {
throw e
}
}
return {
value, log, condition, message, error, retval
}
}
it(`returns the input and does not throw in SKIP`, () => {
const {value, retval} = run({
...defaults,
config: AssertionConfig.Skip
})
expect(retval).toBe(value)
});
it(`does not call the condition or message in SKIP`, () => {
const {condition, message} = run({
...defaults,
config: AssertionConfig.Skip
})
expect(condition).not.toHaveBeenCalled()
expect(message).not.toHaveBeenCalled()
});
it(`calls the log function with an error, returns the value and does not throw when the condition fails in LOG`, () => {
const {value, log, retval} = run({
...defaults,
config: AssertionConfig.Log(() => {}),
conditionImpl: () => false,
messageImpl: () => "Mockery"
})
expect(log).toHaveBeenCalledTimes(1)
expect(log).toHaveBeenCalledWith(expect.objectContaining({message: "Mockery"}))
expect(log).toHaveBeenCalledWith(expect.any(Error))
expect(retval).toBe(value)
});
it(`throws an error when the condition fails in THROW`, () => {
const {error} = run({
...defaults,
config: AssertionConfig.Throw,
conditionImpl: () => false,
messageImpl: () => "Mockery"
})
expect(error).toEqual(expect.objectContaining({message: "Mockery"}))
expect(error).toBeInstanceOf(Error)
});
it(`does not log and returns the input when the condition succeeds in LOG`, () => {
const {value, retval, log} = run({
...defaults,
config: AssertionConfig.Log(() => {}),
conditionImpl: () => true
})
expect(log).not.toHaveBeenCalled()
expect(retval).toBe(value)
});
it(`does not throw and returns the input when the condition succeeds in THROW`, () => {
const {value, retval, error} = run({
...defaults,
config: AssertionConfig.Throw,
conditionImpl: () => true
})
expect(error).toBeUndefined()
expect(retval).toBe(value)
});
[AssertionConfig.Log(() => {}), AssertionConfig.Throw].forEach((config) => {
it(`calls the condition, but not the message, once with the value in ${config.mode} when the condition succeeds`, () => {
const {value, condition, message} = run({
...defaults,
config,
conditionImpl: () => true
})
expect(condition).toHaveBeenCalledWith(value)
expect(condition).toHaveBeenCalledTimes(1)
expect(message).not.toHaveBeenCalled()
})
it(`calls the condition and message once each with the value in ${config.mode} when the condition fails`, () => {
const {value, condition, message} = run({
...defaults,
config,
conditionImpl: () => false
})
expect(condition).toHaveBeenCalledWith(value)
expect(condition).toHaveBeenCalledTimes(1)
expect(message).toHaveBeenCalledWith(value)
expect(message).toHaveBeenCalledTimes(1)
})
})
})
function testCheck<T>(run: (input: T) => void, valid: T[], invalid: T[]) {
valid.forEach((input) => {
test(`passes ${input}`, () => {
expect(() => run(input)).not.toThrow()
})
})
invalid.forEach((input) => {
test(`fails ${input}`, () => {
expect(() => run(input)).toThrow()
})
})
}
describe("checkInteger", () => {
function run(input: number) {
const assertion = new Assertions()
assertion.config = AssertionConfig.Throw
assertion.checkInteger(input, () => "Assertion failed")
}
const valid: number[] = [0, 1, -1, Number.MIN_SAFE_INTEGER, Number.MAX_SAFE_INTEGER]
const invalid: number[] = [Number.NaN, Number.NEGATIVE_INFINITY, Number.POSITIVE_INFINITY, Number.MAX_SAFE_INTEGER + 999, Number.MAX_VALUE, Number.MIN_SAFE_INTEGER - 999, Number.EPSILON]
testCheck(run, valid, invalid)
})
describe("checkPositiveIntegerOrZero", () => {
function run(input: number) {
const assertion = new Assertions()
assertion.config = AssertionConfig.Throw
assertion.checkPositiveIntegerOrZero(input, () => "Assertion failed")
}
const valid: number[] = [0, 1, Number.MAX_SAFE_INTEGER]
const invalid: number[] = [-1, Number.MIN_SAFE_INTEGER, Number.NaN, Number.NEGATIVE_INFINITY, Number.POSITIVE_INFINITY, Number.MAX_SAFE_INTEGER + 999, Number.MAX_VALUE, Number.MIN_SAFE_INTEGER - 999, Number.EPSILON]
testCheck(run, valid, invalid)
})
})