Add tests
This commit is contained in:
@@ -0,0 +1,2 @@
|
||||
[test]
|
||||
preload = "./test/init.ts"
|
||||
@@ -0,0 +1,5 @@
|
||||
import {Console} from 'logger-console'
|
||||
import {Level, writers} from 'log'
|
||||
|
||||
const logger = new Console({minLevel: Level.TRACE, with_color: true})
|
||||
writers.set('console', logger)
|
||||
@@ -0,0 +1,223 @@
|
||||
import {expect, test, describe, beforeEach, afterEach} from 'bun:test'
|
||||
import {type TDate, type TSchema, Type} from "@sinclair/typebox"
|
||||
import {type FileResult, fileSync} from 'tmp'
|
||||
import fs from 'node:fs/promises'
|
||||
|
||||
import {read_date, read_object, read_type} from '../src/read_type'
|
||||
import {parse} from '..'
|
||||
|
||||
beforeEach(() => process.env = {})
|
||||
|
||||
async function testing<T extends TSchema>(value: string | undefined, scheme: T, should_be_successful: boolean, data?: any) {
|
||||
const key = 'DB_PORT'
|
||||
if (value !== undefined) process.env[key] = value
|
||||
|
||||
const res = await read_type(scheme, key)
|
||||
|
||||
if (should_be_successful) {
|
||||
expect(res).toEqual({ok: true, data})
|
||||
} else {
|
||||
expect(res).toEqual({ok: false})
|
||||
}
|
||||
}
|
||||
|
||||
// Most important function : read_object
|
||||
describe('Object', () => {
|
||||
test('basic', async () => {
|
||||
const scheme = Type.Object({})
|
||||
const res = await read_object(scheme)
|
||||
expect(res).toEqual({ok: true, data: {}})
|
||||
})
|
||||
test('One level', async () => {
|
||||
process.env = {
|
||||
NAME: 'test',
|
||||
PORT: '12',
|
||||
TS: '2025-06-08 23:51:12'
|
||||
}
|
||||
const scheme = Type.Object({name: Type.String(), port: Type.Integer(), ts: Type.Date()})
|
||||
const res = await read_object(scheme)
|
||||
expect(res).toEqual({ok: true, data: {name: 'test', port: 12, ts: new Date('2025-06-08 23:51:12')}})
|
||||
})
|
||||
test('Two levels', async () => {
|
||||
process.env = {
|
||||
DB_PORT: '12',
|
||||
DB_HOST: 'localhost',
|
||||
THING_A: 'Hello!',
|
||||
THING_B: '212',
|
||||
NAME: 'Pascal',
|
||||
AGE: '27',
|
||||
}
|
||||
const expected = {
|
||||
name: 'Pascal',
|
||||
age: 27,
|
||||
db: {
|
||||
port: 12,
|
||||
host: 'localhost'
|
||||
},
|
||||
thing: {
|
||||
a: 'Hello!',
|
||||
b: 212,
|
||||
}
|
||||
}
|
||||
|
||||
const scheme = Type.Object({
|
||||
name: Type.String(),
|
||||
age: Type.Number(),
|
||||
db: Type.Object({
|
||||
port: Type.Integer(),
|
||||
host: Type.String()
|
||||
}),
|
||||
thing: Type.Object({
|
||||
a: Type.String(),
|
||||
b: Type.Integer(),
|
||||
})
|
||||
})
|
||||
|
||||
const res = await read_object(scheme)
|
||||
expect(res).toEqual({ok: true, data: expected})
|
||||
|
||||
const res2 = await parse(scheme)
|
||||
expect(res2).toEqual(expected)
|
||||
})
|
||||
test('Three levels', async () => {
|
||||
|
||||
process.env = {
|
||||
DB_HOST: 'localhost',
|
||||
THING_A: 'Hello!',
|
||||
AGE: '27',
|
||||
THING_THANG_A: '2025-06-06 23:56:12'
|
||||
}
|
||||
const expected = {
|
||||
age: 27,
|
||||
db: {
|
||||
host: 'localhost'
|
||||
},
|
||||
thing: {
|
||||
a: 'Hello!',
|
||||
thang: {
|
||||
a: new Date('2025-06-06 23:56:12')
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const scheme = Type.Object({
|
||||
age: Type.Number(),
|
||||
db: Type.Object({
|
||||
host: Type.String()
|
||||
}),
|
||||
thing: Type.Object({
|
||||
a: Type.String(),
|
||||
thang: Type.Object({
|
||||
a: Type.Date()
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
const res = await read_object(scheme)
|
||||
expect(res).toEqual({ok: true, data: expected})
|
||||
|
||||
const res2 = await parse(scheme)
|
||||
expect(res2).toEqual(expected)
|
||||
})
|
||||
})
|
||||
|
||||
// Read any from file
|
||||
describe('from_file', () => {
|
||||
let file: FileResult | undefined = undefined;
|
||||
afterEach(() => {
|
||||
file?.removeCallback()
|
||||
file = undefined
|
||||
})
|
||||
|
||||
async function testing<T extends TSchema>(value: any | undefined, scheme: T, should_be_successful: boolean, data?: any) {
|
||||
file = fileSync()
|
||||
|
||||
const name = file.name
|
||||
if (value !== undefined) {
|
||||
await fs.writeFile(file.name, value)
|
||||
} else {
|
||||
file.removeCallback()
|
||||
file = undefined
|
||||
}
|
||||
|
||||
const key = 'DB_PORT'
|
||||
process.env[key + '__FILE'] = name
|
||||
|
||||
const res = await read_type(scheme, key)
|
||||
|
||||
if (should_be_successful) {
|
||||
expect(res).toEqual({ok: true, data})
|
||||
} else {
|
||||
expect(res).toEqual({ok: false})
|
||||
}
|
||||
}
|
||||
|
||||
test('basic string', () => testing('Coucou', Type.String(), true, 'Coucou'))
|
||||
test('basic number', () => testing(12, Type.Integer(), true, 12))
|
||||
test('file does not exist', () => testing(undefined, Type.String(), false))
|
||||
test('Last empty line is trimmed', () => testing('Coucou\n\r\n ', Type.String(), true, 'Coucou'))
|
||||
})
|
||||
|
||||
// Read generic types
|
||||
describe('Integer', () => {
|
||||
test('basic', () => testing('12', Type.Integer(), true, 12))
|
||||
test('not present', () => testing(undefined, Type.Integer(), false))
|
||||
test('optional', () => testing(undefined, Type.Optional(Type.Integer()), true))
|
||||
test('default', () => testing(undefined, Type.Integer({default: 21}), true, 21))
|
||||
test('string', () => testing('coucou', Type.Integer(), false))
|
||||
test('min-max OK', () => testing('12', Type.Integer({minimum: 11, maximum: 13}), true, 12))
|
||||
test('min-max KO', () => testing('10', Type.Integer({minimum: 11, maximum: 13}), false, 10))
|
||||
})
|
||||
describe('String', () => {
|
||||
test('basic', () => testing('coucou', Type.String(), true, 'coucou'))
|
||||
test('not present', () => testing(undefined, Type.String(), false))
|
||||
test('optional', () => testing(undefined, Type.Optional(Type.String()), true, undefined))
|
||||
test('default', () => testing(undefined, Type.String({default: 'yeet'}), true, 'yeet'))
|
||||
test('Length min-max', () => testing('coucou', Type.String({maxLength: 7}), true, 'coucou'))
|
||||
test('KO: Length min-max', () => testing('coucou', Type.String({maxLength: 5}), false))
|
||||
})
|
||||
describe('boolean', () => {
|
||||
test('basic true', () => testing('true', Type.Boolean(), true, true))
|
||||
test('basic 1', () => testing('1', Type.Boolean(), true, true))
|
||||
test('not present', () => testing(undefined, Type.Boolean(), false))
|
||||
test('optional', () => testing(undefined, Type.Optional(Type.Boolean()), true))
|
||||
test('default', () => testing(undefined, Type.Boolean({default: false}), true, false))
|
||||
})
|
||||
describe('Enum', () => {
|
||||
enum TestNumber {
|
||||
A,
|
||||
B,
|
||||
C,
|
||||
D
|
||||
}
|
||||
enum TestString {
|
||||
A = "a",
|
||||
B = "b",
|
||||
}
|
||||
|
||||
test('basic number', () => testing('1', Type.Enum(TestNumber), true, TestNumber.B))
|
||||
test('number out', () => testing('4', Type.Enum(TestNumber), false))
|
||||
test('basic string', () => testing('a', Type.Enum(TestString), true, TestString.A))
|
||||
test('string out', () => testing('c', Type.Enum(TestString), false))
|
||||
})
|
||||
|
||||
// Read specific types
|
||||
describe('Date', () => {
|
||||
async function testing<T extends TDate>(value: string | undefined, scheme: T, should_be_successful: boolean, data?: any) {
|
||||
const key = 'DB_PORT'
|
||||
if (value !== undefined) process.env[key] = value
|
||||
|
||||
const res = await read_date(scheme, key)
|
||||
|
||||
if (should_be_successful) {
|
||||
expect(res).toEqual({ok: true, data})
|
||||
} else {
|
||||
expect(res).toEqual({ok: false})
|
||||
}
|
||||
}
|
||||
|
||||
test('basic', () => testing('2025-06-08 22:40:17', Type.Date(), true, new Date('2025-06-08 22:40:17')))
|
||||
test('optional', () => testing(undefined, Type.Optional(Type.Date()), true))
|
||||
})
|
||||
describe.todo('Array')
|
||||
describe.todo('Tuple')
|
||||
Reference in New Issue
Block a user