From c05ece1a9fd3f672c9dd0b4dffd524f9365ebdd4 Mon Sep 17 00:00:00 2001 From: Leif Niemczik Date: Wed, 17 May 2023 14:25:03 +0200 Subject: [PATCH 1/3] add native route options to shorthand --- src/FastifyZod.ts | 27 +++++++++++++++++++++++++-- src/__tests__/models.fixtures.ts | 4 ++++ src/__tests__/server.fixtures.ts | 18 ++++++++++++++++++ 3 files changed, 47 insertions(+), 2 deletions(-) diff --git a/src/FastifyZod.ts b/src/FastifyZod.ts index c6274c5..484af32 100644 --- a/src/FastifyZod.ts +++ b/src/FastifyZod.ts @@ -8,6 +8,7 @@ import { RouteHandlerMethod, FastifyReply, RawServerBase, + RouteShorthandOptions, } from "fastify"; import fastifySwagger, { FastifyDynamicSwaggerOptions } from "@fastify/swagger"; import fastifySwaggerUi, { FastifySwaggerUiOptions } from "@fastify/swagger-ui"; @@ -137,6 +138,10 @@ type RouteConfig< } : void; readonly handler: RouteHandler; + nativeRouteOptions?: Omit< + RouteShorthandOptions, + `schema` | `attachValidation` | `validatorCompiler` | `serializerCompiler` + >; } & FastifySchema; export type FastifyZod = { @@ -269,7 +274,16 @@ export const register = async ( response, querystring, handler, - ...fastifySchema + hide, + deprecated, + tags, + description, + summary, + consumes, + produces, + externalDocs, + security, + nativeRouteOptions, }: RouteConfig): void => { const customSchema: FastifySchema = {}; if (operationId) { @@ -312,8 +326,17 @@ export const register = async ( { schema: { ...customSchema, - ...fastifySchema, + hide, + deprecated, + tags, + description, + summary, + consumes, + produces, + externalDocs, + security, }, + ...nativeRouteOptions, }, handler as RouteHandlerMethod, ); diff --git a/src/__tests__/models.fixtures.ts b/src/__tests__/models.fixtures.ts index c62ecdf..7cbed17 100644 --- a/src/__tests__/models.fixtures.ts +++ b/src/__tests__/models.fixtures.ts @@ -41,6 +41,9 @@ export type TodoItemsGroupedByStatus = z.infer; const FortyTwo = z.literal(42); export type FortyTwo = z.infer; +const Teapot = z.literal(`Oh, it's Teatime`); +export type Teapot = z.infer; + export const models = { TodoState, TodoItemId, @@ -49,4 +52,5 @@ export const models = { TodoItems, TodoItemsGroupedByStatus, FortyTwo, + Teapot, }; diff --git a/src/__tests__/server.fixtures.ts b/src/__tests__/server.fixtures.ts index f65255c..ecd3397 100644 --- a/src/__tests__/server.fixtures.ts +++ b/src/__tests__/server.fixtures.ts @@ -135,5 +135,23 @@ export const createTestServer = async ( async () => 42, ); + f.zod.get( + `/native-options`, + { + operationId: `nativeOptions`, + response: { + 418: `Teapot`, + }, + nativeRouteOptions: { + preHandler: [ + (_, res) => { + res.code(418); + }, + ], + }, + }, + async () => `Oh, it's Teatime`, + ); + return f; }; From 2c8b07dec14a164d17f7f7b272a3fc1e88195111 Mon Sep 17 00:00:00 2001 From: Leif Niemczik Date: Wed, 17 May 2023 15:44:39 +0200 Subject: [PATCH 2/3] fix tests and build process --- babel.config.json | 14 --- jest.config.js | 2 +- package.json | 144 +++++++++++++++---------------- src/FastifyZod.ts | 2 +- src/{Path.ts => SchemaPath.ts} | 0 src/SpecTransformer.ts | 2 +- src/__tests__/FastifyZod.test.ts | 11 +++ src/__tests__/server.fixtures.ts | 8 +- tsconfig.json | 140 ++++++++++++++---------------- 9 files changed, 155 insertions(+), 168 deletions(-) delete mode 100644 babel.config.json rename src/{Path.ts => SchemaPath.ts} (100%) diff --git a/babel.config.json b/babel.config.json deleted file mode 100644 index 776ea02..0000000 --- a/babel.config.json +++ /dev/null @@ -1,14 +0,0 @@ -{ - "presets": [ - [ - "@babel/env", - { - "targets": { - "node": "current" - } - } - ], - "@babel/typescript" - ], - "sourceMaps": true -} \ No newline at end of file diff --git a/jest.config.js b/jest.config.js index c9cdd91..b565ad7 100644 --- a/jest.config.js +++ b/jest.config.js @@ -1,6 +1,6 @@ module.exports = { testEnvironment: `node`, - testMatch: [`**/*.test.js`], + testMatch: [`**/*.test.ts`], maxConcurrency: 30, testTimeout: 240000, }; diff --git a/package.json b/package.json index 485b4d5..69261ab 100644 --- a/package.json +++ b/package.json @@ -1,73 +1,73 @@ { - "name": "fastify-zod", - "version": "1.3.2", - "description": "Zod integration with Fastify", - "main": "build/index.js", - "scripts": { - "check:types": "tsc -p . --noEmit", - "check:lint": "eslint src", - "check": "npm run check:types && npm run check:lint", - "clean": "rm -rf build", - "build:types": "tsc -p . --emitDeclarationOnly", - "build:babel": "babel src --out-dir build --extensions '.ts' --source-maps", - "build:openapi-spec": "node build/__tests__/generate-spec.fixtures.js", - "build:openapi-client": "rm -rf test-openapi-client && openapi-generator-cli generate && cd test-openapi-client && npm i", - "build": "npm run clean && npm run build:babel && npm run build:openapi-spec && npm run build:openapi-client && npm run build:types", - "test": "jest" - }, - "repository": { - "type": "git", - "url": "git+https://github.com/elierotenberg/fastify-zod.git" - }, - "keywords": [ - "zod", - "fastify", - "openapi" - ], - "author": "Elie Rotenberg ", - "license": "MIT", - "bugs": { - "url": "https://github.com/elierotenberg/fastify-zod/issues" - }, - "homepage": "https://github.com/elierotenberg/fastify-zod#readme", - "devDependencies": { - "@babel/cli": "^7.21.0", - "@babel/core": "^7.21.4", - "@babel/preset-env": "^7.21.4", - "@babel/preset-typescript": "^7.21.4", - "@openapitools/openapi-generator-cli": "^2.5.2", - "@types/http-errors": "^2.0.1", - "@types/jest": "^29.5.0", - "@types/node": "^18.15.11", - "@typescript-eslint/eslint-plugin": "^5.57.1", - "@typescript-eslint/parser": "^5.57.1", - "ajv-errors": "^3.0.0", - "eslint": "^8.38.0", - "eslint-config-prettier": "^8.8.0", - "eslint-plugin-import": "^2.27.5", - "eslint-plugin-prettier": "^4.2.1", - "fastify": "^4.15.0", - "fastify-zod-test-openapi-client": "file:test-openapi-client", - "http-errors": "^2.0.0", - "jest": "^29.5.0", - "node-fetch": "^3.3.1", - "pino-pretty": "^10.0.0", - "prettier": "^2.8.7", - "typed-jest-expect": "^1.0.1", - "typescript": "^5.0.4" - }, - "peerDependencies": { - "fastify": "^4.15.0" - }, - "dependencies": { - "@fastify/swagger": "^8.3.1", - "@fastify/swagger-ui": "^1.7.0", - "@types/js-yaml": "^4.0.5", - "change-case": "^4.1.2", - "fast-deep-equal": "^3.1.3", - "js-yaml": "^4.1.0", - "tslib": "^2.5.0", - "zod": "^3.21.4", - "zod-to-json-schema": "^3.20.4" - } -} \ No newline at end of file + "name": "fastify-zod", + "version": "1.3.2", + "description": "Zod integration with Fastify", + "main": "build/index.js", + "scripts": { + "check:types": "tsc -p . --noEmit", + "check:lint": "eslint src", + "check": "npm run check:types && npm run check:lint", + "clean": "rimraf build", + "build:ts": "tsc -p . ", + "build:openapi-spec": "node build/__tests__/generate-spec.fixtures.js", + "build:openapi-client": "rimraf test-openapi-client && openapi-generator-cli generate && cd test-openapi-client && npm i", + "build": "npm run clean && npm run build:ts && npm run build:openapi-spec && npm run build:openapi-client", + "test": "jest --verbose" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/elierotenberg/fastify-zod.git" + }, + "keywords": [ + "zod", + "fastify", + "openapi" + ], + "author": "Elie Rotenberg ", + "license": "MIT", + "bugs": { + "url": "https://github.com/elierotenberg/fastify-zod/issues" + }, + "homepage": "https://github.com/elierotenberg/fastify-zod#readme", + "devDependencies": { + "@babel/cli": "^7.21.0", + "@babel/core": "^7.21.4", + "@babel/preset-env": "^7.21.4", + "@babel/preset-typescript": "^7.21.4", + "@openapitools/openapi-generator-cli": "^2.5.2", + "@types/http-errors": "^2.0.1", + "@types/jest": "^29.5.0", + "@types/node": "^18.15.11", + "@typescript-eslint/eslint-plugin": "^5.57.1", + "@typescript-eslint/parser": "^5.57.1", + "ajv-errors": "^3.0.0", + "eslint": "^8.38.0", + "eslint-config-prettier": "^8.8.0", + "eslint-plugin-import": "^2.27.5", + "eslint-plugin-prettier": "^4.2.1", + "fastify": "^4.15.0", + "fastify-zod-test-openapi-client": "file:test-openapi-client", + "http-errors": "^2.0.0", + "jest": "^29.5.0", + "node-fetch": "^3.3.1", + "pino-pretty": "^10.0.0", + "prettier": "^2.8.7", + "rimraf": "^5.0.0", + "typed-jest-expect": "^1.0.1", + "typescript": "^5.0.4" + }, + "peerDependencies": { + "fastify": "^4.15.0" + }, + "dependencies": { + "@fastify/swagger": "^8.3.1", + "@fastify/swagger-ui": "^1.7.0", + "@types/js-yaml": "^4.0.5", + "change-case": "^4.1.2", + "fast-deep-equal": "^3.1.3", + "js-yaml": "^4.1.0", + "tslib": "^2.5.0", + "zod": "^3.21.4", + "zod-to-json-schema": "^3.20.4" + } +} diff --git a/src/FastifyZod.ts b/src/FastifyZod.ts index 484af32..d25b621 100644 --- a/src/FastifyZod.ts +++ b/src/FastifyZod.ts @@ -274,6 +274,7 @@ export const register = async ( response, querystring, handler, + nativeRouteOptions, hide, deprecated, tags, @@ -283,7 +284,6 @@ export const register = async ( produces, externalDocs, security, - nativeRouteOptions, }: RouteConfig): void => { const customSchema: FastifySchema = {}; if (operationId) { diff --git a/src/Path.ts b/src/SchemaPath.ts similarity index 100% rename from src/Path.ts rename to src/SchemaPath.ts diff --git a/src/SpecTransformer.ts b/src/SpecTransformer.ts index 56ecf2e..2d06aec 100644 --- a/src/SpecTransformer.ts +++ b/src/SpecTransformer.ts @@ -12,7 +12,7 @@ import { matchPathPrefix, replacePathPrefix, setAtPath, -} from "./Path"; +} from "./SchemaPath"; import { findFirstDeep, isRecord, visitDeep } from "./util"; type Ref = { diff --git a/src/__tests__/FastifyZod.test.ts b/src/__tests__/FastifyZod.test.ts index 70b132f..f8a86bb 100644 --- a/src/__tests__/FastifyZod.test.ts +++ b/src/__tests__/FastifyZod.test.ts @@ -1,3 +1,5 @@ +import { log } from "console"; + import { buildJsonSchemas } from "../JsonSchema"; import { models } from "./models.fixtures"; @@ -139,4 +141,13 @@ test(`FastifyZod`, async () => { message: `params/id invalid todo item id`, statusCode: 400, }); + + await expect( + f + .inject({ + method: `get`, + url: `/native-options`, + }) + .then((res) => res.statusCode), + ).resolves.toEqual(418); }); diff --git a/src/__tests__/server.fixtures.ts b/src/__tests__/server.fixtures.ts index ecd3397..eccd288 100644 --- a/src/__tests__/server.fixtures.ts +++ b/src/__tests__/server.fixtures.ts @@ -139,18 +139,16 @@ export const createTestServer = async ( `/native-options`, { operationId: `nativeOptions`, - response: { - 418: `Teapot`, - }, nativeRouteOptions: { preHandler: [ - (_, res) => { + (_, res, next) => { res.code(418); + next(); }, ], }, }, - async () => `Oh, it's Teatime`, + async (_, res) => res.send(), ); return f; diff --git a/tsconfig.json b/tsconfig.json index 2c464eb..a7ef026 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,75 +1,67 @@ { - "compilerOptions": { - /* Basic Options */ - "target": "es2019" /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019' or 'ESNEXT'. */, - "module": "commonjs" /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', or 'ESNext'. */, - "lib": [ - "es2019", - "ESNext", - "DOM", // for Fetch API - ] /* Specify library files to be included in the compilation. */, - // "allowJs": true, /* Allow javascript files to be compiled. */ - // "checkJs": true /* Report errors in .js files. */, - "jsx": "preserve" /* Specify JSX code generation: 'preserve', 'react-native', or 'react'. */, - "declaration": true /* Generates corresponding '.d.ts' file. */, - // "declarationMap": true /* Generates a sourcemap for each corresponding '.d.ts' file. */, - "sourceMap": true /* Generates corresponding '.map' file. */, - // "outFile": "./", /* Concatenate and emit output to single file. */ - "outDir": "build" /* Redirect output structure to the directory. */, - // "rootDir": "src" /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */, - "rootDirs": [ - "src" - ], - // "composite": true /* Enable project compilation */, - // "incremental": true /* Enable incremental compilation */, - // "tsBuildInfoFile": "./.tsbuildinfo", /* Specify file to store incremental compilation information */ - // "removeComments": true, /* Do not emit comments to output. */ - // "noEmit": true, /* Do not emit outputs. */ - "importHelpers": true /* Import emit helpers from 'tslib'. */, - // "downlevelIteration": true, /* Provide full support for iterables in 'for-of', spread, and destructuring when targeting 'ES5' or 'ES3'. */ - "isolatedModules": true /* Transpile each file as a separate module (similar to 'ts.transpileModule'). */, - /* Strict Type-Checking Options */ - "strict": true /* Enable all strict type-checking options. */, - // "noImplicitAny": true, /* Raise error on expressions and declarations with an implied 'any' type. */ - // "strictNullChecks": true, /* Enable strict null checks. */ - // "strictFunctionTypes": true, /* Enable strict checking of function types. */ - // "strictBindCallApply": true, /* Enable strict 'bind', 'call', and 'apply' methods on functions. */ - // "strictPropertyInitialization": true, /* Enable strict checking of property initialization in classes. */ - // "noImplicitThis": true, /* Raise error on 'this' expressions with an implied 'any' type. */ - // "alwaysStrict": true, /* Parse in strict mode and emit "use strict" for each source file. */ - /* Additional Checks */ - "noUnusedLocals": true /* Report errors on unused locals. */, - "noUnusedParameters": true /* Report errors on unused parameters. */, - "noImplicitReturns": true /* Report error when not all code paths in function return a value. */, - "noFallthroughCasesInSwitch": true /* Report errors for fallthrough cases in switch statement. */, - "useUnknownInCatchVariables": false, - /* Module Resolution Options */ - "moduleResolution": "node" /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */, - "baseUrl": "src" /* Base directory to resolve non-absolute module names. */, - // "paths": {}, /* A series of entries which re-map imports to lookup locations relative to the 'baseUrl'. */ - // "rootDirs": [], /* List of root folders whose combined content represents the structure of the project at runtime. */ - // "typeRoots": [], /* List of folders to include type definitions from. */ - // "types": [], /* Type declaration files to be included in compilation. */ - "allowSyntheticDefaultImports": true /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */, - "esModuleInterop": true /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */, - "noErrorTruncation": true, - // "preserveSymlinks": true, /* Do not resolve the real path of symlinks. */ - /* Source Map Options */ - // "sourceRoot": "", /* Specify the location where debugger should locate TypeScript files instead of source locations. */ - // "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */ - // "inlineSourceMap": true, /* Emit a single file with source maps instead of having a separate file. */ - // "inlineSources": true, /* Emit the source alongside the sourcemaps within a single file; requires '--inlineSourceMap' or '--sourceMap' to be set. */ - /* Experimental Options */ - "experimentalDecorators": true /* Enables experimental support for ES7 decorators. */, - "emitDecoratorMetadata": true /* Enables experimental support for emitting type metadata for decorators. */ - }, - "include": [ - "src/**/*.ts", - "src/**/*.d.ts" - ], - "exclude": [ - "node_modules", - "build" - ], - "compileOnSave": true -} \ No newline at end of file + "compilerOptions": { + /* Basic Options */ + "target": "es2019" /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019' or 'ESNEXT'. */, + "module": "commonjs" /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', or 'ESNext'. */, + "lib": [ + "es2019", + "ESNext", + "DOM" // for Fetch API + ] /* Specify library files to be included in the compilation. */, + // "allowJs": true, /* Allow javascript files to be compiled. */ + // "checkJs": true /* Report errors in .js files. */, + "jsx": "preserve" /* Specify JSX code generation: 'preserve', 'react-native', or 'react'. */, + "declaration": true /* Generates corresponding '.d.ts' file. */, + // "declarationMap": true /* Generates a sourcemap for each corresponding '.d.ts' file. */, + "sourceMap": true /* Generates corresponding '.map' file. */, + // "outFile": "./", /* Concatenate and emit output to single file. */ + "outDir": "build" /* Redirect output structure to the directory. */, + // "rootDir": "src" /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */, + "rootDirs": ["src"], + // "composite": true /* Enable project compilation */, + // "incremental": true /* Enable incremental compilation */, + // "tsBuildInfoFile": "./.tsbuildinfo", /* Specify file to store incremental compilation information */ + // "removeComments": true, /* Do not emit comments to output. */ + // "noEmit": true, /* Do not emit outputs. */ + "importHelpers": true /* Import emit helpers from 'tslib'. */, + // "downlevelIteration": true, /* Provide full support for iterables in 'for-of', spread, and destructuring when targeting 'ES5' or 'ES3'. */ + "isolatedModules": true /* Transpile each file as a separate module (similar to 'ts.transpileModule'). */, + /* Strict Type-Checking Options */ + "strict": true /* Enable all strict type-checking options. */, + // "noImplicitAny": true, /* Raise error on expressions and declarations with an implied 'any' type. */ + // "strictNullChecks": true, /* Enable strict null checks. */ + // "strictFunctionTypes": true, /* Enable strict checking of function types. */ + // "strictBindCallApply": true, /* Enable strict 'bind', 'call', and 'apply' methods on functions. */ + // "strictPropertyInitialization": true, /* Enable strict checking of property initialization in classes. */ + // "noImplicitThis": true, /* Raise error on 'this' expressions with an implied 'any' type. */ + // "alwaysStrict": true, /* Parse in strict mode and emit "use strict" for each source file. */ + /* Additional Checks */ + "noUnusedLocals": true /* Report errors on unused locals. */, + "noUnusedParameters": true /* Report errors on unused parameters. */, + "noImplicitReturns": true /* Report error when not all code paths in function return a value. */, + "noFallthroughCasesInSwitch": true /* Report errors for fallthrough cases in switch statement. */, + "useUnknownInCatchVariables": false, + /* Module Resolution Options */ + "moduleResolution": "node" /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */, + "baseUrl": "src" /* Base directory to resolve non-absolute module names. */, + // "paths": {}, /* A series of entries which re-map imports to lookup locations relative to the 'baseUrl'. */ + // "rootDirs": [], /* List of root folders whose combined content represents the structure of the project at runtime. */ + // "typeRoots": [], /* List of folders to include type definitions from. */ + // "types": [], /* Type declaration files to be included in compilation. */ + "allowSyntheticDefaultImports": true /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */, + "esModuleInterop": true /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */, + "noErrorTruncation": true, + // "preserveSymlinks": true, /* Do not resolve the real path of symlinks. */ + /* Source Map Options */ + // "sourceRoot": "", /* Specify the location where debugger should locate TypeScript files instead of source locations. */ + // "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */ + // "inlineSourceMap": true, /* Emit a single file with source maps instead of having a separate file. */ + // "inlineSources": true, /* Emit the source alongside the sourcemaps within a single file; requires '--inlineSourceMap' or '--sourceMap' to be set. */ + /* Experimental Options */ + "experimentalDecorators": true /* Enables experimental support for ES7 decorators. */, + "emitDecoratorMetadata": true /* Enables experimental support for emitting type metadata for decorators. */ + }, + "include": ["src/**/*.ts", "src/**/*.d.ts"], + "exclude": ["node_modules", "build"], + "compileOnSave": true +} From da327a93cd68a06916d05cbdc124349ca36f5941 Mon Sep 17 00:00:00 2001 From: Leif Niemczik Date: Sun, 6 Aug 2023 01:05:24 +0200 Subject: [PATCH 3/3] built files for my thesis --- openapi.original.json | 19 ++- openapi.transformed.json | 10 ++ openapi.transformed.yml | 6 + src/__tests__/FastifyZod.test.ts | 282 +++++++++++++++---------------- 4 files changed, 174 insertions(+), 143 deletions(-) diff --git a/openapi.original.json b/openapi.original.json index 57176cb..e598771 100644 --- a/openapi.original.json +++ b/openapi.original.json @@ -123,6 +123,12 @@ "enum": [ 42 ] + }, + "Teapot": { + "type": "string", + "enum": [ + "Oh, it's Teatime" + ] } }, "required": [ @@ -132,7 +138,8 @@ "TodoItemNotFoundError", "TodoItems", "TodoItemsGroupedByStatus", - "FortyTwo" + "FortyTwo", + "Teapot" ], "additionalProperties": false } @@ -305,6 +312,16 @@ } } } + }, + "/native-options": { + "get": { + "operationId": "nativeOptions", + "responses": { + "200": { + "description": "Default Response" + } + } + } } } } \ No newline at end of file diff --git a/openapi.transformed.json b/openapi.transformed.json index bfd1dda..977d463 100644 --- a/openapi.transformed.json +++ b/openapi.transformed.json @@ -302,6 +302,16 @@ } } } + }, + "/native-options": { + "get": { + "operationId": "nativeOptions", + "responses": { + "200": { + "description": "Default Response" + } + } + } } } } \ No newline at end of file diff --git a/openapi.transformed.yml b/openapi.transformed.yml index 2ba102d..30f2e94 100644 --- a/openapi.transformed.yml +++ b/openapi.transformed.yml @@ -192,3 +192,9 @@ paths: application/json: schema: $ref: '#/components/schemas/Schema_FortyTwo' + /native-options: + get: + operationId: nativeOptions + responses: + '200': + description: Default Response diff --git a/src/__tests__/FastifyZod.test.ts b/src/__tests__/FastifyZod.test.ts index f8a86bb..907ae95 100644 --- a/src/__tests__/FastifyZod.test.ts +++ b/src/__tests__/FastifyZod.test.ts @@ -1,153 +1,151 @@ -import { log } from "console"; +import { buildJsonSchemas } from "../JsonSchema" -import { buildJsonSchemas } from "../JsonSchema"; - -import { models } from "./models.fixtures"; -import { createTestServer } from "./server.fixtures"; +import { models } from "./models.fixtures" +import { createTestServer } from "./server.fixtures" test(`FastifyZod`, async () => { - const jsonSchemas = buildJsonSchemas(models, { errorMessages: true }); - const f = await createTestServer( - { - ajv: { - customOptions: { - allErrors: true, - }, - plugins: [require(`ajv-errors`)], - }, - }, - { - jsonSchemas, - transformSpec: { - options: { - mergeRefs: [jsonSchemas.$ref(`TodoState`)], - }, - }, - }, - ); + const jsonSchemas = buildJsonSchemas(models, { errorMessages: true }) + const f = await createTestServer( + { + ajv: { + customOptions: { + allErrors: true, + }, + plugins: [require(`ajv-errors`)], + }, + }, + { + jsonSchemas, + transformSpec: { + options: { + mergeRefs: [jsonSchemas.$ref(`TodoState`)], + }, + }, + } + ) - await expect( - f - .inject({ - method: `get`, - url: `/item`, - }) - .then((res) => res.json()), - ).resolves.toEqual({ todoItems: [] }); + await expect( + f + .inject({ + method: `get`, + url: `/item`, + }) + .then(res => res.json()) + ).resolves.toEqual({ todoItems: [] }) - await expect( - f - .inject({ - method: `post`, - url: `/item`, - payload: {}, - }) - .then((res) => res.json()), - ).resolves.toEqual({ - error: `Bad Request`, - message: `body must have required property 'id', body must have required property 'label', body must have required property 'state'`, - statusCode: 400, - }); + await expect( + f + .inject({ + method: `post`, + url: `/item`, + payload: {}, + }) + .then(res => res.json()) + ).resolves.toEqual({ + error: `Bad Request`, + message: `body must have required property 'id', body must have required property 'label', body must have required property 'state'`, + statusCode: 400, + }) - await expect( - f - .inject({ - method: `post`, - url: `/item`, - payload: { - id: 1337, - label: `todo`, - state: `todo`, - }, - }) - .then((res) => res.json()), - ).resolves.toEqual({ - error: `Bad Request`, - message: `body/id invalid todo item id`, - statusCode: 400, - }); + await expect( + f + .inject({ + method: `post`, + url: `/item`, + payload: { + id: 1337, + label: `todo`, + state: `todo`, + }, + }) + .then(res => res.json()) + ).resolves.toEqual({ + error: `Bad Request`, + message: `body/id invalid todo item id`, + statusCode: 400, + }) - await expect( - f - .inject({ - method: `get`, - url: `/item/e7f7082a-4f16-430d-8c3b-db6b8d4d3e73`, - }) - .then(async (response) => ({ - statusCode: response.statusCode, - body: await response.json(), - })), - ).resolves.toEqual({ - statusCode: 404, - body: { - id: `e7f7082a-4f16-430d-8c3b-db6b8d4d3e73`, - message: `item not found`, - }, - }); + await expect( + f + .inject({ + method: `get`, + url: `/item/e7f7082a-4f16-430d-8c3b-db6b8d4d3e73`, + }) + .then(async response => ({ + statusCode: response.statusCode, + body: await response.json(), + })) + ).resolves.toEqual({ + statusCode: 404, + body: { + id: `e7f7082a-4f16-430d-8c3b-db6b8d4d3e73`, + message: `item not found`, + }, + }) - await expect( - f - .inject({ - method: `post`, - url: `/item`, - payload: { - id: `e7f7082a-4f16-430d-8c3b-db6b8d4d3e73`, - label: `todo`, - state: `todo`, - dueDateMs: new Date(1337).getTime(), - }, - }) - .then((res) => res.json()), - ).resolves.toEqual({ - todoItems: [ - { - id: `e7f7082a-4f16-430d-8c3b-db6b8d4d3e73`, - label: `todo`, - state: `todo`, - dueDateMs: new Date(1337).getTime(), - }, - ], - }); + await expect( + f + .inject({ + method: `post`, + url: `/item`, + payload: { + id: `e7f7082a-4f16-430d-8c3b-db6b8d4d3e73`, + label: `todo`, + state: `todo`, + dueDateMs: new Date(1337).getTime(), + }, + }) + .then(res => res.json()) + ).resolves.toEqual({ + todoItems: [ + { + id: `e7f7082a-4f16-430d-8c3b-db6b8d4d3e73`, + label: `todo`, + state: `todo`, + dueDateMs: new Date(1337).getTime(), + }, + ], + }) - await expect( - f - .inject({ - method: `get`, - url: `/item/e7f7082a-4f16-430d-8c3b-db6b8d4d3e73`, - }) - .then(async (response) => ({ - statusCode: response.statusCode, - body: await response.json(), - })), - ).resolves.toEqual({ - statusCode: 200, - body: { - id: `e7f7082a-4f16-430d-8c3b-db6b8d4d3e73`, - label: `todo`, - state: `todo`, - dueDateMs: new Date(1337).getTime(), - }, - }); + await expect( + f + .inject({ + method: `get`, + url: `/item/e7f7082a-4f16-430d-8c3b-db6b8d4d3e73`, + }) + .then(async response => ({ + statusCode: response.statusCode, + body: await response.json(), + })) + ).resolves.toEqual({ + statusCode: 200, + body: { + id: `e7f7082a-4f16-430d-8c3b-db6b8d4d3e73`, + label: `todo`, + state: `todo`, + dueDateMs: new Date(1337).getTime(), + }, + }) - await expect( - f - .inject({ - method: `put`, - url: `/item/1337`, - }) - .then((res) => res.json()), - ).resolves.toEqual({ - error: `Bad Request`, - message: `params/id invalid todo item id`, - statusCode: 400, - }); + await expect( + f + .inject({ + method: `put`, + url: `/item/1337`, + }) + .then(res => res.json()) + ).resolves.toEqual({ + error: `Bad Request`, + message: `params/id invalid todo item id`, + statusCode: 400, + }) - await expect( - f - .inject({ - method: `get`, - url: `/native-options`, - }) - .then((res) => res.statusCode), - ).resolves.toEqual(418); -}); + await expect( + f + .inject({ + method: `get`, + url: `/native-options`, + }) + .then(res => res.statusCode) + ).resolves.toEqual(418) +})