Skip to content

Commit

Permalink
test: Use property-based testing
Browse files Browse the repository at this point in the history
  • Loading branch information
vbudovski committed Jul 15, 2024
1 parent 22e11e4 commit b108334
Show file tree
Hide file tree
Showing 18 changed files with 1,345 additions and 934 deletions.
1 change: 1 addition & 0 deletions paseri-lib/deno.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
"@std/expect": "jsr:@std/expect@^0.224.4",
"esbuild": "npm:esbuild@^0.21.4",
"expect-type": "npm:expect-type@^0.19.0",
"fast-check": "npm:fast-check@^3.20.0",
"type-fest": "npm:type-fest@^4.20.0",
"typescript": "npm:typescript@^5.4.5",
"zod": "npm:zod@^3.23.8"
Expand Down
12 changes: 12 additions & 0 deletions paseri-lib/deno.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

241 changes: 146 additions & 95 deletions paseri-lib/src/schemas/array.test.ts
Original file line number Diff line number Diff line change
@@ -1,110 +1,151 @@
import { expect } from '@std/expect';
import { expectTypeOf } from 'expect-type';
import fc from 'fast-check';
import * as p from '../index.ts';
import type { TreeNode } from '../issue.ts';

const { test } = Deno;

test('Type', async (t) => {
test('Valid type', () => {
const schema = p.array(p.number());

await t.step('Valid', () => {
const result = schema.safeParse([1, 2, 3]);
if (result.ok) {
expectTypeOf(result.value).toEqualTypeOf<number[]>;
expect(result.value).toEqual([1, 2, 3]);
} else {
expect(result.ok).toBeTruthy();
}
});
fc.assert(
fc.property(fc.array(fc.float()), (data) => {
const result = schema.safeParse(data);
if (result.ok) {
expectTypeOf(result.value).toEqualTypeOf<number[]>;
expect(result.value).toEqual(data);
} else {
expect(result.ok).toBeTruthy();
}
}),
);
});

await t.step('Invalid', () => {
const result = schema.safeParse(null);
if (!result.ok) {
const expectedResult: TreeNode = { type: 'leaf', code: 'invalid_type' };
expect(result.issue).toEqual(expectedResult);
} else {
expect(result.ok).toBeFalsy();
}
});
test('Invalid type', () => {
const schema = p.string();

fc.assert(
fc.property(
fc.anything().filter((value) => Array.isArray(value)),
(data) => {
const result = schema.safeParse(data);
if (!result.ok) {
expect(result.issue).toEqual({ type: 'leaf', code: 'invalid_type' });
} else {
expect(result.ok).toBeFalsy();
}
},
),
);
});

test('Min', async (t) => {
test('Valid min', () => {
const schema = p.array(p.number()).min(3);

await t.step('Valid', () => {
const result = schema.safeParse([1, 2, 3]);
if (result.ok) {
expectTypeOf(result.value).toEqualTypeOf<number[]>;
expect(result.value).toEqual([1, 2, 3]);
} else {
expect(result.ok).toBeTruthy();
}
});
fc.assert(
fc.property(fc.array(fc.float(), { minLength: 3 }), (data) => {
const result = schema.safeParse(data);
if (result.ok) {
expectTypeOf(result.value).toEqualTypeOf<number[]>;
expect(result.value).toBe(data);
} else {
expect(result.ok).toBeTruthy();
}
}),
);
});

await t.step('Too short', () => {
const result = schema.safeParse([1, 2]);
if (!result.ok) {
expect(result.issue).toEqual({ type: 'leaf', code: 'too_short' });
} else {
expect(result.ok).toBeFalsy();
}
});
test('Invalid min', () => {
const schema = p.array(p.number()).min(3);

fc.assert(
fc.property(fc.array(fc.float(), { maxLength: 2 }), (data) => {
const result = schema.safeParse(data);
if (!result.ok) {
expect(result.issue).toEqual({ type: 'leaf', code: 'too_short' });
} else {
expect(result.ok).toBeFalsy();
}
}),
);
});

test('Max', async (t) => {
test('Valid max', () => {
const schema = p.array(p.number()).max(3);

await t.step('Valid', () => {
const result = schema.safeParse([1, 2, 3]);
if (result.ok) {
expectTypeOf(result.value).toEqualTypeOf<number[]>;
expect(result.value).toEqual([1, 2, 3]);
} else {
expect(result.ok).toBeTruthy();
}
});
fc.assert(
fc.property(fc.array(fc.float(), { maxLength: 3 }), (data) => {
const result = schema.safeParse(data);
if (result.ok) {
expectTypeOf(result.value).toEqualTypeOf<number[]>;
expect(result.value).toBe(data);
} else {
expect(result.ok).toBeTruthy();
}
}),
);
});

await t.step('Too long', () => {
const result = schema.safeParse([1, 2, 3, 4]);
if (!result.ok) {
expect(result.issue).toEqual({ type: 'leaf', code: 'too_long' });
} else {
expect(result.ok).toBeFalsy();
}
});
test('Invalid max', () => {
const schema = p.array(p.number()).max(3);

fc.assert(
fc.property(fc.array(fc.float(), { minLength: 4 }), (data) => {
const result = schema.safeParse(data);
if (!result.ok) {
expect(result.issue).toEqual({ type: 'leaf', code: 'too_long' });
} else {
expect(result.ok).toBeFalsy();
}
}),
);
});

test('Length', async (t) => {
test('Valid length', () => {
const schema = p.array(p.number()).length(3);

await t.step('Valid', () => {
const result = schema.safeParse([1, 2, 3]);
if (result.ok) {
expectTypeOf(result.value).toEqualTypeOf<number[]>;
expect(result.value).toEqual([1, 2, 3]);
} else {
expect(result.ok).toBeTruthy();
}
});
fc.assert(
fc.property(fc.array(fc.float(), { minLength: 3, maxLength: 3 }), (data) => {
const result = schema.safeParse(data);
if (result.ok) {
expectTypeOf(result.value).toEqualTypeOf<number[]>;
expect(result.value).toBe(data);
} else {
expect(result.ok).toBeTruthy();
}
}),
);
});

await t.step('Too long', () => {
const result = schema.safeParse([1, 2, 3, 4]);
if (!result.ok) {
expect(result.issue).toEqual({ type: 'leaf', code: 'too_long' });
} else {
expect(result.ok).toBeFalsy();
}
});
test('Invalid length (too long)', () => {
const schema = p.array(p.number()).length(3);

await t.step('Too short', () => {
const result = schema.safeParse([1, 2]);
if (!result.ok) {
expect(result.issue).toEqual({ type: 'leaf', code: 'too_short' });
} else {
expect(result.ok).toBeFalsy();
}
});
fc.assert(
fc.property(fc.array(fc.float(), { minLength: 4 }), (data) => {
const result = schema.safeParse(data);
if (!result.ok) {
expect(result.issue).toEqual({ type: 'leaf', code: 'too_long' });
} else {
expect(result.ok).toBeFalsy();
}
}),
);
});

test('Invalid length (too short)', () => {
const schema = p.array(p.number()).length(3);

fc.assert(
fc.property(fc.array(fc.float(), { maxLength: 2 }), (data) => {
const result = schema.safeParse(data);
if (!result.ok) {
expect(result.issue).toEqual({ type: 'leaf', code: 'too_short' });
} else {
expect(result.ok).toBeFalsy();
}
}),
);
});

test('Invalid elements', () => {
Expand All @@ -126,24 +167,34 @@ test('Invalid elements', () => {

test('Optional', () => {
const schema = p.array(p.number()).optional();
const result = schema.safeParse(undefined);
if (result.ok) {
expectTypeOf(result.value).toEqualTypeOf<number[] | undefined>;
expect(result.value).toBe(undefined);
} else {
expect(result.ok).toBeTruthy();
}

fc.assert(
fc.property(fc.option(fc.array(fc.float()), { nil: undefined }), (data) => {
const result = schema.safeParse(data);
if (result.ok) {
expectTypeOf(result.value).toEqualTypeOf<number[] | undefined>;
expect(result.value).toEqual(data);
} else {
expect(result.ok).toBeTruthy();
}
}),
);
});

test('Nullable', () => {
const schema = p.array(p.number()).nullable();
const result = schema.safeParse(null);
if (result.ok) {
expectTypeOf(result.value).toEqualTypeOf<number[] | null>;
expect(result.value).toBe(null);
} else {
expect(result.ok).toBeTruthy();
}

fc.assert(
fc.property(fc.option(fc.array(fc.float()), { nil: null }), (data) => {
const result = schema.safeParse(data);
if (result.ok) {
expectTypeOf(result.value).toEqualTypeOf<number[] | null>;
expect(result.value).toEqual(data);
} else {
expect(result.ok).toBeTruthy();
}
}),
);
});

test('Immutable', async (t) => {
Expand Down
Loading

0 comments on commit b108334

Please sign in to comment.