jsonbinpack
Version:
A space-efficient schema-driven binary JSON serialization format based on JSON Schema
1,224 lines (1,139 loc) • 27 kB
text/typescript
/*
* Copyright 2021 Juan Cruz Viotti
*
* 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.
*/
import tap from 'tap'
import {
EncodingSchema
} from '../../lib/schema'
import {
Encoding,
getStates,
getEncoding
} from '../../lib/mapper'
tap.test('should encode a bounded object with only required keys', (test) => {
const schema: EncodingSchema = {
type: 'object',
additionalProperties: false,
required: [ 'foo', 'bar' ]
}
const result: Encoding = getEncoding(schema, 0)
test.is(getStates(schema), Infinity)
test.strictSame(result, {
type: 'object',
encoding: 'REQUIRED_ONLY_BOUNDED_TYPED_OBJECT',
options: {
booleanRequiredProperties: [],
propertyEncodings: {
foo: {
type: 'any',
encoding: 'ANY_PACKED_TYPE_TAG_BYTE_PREFIX',
options: {}
},
bar: {
type: 'any',
encoding: 'ANY_PACKED_TYPE_TAG_BYTE_PREFIX',
options: {}
}
},
requiredProperties: [ 'bar', 'foo' ]
}
})
test.end()
})
tap.test('should encode a bounded object with required keys and empty properties', (test) => {
const schema: EncodingSchema = {
type: 'object',
additionalProperties: false,
properties: {},
required: [ 'foo', 'bar' ]
}
const result: Encoding = getEncoding(schema, 0)
test.is(getStates(schema), Infinity)
test.strictSame(result, {
type: 'object',
encoding: 'REQUIRED_ONLY_BOUNDED_TYPED_OBJECT',
options: {
booleanRequiredProperties: [],
propertyEncodings: {
foo: {
type: 'any',
encoding: 'ANY_PACKED_TYPE_TAG_BYTE_PREFIX',
options: {}
},
bar: {
type: 'any',
encoding: 'ANY_PACKED_TYPE_TAG_BYTE_PREFIX',
options: {}
}
},
requiredProperties: [ 'bar', 'foo' ]
}
})
test.end()
})
tap.test('should encode a bounded object with partially defined required keys', (test) => {
const schema: EncodingSchema = {
type: 'object',
additionalProperties: false,
required: [ 'foo', 'bar' ],
properties: {
foo: {
type: 'string',
maxLength: 5
}
}
}
const result: Encoding = getEncoding(schema, 0)
test.is(getStates(schema), Infinity)
test.strictSame(result, {
type: 'object',
encoding: 'REQUIRED_ONLY_BOUNDED_TYPED_OBJECT',
options: {
booleanRequiredProperties: [],
propertyEncodings: {
foo: {
type: 'string',
encoding: 'BOUNDED_PREFIX_LENGTH_8BIT_FIXED',
options: {
minimum: 0,
maximum: 5
}
},
bar: {
type: 'any',
encoding: 'ANY_PACKED_TYPE_TAG_BYTE_PREFIX',
options: {}
}
},
requiredProperties: [ 'bar', 'foo' ]
}
})
test.end()
})
tap.test('should encode a bounded object with fully defined required keys', (test) => {
const schema: EncodingSchema = {
type: 'object',
additionalProperties: false,
required: [ 'foo', 'bar' ],
properties: {
bar: {
type: 'string'
},
foo: {
type: 'string',
maxLength: 5
}
}
}
const result: Encoding = getEncoding(schema, 0)
test.is(getStates(schema), Infinity)
test.strictSame(result, {
type: 'object',
encoding: 'REQUIRED_ONLY_BOUNDED_TYPED_OBJECT',
options: {
booleanRequiredProperties: [],
propertyEncodings: {
foo: {
type: 'string',
encoding: 'BOUNDED_PREFIX_LENGTH_8BIT_FIXED',
options: {
minimum: 0,
maximum: 5
}
},
bar: {
type: 'string',
encoding: 'FLOOR_PREFIX_LENGTH_ENUM_VARINT',
options: {
minimum: 0
}
}
},
requiredProperties: [ 'bar', 'foo' ]
}
})
test.end()
})
tap.test('should encode a bounded object with no required nor optionals', (test) => {
const schema: EncodingSchema = {
type: 'object',
additionalProperties: false
}
const result: Encoding = getEncoding(schema, 0)
test.strictSame(getStates(schema), [
{}
])
test.strictSame(result, {
type: 'enum',
encoding: 'CONST_NONE',
options: {
value: {}
}
})
test.end()
})
tap.test('should encode a bounded object with optional properties', (test) => {
const schema: EncodingSchema = {
type: 'object',
additionalProperties: false,
properties: {
foo: {
type: 'string',
maxLength: 5
}
}
}
const result: Encoding = getEncoding(schema, 0)
test.is(getStates(schema), Infinity)
test.strictSame(result, {
type: 'object',
encoding: 'NON_REQUIRED_BOUNDED_TYPED_OBJECT',
options: {
propertyEncodings: {
foo: {
type: 'string',
encoding: 'BOUNDED_PREFIX_LENGTH_8BIT_FIXED',
options: {
minimum: 0,
maximum: 5
}
}
},
optionalProperties: [ 'foo' ]
}
})
test.end()
})
tap.test('should encode a bounded object with more than one optional keys', (test) => {
const schema: EncodingSchema = {
type: 'object',
additionalProperties: false,
properties: {
bar: {
type: 'string'
},
foo: {
type: 'string',
maxLength: 5
}
}
}
const result: Encoding = getEncoding(schema, 0)
test.is(getStates(schema), Infinity)
test.strictSame(result, {
type: 'object',
encoding: 'NON_REQUIRED_BOUNDED_TYPED_OBJECT',
options: {
propertyEncodings: {
foo: {
type: 'string',
encoding: 'BOUNDED_PREFIX_LENGTH_8BIT_FIXED',
options: {
minimum: 0,
maximum: 5
}
},
bar: {
type: 'string',
encoding: 'FLOOR_PREFIX_LENGTH_ENUM_VARINT',
options: {
minimum: 0
}
}
},
optionalProperties: [ 'bar', 'foo' ]
}
})
test.end()
})
tap.test('should encode a bounded object with more than one optional keys and empty required', (test) => {
const schema: EncodingSchema = {
type: 'object',
additionalProperties: false,
required: [],
properties: {
bar: {
type: 'string'
},
foo: {
type: 'string',
maxLength: 5
}
}
}
const result: Encoding = getEncoding(schema, 0)
test.is(getStates(schema), Infinity)
test.strictSame(result, {
type: 'object',
encoding: 'NON_REQUIRED_BOUNDED_TYPED_OBJECT',
options: {
propertyEncodings: {
foo: {
type: 'string',
encoding: 'BOUNDED_PREFIX_LENGTH_8BIT_FIXED',
options: {
minimum: 0,
maximum: 5
}
},
bar: {
type: 'string',
encoding: 'FLOOR_PREFIX_LENGTH_ENUM_VARINT',
options: {
minimum: 0
}
}
},
optionalProperties: [ 'bar', 'foo' ]
}
})
test.end()
})
tap.test('should encode a bounded object with an optional and a required property', (test) => {
const schema: EncodingSchema = {
type: 'object',
additionalProperties: false,
required: [ 'bar' ],
properties: {
foo: {
type: 'string',
maxLength: 5
}
}
}
const result: Encoding = getEncoding(schema, 0)
test.is(getStates(schema), Infinity)
test.strictSame(result, {
type: 'object',
encoding: 'MIXED_BOUNDED_TYPED_OBJECT',
options: {
propertyEncodings: {
foo: {
type: 'string',
encoding: 'BOUNDED_PREFIX_LENGTH_8BIT_FIXED',
options: {
minimum: 0,
maximum: 5
}
},
bar: {
type: 'any',
encoding: 'ANY_PACKED_TYPE_TAG_BYTE_PREFIX',
options: {}
}
},
optionalProperties: [ 'foo' ],
booleanRequiredProperties: [],
requiredProperties: [ 'bar' ]
}
})
test.end()
})
tap.test('should encode a bounded object with an optional and a typed required property', (test) => {
const schema: EncodingSchema = {
type: 'object',
additionalProperties: false,
required: [ 'bar' ],
properties: {
bar: {
type: 'string'
},
foo: {
type: 'string',
maxLength: 5
}
}
}
const result: Encoding = getEncoding(schema, 0)
test.is(getStates(schema), Infinity)
test.strictSame(result, {
type: 'object',
encoding: 'MIXED_BOUNDED_TYPED_OBJECT',
options: {
propertyEncodings: {
foo: {
type: 'string',
encoding: 'BOUNDED_PREFIX_LENGTH_8BIT_FIXED',
options: {
minimum: 0,
maximum: 5
}
},
bar: {
type: 'string',
encoding: 'FLOOR_PREFIX_LENGTH_ENUM_VARINT',
options: {
minimum: 0
}
}
},
optionalProperties: [ 'foo' ],
booleanRequiredProperties: [],
requiredProperties: [ 'bar' ]
}
})
test.end()
})
tap.test('should encode a simple unbounded object', (test) => {
const schema: EncodingSchema = {
type: 'object'
}
const result: Encoding = getEncoding(schema, 0)
test.is(getStates(schema), Infinity)
test.strictSame(result, {
type: 'object',
encoding: 'ARBITRARY_TYPED_KEYS_OBJECT',
options: {
keyEncoding: {
type: 'string',
encoding: 'STRING_UNBOUNDED_SCOPED_PREFIX_LENGTH',
options: {}
},
encoding: {
type: 'any',
encoding: 'ANY_PACKED_TYPE_TAG_BYTE_PREFIX',
options: {}
}
}
})
test.end()
})
tap.test('should encode a simple unbounded object with empty required', (test) => {
const schema: EncodingSchema = {
type: 'object',
required: []
}
const result: Encoding = getEncoding(schema, 0)
test.is(getStates(schema), Infinity)
test.strictSame(result, {
type: 'object',
encoding: 'ARBITRARY_TYPED_KEYS_OBJECT',
options: {
keyEncoding: {
type: 'string',
encoding: 'STRING_UNBOUNDED_SCOPED_PREFIX_LENGTH',
options: {}
},
encoding: {
type: 'any',
encoding: 'ANY_PACKED_TYPE_TAG_BYTE_PREFIX',
options: {}
}
}
})
test.end()
})
tap.test('should encode a simple unbounded object with additionalProperties: true', (test) => {
const schema: EncodingSchema = {
type: 'object',
additionalProperties: true
}
const result: Encoding = getEncoding(schema, 0)
test.is(getStates(schema), Infinity)
test.strictSame(result, {
type: 'object',
encoding: 'ARBITRARY_TYPED_KEYS_OBJECT',
options: {
keyEncoding: {
type: 'string',
encoding: 'STRING_UNBOUNDED_SCOPED_PREFIX_LENGTH',
options: {}
},
encoding: {
type: 'any',
encoding: 'ANY_PACKED_TYPE_TAG_BYTE_PREFIX',
options: {}
}
}
})
test.end()
})
tap.test('should encode a simple unbounded object with additionalProperties: schema', (test) => {
const schema: EncodingSchema = {
type: 'object',
additionalProperties: {
type: 'string'
}
}
const result: Encoding = getEncoding(schema, 0)
test.is(getStates(schema), Infinity)
test.strictSame(result, {
type: 'object',
encoding: 'ARBITRARY_TYPED_KEYS_OBJECT',
options: {
keyEncoding: {
type: 'string',
encoding: 'STRING_UNBOUNDED_SCOPED_PREFIX_LENGTH',
options: {}
},
encoding: {
type: 'string',
encoding: 'FLOOR_PREFIX_LENGTH_ENUM_VARINT',
options: {
minimum: 0
}
}
}
})
test.end()
})
tap.test('should encode a simple unbounded object with propertyNames', (test) => {
const schema: EncodingSchema = {
type: 'object',
propertyNames: {
type: 'string',
maxLength: 5
}
}
const result: Encoding = getEncoding(schema, 0)
test.is(getStates(schema), Infinity)
test.strictSame(result, {
type: 'object',
encoding: 'ARBITRARY_TYPED_KEYS_OBJECT',
options: {
keyEncoding: {
type: 'string',
encoding: 'BOUNDED_PREFIX_LENGTH_8BIT_FIXED',
options: {
minimum: 0,
maximum: 5
}
},
encoding: {
type: 'any',
encoding: 'ANY_PACKED_TYPE_TAG_BYTE_PREFIX',
options: {}
}
}
})
test.end()
})
tap.test('should encode a simple unbounded object with propertyNames and additionalProperties: true', (test) => {
const schema: EncodingSchema = {
type: 'object',
additionalProperties: true,
propertyNames: {
type: 'string',
maxLength: 5
}
}
const result: Encoding = getEncoding(schema, 0)
test.is(getStates(schema), Infinity)
test.strictSame(result, {
type: 'object',
encoding: 'ARBITRARY_TYPED_KEYS_OBJECT',
options: {
keyEncoding: {
type: 'string',
encoding: 'BOUNDED_PREFIX_LENGTH_8BIT_FIXED',
options: {
minimum: 0,
maximum: 5
}
},
encoding: {
type: 'any',
encoding: 'ANY_PACKED_TYPE_TAG_BYTE_PREFIX',
options: {}
}
}
})
test.end()
})
tap.test('should encode a simple unbounded object with propertyNames and additionalProperties: schema', (test) => {
const schema: EncodingSchema = {
type: 'object',
additionalProperties: {
type: 'string'
},
propertyNames: {
type: 'string',
maxLength: 5
}
}
const result: Encoding = getEncoding(schema, 0)
test.is(getStates(schema), Infinity)
test.strictSame(result, {
type: 'object',
encoding: 'ARBITRARY_TYPED_KEYS_OBJECT',
options: {
keyEncoding: {
type: 'string',
encoding: 'BOUNDED_PREFIX_LENGTH_8BIT_FIXED',
options: {
minimum: 0,
maximum: 5
}
},
encoding: {
type: 'string',
encoding: 'FLOOR_PREFIX_LENGTH_ENUM_VARINT',
options: {
minimum: 0
}
}
}
})
test.end()
})
tap.test('should encode a simple unbounded object with a required property', (test) => {
const schema: EncodingSchema = {
type: 'object',
required: [ 'foo' ]
}
const result: Encoding = getEncoding(schema, 0)
test.is(getStates(schema), Infinity)
test.strictSame(result, {
type: 'object',
encoding: 'REQUIRED_UNBOUNDED_TYPED_OBJECT',
options: {
requiredProperties: [ 'foo' ],
booleanRequiredProperties: [],
propertyEncodings: {
foo: {
type: 'any',
encoding: 'ANY_PACKED_TYPE_TAG_BYTE_PREFIX',
options: {}
}
},
keyEncoding: {
type: 'string',
encoding: 'STRING_UNBOUNDED_SCOPED_PREFIX_LENGTH',
options: {}
},
encoding: {
type: 'any',
encoding: 'ANY_PACKED_TYPE_TAG_BYTE_PREFIX',
options: {}
}
}
})
test.end()
})
tap.test('should encode a simple unbounded object with a required typed property', (test) => {
const schema: EncodingSchema = {
type: 'object',
required: [ 'foo' ],
properties: {
foo: {
type: 'string'
}
}
}
const result: Encoding = getEncoding(schema, 0)
test.is(getStates(schema), Infinity)
test.strictSame(result, {
type: 'object',
encoding: 'REQUIRED_UNBOUNDED_TYPED_OBJECT',
options: {
requiredProperties: [ 'foo' ],
booleanRequiredProperties: [],
propertyEncodings: {
foo: {
type: 'string',
encoding: 'FLOOR_PREFIX_LENGTH_ENUM_VARINT',
options: {
minimum: 0
}
}
},
keyEncoding: {
type: 'string',
encoding: 'STRING_UNBOUNDED_SCOPED_PREFIX_LENGTH',
options: {}
},
encoding: {
type: 'any',
encoding: 'ANY_PACKED_TYPE_TAG_BYTE_PREFIX',
options: {}
}
}
})
test.end()
})
tap.test('should encode a simple unbounded object with two optional properties', (test) => {
const schema: EncodingSchema = {
type: 'object',
properties: {
foo: {
type: 'string'
},
bar: {
type: 'string',
maxLength: 5
}
}
}
const result: Encoding = getEncoding(schema, 0)
test.is(getStates(schema), Infinity)
test.strictSame(result, {
type: 'object',
encoding: 'OPTIONAL_UNBOUNDED_TYPED_OBJECT',
options: {
optionalProperties: [ 'bar', 'foo' ],
propertyEncodings: {
foo: {
type: 'string',
encoding: 'FLOOR_PREFIX_LENGTH_ENUM_VARINT',
options: {
minimum: 0
}
},
bar: {
type: 'string',
encoding: 'BOUNDED_PREFIX_LENGTH_8BIT_FIXED',
options: {
minimum: 0,
maximum: 5
}
}
},
keyEncoding: {
type: 'string',
encoding: 'STRING_UNBOUNDED_SCOPED_PREFIX_LENGTH',
options: {}
},
encoding: {
type: 'any',
encoding: 'ANY_PACKED_TYPE_TAG_BYTE_PREFIX',
options: {}
}
}
})
test.end()
})
tap.test('should encode a complex unbounded object', (test) => {
const schema: EncodingSchema = {
type: 'object',
required: [ 'foo', 'bar' ],
additionalProperties: {
type: 'integer'
},
propertyNames: {
type: 'string',
minLength: 3
},
properties: {
bar: {
type: 'string',
maxLength: 5
},
baz: {
type: 'string'
}
}
}
const result: Encoding = getEncoding(schema, 0)
test.is(getStates(schema), Infinity)
test.strictSame(result, {
type: 'object',
encoding: 'MIXED_UNBOUNDED_TYPED_OBJECT',
options: {
optionalProperties: [ 'baz' ],
requiredProperties: [ 'bar', 'foo' ],
booleanRequiredProperties: [],
propertyEncodings: {
foo: {
type: 'integer',
encoding: 'ARBITRARY_MULTIPLE_ZIGZAG_VARINT',
options: {
multiplier: 1
}
},
bar: {
type: 'string',
encoding: 'BOUNDED_PREFIX_LENGTH_8BIT_FIXED',
options: {
minimum: 0,
maximum: 5
}
},
baz: {
type: 'string',
encoding: 'FLOOR_PREFIX_LENGTH_ENUM_VARINT',
options: {
minimum: 0
}
}
},
keyEncoding: {
type: 'string',
encoding: 'FLOOR_PREFIX_LENGTH_ENUM_VARINT',
options: {
minimum: 3
}
},
encoding: {
type: 'integer',
encoding: 'ARBITRARY_MULTIPLE_ZIGZAG_VARINT',
options: {
multiplier: 1
}
}
}
})
test.end()
})
tap.test('should encode a bounded object with only boolean required keys', (test) => {
const schema: EncodingSchema = {
type: 'object',
additionalProperties: false,
required: [ 'foo', 'bar' ],
properties: {
foo: {
type: 'boolean'
},
bar: {
type: 'boolean'
}
}
}
const result: Encoding = getEncoding(schema, 0)
test.strictSame(getStates(schema), [
{
foo: false,
bar: false
},
{
foo: false,
bar: true
},
{
foo: true,
bar: false
},
{
foo: true,
bar: true
}
])
test.strictSame(result, {
type: 'enum',
encoding: 'TOP_LEVEL_8BIT_CHOICE_INDEX',
options: {
choices: [
{
foo: false,
bar: false
},
{
foo: false,
bar: true
},
{
foo: true,
bar: false
},
{
foo: true,
bar: true
}
]
}
})
test.end()
})
tap.test('should encode a bounded property with a single required boolean', (test) => {
const schema: EncodingSchema = {
type: 'object',
required: [ 'jsx' ],
additionalProperties: false,
properties: {
jsx: {
type: 'boolean'
}
}
}
const result: Encoding = getEncoding(schema, 0)
test.strictSame(getStates(schema), [
{
jsx: false
},
{
jsx: true
}
])
test.strictSame(result, {
type: 'enum',
encoding: 'TOP_LEVEL_8BIT_CHOICE_INDEX',
options: {
choices: [
{
jsx: false
},
{
jsx: true
}
]
}
})
test.end()
})
tap.test('should encode a bounded property with a single boolean', (test) => {
const schema: EncodingSchema = {
type: 'object',
additionalProperties: false,
properties: {
jsx: {
type: 'boolean'
}
}
}
const result: Encoding = getEncoding(schema, 0)
test.strictSame(getStates(schema), [
{
jsx: false
},
{
jsx: true
},
{}
])
test.strictSame(result, {
type: 'enum',
encoding: 'TOP_LEVEL_8BIT_CHOICE_INDEX',
options: {
choices: [
{
jsx: false
},
{
jsx: true
},
{}
]
}
})
test.end()
})
tap.test('should encode an unbounded object with bounded integers', (test) => {
const schema: EncodingSchema = {
type: 'object',
additionalProperties: {
type: 'integer',
minimum: 0,
maximum: 2
},
required: [ 'foo', 'bar', 'baz', 'name', 'qux', 'extra', 'flag' ],
properties: {
name: {
type: 'string'
},
age: {
type: 'integer',
minimum: 0
},
extra: {
type: 'integer',
minimum: 0,
maximum: 2
},
flag: {
type: 'boolean'
}
}
}
const result: Encoding = getEncoding(schema, 0)
test.is(getStates(schema), Infinity)
test.strictSame(result, {
type: 'object',
encoding: 'PACKED_UNBOUNDED_OBJECT',
options: {
packedRequiredProperties: [ 'bar', 'baz', 'extra', 'foo', 'qux' ],
packedEncoding: {
type: 'integer',
encoding: 'BOUNDED_MULTIPLE_8BITS_ENUM_FIXED',
options: {
minimum: 0,
maximum: 2,
multiplier: 1
}
},
encoding: {
type: 'any',
encoding: 'ANY_PACKED_TYPE_TAG_BYTE_PREFIX',
options: {}
},
propertyEncodings: {
name: {
type: 'string',
encoding: 'FLOOR_PREFIX_LENGTH_ENUM_VARINT',
options: {
minimum: 0
}
},
age: {
type: 'integer',
encoding: 'FLOOR_MULTIPLE_ENUM_VARINT',
options: {
minimum: 0,
multiplier: 1
}
},
flag: {
type: 'enum',
encoding: 'BOUNDED_CHOICE_INDEX',
options: {
choices: [ false, true ]
}
}
},
optionalProperties: [ 'age' ],
requiredProperties: [ 'name' ],
booleanRequiredProperties: [ 'flag' ],
keyEncoding: {
type: 'string',
encoding: 'STRING_UNBOUNDED_SCOPED_PREFIX_LENGTH',
options: {}
}
}
})
test.end()
})
tap.test('should encode an unbounded object with bounded integers with maxProperties > length', (test) => {
const schema: EncodingSchema = {
type: 'object',
maxProperties: 8,
additionalProperties: {
type: 'integer',
minimum: 0,
maximum: 2
},
required: [ 'foo', 'bar', 'baz', 'name', 'qux', 'extra', 'flag' ],
properties: {
name: {
type: 'string'
},
extra: {
type: 'integer',
minimum: 0,
maximum: 2
},
flag: {
type: 'boolean'
}
}
}
const result: Encoding = getEncoding(schema, 0)
test.is(getStates(schema), Infinity)
test.strictSame(result, {
type: 'object',
encoding: 'PACKED_UNBOUNDED_OBJECT',
options: {
packedRequiredProperties: [ 'bar', 'baz', 'extra', 'foo', 'qux' ],
packedEncoding: {
type: 'integer',
encoding: 'BOUNDED_MULTIPLE_8BITS_ENUM_FIXED',
options: {
minimum: 0,
maximum: 2,
multiplier: 1
}
},
encoding: {
type: 'any',
encoding: 'ANY_PACKED_TYPE_TAG_BYTE_PREFIX',
options: {}
},
propertyEncodings: {
name: {
type: 'string',
encoding: 'FLOOR_PREFIX_LENGTH_ENUM_VARINT',
options: {
minimum: 0
}
},
flag: {
type: 'enum',
encoding: 'BOUNDED_CHOICE_INDEX',
options: {
choices: [ false, true ]
}
}
},
optionalProperties: [],
requiredProperties: [ 'name' ],
booleanRequiredProperties: [ 'flag' ],
keyEncoding: {
type: 'string',
encoding: 'STRING_UNBOUNDED_SCOPED_PREFIX_LENGTH',
options: {}
}
}
})
test.end()
})
tap.test('should encode an unbounded object with bounded integers with maxProperties = length', (test) => {
const schema: EncodingSchema = {
type: 'object',
maxProperties: 7,
additionalProperties: {
type: 'integer',
minimum: 0,
maximum: 2
},
required: [ 'foo', 'bar', 'baz', 'name', 'qux', 'extra', 'flag' ],
properties: {
name: {
type: 'string'
},
extra: {
type: 'integer',
minimum: 0,
maximum: 2
},
flag: {
type: 'boolean'
}
}
}
const result: Encoding = getEncoding(schema, 0)
test.is(getStates(schema), Infinity)
test.strictSame(result, {
type: 'object',
encoding: 'PACKED_BOUNDED_REQUIRED_OBJECT',
options: {
packedRequiredProperties: [ 'bar', 'baz', 'extra', 'foo', 'qux' ],
packedEncoding: {
type: 'integer',
encoding: 'BOUNDED_MULTIPLE_8BITS_ENUM_FIXED',
options: {
minimum: 0,
maximum: 2,
multiplier: 1
}
},
propertyEncodings: {
name: {
type: 'string',
encoding: 'FLOOR_PREFIX_LENGTH_ENUM_VARINT',
options: {
minimum: 0
}
},
flag: {
type: 'enum',
encoding: 'BOUNDED_CHOICE_INDEX',
options: {
choices: [ false, true ]
}
}
},
requiredProperties: [ 'name' ],
booleanRequiredProperties: [ 'flag' ]
}
})
test.end()
})