//
// Copyright 2024 Formata, Inc. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
rename_field: {
schema: {
#[schema((name: Box<str>): bool => {
name = 'other';
return true; // even if this field doesn't exist, that's okay
})]
field: ''
}
target: {
field: 'dude'
}
dne: {}
#[test]
fn field_to_other() {
assert(self.schema.schemafy(self.target));
assertEq(self.target.other, 'dude');
assertNull(self.target.field);
}
#[test]
fn dne() {
assert(self.schema.schemafy(self.dne)); // valid because we are not null-checking the value
assertNull(self.dne.field);
assertNull(self.dne.other);
}
}
type_checks: {
string: {
schema: {
#[schema((value: unknown): bool => isString(value))]
field: ''
}
valid_target: {
field: 'hello'
}
invalid_target: {
field: 10
}
#[test]
fn valid_check() {
assert(self.schema.schemafy(self.valid_target));
assertEq(self.valid_target.field, 'hello');
}
#[test]
fn invalid_check() {
assertNot(self.schema.schemafy(self.invalid_target));
assertNull(self.invalid_target.field); // removes field
}
}
numbers: {
schema: {
// Optional number value, set a default if null
#[schema((target: obj, schema: obj, value: unknown, field: str): bool => {
if (isNull(value)) target.set(field, schema.at(field));
return isNull(value) || isNumber(value);
})]
field: 42
}
valid_target: {
// will set the schema's field value on this object
}
invalid_target: {
field: 'hello'
}
#[test]
fn valid_check() {
assert(self.schema.schemafy(self.valid_target));
assertEq(self.valid_target.field, 42);
}
#[test]
fn invalid_check() {
assertNot(self.schema.schemafy(self.invalid_target));
assertNull(self.invalid_target.field); // removes field
}
}
}
manipulate_field: {
schema: {
// field is valid if it's an int between 0 and 100. If valid, add 5
#[schema((value: Box<int>): bool => {
if (value > 0 && value < 100) {
value += 5;
return true;
}
return false;
})]
field: 0
}
valid_target: {
field: 40;
}
#[test]
fn valid() {
assert(self.schema.schemafy(self.valid_target));
assertEq(self.valid_target.field, 45);
}
invalid_target: {
field: -23;
}
#[test]
fn invalid() {
assertNot(self.schema.schemafy(self.invalid_target, false)); // don't remove the field
assertEq(self.invalid_target.field, -23);
}
}
set_field: {
schema: {
#[schema((): unknown => 'overridden, always & always valid')]
field: 10
}
dne_target: {
// will get a field created
}
#[test]
fn dne() {
assert(self.schema.schemafy(self.dne_target));
assertEq(self.dne_target.field, 'overridden, always & always valid');
}
target: {
field: true
}
#[test]
fn target() {
assert(self.schema.schemafy(self.target));
assertEq(self.target.field, 'overridden, always & always valid');
}
}
proxy_schema: {
schema: {
#[schema((value: unknown): bool => set('s', 'm', 'l', 'xl', 'xxl').contains(value))]
field: ''
}
proxy: {
#[schema(super.schema)]
field: ''
}
#[test]
fn valid() {
let record = new {
field: 'xl';
};
assert(self.proxy.schemafy(record));
assertEq(record.field, 'xl');
}
#[test]
fn invalid() {
let record = new {
field: 'small';
};
assertNot(self.proxy.schemafy(record));
assertNull(record.field);
}
}
validation_array: {
schema: {
#[private]
hex_chars: {
let chars = 0..10;
for (char in chars) chars.set(index, char as str);
chars = chars as set;
chars.insert('a');
chars.insert('b');
chars.insert('c');
chars.insert('d');
chars.insert('e');
chars.insert('f');
return box(chars);
};
fn valid_hex_chars(hex: str): bool {
hex = hex.toLower().substring(1);
let valid = self.hex_chars;
for (char in hex) if (!valid.contains(char)) return false;
return true;
}
#[schema([
(value: Box<unknown>): bool => value != null, // has to exist
(value: Box<unknown>): bool => isString(value), // has to be a string
(value: Box<unknown>): bool => value.startsWith('#'), // has to start with '#'
(value: Box<unknown>): bool => value.len() == 4 || value.len() == 7, // has to be the right length
(value: str): bool => self.valid_hex_chars(value), // have to be valid chars
])]
hex: '#000'
}
#[test]
fn doesnt_exist() {
let record = new {};
assertNot(self.schema.schemafy(record));
}
#[test]
fn not_string() {
let record = new { hex: 12 };
assertNot(self.schema.schemafy(record));
}
#[test]
fn doesnt_start_with() {
let record = new { hex: 'aaaa' };
assertNot(self.schema.schemafy(record));
}
#[test]
fn not_right_length() {
let record = new { hex: '#aa' };
assertNot(self.schema.schemafy(record));
}
#[test]
fn not_valid_char() {
let record = new { hex: '#aaaaag' };
assertNot(self.schema.schemafy(record));
assertNull(record.hex);
}
#[test]
fn valid_three() {
let record = new { hex: '#fff' };
assert(self.schema.schemafy(record));
assertEq(record.hex, '#fff');
}
#[test]
fn valid_hex() {
let record = new { hex: '#08fa2e' };
assert(self.schema.schemafy(record));
}
}
sub_object_schema: {
schema: {
//#[schema((value: obj): bool => self.object.schemafy(value))]
#[schema] // shorthand for the above when evaluating objects...
object: {
#[schema((value: str): bool => value.len() > 4)]
field: ''
}
}
valid_target: {
object: {
field: 'hello'
}
}
#[test]
fn valid() {
assert(self.schema.schemafy(self.valid_target));
assertEq(self.valid_target.object.field, 'hello');
}
invalid_target: {
object: {
field: 'hi'
}
}
#[test]
fn invalid() {
assertNot(self.schema.schemafy(self.invalid_target));
assertNull(self.invalid_target.object);
}
invalid_target_keep: {
object: {
field: 'hi'
}
}
#[test]
fn invalid_keep() {
assertNot(self.schema.schemafy(self.invalid_target_keep, false));
assertEq(self.invalid_target_keep.object.field, 'hi');
}
}
arrays: {
object_array: {
entry_schema: {
#[schema((value: int): bool => value >= 0)]
field: 0
}
schema: {
#[schema((array: Box<vec>): bool => {
for (val in array) if (!super.entry_schema.schemafy(val)) return false;
return true;
})]
array: []
}
valid_target: {
array: [
{ field: 0 },
{ field: 10 },
{ field: 50 },
]
}
#[test]
fn valid() {
assert(self.schema.schemafy(self.valid_target));
}
invalid_target: {
array: [
{ field: 0 },
{ field: -10 },
{ field: 50 },
]
}
#[test]
fn invalid() {
assertNot(self.schema.schemafy(self.invalid_target));
assertNull(self.invalid_target.array);
}
}
value_array: {
schema: {
fn validate_value(val: float): bool {
return val.isLength(); // must be length units
}
#[schema((array: Box<vec>): bool => {
for (v in array) if (!self.validate_value(v)) return false;
return true;
})]
array: []
}
valid_target: {
array: [2km, 10m, 15in, 22ft, 45mi, 10yd]
}
#[test]
fn valid() {
assert(self.schema.schemafy(self.valid_target));
}
invalid_target: {
array: [2km, 10m, 15g, 22ft, 45mi, 10yd] // has grams (mass) in it
}
#[test]
fn invalid() {
assertNot(self.schema.schemafy(self.invalid_target));
assertNull(self.invalid_target.array);
}
}
}
remove_undefined: {
schema: {
#[schema((value: str): bool => value.len() > 0)]
name: ''
title: ''
#[schema]
partner: {
#[schema((value: str): bool => value.len() > 0)]
name: ''
title: ''
}
}
valid_target: {
name: 'Bob Jones'
title: 'CFO'
address: '3456 Westbound Ave.'
hair_color: 'brown'
partner: {
name: 'Kate Miller'
title: 'CPO'
address: '1234 Eastbound Way.'
}
}
#[test]
fn valid() {
assert(self.schema.schemafy(self.valid_target, true, true));
assertNull(self.valid_target.address);
assertNull(self.valid_target.hair_color);
assertNull(self.valid_target.partner.address);
assertEq(self.valid_target.name, 'Bob Jones');
assertEq(self.valid_target.title, 'CFO');
assertEq(self.valid_target.partner.name, 'Kate Miller');
assertEq(self.valid_target.partner.title, 'CPO');
}
invalid_target: {
name: 'Bob Jones'
title: 'CFO'
address: '3456 Westbound Ave.'
hair_color: 'brown'
partner: {
name: ''
title: 'CPO'
address: '1234 Eastbound Way.'
}
}
#[test]
fn invalid() {
assertNot(self.schema.schemafy(self.invalid_target, true, true));
assertNull(self.invalid_target.address);
assertNull(self.invalid_target.hair_color);
assertNull(self.invalid_target.partner);
assertEq(self.invalid_target.name, 'Bob Jones');
assertEq(self.invalid_target.title, 'CFO');
}
}
shirt: {
// we have a t-shirt record that we want to validate
// there are 2 different size charts, female and male
Box<set> male_sizes: ['xs', 's', 'm', 'l', 'xl', '2xl', '3xl'];
Box<set> female_sizes: ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9'];
t_shirt_schema: {
#[schema((value: str): bool => value.toLower() == 'male' || value.toLower() == 'female')]
type: ''
#[schema((target: obj, schema: obj, value: Box<unknown>): bool => {
value = (value as str).toLower(); // set the value to lowercase
let male = target.at('type').toLower() == 'male';
if (male) return super.male_sizes.contains(value);
return super.female_sizes.contains(value);
})]
size: ''
}
female_shirt_valid: {
type: 'Female'
size: 5 // will get cast to a str by validation function
}
#[test]
fn female_valid() {
assert(self.t_shirt_schema.schemafy(self.female_shirt_valid));
assertEq(self.female_shirt_valid.size, '5');
}
female_shirt_invalid: {
type: 'female'
size: 'xs' // could convert this in the schema though ofc...
}
#[test]
fn female_invalid() {
assertNot(self.t_shirt_schema.schemafy(self.female_shirt_invalid));
assertNull(self.female_shirt_invalid.size);
}
male_shirt_valid: {
type: 'Male'
size: 'XS'
}
#[test]
fn male_valid() {
assert(self.t_shirt_schema.schemafy(self.male_shirt_valid));
assertEq(self.male_shirt_valid.size, 'xs');
}
male_shirt_invalid: {
type: 'male'
size: 'xxs'
}
#[test]
fn male_invalid() {
assertNot(self.t_shirt_schema.schemafy(self.male_shirt_invalid));
assertNull(self.male_shirt_invalid.size);
}
}