renovate/lib/util/schema-utils.spec.ts
Joshua Tang ee6c40f800
fix(manager/pub): extract sdk constraint correctly (#23367)
Co-authored-by: Sergei Zharinov <zharinov@users.noreply.github.com>
Co-authored-by: Michael Kriese <michael.kriese@visualon.de>
2023-08-07 06:04:54 +00:00

343 lines
8.3 KiB
TypeScript

import { z } from 'zod';
import {
Json,
Json5,
LooseArray,
LooseRecord,
Url,
UtcDate,
Yaml,
} from './schema-utils';
describe('util/schema-utils', () => {
describe('LooseArray', () => {
it('parses array', () => {
const s = LooseArray(z.string());
expect(s.parse(['foo', 'bar'])).toEqual(['foo', 'bar']);
});
it('drops wrong items', () => {
const s = LooseArray(z.string());
expect(s.parse(['foo', 123, null, undefined, []])).toEqual(['foo']);
});
it('runs callback for wrong elements', () => {
let err: z.ZodError | undefined = undefined;
const Schema = LooseArray(z.string(), {
onError: ({ error }) => {
err = error;
},
});
const res = Schema.parse(['foo', 123, 'bar']);
expect(res).toEqual(['foo', 'bar']);
expect(err).toMatchObject({
issues: [
{
message: 'Expected string, received number',
code: 'invalid_type',
expected: 'string',
received: 'number',
path: [1],
},
],
});
});
});
describe('LooseRecord', () => {
it('parses record', () => {
const s = LooseRecord(z.string());
expect(s.parse({ foo: 'bar' })).toEqual({ foo: 'bar' });
});
it('drops wrong items', () => {
const s = LooseRecord(z.string());
expect(s.parse({ foo: 'foo', bar: 123 })).toEqual({ foo: 'foo' });
});
it('supports key schema', () => {
const s = LooseRecord(
z
.string()
.refine((x) => x === 'bar')
.transform((x) => x.toUpperCase()),
z.string().transform((x) => x.toUpperCase())
);
expect(s.parse({ foo: 'foo', bar: 'bar' })).toEqual({ BAR: 'BAR' });
});
it('reports key schema errors', () => {
let errorData: unknown = null;
const s = LooseRecord(
z.string().refine((x) => x === 'bar'),
z.string(),
{
onError: (x) => {
errorData = x;
},
}
);
s.parse({ foo: 'foo', bar: 'bar' });
expect(errorData).toMatchObject({
error: {
issues: [
{
code: 'custom',
message: 'Invalid input',
path: ['foo'],
},
],
},
input: { bar: 'bar', foo: 'foo' },
});
});
it('runs callback for wrong elements', () => {
let err: z.ZodError | undefined = undefined;
const Schema = LooseRecord(
z.object({ foo: z.object({ bar: z.string() }) }),
{
onError: ({ error }) => {
err = error;
},
}
);
const res = Schema.parse({
aaa: { foo: { bar: 42 } },
bbb: { foo: { baz: 'asdf' } },
ccc: { foo: { bar: 'baz' } },
});
expect(res).toEqual({ ccc: { foo: { bar: 'baz' } } });
expect(err).toMatchObject({
issues: [
{
message: 'Expected string, received number',
code: 'invalid_type',
expected: 'string',
received: 'number',
path: ['aaa', 'foo', 'bar'],
},
{
message: 'Required',
code: 'invalid_type',
expected: 'string',
received: 'undefined',
path: ['bbb', 'foo', 'bar'],
},
],
});
});
});
describe('Json', () => {
it('parses json', () => {
const Schema = Json.pipe(z.object({ foo: z.literal('bar') }));
expect(Schema.parse('{"foo": "bar"}')).toEqual({ foo: 'bar' });
expect(Schema.safeParse(42)).toMatchObject({
error: {
issues: [
{
message: 'Expected string, received number',
code: 'invalid_type',
expected: 'string',
received: 'number',
path: [],
},
],
},
success: false,
});
expect(Schema.safeParse('{"foo": "foo"}')).toMatchObject({
error: {
issues: [
{
message: 'Invalid literal value, expected "bar"',
code: 'invalid_literal',
expected: 'bar',
received: 'foo',
path: ['foo'],
},
],
},
success: false,
});
expect(Schema.safeParse('["foo", "bar"]')).toMatchObject({
error: {
issues: [
{
message: 'Expected object, received array',
code: 'invalid_type',
expected: 'object',
received: 'array',
path: [],
},
],
},
success: false,
});
expect(Schema.safeParse('{{{}}}')).toMatchObject({
error: {
issues: [
{
message: 'Invalid JSON',
code: 'custom',
path: [],
},
],
},
success: false,
});
});
});
describe('Json5', () => {
it('parses JSON5', () => {
const Schema = Json5.pipe(z.object({ foo: z.literal('bar') }));
expect(Schema.parse('{"foo": "bar"}')).toEqual({ foo: 'bar' });
expect(Schema.safeParse(42)).toMatchObject({
error: {
issues: [
{
message: 'Expected string, received number',
code: 'invalid_type',
expected: 'string',
received: 'number',
path: [],
},
],
},
success: false,
});
expect(Schema.safeParse('{"foo": "foo"}')).toMatchObject({
error: {
issues: [
{
message: 'Invalid literal value, expected "bar"',
code: 'invalid_literal',
expected: 'bar',
received: 'foo',
path: ['foo'],
},
],
},
success: false,
});
expect(Schema.safeParse('["foo", "bar"]')).toMatchObject({
error: {
issues: [
{
message: 'Expected object, received array',
code: 'invalid_type',
expected: 'object',
received: 'array',
path: [],
},
],
},
success: false,
});
expect(Schema.safeParse('{{{}}}')).toMatchObject({
error: {
issues: [
{
message: 'Invalid JSON5',
code: 'custom',
path: [],
},
],
},
success: false,
});
});
});
describe('UtcDate', () => {
it('parses date', () => {
expect(UtcDate.parse('2020-04-04').toString()).toBe(
'2020-04-04T00:00:00.000Z'
);
});
it('rejects invalid date', () => {
expect(() => UtcDate.parse('foobar')).toThrow();
});
});
describe('Url', () => {
it('parses valid URLs', () => {
const urlStr = 'https://www.example.com/foo/bar?baz=qux';
const parsedUrl = Url.parse(urlStr);
expect(parsedUrl).toMatchObject({
protocol: 'https:',
hostname: 'www.example.com',
pathname: '/foo/bar',
search: '?baz=qux',
});
});
it('throws an error for invalid URLs', () => {
const urlStr = 'invalid-url-string';
expect(() => Url.parse(urlStr)).toThrow('Invalid URL');
});
});
describe('Yaml', () => {
const Schema = Yaml.pipe(
z.object({ foo: z.array(z.object({ bar: z.literal('baz') })) })
);
it('parses valid yaml', () => {
expect(Schema.parse('foo:\n- bar: baz')).toEqual({
foo: [{ bar: 'baz' }],
});
});
it('throws error for non-string', () => {
expect(Schema.safeParse(42)).toMatchObject({
error: {
issues: [
{
message: 'Expected string, received number',
code: 'invalid_type',
expected: 'string',
received: 'number',
path: [],
},
],
},
success: false,
});
});
it('throws error for invalid yaml', () => {
expect(Schema.safeParse('clearly: "invalid" "yaml"')).toMatchObject({
error: {
issues: [
{
message: 'Invalid YAML',
code: 'custom',
path: [],
},
],
},
success: false,
});
});
});
});