model-layer
Version:
457 lines (371 loc) • 11.6 kB
text/typescript
import {Model, Types} from "../../lib/index";
import assert from "assert";
describe("Model validate", () => {
it("required field", () => {
class SomeModel extends Model<SomeModel> {
structure() {
return {
name: Types.String({
required: true
}),
age: Types.Number
};
}
}
assert.throws(
() => {
const someModel = new SomeModel();
},
(err: Error) =>
err.message === "required name"
);
const model = new SomeModel({
name: "Andrew"
});
assert.equal( model.get("name"), "Andrew" );
assert.equal( model.row.name, "Andrew" );
model.set({age: 40});
assert.strictEqual( model.get("age"), 40 );
assert.strictEqual( model.row.age, 40 );
assert.throws(
() => {
model.set({name: null as any});
},
(err: Error) =>
err.message === "required name"
);
assert.equal( model.get("name"), "Andrew" );
assert.equal( model.row.name, "Andrew" );
assert.strictEqual( model.get("age"), 40 );
assert.strictEqual( model.row.age, 40 );
});
it("validate method", () => {
class AgeModel extends Model<AgeModel> {
structure() {
return {
age: Types.Number({
default: 0
})
};
}
validate(row: any) {
if ( row.age < 0 ) {
throw new Error("invalid age");
}
}
}
// validate in constructor
assert.throws(
() => {
const ageModel = new AgeModel({
age: -1
});
},
(err: Error) =>
err.message === "invalid age"
);
const model = new AgeModel({
age: 100
});
// validate in set
assert.throws(
() => {
model.set({
age: -100
});
},
(err: Error) =>
err.message === "invalid age"
);
// error in validate, returns previous state
assert.equal( model.row.age, 100 );
});
it("cannot change state in validate method", () => {
class AgeModel extends Model<AgeModel> {
structure() {
return {
age: Types.Number({
default: 0
})
};
}
validate(row: any) {
row.age = 200;
}
}
assert.throws(
() => {
const ageModel = new AgeModel({ age: 1 });
},
(err: Error) =>
/Cannot assign to read only property/.test(err.message)
);
});
it("this.row in validate is previous state", () => {
class AgeModel extends Model<AgeModel> {
structure() {
return {
age: Types.Number({
default: 100
})
};
}
validate(row: any) {
assert.equal( this.row.age, 100 );
assert.equal( row.age, 200 );
}
}
const ageModel = new AgeModel({
age: 200
});
});
it("validate field", () => {
class AgeModel extends Model<AgeModel> {
structure() {
return {
age: Types.Number({
default: 0,
validate: (age) =>
age >= 0
})
};
}
}
// validate in constructor
assert.throws(
() => {
const ageModel = new AgeModel({
age: -1
});
},
(err: Error) =>
err.message === "invalid age: -1"
);
const model = new AgeModel({
age: 100
});
// validate in set
assert.throws(
() => {
model.set({
age: -100
});
},
(err: Error) =>
err.message === "invalid age: -100"
);
// error in validate, returns previous state
assert.equal( model.row.age, 100 );
});
it("do not validate null value", () => {
class SomeModel extends Model<SomeModel> {
structure() {
return {
prop: Types.String({
validate: (value) =>
value === "nice"
})
};
}
}
let model;
// safety create empty model
const someModel = new SomeModel();
// safety set null
model = new SomeModel({
prop: "nice"
});
assert.strictEqual(model.row.prop, "nice");
model.set({prop: null as any});
assert.strictEqual(model.row.prop, null);
});
it("validate field by RegExp", () => {
class WordModel extends Model<WordModel> {
structure() {
return {
word: Types.String({
validate: /^\w+$/
})
};
}
}
// validate in constructor
assert.throws(
() => {
const wordModel = new WordModel({
word: " some 12123 "
});
},
(err: Error) =>
err.message === "invalid word: \" some 12123 \""
);
const model = new WordModel({
word: "test"
});
// validate in set
assert.throws(
() => {
model.set({
word: "some wrong"
});
},
(err: Error) =>
err.message === "invalid word: \"some wrong\""
);
// error in validate, returns previous state
assert.equal( model.row.word, "test" );
});
it("enum validate", () => {
class EnumModel extends Model<EnumModel> {
structure() {
return {
color: Types.String({
enum: ["red", "green", "blue"]
})
};
}
}
const model = new EnumModel();
assert.strictEqual( model.row.color, null );
// validate in set
assert.throws(
() => {
model.set({
color: "orange"
});
},
(err: Error) =>
err.message === "invalid color: \"orange\""
);
// error in validate, returns previous state
assert.strictEqual( model.row.color, null );
// validate in constructor
assert.throws(
() => {
const enumModel = new EnumModel({
color: "dark blue"
});
},
(err: Error) =>
err.message === "invalid color: \"dark blue\""
);
model.set({color: "red"});
assert.equal(model.get("color"), "red");
// null is valid value for enum validation
model.set({color: null as any});
assert.equal(model.get("color"), null);
});
it("isValid(row)", () => {
class SomeModel extends Model<SomeModel> {
structure() {
return {
sale: Types.Number({
required: true
}),
buy: Types.Number({
required: true
}),
color: Types.String({
enum: ["red", "green", "blue"]
}),
word: Types.String({
validate: /^\w+$/
}),
age: Types.Number({
validate: (age) =>
age > 0
}),
prop: Types.String
};
}
validate(row: any) {
if ( row.buy > row.sale ) {
throw new Error("invalid sale");
}
}
}
const model = new SomeModel({
buy: 10,
sale: 30
});
// valid
assert.strictEqual(model.isValid({
buy: 100,
sale: 200
}), true);
// ignore required error, because isValid we using before .set
assert.strictEqual(model.isValid({}), true);
// buy > sale
assert.strictEqual(model.isValid({
buy: 1000
}), false);
// enum validate
assert.strictEqual(model.isValid({
buy: 100,
sale: 200,
color: "pink"
}), false);
// regexp validate
assert.strictEqual(model.isValid({
buy: 1,
sale: 10,
word: "wrong word"
}), false);
// function validate for field
assert.strictEqual(model.isValid({
buy: 1,
sale: 2,
age: -1
}), false);
const anyModel = model as any;
// unknown property
assert.strictEqual(anyModel.isValid({
buy: 1,
sale: 101,
xx: true
}), false);
// custom validate row
assert.strictEqual(model.isValid({
buy: 100,
sale: 20
}), false);
assert.throws(
() => {
anyModel.isValid();
},
(err: Error) =>
err.message === "row must be are object"
);
});
it("const prop", () => {
class SomeModel extends Model<SomeModel> {
structure() {
return {
name: Types.String({
const: true
})
};
}
}
let model = new SomeModel();
assert.strictEqual( model.row.name, null );
assert.throws(
() => {
model.set({name: "new name"});
},
(err: Error) =>
err.message === "cannot assign to read only property: name"
);
model = new SomeModel({
name: "Bob"
});
assert.equal( model.row.name, "Bob" );
assert.throws(
() => {
model.set({name: "new name"});
},
(err: Error) =>
err.message === "cannot assign to read only property: name"
);
});
});