firebase-tools
Version:
Command-Line Interface for Firebase
469 lines (468 loc) • 15.6 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const chai_1 = require("chai");
const util_1 = require("./util");
const testCases = [
{
desc: "should remove $schema property",
input: {
$schema: "http://json-schema.org/draft-07/schema#",
type: "object",
properties: { name: { type: "string" } },
},
expected: {
type: "object",
properties: { name: { type: "string" } },
},
},
{
desc: "should remove additionalProperties field",
input: {
type: "object",
properties: { name: { type: "string" } },
additionalProperties: false,
},
expected: {
type: "object",
properties: { name: { type: "string" } },
},
},
{
desc: "should remove additionalProperties from nested objects",
input: {
type: "object",
properties: {
user: {
type: "object",
properties: { id: { type: "number" } },
additionalProperties: true,
},
meta: {
type: "object",
additionalProperties: { type: "string" },
},
},
additionalProperties: false,
},
expected: {
type: "object",
properties: {
user: {
type: "object",
properties: { id: { type: "number" } },
},
meta: {
type: "object",
},
},
},
},
{
desc: "should remove top-level array type (string)",
input: { type: "array", items: { type: "string" } },
expected: {},
},
{
desc: "should remove top-level array type (array of types including array)",
input: { type: ["array", "string"], items: { type: "string" } },
expected: { type: "string", items: { type: "string" } },
},
{
desc: "should remove top-level array type (array of types including array and null)",
input: { type: ["array", "null"], items: { type: "string" } },
expected: {},
},
{
desc: "should remove top-level null type",
input: { type: "null" },
expected: {},
},
{
desc: "should KEEP array type in properties",
input: {
type: "object",
properties: {
tags: { type: "array", items: { type: "string" } },
name: { type: "string" },
},
},
expected: {
type: "object",
properties: {
tags: { type: "array", items: { type: "string" } },
name: { type: "string" },
},
},
},
{
desc: "should remove null type from properties",
input: {
type: "object",
properties: {
optionalField: { type: "null" },
name: { type: "string" },
},
},
expected: {
type: "object",
properties: { name: { type: "string" } },
},
},
{
desc: "should convert type: ['string', 'null', 'array'] to anyOf: [{type: 'string'}, {type: 'array'}] in properties",
input: {
type: "object",
properties: {
mixed: { type: ["string", "null", "array"] },
},
},
expected: {
type: "object",
properties: {
mixed: { anyOf: [{ type: "string" }, { type: "array" }] },
},
},
},
{
desc: "should convert type: ['string', 'number', 'null', 'array'] to anyOf in properties",
input: {
type: "object",
properties: {
mixed: { type: ["string", "number", "null", "array"] },
},
},
expected: {
type: "object",
properties: {
mixed: { anyOf: [{ type: "string" }, { type: "number" }, { type: "array" }] },
},
},
},
{
desc: "should simplify type: ['string', 'null'] to type: 'string' in properties",
input: {
type: "object",
properties: {
simpleMixed: { type: ["string", "null"] },
},
},
expected: {
type: "object",
properties: {
simpleMixed: { type: "string" },
},
},
},
{
desc: "should remove property if its type array becomes empty after filtering (e.g. only null)",
input: {
type: "object",
properties: {
onlyNull: { type: ["null"] },
name: { type: "string" },
},
},
expected: {
type: "object",
properties: { name: { type: "string" } },
},
},
{
desc: "should keep property if its type array contains only 'array' (not root) and simplify",
input: {
type: "object",
properties: {
onlyArray: { type: ["array", "null"] },
name: { type: "string" },
},
},
expected: {
type: "object",
properties: {
onlyArray: { type: "array" },
name: { type: "string" },
},
},
},
{
desc: "should handle nested objects and clean them (arrays kept in nested, type arrays become anyOf or simplified)",
input: {
type: "object",
properties: {
user: {
type: "object",
properties: {
id: { type: "number" },
tags: { type: "array", items: { type: "string" } },
status: { type: ["string", "integer", "null"] },
maybeName: { type: ["string", "null"] },
},
},
},
},
expected: {
type: "object",
properties: {
user: {
type: "object",
properties: {
id: { type: "number" },
tags: { type: "array", items: { type: "string" } },
status: { anyOf: [{ type: "string" }, { type: "integer" }] },
maybeName: { type: "string" },
},
},
},
},
},
{
desc: "should remove items if its schema becomes null",
input: {
type: "object",
properties: {
someObjectWithItems: {
type: "object",
items: { type: "null" },
},
},
},
expected: {
type: "object",
properties: {
someObjectWithItems: {
type: "object",
},
},
},
},
{
desc: "should clean definitions ($defs), convert type arrays to anyOf/simplified",
input: {
type: "object",
properties: {
myDef: { $ref: "#/$defs/invalidDef" },
myValidDef: { $ref: "#/$defs/validDefWithArray" },
myComplexDef: { $ref: "#/$defs/complexDef" },
},
$defs: {
invalidDef: { type: "null" },
validDef: { type: "string" },
validDefWithArray: { type: "array", items: { type: "number" } },
complexDef: { type: ["boolean", "string", "null"] },
},
},
expected: {
type: "object",
properties: {
myDef: { $ref: "#/$defs/invalidDef" },
myValidDef: { $ref: "#/$defs/validDefWithArray" },
myComplexDef: { $ref: "#/$defs/complexDef" },
},
$defs: {
validDef: { type: "string" },
validDefWithArray: { type: "array", items: { type: "number" } },
complexDef: { anyOf: [{ type: "boolean" }, { type: "string" }] },
},
},
},
{
desc: "should remove $defs if all definitions become invalid (e.g. all null)",
input: {
type: "object",
$defs: {
invalidDef1: { type: "null" },
invalidDef2: { type: "null" },
},
},
expected: {
type: "object",
},
},
{
desc: "should clean schema arrays (anyOf, allOf, oneOf), keep nested arrays, convert internal type arrays",
input: {
anyOf: [
{ type: "string" },
{ type: "array", items: { type: "number" } },
{ type: "null" },
{ type: ["integer", "boolean", "null"] },
],
allOf: [{ type: "number" }],
oneOf: [{ type: "boolean" }, { type: ["null", "array"] }],
},
expected: {
anyOf: [
{ type: "string" },
{ type: "array", items: { type: "number" } },
{ anyOf: [{ type: "integer" }, { type: "boolean" }] },
],
allOf: [{ type: "number" }],
oneOf: [{ type: "boolean" }, { type: "array" }],
},
},
{
desc: "should remove schema array keywords if their arrays become empty (e.g. all null)",
input: {
anyOf: [{ type: "null" }, { type: "null" }],
description: "test",
},
expected: {
description: "test",
},
},
{
desc: "should return an empty object if the entire schema is just { type: 'array' }",
input: { type: "array" },
expected: {},
},
{
desc: "should return an empty object if the entire schema is just { type: 'null' }",
input: { type: "null" },
expected: {},
},
{
desc: "should return an empty object if the entire schema is { type: ['null', 'array'] }",
input: { type: ["null", "array"] },
expected: {},
},
{
desc: "should not modify a schema that is already clean (with nested array and anyOf)",
input: {
type: "object",
properties: {
name: { type: "string" },
age: { type: "integer" },
scores: { type: "array", items: { type: "number" } },
choice: { anyOf: [{ type: "string" }, { type: "boolean" }] },
},
required: ["name"],
},
expected: {
type: "object",
properties: {
name: { type: "string" },
age: { type: "integer" },
scores: { type: "array", items: { type: "number" } },
choice: { anyOf: [{ type: "string" }, { type: "boolean" }] },
},
required: ["name"],
},
},
{
desc: "should handle deeply nested structures with various cleaning needs (arrays kept if not root, type arrays to anyOf)",
input: {
$schema: "http://json-schema.org/draft-07/schema#",
title: "Complex Test",
type: "object",
additionalProperties: false,
properties: {
validProp: { type: "string" },
propToBeKept: { type: "array", items: { type: "number" } },
objectWithMixedTypes: {
type: "object",
additionalProperties: true,
properties: {
subPropString: { type: "string" },
subPropNull: { type: "null" },
subPropArrayType: { type: ["integer", "null", "array", "string"] },
},
},
anotherArrayProp: { type: "array", items: { type: "boolean" } },
},
$defs: {
reusableInvalid: { type: "null" },
reusableValid: {
type: "object",
additionalProperties: { type: "string" },
properties: {
detail: { type: "string" },
unwantedList: { type: "array", items: { type: "string" } },
statusOptions: { type: ["number", "string", "null"] },
},
},
toBeEmptyDef: { type: "null" },
},
anyOf: [
{ type: "string" },
{ type: "array", items: { type: "object" } },
{ $ref: "#/$defs/reusableInvalid" },
{ type: ["boolean", "null", "integer"] },
],
},
expected: {
title: "Complex Test",
type: "object",
properties: {
validProp: { type: "string" },
propToBeKept: { type: "array", items: { type: "number" } },
objectWithMixedTypes: {
type: "object",
properties: {
subPropString: { type: "string" },
subPropArrayType: {
anyOf: [{ type: "integer" }, { type: "array" }, { type: "string" }],
},
},
},
anotherArrayProp: { type: "array", items: { type: "boolean" } },
},
$defs: {
reusableValid: {
type: "object",
properties: {
detail: { type: "string" },
unwantedList: { type: "array", items: { type: "string" } },
statusOptions: { anyOf: [{ type: "number" }, { type: "string" }] },
},
},
},
anyOf: [
{ type: "string" },
{ type: "array", items: { type: "object" } },
{ anyOf: [{ type: "boolean" }, { type: "integer" }] },
],
},
},
{
desc: "should remove properties if properties object becomes empty (all null)",
input: {
type: "object",
properties: {
field1: { type: "null" },
field2: { type: "null" },
},
},
expected: {
type: "object",
},
},
{
desc: "top level schema with type: ['string', 'array'] should become type: 'string'",
input: {
type: ["string", "array"],
description: "Test",
},
expected: {
type: "string",
description: "Test",
},
},
{
desc: "top level schema with type: ['string', 'number', 'array'] should become anyOf: [{type: string}, {type: number}]",
input: {
type: ["string", "number", "array"],
description: "Test AnyOf Root",
},
expected: {
anyOf: [{ type: "string" }, { type: "number" }],
description: "Test AnyOf Root",
},
},
];
describe("cleanSchema", () => {
testCases.forEach((tc) => {
it(tc.desc, () => {
(0, chai_1.expect)((0, util_1.cleanSchema)(tc.input)).to.deep.equal(tc.expected);
});
});
});