Repository: icebob/fastest-validator (js)

Issue:
Multiple validators not working with objects
It seems like multiple validators are not working properly when they are objects.
Here is an example:

```
const field = {
    type: 'object',
    props: {
        type: {
            type: 'string',
            pattern: /^field$/
        },
        fieldId: {type: 'number', positive: true, integer: true},
        title: {type: 'string', min: 4, max: 50, optional: true},
        values: {
            type: 'array',
            items: {type: 'string', min: 1, max: 50}
        }
    }
};

const seperator = {
    type: 'object',
    props: {
        type: {
            type: 'string',
            pattern: /^seperator$/
        }
    }
};

const col = {
    type: "array",
    min: 1,
    max: 4,
    items: [
        field,
        seperator
    ],
};

const schema = {bla: col};
console.log(validator.validate({bla:[{"type": "sfdf"}]}, schema));
```

I expect this validation to fail, but it succeeds. It seems like it only checks whether the array contains an object, but does no further validation regarding the properties.

With other types than objects it is working properly, example: 

```
const col = {
    type: "array",
    min: 1,
    max: 4,
    items: [
        {type: 'boolean'},
        {type: 'string', min: 5}
    ],
};

const schema = {bla: col};
console.log(validator.validate({bla:["sfdf"]}, schema));
```
Fails as expected because the string is to short.

And for a single validator, everything is working fine, too:

```
const col = {
    type: "array",
    min: 1,
    max: 4,
    items: field
};
```

Pull request:
fixes #22
Added tests for the issue.
All tests pass.

I added the field "secondPart" to the special cases of array and object types. This enables checking in the _checkWraper function whether this is a split up rule. If this is the case the _checkWraper function will also check the second part.

Patch:
diff --git a/lib/validator.js b/lib/validator.js
index 265072e..f37c7ad 100644
--- a/lib/validator.js
+++ b/lib/validator.js
@@ -109,6 +109,15 @@ Validator.prototype._processRule = function(rule, name, iterate) {
 		throw new Error("Invalid '" + rule.type + "' type in validator schema!");
 	}
 
+
+	/**
+	 * !IMPORTANT!: For the functioning of multiRule cases it is important that
+	 * pushing of object and array special rules is done in directly after this
+	 * simple rule. 
+	 * If you which to push other checks, do it before the simple ones or after
+	 * the array special case.
+	 * See the comments in _checkWrapper for further explanation.
+	 */
 	checks.push({
 		fn: this.rules[rule.type],
 		type: rule.type,
@@ -125,7 +134,8 @@ Validator.prototype._processRule = function(rule, name, iterate) {
 			type: rule.type,
 			name: name,
 			schema: rule,
-			iterate: iterate
+			iterate: iterate,
+			secondPart: true //first part is the "primitive" typeof check above
 		});
 	}
 
@@ -137,7 +147,8 @@ Validator.prototype._processRule = function(rule, name, iterate) {
 			type: rule.type,
 			name: name,
 			schema: rule,
-			iterate: true
+			iterate: true,
+			secondPart: true //first part is the "primitive" typeof check above
 		});
 	}
 
@@ -181,18 +192,37 @@ Validator.prototype._checkWrapper = function(rules, isMultipleRules) {
 			} else {
 				// Call the checker function
 				if (check.iterate) {
+					let errorInCurrentArray = false;
 					const l = value.length;
 					for (let i = 0; i < l; i++) {
 						let _stack = stack + "[" + i + "]";
 						let res = check.fn.call(self, value[i], schema, _stack, obj);
-						if (res !== true)
+						if (res !== true) {
+							errorInCurrentArray = true;
 							self.handleResult(errors, _stack, res);
+						}
+					}
+					/**
+					 * If this is second part of a multiRule array check and the array
+					 * is valid, then the rule is valid.
+					 */
+					if (!errorInCurrentArray && isMultipleRules && check.secondPart) {
+						return true;
 					}
 				} else {
 					let res = check.fn.call(self, value, schema, stack, obj);
 
 					if (isMultipleRules) {
 						if (res === true) {
+							/**
+							 * Object and array checks are divided into two internal checks. In case of a multiRule
+							 * we have to make sure to check both parts. Thus we we continue to also do the second
+							 * check if their is one.
+							 */
+							const nextRule = rules[i+1]
+							if (nextRule && nextRule.secondPart){
+								continue;
+							}
 							// Jump out after first success and clear previous errors
 							return true;
 						}


Test patch:
diff --git a/test/validator.spec.js b/test/validator.spec.js
index 1defe67..24aabe4 100644
--- a/test/validator.spec.js
+++ b/test/validator.spec.js
@@ -631,3 +631,266 @@ describe("Test multiple rules", () => {
 	});
 
 });
+
+describe("Test multiple rules with objects", () => {
+	const v = new Validator();
+
+	let schema = {
+		list: [
+			{ 
+				type: "object",
+				props: {
+					name: {type: "string"},
+					age: {type: "number"},
+				} 
+			},
+			{ 
+				type: "object",
+				props: {
+					country: {type: "string"},
+					code: {type: "string"},
+				} 
+			}
+		]
+	};
+
+	let check = v.compile(schema);
+
+	it("should give true if first object is given", () => {
+		let obj = { list: {
+			name: "Joe",
+			age: 34
+		} };
+
+		let res = check(obj);
+
+		expect(res).toBe(true);
+	});
+
+	it("should give true if second object is given", () => {
+		let obj = { list: {
+			country: "germany",
+			code: "de"
+		}};
+
+		let res = check(obj);
+
+		expect(res).toBe(true);
+	});
+
+	it("should give error if the object is broken", () => {
+		let obj = { list: {
+			name: "Average",
+			age: "Joe"
+		} };
+
+		let res = check(obj);
+
+		expect(res).toBeInstanceOf(Array);
+		expect(res.length).toBe(3);
+		expect(res[0].type).toBe("number");
+		expect(res[0].field).toBe("list.age");
+
+		expect(res[1].type).toBe("required");
+		expect(res[1].field).toBe("list.country");
+	});
+
+	it("should give error if the object is only partly given", () => {
+		let obj = { list: {} };
+		let res = check(obj);
+
+		expect(res).toBeInstanceOf(Array);
+		expect(res.length).toBe(4);
+		expect(res[0].type).toBe("required");
+		expect(res[0].field).toBe("list.name");
+
+		expect(res[1].type).toBe("required");
+		expect(res[1].field).toBe("list.age");
+
+	});
+
+});
+
+describe("Test multiple rules with objects within array", () => {
+	const v = new Validator();
+
+	let schema = {
+		list: {
+			type: "array",
+			items: [
+				{ 
+					type: "object",
+					props: {
+						name: {type: "string"},
+						age: {type: "number"},
+					} 
+				},
+				{ 
+					type: "object",
+					props: {
+						country: {type: "string"},
+						code: {type: "string"},
+					} 
+				}
+			]
+		}
+	};
+
+	let check = v.compile(schema);
+
+	it("should give true if one valid object is given", () => {
+		let obj = { list: [
+			{
+				name: "Joe",
+				age: 34
+			}
+		]};
+		let res = check(obj);
+		expect(res).toBe(true);
+
+		let obj2 = { list: [
+			{
+				country: "germany",
+				code: "de"
+			}
+		]};
+		let res2 = check(obj2);
+		expect(res2).toBe(true);
+	});
+
+	it("should give true if three valid objects given", () => {
+		let obj = { list: [
+			{
+				name: "Joe",
+				age: 34
+			},
+			{
+				country: "germany",
+				code: "de"
+			},
+			{
+				country: "hungary",
+				code: "hu"
+			}
+		]};
+		let res = check(obj);
+		expect(res).toBe(true);
+	});
+
+	it("should give error if one object is broken", () => {
+		let obj = { list: [
+			{
+				name: "Joe",
+				age: 34
+			},
+			{
+				country: "germany",
+			},
+			{
+				country: "hungary",
+				code: "hu"
+			}
+		]};
+
+		let res = check(obj);
+
+		expect(res).toBeInstanceOf(Array);
+		expect(res.length).toBe(3);
+		expect(res[0].type).toBe("required");
+		expect(res[0].field).toBe("list[1].name");
+
+		expect(res[1].type).toBe("required");
+		expect(res[1].field).toBe("list[1].age");
+	});
+
+	it("should give error if one object is empty", () => {
+		let obj = { list: [
+			{
+				name: "Joe",
+				age: 34
+			},
+			{
+				country: "hungary",
+				code: "hu"
+			},
+			{
+			}
+		]};
+
+		let res = check(obj);
+
+		expect(res).toBeInstanceOf(Array);
+		expect(res.length).toBe(4);
+		expect(res[0].type).toBe("required");
+		expect(res[0].field).toBe("list[2].name");
+
+		expect(res[1].type).toBe("required");
+		expect(res[1].field).toBe("list[2].age");
+
+	});
+
+});
+
+describe("Test multiple rules with arrays", () => {
+	const v = new Validator();
+
+	let schema = {
+		list: [
+			{ 
+				type: "array",
+				items: "string" 
+			},
+			{ 
+				type: "array",
+				items: "number" 
+			}
+		]
+	};
+
+	let check = v.compile(schema);
+
+	it("should give true if first array is given", () => {
+		let obj = { list: ["hello", "there", "this", "is", "a", "test"] };
+
+		let res = check(obj);
+
+		expect(res).toBe(true);
+	});
+
+	it("should give true if second array is given", () => {
+		let obj = { list: [1, 3, 3, 7] };
+
+		let res = check(obj);
+
+		expect(res).toBe(true);
+	});
+
+	it("should give error if the array is broken", () => {
+		let obj = { list: ["hello", 3] };
+
+		let res = check(obj);
+
+		expect(res).toBeInstanceOf(Array);
+		expect(res.length).toBe(2);
+		expect(res[0].type).toBe("string");
+		expect(res[0].field).toBe("list[1]");
+
+		expect(res[1].type).toBe("number");
+		expect(res[1].field).toBe("list[0]");
+	});
+
+	it("should give error if the array is broken", () => {
+		let obj = { list: [true, false] };
+		let res = check(obj);
+
+		expect(res).toBeInstanceOf(Array);
+		expect(res.length).toBe(4);
+		expect(res[0].type).toBe("string");
+		expect(res[0].field).toBe("list[0]");
+
+		expect(res[1].type).toBe("string");
+		expect(res[1].field).toBe("list[1]");
+
+	});
+
+});
\ No newline at end of file