From ecd439dcd102803e311421e819052d0347868287 Mon Sep 17 00:00:00 2001 From: Pascal Perrenoud Date: Thu, 11 Sep 2025 17:13:19 +0200 Subject: [PATCH] WIP : tests --- test/all.test.ts | 211 +++++++++++++++++++++++++++++++++++++++++++ test/example.test.ts | 3 - test/init.ts | 5 + 3 files changed, 216 insertions(+), 3 deletions(-) create mode 100644 test/all.test.ts delete mode 100644 test/example.test.ts create mode 100644 test/init.ts diff --git a/test/all.test.ts b/test/all.test.ts new file mode 100644 index 0000000..6f23c85 --- /dev/null +++ b/test/all.test.ts @@ -0,0 +1,211 @@ +import {expect, test, describe, beforeEach, afterEach} from 'bun:test' +import {yup} from '../src/index' +import {type FileResult, fileSync} from 'tmp' +import * as fs from 'node:fs/promises' + +import * as parsing from '../src/parsing' +import {parse} from '../src' + +beforeEach(() => process.env = {}) + +async function testing(value: string | undefined, scheme: yup.AnySchema, should_be_successful: boolean, data?: any) { + const key = 'DB_PORT' + if (value !== undefined) process.env[key] = value + + const res = await parsing.generic(scheme, key) + + if (should_be_successful) expect(res).toEqual({ok: true, data}) + else expect(res.ok).not.toBeTrue() +} + +// Most important function : read_object +describe('Object', () => { + test('basic', async () => { + const scheme = yup.object({}) + const res = await parsing.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 = yup.object({name: yup.string(), port: yup.number(), ts: yup.date()}) + const res = await parsing.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 = yup.object({ + name: yup.string().required(), + age: yup.number().required(), + db: yup.object({ + port: yup.number().required(), + host: yup.string().required() + }).required(), + thing: yup.object({ + a: yup.string().required(), + b: yup.number().required(), + }).required() + }) + + const res = await parsing.object(scheme) + expect(res).toEqual({ok: true, data: expected}) + + const res2 = await parse(scheme) + expect(res.ok).toBeTrue() + expect(res2.data).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 = yup.object({ + age: yup.number(), + db: yup.object({ + host: yup.string() + }), + thing: yup.object({ + a: yup.string(), + thang: yup.object({ + a: yup.date() + }) + }) + }) + + const res = await parsing.object(scheme) + expect(res).toEqual({ok: true, data: expected}) + + const res2 = await parse(scheme) + expect(res2.data).toEqual(expected) + }) +}) + +// Read any from file +describe('from_file', () => { + let file: FileResult | undefined = undefined; + afterEach(() => { + file?.removeCallback() + file = undefined + }) + + async function testing(value: string | undefined, scheme: yup.AnySchema, 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 parsing.generic(scheme, key) + + if (should_be_successful) expect(res).toEqual({ok: true, data}) + else expect(res.ok).not.toBeTrue() + } + + test('basic string', () => testing('Coucou', yup.string(), true, 'Coucou')) + test('basic number', () => testing('12', yup.number(), true, 12)) + test('file does not exist', () => testing(undefined, yup.string(), false)) + test('Last empty line is trimmed', () => testing('Coucou\n\r\n ', yup.string(), true, 'Coucou')) +}) + +// Read generic types +describe('Integer', () => { + test('basic', () => testing('12', yup.number(), true, 12)) + test('not present', () => testing(undefined, yup.number().required(), false)) + test('optional', () => testing(undefined, yup.number(), true)) + test('default', () => testing(undefined, yup.number().default(21), true, 21)) + test('string', () => testing('coucou', yup.number(), false)) + test('min-max OK', () => testing('12', yup.number().min(11).max(13), true, 12)) + test('min-max KO', () => testing('10', yup.number().min(11).max(13), false, 10)) + test('min-max KO 2', () => testing('10', yup.number().min(11).max(13), false, 14)) +}) +describe('String', () => { + test('basic', () => testing('coucou', yup.string(), true, 'coucou')) + test('not present', () => testing(undefined, yup.string().required(), false)) + test('optional', () => testing(undefined, yup.string(), true, undefined)) + test('default', () => testing(undefined, yup.string().default('yeet'), true, 'yeet')) + test('Length min-max', () => testing('coucou', yup.string().max(7), true, 'coucou')) + test('KO: Length min-max', () => testing('coucou', yup.string().max(5), false)) +}) +describe('boolean', () => { + test('basic true', () => testing('true', yup.boolean(), true, true)) + test('basic 1', () => testing('1', yup.boolean(), true, true)) + test('basic 0', () => testing('0', yup.boolean(), true, false)) + test('not present', () => testing(undefined, yup.boolean().required(), false)) + test('optional', () => testing(undefined, yup.boolean(), true)) + test('default', () => testing(undefined, yup.boolean().default(false), true, false)) +}) + +// Read specific types +describe('Date', () => { + async function testing(value: string | undefined, scheme: yup.DateSchema, should_be_successful: boolean, data?: any) { + const key = 'DB_PORT' + if (value !== undefined) process.env[key] = value + + const res = await parsing.generic(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', yup.date(), true, new Date('2025-06-08 22:40:17'))) + test('optional', () => testing(undefined, yup.date(), true)) +}) +describe('Array', () => { + test('basic', () => testing('12,13,14', yup.array(yup.number()), true, [12,13,14])) + /*test('not present', () => testing(undefined, yup.number().required(), false)) + test('optional', () => testing(undefined, yup.number(), true)) + test('default', () => testing(undefined, yup.number().default(21), true, 21)) + test('string', () => testing('coucou', yup.number(), false)) + test('min-max OK', () => testing('12', yup.number().min(11).max(13), true, 12)) + test('min-max KO', () => testing('10', yup.number().min(11).max(13), false, 10)) + test('min-max KO 2', () => testing('10', yup.number().min(11).max(13), false, 14))*/ +}) +describe.todo('Tuple') diff --git a/test/example.test.ts b/test/example.test.ts deleted file mode 100644 index 54e69fb..0000000 --- a/test/example.test.ts +++ /dev/null @@ -1,3 +0,0 @@ -import {test} from 'bun:test' - -test('Basic test', () => 'Hello, world!') diff --git a/test/init.ts b/test/init.ts new file mode 100644 index 0000000..9c3ad96 --- /dev/null +++ b/test/init.ts @@ -0,0 +1,5 @@ +import {Console} from 'logger-console' +import {Level, writers} from 'log' + +const logger = new Console({minLevel: Level.DEBUG, with_color: true}) +writers.set('console', logger)