import mongoose from "mongoose"; import Chance from "chance"; import { setupModel } from "./utils"; import createError from "http-errors"; const chance = new Chance(); describe("DocumentValidator", () => { describe("validate", () => { describe("unsupported type", () => { test("throws error", async () => { const definition = { name: {} }; try { setupModel(definition); fail(); } catch (err) { expect(err).toBeInstanceOf(Error); } }); }); describe("String", () => { test("required - failed", async () => { const definition = { name: { type: String, required: true } }; const { Model, validator } = setupModel(definition); const doc = new Model({ name: null }); try { await validator.validate(doc); fail(); } catch (err) { expect(err).toBeInstanceOf(createError.BadRequest); } }); test("required - success", async () => { const definition = { name: { type: String, required: true } }; const { Model, validator } = setupModel(definition); const doc = new Model({ name: "xxxx" }); await validator.validate(doc); }); test("email - failed", async () => { const definition = { name: { type: String, email: true } }; const { Model, validator } = setupModel(definition); const doc = new Model({ name: "xxxx" }); try { await validator.validate(doc); fail(); } catch (err) { expect(err).toBeInstanceOf(createError.BadRequest); } }); test("email - success", async () => { const definition = { name: { type: String, email: true } }; const { Model, validator } = setupModel(definition); const doc = new Model({ name: chance.email() }); await validator.validate(doc); }); test("creditCard - failed", async () => { const definition = { name: { type: String, creditCard: true } }; const { Model, validator } = setupModel(definition); const doc = new Model({ name: "2133432" }); try { await validator.validate(doc); fail(); } catch (err) { expect(err).toBeInstanceOf(createError.BadRequest); } }); test("creditCard - success", async () => { const definition = { name: { type: String, creditCard: true } }; const { Model, validator } = setupModel(definition); const doc = new Model({ name: "31211" }); await validator.validate(doc); }); test("alphanum - failed", async () => { const definition = { name: { type: String, alphanum: true } }; const { Model, validator } = setupModel(definition); const doc = new Model({ name: "213d!sa343AD2" }); try { await validator.validate(doc); fail(); } catch (err) { expect(err).toBeInstanceOf(createError.BadRequest); } }); test("alphanum - success", async () => { const definition = { name: { type: String, alphanum: true } }; const { Model, validator } = setupModel(definition); const doc = new Model({ name: "213dsa343AD2" }); await validator.validate(doc); }); test("token - failed", async () => { const definition = { name: { type: String, token: true } }; const { Model, validator } = setupModel(definition); const doc = new Model({ name: "213d!sa3_43AD2" }); try { await validator.validate(doc); fail(); } catch (err) { expect(err).toBeInstanceOf(createError.BadRequest); } }); test("token - success", async () => { const definition = { name: { type: String, token: true } }; const { Model, validator } = setupModel(definition); const doc = new Model({ name: "213dsa3_43AD2" }); await validator.validate(doc); }); test("ip - failed", async () => { const definition = { name: { type: String, ip: true } }; const { Model, validator } = setupModel(definition); const doc = new Model({ name: "213d!sa3_43AD2" }); try { await validator.validate(doc); fail(); } catch (err) { expect(err).toBeInstanceOf(createError.BadRequest); } }); test("ip - success", async () => { const definition = { name: { type: String, ip: true } }; const { Model, validator } = setupModel(definition); const doc = new Model({ name: chance.ip() }); await validator.validate(doc); }); test("uri - failed", async () => { const definition = { name: { type: String, uri: true } }; const { Model, validator } = setupModel(definition); const doc = new Model({ name: "213d!sa3_43AD2" }); try { await validator.validate(doc); fail(); } catch (err) { expect(err).toBeInstanceOf(createError.BadRequest); } }); test("uri - success", async () => { const definition = { name: { type: String, uri: true } }; const { Model, validator } = setupModel(definition); const doc = new Model({ name: chance.url() }); await validator.validate(doc); }); test("minlength - failed", async () => { const definition = { name: { type: String, minlength: 3 } }; const { Model, validator } = setupModel(definition); const doc = new Model({ name: "12" }); try { await validator.validate(doc); fail(); } catch (err) { expect(err).toBeInstanceOf(createError.BadRequest); } }); test("minlength - success", async () => { const definition = { name: { type: String, minlength: 3 } }; const { Model, validator } = setupModel(definition); const doc = new Model({ name: "1234" }); await validator.validate(doc); }); test("maxlength - failed", async () => { const definition = { name: { type: String, maxlength: 3 } }; const { Model, validator } = setupModel(definition); const doc = new Model({ name: "1234" }); try { await validator.validate(doc); fail(); } catch (err) { expect(err).toBeInstanceOf(createError.BadRequest); } }); test("maxlength - success", async () => { const definition = { name: { type: String, maxlength: 3 } }; const { Model, validator } = setupModel(definition); const doc = new Model({ name: "123" }); await validator.validate(doc); }); test("match - failed", async () => { const definition = { name: { type: String, match: /^test$/ } }; const { Model, validator } = setupModel(definition); const doc = new Model({ name: "test1" }); try { await validator.validate(doc); fail(); } catch (err) { expect(err).toBeInstanceOf(createError.BadRequest); } }); test("match - success", async () => { const definition = { name: { type: String, match: /^test$/ } }; const { Model, validator } = setupModel(definition); const doc = new Model({ name: "test" }); await validator.validate(doc); }); test("optional - success - null", async () => { const definition = { name: { type: String, required: false } }; const { Model, validator } = setupModel(definition); const doc = new Model({ name: null }); await validator.validate(doc); }); test("optional - success - random", async () => { const definition = { name: { type: String, required: false } }; const { Model, validator } = setupModel(definition); const doc = new Model({ name: chance.string() }); await validator.validate(doc); }); test("enum string - invalid value", async () => { const definition = { name: { type: String, enum: ["a", "b"] } }; const { Model, validator } = setupModel(definition); const doc = new Model({ name: "c" }); try { await validator.validate(doc); fail(); } catch (err) { expect(err).toBeInstanceOf(createError.BadRequest); } }); test("enum string - valid value", async () => { const definition = { name: { type: String, enum: ["a", "b"] } }; const { Model, validator } = setupModel(definition); const doc = new Model({ name: "a" }); await validator.validate(doc); }); }); describe("Number", () => { test("required - failed", async () => { const definition = { count: { type: Number, required: true } }; const { Model, validator } = setupModel(definition); const doc = new Model({ count: null }); try { await validator.validate(doc); fail(); } catch (err) { expect(err).toBeInstanceOf(createError.BadRequest); } }); test("required - success", async () => { const definition = { count: { type: Number, required: true } }; const { Model, validator } = setupModel(definition); const doc = new Model({ count: 100 }); await validator.validate(doc); }); test("optional - null", async () => { const definition = { count: { type: Number, required: false } }; const { Model, validator } = setupModel(definition); const doc = new Model({ count: null }); await validator.validate(doc); }); test("optional - 100", async () => { const definition = { count: { type: Number, required: false } }; const { Model, validator } = setupModel(definition); const doc = new Model({ count: 100 }); await validator.validate(doc); }); test("required - string", async () => { const definition = { hello: { type: Number, required: true } }; const { Model, validator } = setupModel(definition); const doc: any = new Model(); doc.hello = "test"; try { await validator.validate(doc); fail(); } catch (err) { expect(err).toBeInstanceOf(createError.BadRequest); } }); test("positive", async () => { const definition = { count: { type: Number, required: false, positive: true } }; const { Model, validator } = setupModel(definition); const doc = new Model({ count: -1 }); try { await validator.validate(doc); fail(); } catch (err) { expect(err).toBeInstanceOf(createError.BadRequest); } }); test("integer 1.5", async () => { const definition = { count: { type: Number, required: false, integer: true } }; const { Model, validator } = setupModel(definition); const doc = new Model({ count: 1.5 }); try { await validator.validate(doc); fail(); } catch (err) { expect(err).toBeInstanceOf(createError.BadRequest); } }); test("integer 1.5", async () => { const definition = { count: { type: Number, required: false, integer: true } }; const { Model, validator } = setupModel(definition); const doc = new Model({ count: 1 }); await validator.validate(doc); }); test("negative 1", async () => { const definition = { count: { type: Number, required: false, negative: true } }; const { Model, validator } = setupModel(definition); const doc = new Model({ count: 1 }); try { await validator.validate(doc); fail(); } catch (err) { expect(err).toBeInstanceOf(createError.BadRequest); } }); test("negative -1", async () => { const definition = { count: { type: Number, required: false, negative: true } }; const { Model, validator } = setupModel(definition); const doc = new Model({ count: -1 }); await validator.validate(doc); }); test("max - 11", async () => { const definition = { count: { type: Number, required: false, max: 10 } }; const { Model, validator } = setupModel(definition); const doc = new Model({ count: 11 }); try { await validator.validate(doc); fail(); } catch (err) { expect(err).toBeInstanceOf(createError.BadRequest); } }); test("max - 10", async () => { const definition = { count: { type: Number, required: false, max: 10 } }; const { Model, validator } = setupModel(definition); const doc = new Model({ count: 10 }); await validator.validate(doc); }); test("max - 9", async () => { const definition = { count: { type: Number, required: false, max: 10 } }; const { Model, validator } = setupModel(definition); const doc = new Model({ count: 9 }); await validator.validate(doc); }); test("less - 11", async () => { const definition = { count: { type: Number, required: false, less: 10 } }; const { Model, validator } = setupModel(definition); const doc = new Model({ count: 11 }); try { await validator.validate(doc); fail(); } catch (err) { expect(err).toBeInstanceOf(createError.BadRequest); } }); test("less - 10", async () => { const definition = { count: { type: Number, required: false, less: 10 } }; const { Model, validator } = setupModel(definition); const doc = new Model({ count: 10 }); try { await validator.validate(doc); fail(); } catch (err) { expect(err).toBeInstanceOf(createError.BadRequest); } }); test("less - 9", async () => { const definition = { count: { type: Number, required: false, less: 10 } }; const { Model, validator } = setupModel(definition); const doc = new Model({ count: 9 }); await validator.validate(doc); }); test("min - 11", async () => { const definition = { count: { type: Number, required: false, min: 10 } }; const { Model, validator } = setupModel(definition); const doc = new Model({ count: 11 }); await validator.validate(doc); }); test("min - 10", async () => { const definition = { count: { type: Number, required: false, min: 10 } }; const { Model, validator } = setupModel(definition); const doc = new Model({ count: 10 }); await validator.validate(doc); }); test("min - 9", async () => { const definition = { count: { type: Number, required: false, min: 10 } }; const { Model, validator } = setupModel(definition); const doc = new Model({ count: 9 }); try { await validator.validate(doc); fail(); } catch (err) { expect(err).toBeInstanceOf(createError.BadRequest); } }); test("greater - 11", async () => { const definition = { count: { type: Number, required: false, greater: 10 } }; const { Model, validator } = setupModel(definition); const doc = new Model({ count: 11 }); await validator.validate(doc); }); test("greater - 10", async () => { const definition = { count: { type: Number, required: false, greater: 10 } }; const { Model, validator } = setupModel(definition); const doc = new Model({ count: 10 }); try { await validator.validate(doc); fail(); } catch (err) { expect(err).toBeInstanceOf(createError.BadRequest); } }); test("greater - 9", async () => { const definition = { count: { type: Number, required: false, greater: 10 } }; const { Model, validator } = setupModel(definition); const doc = new Model({ count: 9 }); try { await validator.validate(doc); fail(); } catch (err) { expect(err).toBeInstanceOf(createError.BadRequest); } }); test("precision - 1 - 9.55", async () => { const definition = { count: { type: Number, required: false, precision: 1 } }; const { Model, validator } = setupModel(definition); const doc = new Model({ count: 9.55 }); try { await validator.validate(doc); fail(); } catch (err) { expect(err).toBeInstanceOf(createError.BadRequest); } }); test("multiple - 2 - 3", async () => { const definition = { count: { type: Number, required: false, multiple: 2 } }; const { Model, validator } = setupModel(definition); const doc = new Model({ count: 3 }); try { await validator.validate(doc); fail(); } catch (err) { expect(err).toBeInstanceOf(createError.BadRequest); } }); test("multiple - 2 - 4", async () => { const definition = { count: { type: Number, required: false, multiple: 2 } }; const { Model, validator } = setupModel(definition); const doc = new Model({ count: 4 }); await validator.validate(doc); }); test("enum number - invalid value", async () => { const definition = { name: { type: Number, enum: [1, 2] } }; const { Model, validator } = setupModel(definition); const doc = new Model({ name: 3 }); try { await validator.validate(doc); fail(); } catch (err) { expect(err).toBeInstanceOf(createError.BadRequest); } }); test("enum number - valid value", async () => { const definition = { name: { type: Number, enum: [1, 2] } }; const { Model, validator } = setupModel(definition); const doc = new Model({ name: 1 }); await validator.validate(doc); }); }); describe("Boolean", () => { test("required - null", async () => { const definition = { active: { type: Boolean, required: true } }; const { Model, validator } = setupModel(definition); const doc = new Model({ active: null }); try { await validator.validate(doc); fail(); } catch (err) { expect(err).toBeInstanceOf(createError.BadRequest); } }); test("required - true", async () => { const definition = { active: { type: Boolean, required: true } }; const { Model, validator } = setupModel(definition); const doc = new Model({ active: true }); await validator.validate(doc); }); test("required - false", async () => { const definition = { active: { type: Boolean, required: true } }; const { Model, validator } = setupModel(definition); const doc = new Model({ active: false }); await validator.validate(doc); }); test("optional - null", async () => { const definition = { active: { type: Boolean, required: false } }; const { Model, validator } = setupModel(definition); const doc = new Model({ active: null }); await validator.validate(doc); }); test("optional - true", async () => { const definition = { active: { type: Boolean, required: false } }; const { Model, validator } = setupModel(definition); const doc = new Model({ active: true }); await validator.validate(doc); }); test("optional - false", async () => { const definition = { active: { type: Boolean, required: false } }; const { Model, validator } = setupModel(definition); const doc = new Model({ active: false }); await validator.validate(doc); }); }); describe("Mixed", () => { test("null", async () => { const definition = { data: { type: mongoose.Schema.Types.Mixed } }; const { Model, validator } = setupModel(definition); const doc = new Model({ data: null }); await validator.validate(doc); }); test("object", async () => { const definition = { data: { type: mongoose.Schema.Types.Mixed } }; const { Model, validator } = setupModel(definition); const doc = new Model({ data: { a: 1, b: 2 } }); await validator.validate(doc); }); test("array", async () => { const definition = { data: { type: mongoose.Schema.Types.Mixed } }; const { Model, validator } = setupModel(definition); const doc = new Model({ data: [1, 2, 3] }); await validator.validate(doc); }); test("true", async () => { const definition = { data: { type: mongoose.Schema.Types.Mixed } }; const { Model, validator } = setupModel(definition); const doc = new Model({ data: true }); await validator.validate(doc); }); }); describe("Date", () => { test("required - null", async () => { const definition = { active: { type: Date, required: true } }; const { Model, validator } = setupModel(definition); const doc = new Model({ active: null }); try { await validator.validate(doc); fail(); } catch (err) { expect(err).toBeInstanceOf(createError.BadRequest); } }); test("required - now", async () => { const definition = { active: { type: Date, required: true } }; const { Model, validator } = setupModel(definition); const doc = new Model({ active: new Date() }); await validator.validate(doc); }); test("optional - null", async () => { const definition = { active: { type: Date, required: false } }; const { Model, validator } = setupModel(definition); const doc = new Model({ active: null }); await validator.validate(doc); }); test("optional - now", async () => { const definition = { active: { type: Date, required: false } }; const { Model, validator } = setupModel(definition); const doc = new Model({ active: new Date() }); await validator.validate(doc); }); }); describe("ObjectId", () => { test("required - null", async () => { const definition = { externalId: { type: mongoose.Schema.Types.ObjectId, required: true } }; const { Model, validator } = setupModel(definition); const doc = new Model({ externalId: null }); try { await validator.validate(doc); fail(); } catch (err) { expect(err).toBeInstanceOf(createError.BadRequest); } }); test("required - now", async () => { const definition = { externalId: { type: mongoose.Schema.Types.ObjectId, required: true } }; const { Model, validator } = setupModel(definition); const doc = new Model({ externalId: new mongoose.Types.ObjectId() }); await validator.validate(doc); }); test("optional - null", async () => { const definition = { externalId: { type: mongoose.Schema.Types.ObjectId, required: false } }; const { Model, validator } = setupModel(definition); const doc = new Model({ externalId: null }); await validator.validate(doc); }); test("optional - now", async () => { const definition = { externalId: { type: mongoose.Schema.Types.ObjectId, required: false } }; const { Model, validator } = setupModel(definition); const doc = new Model({ externalId: new mongoose.Types.ObjectId() }); await validator.validate(doc); }); }); describe("embedded object - required object and field", () => { test("required - null", async () => { const definition = { users: { type: { name: { type: String, required: true } }, required: true } }; const { Model, validator } = setupModel(definition); const doc = new Model({ users: null }); try { await validator.validate(doc); fail(); } catch (err) { expect(err).toBeInstanceOf(createError.BadRequest); } }); test("required - name null", async () => { const definition = { users: { type: { name: { type: String, required: true } }, required: true } }; const { Model, validator } = setupModel(definition); const doc = new Model({ users: { name: null } }); try { await validator.validate(doc); fail(); } catch (err) { expect(err).toBeInstanceOf(createError.BadRequest); } }); test("required - name random", async () => { const definition = { users: { type: { name: { type: String, required: true } }, required: true } }; const { Model, validator } = setupModel(definition); const doc = new Model({ users: { name: chance.string() } }); await validator.validate(doc); }); test("required - email invalid", async () => { const definition = { users: { type: { email: { type: String, required: true, email: true } }, required: true } }; const { Model, validator } = setupModel(definition); const doc = new Model({ users: { email: "test" } }); try { await validator.validate(doc); fail(); } catch (err) { expect(err).toBeInstanceOf(createError.BadRequest); } }); }); describe("embedded object - required object and optional field", () => { test("required - null", async () => { const definition = { users: { type: { name: { type: String, required: false } }, required: true } }; const { Model, validator } = setupModel(definition); const doc = new Model({ users: null }); try { await validator.validate(doc); fail(); } catch (err) { expect(err).toBeInstanceOf(createError.BadRequest); } }); test("required - name null", async () => { const definition = { users: { type: { name: { type: String, required: false } }, required: false } }; const { Model, validator } = setupModel(definition); const doc = new Model({ users: { name: null } }); await validator.validate(doc); }); test("required - name random", async () => { const definition = { users: { type: { name: { type: String, required: false } }, required: false } }; const { Model, validator } = setupModel(definition); const doc = new Model({ users: { name: chance.string() } }); await validator.validate(doc); }); test("required - email invalid", async () => { const definition = { users: { type: { email: { type: String, required: false, email: true } }, required: false } }; const { Model, validator } = setupModel(definition); const doc = new Model({ users: { email: chance.string() } }); try { await validator.validate(doc); fail(); } catch (err) { expect(err).toBeInstanceOf(createError.BadRequest); } }); }); describe("embedded object - optional object and required field", () => { test("required - null", async () => { const definition = { users: { type: { name: { type: String, required: true } }, required: false } }; const { Model, validator } = setupModel(definition); const doc = new Model({ users: null }); await validator.validate(doc); }); test("required - name null", async () => { const definition = { users: { type: { name: { type: String, required: true } }, required: false } }; const { Model, validator } = setupModel(definition); const doc = new Model({ users: { name: null } }); try { await validator.validate(doc); fail(); } catch (err) { expect(err).toBeInstanceOf(createError.BadRequest); } }); test("required - name random", async () => { const definition = { users: { type: { name: { type: String, required: true } }, required: false } }; const { Model, validator } = setupModel(definition); const doc = new Model({ users: { name: chance.string() } }); await validator.validate(doc); }); test("required - email invalid", async () => { const definition = { users: { type: { email: { type: String, required: true, email: true } }, required: false } }; const { Model, validator } = setupModel(definition); const doc = new Model({ users: { email: "test" } }); try { await validator.validate(doc); fail(); } catch (err) { expect(err).toBeInstanceOf(createError.BadRequest); } }); }); describe("embedded schema - required object and field", () => { test("required - null", async () => { const definition = { users: { type: new mongoose.Schema({ name: { type: String, required: true } }), required: true } }; const { Model, validator } = setupModel(definition); const doc = new Model({ users: null }); try { await validator.validate(doc); fail(); } catch (err) { expect(err).toBeInstanceOf(createError.BadRequest); } }); test("required - name null", async () => { const definition = { users: { type: new mongoose.Schema({ name: { type: String, required: true } }), required: true } }; const { Model, validator } = setupModel(definition); const doc = new Model({ users: { name: null } }); try { await validator.validate(doc); fail(); } catch (err) { expect(err).toBeInstanceOf(createError.BadRequest); } }); test("required - name random", async () => { const definition = { users: { type: new mongoose.Schema({ name: { type: String, required: true } }), required: true } }; const { Model, validator } = setupModel(definition); const doc = new Model({ users: { name: chance.string() } }); await validator.validate(doc); }); test("required - email invalid", async () => { const definition = { users: { type: new mongoose.Schema({ email: { type: String, required: true, email: true } }), required: true } }; const { Model, validator } = setupModel(definition); const doc = new Model({ users: { email: "testing" } }); try { await validator.validate(doc); fail(); } catch (err) { expect(err).toBeInstanceOf(createError.BadRequest); } }); }); describe("embedded schema - required object and optional field", () => { test("required - null", async () => { const definition = { users: { type: new mongoose.Schema({ name: { type: String, required: false } }), required: true } }; const { Model, validator } = setupModel(definition); const doc = new Model({ users: null }); try { await validator.validate(doc); fail(); } catch (err) { expect(err).toBeInstanceOf(createError.BadRequest); } }); test("required - name null", async () => { const definition = { users: { type: new mongoose.Schema({ name: { type: String, required: false } }), required: false } }; const { Model, validator } = setupModel(definition); const doc = new Model({ users: { name: null } }); await validator.validate(doc); }); test("required - name random", async () => { const definition = { users: { type: new mongoose.Schema({ name: { type: String, required: false } }), required: false } }; const { Model, validator } = setupModel(definition); const doc = new Model({ users: { name: chance.string() } }); await validator.validate(doc); }); test("required - email invalid", async () => { const definition = { users: { type: new mongoose.Schema({ email: { type: String, required: false, email: true } }), required: false } }; const { Model, validator } = setupModel(definition); const doc = new Model({ users: { email: "hello" } }); try { await validator.validate(doc); fail(); } catch (err) { expect(err).toBeInstanceOf(createError.BadRequest); } }); }); describe("embedded schema - optional object and required field", () => { test("required - null", async () => { const definition = { users: { type: new mongoose.Schema({ name: { type: String, required: true } }), required: false } }; const { Model, validator } = setupModel(definition); const doc = new Model({ users: null }); await validator.validate(doc); }); test("required - name null", async () => { const definition = { users: { type: new mongoose.Schema({ name: { type: String, required: true } }), required: false } }; const { Model, validator } = setupModel(definition); const doc = new Model({ users: { name: null } }); try { await validator.validate(doc); fail(); } catch (err) { expect(err).toBeInstanceOf(createError.BadRequest); } }); test("required - name random", async () => { const definition = { users: { type: new mongoose.Schema({ name: { type: String, required: true } }), required: false } }; const { Model, validator } = setupModel(definition); const doc = new Model({ users: { name: chance.string() } }); await validator.validate(doc); }); test("required - email invalid", async () => { const definition = { users: { type: new mongoose.Schema({ email: { type: String, required: true, email: true } }), required: false } }; const { Model, validator } = setupModel(definition); const doc = new Model({ users: { email: "test" } }); try { await validator.validate(doc); fail(); } catch (err) { expect(err).toBeInstanceOf(createError.BadRequest); } }); }); describe("embedded array of objects", () => { test("required", async () => { const definition = { users: { type: [ new mongoose.Schema({ email: { type: String, required: true, email: true } }) ], required: false } }; const { Model, validator } = setupModel(definition); const doc = new Model({ users: [{ email: "test" }] }); try { await validator.validate(doc); fail(); } catch (err) { expect(err).toBeInstanceOf(createError.BadRequest); } }); }); describe("embedded array of strings", () => { test("optional", async () => { const definition = { ids: { type: [String], required: false } }; const { Model, validator } = setupModel(definition); const doc = new Model({ ids: [] }); await validator.validate(doc); }); test("required - null", async () => { const definition = { ids: { type: [String], required: true } }; const { Model, validator } = setupModel(definition); const doc = new Model({ ids: null }); try { await validator.validate(doc); fail(); } catch (err) { expect(err).toBeInstanceOf(createError.BadRequest); } }); test("required - empty array", async () => { const definition = { ids: { type: [String], required: true } }; const { Model, validator } = setupModel(definition); const doc = new Model({ ids: [] }); await validator.validate(doc); }); test("success", async () => { const definition = { ids: { type: [String], required: false } }; const { Model, validator } = setupModel(definition); const doc = new Model({ ids: ["test"] }); await validator.validate(doc); }); test("optional - with type", async () => { const definition = { ids: { type: [{ type: String, required: true }], required: false } }; const { Model, validator } = setupModel(definition); const doc = new Model({ ids: [null] }); try { await validator.validate(doc); fail(); } catch (err) { expect(err).toBeInstanceOf(createError.BadRequest); } }); test("required - with type", async () => { const definition = { ids: { type: [{ type: String }], required: true } }; const { Model, validator } = setupModel(definition); const doc = new Model({ ids: [] }); await validator.validate(doc); }); test("success - with type", async () => { const definition = { ids: { type: [{ type: String }], required: false } }; const { Model, validator } = setupModel(definition); const doc = new Model({ ids: ["test"] }); await validator.validate(doc); }); test("email - with type", async () => { const definition = { ids: { type: [{ type: String, email: true }], required: false } }; const { Model, validator } = setupModel(definition); const doc = new Model({ ids: ["test"] }); try { await validator.validate(doc); fail(); } catch (err) { expect(err).toBeInstanceOf(createError.BadRequest); } }); }); describe("embedded array of object ids", () => { test("optional", async () => { const definition = { ids: { type: [mongoose.Schema.Types.ObjectId], required: false } }; const { Model, validator } = setupModel(definition); const doc = new Model({ ids: [] }); await validator.validate(doc); }); test("1 object id", async () => { const definition = { ids: { type: [mongoose.Schema.Types.ObjectId], required: false } }; const { Model, validator } = setupModel(definition); const doc = new Model({ ids: [new mongoose.Types.ObjectId()] }); await validator.validate(doc); }); test("1 string", async () => { const definition = { ids: { type: [mongoose.Schema.Types.ObjectId], required: false } }; const { Model, validator } = setupModel(definition); const doc = new Model({ ids: [new mongoose.Types.ObjectId().toString()] }); await validator.validate(doc); }); }); describe("embedded array of embedded objects", () => { test("required", async () => { const definition = { users: { type: [ { email: { type: String, required: true, email: true } } ], required: false } }; const { Model, validator } = setupModel(definition); const doc = new Model({ users: [{ email: "test" }] }); try { await validator.validate(doc); fail(); } catch (err) { expect(err).toBeInstanceOf(createError.BadRequest); } }); }); describe("without", () => { test("", async () => { const definition = { name: { type: String }, age: { type: Number } }; const { Model, validator } = setupModel(definition, { without: { name: "age" } }); const doc = new Model({ name: "John" }); await validator.validate(doc); }); test("does not allow other field", async () => { const definition = { name: { type: String }, age: { type: Number } }; const { Model, validator } = setupModel(definition, { without: { name: "age" } }); const doc = new Model({ name: "John", age: 10 }); try { await validator.validate(doc); fail(); } catch (err) { expect(err).toBeInstanceOf(createError.BadRequest); } }); test("does not allow other fields - 1", async () => { const definition = { name: { type: String }, age: { type: Number }, gender: { type: String } }; const { Model, validator } = setupModel(definition, { without: { name: "age" } }); const doc = new Model({ name: "John", age: 10 }); try { await validator.validate(doc); fail(); } catch (err) { expect(err).toBeInstanceOf(createError.BadRequest); } }); test("does not allow other fields - 2", async () => { const definition = { name: { type: String }, age: { type: Number }, gender: { type: String } }; const { Model, validator } = setupModel(definition, { without: { name: "age" } }); const doc = new Model({ name: "John", age: 10, gender: "male" }); try { await validator.validate(doc); fail(); } catch (err) { expect(err).toBeInstanceOf(createError.BadRequest); } }); test("allows to skip two others", async () => { const definition = { name: { type: String }, age: { type: Number }, gender: { type: String } }; const { Model, validator } = setupModel(definition, { without: { name: "John" } }); const doc = new Model({ name: "John" }); await validator.validate(doc); }); }); describe("with", () => { test("works", async () => { const definition = { name: { type: String }, age: { type: Number } }; const { Model, validator } = setupModel(definition, { with: { name: "age" } }); const doc = new Model({ name: "John" }); try { await validator.validate(doc); fail(); } catch (err) { expect(err).toBeInstanceOf(createError.BadRequest); } }); test("allow other field", async () => { const definition = { name: { type: String }, age: { type: Number } }; const { Model, validator } = setupModel(definition, { with: { name: "age" } }); const doc = new Model({ name: "John", age: 10 }); await validator.validate(doc); }); test("allow other fields - 1", async () => { const definition = { name: { type: String }, age: { type: Number }, gender: { type: String } }; const { Model, validator } = setupModel(definition, { with: { name: "age" } }); const doc = new Model({ name: "John", age: 10 }); await validator.validate(doc); }); test("allow other fields - 2", async () => { const definition = { name: { type: String }, age: { type: Number }, gender: { type: String } }; const { Model, validator } = setupModel(definition, { with: { name: "age" } }); const doc = new Model({ name: "John", age: 10, gender: "male" }); await validator.validate(doc); }); test("allows to include two others", async () => { const definition = { name: { type: String }, age: { type: Number }, gender: { type: String } }; const { Model, validator } = setupModel(definition, { with: { name: "John" } }); const doc = new Model({ name: "John" }); try { await validator.validate(doc); fail(); } catch (err) { expect(err).toBeInstanceOf(createError.BadRequest); } }); }); describe("or", () => { test("works 1", async () => { const definition = { name: { type: String }, age: { type: Number } }; const { Model, validator } = setupModel(definition, { or: [["name", "age"]] }); const doc = new Model({ name: "John" }); await validator.validate(doc); }); test("works 2", async () => { const definition = { name: { type: String }, age: { type: Number } }; const { Model, validator } = setupModel(definition, { or: [["name", "age"]] }); const doc = new Model({ age: 12 }); await validator.validate(doc); }); test("works 3", async () => { const definition = { name: { type: String }, age: { type: Number }, gender: { type: String } }; const { Model, validator } = setupModel(definition, { or: [["name", "gender"], ["name", "age"]] }); const doc = new Model({ name: "test" }); await validator.validate(doc); }); test("fails 1", async () => { const definition = { name: { type: String }, age: { type: Number } }; const { Model, validator } = setupModel(definition, { or: [["name", "age"]] }); const doc = new Model({}); try { await validator.validate(doc); fail(); } catch (err) { expect(err).toBeInstanceOf(createError.BadRequest); } }); test("fails 2", async () => { const definition = { name: { type: String }, age: { type: Number }, gender: { type: String } }; const { Model, validator } = setupModel(definition, { or: [["name", "age", "gender"]] }); const doc = new Model({}); try { await validator.validate(doc); fail(); } catch (err) { expect(err).toBeInstanceOf(createError.BadRequest); } }); test("fails 3", async () => { const definition = { name: { type: String }, age: { type: Number }, gender: { type: String } }; const { Model, validator } = setupModel(definition, { or: [["name", "age"], ["name", "gender"]] }); const doc = new Model({ name: "test" }); await validator.validate(doc); }); }); describe("xor", () => { test("works 1", async () => { const definition = { name: { type: String }, age: { type: Number } }; const { Model, validator } = setupModel(definition, { xor: [["name", "age"]] }); const doc = new Model({ name: "John" }); await validator.validate(doc); }); test("works 2", async () => { const definition = { name: { type: String }, age: { type: Number } }; const { Model, validator } = setupModel(definition, { or: [["name", "age"]] }); const doc = new Model({ age: 12 }); await validator.validate(doc); }); test("works 3", async () => { const definition = { name: { type: String }, age: { type: Number }, gender: { type: String } }; const { Model, validator } = setupModel(definition, { xor: [["name", "age", "gender"]] }); const doc = new Model({ gender: "male" }); await validator.validate(doc); }); test("works 4", async () => { const definition = { name: { type: String }, age: { type: Number }, gender: { type: String } }; const { Model, validator } = setupModel(definition, { xor: [["name", ["age", "gender"]]] }); const doc = new Model({ age: 12 }); await validator.validate(doc); }); test("works 5", async () => { const definition = { name: { type: String }, age: { type: Number }, gender: { type: String } }; const { Model, validator } = setupModel(definition, { xor: [["name", "gender"], ["name", "age"]] }); const doc = new Model({ name: "hej" }); await validator.validate(doc); }); test("fails 1", async () => { const definition = { name: { type: String }, age: { type: Number } }; const { Model, validator } = setupModel(definition, { xor: [["name", "age"]] }); const doc = new Model({}); try { await validator.validate(doc); fail(); } catch (err) { expect(err).toBeInstanceOf(createError.BadRequest); } }); test("fails 2", async () => { const definition = { name: { type: String }, age: { type: Number }, gender: { type: String } }; const { Model, validator } = setupModel(definition, { xor: [["name", ["age", "gender"]]] }); const doc = new Model({}); try { await validator.validate(doc); fail(); } catch (err) { expect(err).toBeInstanceOf(createError.BadRequest); } }); test("works 5", async () => { const definition = { name: { type: String }, age: { type: Number }, gender: { type: String } }; const { Model, validator } = setupModel(definition, { xor: [["name", "gender"], ["name", "age"]] }); const doc = new Model({ gender: "hej" }); try { await validator.validate(doc); fail(); } catch (err) { expect(err).toBeInstanceOf(createError.BadRequest); } }); }); describe("and", () => { test("fails 1", async () => { const definition = { name: { type: String }, age: { type: Number } }; const { Model, validator } = setupModel(definition, { and: [["name", "age"]] }); const doc = new Model({ name: "John" }); try { await validator.validate(doc); fail(); } catch (err) { expect(err).toBeInstanceOf(createError.BadRequest); } }); test("fails 2", async () => { const definition = { name: { type: String }, age: { type: Number } }; const { Model, validator } = setupModel(definition, { and: [["name", "age"]] }); const doc = new Model({ age: 12 }); try { await validator.validate(doc); fail(); } catch (err) { expect(err).toBeInstanceOf(createError.BadRequest); } }); test("fails 3", async () => { const definition = { name: { type: String }, age: { type: Number }, gender: { type: String } }; const { Model, validator } = setupModel(definition, { and: [["name", "gender"], ["name", "age"]] }); const doc = new Model({ name: "test" }); try { await validator.validate(doc); fail(); } catch (err) { expect(err).toBeInstanceOf(createError.BadRequest); } }); test("works 1", async () => { const definition = { name: { type: String }, age: { type: Number } }; const { Model, validator } = setupModel(definition, { and: [["name", "age"]] }); const doc = new Model({}); await validator.validate(doc); }); test("works 2", async () => { const definition = { name: { type: String }, age: { type: Number }, gender: { type: String } }; const { Model, validator } = setupModel(definition, { and: [["name", "age", "gender"]] }); const doc = new Model({}); await validator.validate(doc); }); }); describe("nand", () => { test("works 1", async () => { const definition = { name: { type: String }, age: { type: Number } }; const { Model, validator } = setupModel(definition, { nand: [["name", "age"]] }); const doc = new Model({ name: "John" }); await validator.validate(doc); }); test("works 2", async () => { const definition = { name: { type: String }, age: { type: Number } }; const { Model, validator } = setupModel(definition, { nand: [["name", "age"]] }); const doc = new Model({ age: 12 }); await validator.validate(doc); }); test("works 3", async () => { const definition = { name: { type: String }, age: { type: Number }, gender: { type: String } }; const { Model, validator } = setupModel(definition, { nand: [["name", "age", "gender"]] }); const doc = new Model({ gender: "male" }); await validator.validate(doc); }); test("works 4", async () => { const definition = { name: { type: String }, age: { type: Number }, gender: { type: String } }; const { Model, validator } = setupModel(definition, { nand: [["name", ["age", "gender"]]] }); const doc = new Model({ age: 12 }); await validator.validate(doc); }); test("works 5", async () => { const definition = { name: { type: String }, age: { type: Number }, gender: { type: String } }; const { Model, validator } = setupModel(definition, { nand: [["name", "gender"], ["name", "age"]] }); const doc = new Model({ name: "hej" }); await validator.validate(doc); }); test("fails 1", async () => { const definition = { name: { type: String }, age: { type: Number } }; const { Model, validator } = setupModel(definition, { nand: [["name", "age"]] }); const doc = new Model({ name: "test", age: 12 }); try { await validator.validate(doc); fail(); } catch (err) { expect(err).toBeInstanceOf(createError.BadRequest); } }); test("fails 2", async () => { const definition = { name: { type: String }, age: { type: Number }, gender: { type: String } }; const { Model, validator } = setupModel(definition, { nand: [["name", ["age", "gender"]]] }); const doc = new Model({ name: "test", gender: "male", age: 12 }); try { await validator.validate(doc); fail(); } catch (err) { expect(err).toBeInstanceOf(createError.BadRequest); } }); }); describe("immutable", () => { test("passes when new and have value", async () => { const definition = { name: { type: String, immutable: true } }; const { Model, validator } = setupModel(definition); const doc = new Model({ name: "test" }); await validator.validate(doc); }); test("passes when new and have no value", async () => { const definition = { name: { type: String, immutable: true } }; const { Model, validator } = setupModel(definition); const doc = new Model(); await validator.validate(doc); }); test("throws error when modified from other value", async () => { const definition = { name: { type: String, immutable: true } }; const { Model, validator } = setupModel(definition); const doc: any = new Model({ name: "test" }); await doc.save(); try { doc.name = "test2"; await validator.validate(doc); fail(); } catch (err) { expect(err).toBeInstanceOf(createError.BadRequest); } }); test("throws error when modified from no value", async () => { const definition = { name: { type: String, immutable: true } }; const { Model, validator } = setupModel(definition); const doc: any = new Model(); await doc.save(); try { doc.name = "test2"; await validator.validate(doc); fail(); } catch (err) { expect(err).toBeInstanceOf(createError.BadRequest); } }); test("throws error when modified from no value when required", async () => { const definition = { name: { type: String, immutable: true }, age: { type: Number, required: true } }; const { Model, validator } = setupModel(definition); const doc: any = new Model({ age: true }); await doc.save(); try { doc.name = "test2"; doc.age = null; await validator.validate(doc); fail(); } catch (err) { expect(err).toBeInstanceOf(createError.BadRequest); } }); }); }); });