jsonbinpack
Version:
A space-efficient schema-driven binary JSON serialization format based on JSON Schema
122 lines (97 loc) • 3.51 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 {
isDeepStrictEqual
} from 'util'
import {
readdirSync,
readFileSync,
writeFileSync,
statSync
} from 'fs'
import {
resolve
} from 'path'
import {
JSONValue,
JSONSchema,
compileSchema,
serialize,
deserialize,
Encoding
} from '../../lib'
import {
validateSchema,
EncodingSchema
} from '../../lib/schema'
import {
preprocessSchema
} from '../../lib/preprocessor'
const TEST_DIRECTORY: string = __dirname
const SRC_TEST_DIRECTORY: string = resolve(__dirname, '..', '..', '..', 'test', 'e2e')
const safeReadFile = (filePath: string): string | null => {
try {
return readFileSync(filePath, 'utf8')
} catch (error) {
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
if (error.code === 'ENOENT') {
return null
}
throw error
}
}
type SchemaType = 'schema-driven' | 'schema-less'
const writeResult = (testCase: string, type: SchemaType, name: string, value: Encoding | EncodingSchema): void => {
const destination: string = resolve(SRC_TEST_DIRECTORY, testCase, type, name)
const currentContent: string | null = safeReadFile(destination)
if (typeof currentContent === 'string' &&
isDeepStrictEqual(JSON.parse(currentContent), value)) {
return
}
const content: string = JSON.stringify(value, null, 2)
writeFileSync(destination, `${content}\n`, 'utf8')
}
for (const testCase of readdirSync(TEST_DIRECTORY)) {
const testCasePath: string = resolve(TEST_DIRECTORY, testCase)
if (!statSync(testCasePath).isDirectory()) {
continue
}
const types: SchemaType[] = [ 'schema-driven', 'schema-less' ]
for (const type of types) {
tap.test(`${testCase} (${type})`, async (test) => {
const schema: JSONSchema = type === 'schema-driven'
? JSON.parse(readFileSync(resolve(testCasePath, type, 'schema.json'), 'utf8'))
: {}
const value: JSONValue = JSON.parse(readFileSync(resolve(testCasePath, 'document.json'), 'utf8'))
const encodingSchema: EncodingSchema = await preprocessSchema(schema)
test.true(validateSchema(encodingSchema, value))
const encoding: Encoding = await compileSchema(schema)
// Record the encoding and canonical schemas for debugging purposes
writeResult(testCase, type, 'encoding.json', encoding)
writeResult(testCase, type, 'canonical.json', encodingSchema)
const buffer: Buffer = serialize(encoding, value)
const result: JSONValue = deserialize(encoding, buffer)
// Record the buffer for debugging purposes too
writeFileSync(resolve(SRC_TEST_DIRECTORY, testCase, type, 'output.bin'), buffer)
// Record the buffer size for debugging purposes
const size: string = String(buffer.length)
writeFileSync(resolve(SRC_TEST_DIRECTORY, testCase, type, 'size'), `${size}\n`, 'utf8')
test.strictSame(value, result)
test.end()
})
}
}