@@ -1186,4 +1186,167 @@ describe("given a request that contains the differently cased path", () => {
11861186
11871187 expect ( response . body ) . toBe ( "ok" ) ;
11881188 } ) ;
1189+
1190+ describe ( "request validation" , ( ) => {
1191+ const openApiDocument : OpenApiDocument = {
1192+ paths : {
1193+ "/widgets" : {
1194+ post : {
1195+ parameters : [
1196+ { in : "query" , name : "required-query" , required : true } ,
1197+ { in : "query" , name : "optional-query" } ,
1198+ { in : "header" , name : "x-required-header" , required : true } ,
1199+ ] ,
1200+ requestBody : {
1201+ content : {
1202+ "application/json" : {
1203+ schema : {
1204+ properties : {
1205+ name : { type : "string" } ,
1206+ } ,
1207+ required : [ "name" ] ,
1208+ type : "object" ,
1209+ } ,
1210+ } ,
1211+ } ,
1212+ } ,
1213+ responses : {
1214+ 200 : {
1215+ content : { "text/plain" : { schema : { type : "string" } } } ,
1216+ } ,
1217+ } ,
1218+ } ,
1219+ } ,
1220+ } ,
1221+ } ;
1222+
1223+ function makeDispatcher ( validateRequests : boolean ) {
1224+ const registry = new Registry ( ) ;
1225+
1226+ registry . add ( "/widgets" , {
1227+ POST ( ) {
1228+ return { body : "ok" , status : 200 } ;
1229+ } ,
1230+ } ) ;
1231+
1232+ return new Dispatcher ( registry , new ContextRegistry ( ) , openApiDocument , {
1233+ adminApiToken : "" ,
1234+ alwaysFakeOptionals : false ,
1235+ basePath : "/" ,
1236+ buildCache : false ,
1237+ generate : { routes : false , types : false } ,
1238+ openApiPath : "" ,
1239+ port : 3100 ,
1240+ proxyPaths : new Map ( ) ,
1241+ proxyUrl : "" ,
1242+ routePrefix : "" ,
1243+ startAdminApi : false ,
1244+ startRepl : false ,
1245+ startServer : true ,
1246+ validateRequests,
1247+ watch : { routes : false , types : false } ,
1248+ } ) ;
1249+ }
1250+
1251+ it ( "returns 400 when a required query parameter is missing" , async ( ) => {
1252+ const dispatcher = makeDispatcher ( true ) ;
1253+
1254+ const response = await dispatcher . request ( {
1255+ body : { name : "sprocket" } ,
1256+ headers : { "x-required-header" : "yes" } ,
1257+ method : "POST" ,
1258+ path : "/widgets" ,
1259+ query : { } ,
1260+ req : { path : "/widgets" } ,
1261+ } ) ;
1262+
1263+ expect ( response . status ) . toBe ( 400 ) ;
1264+ expect ( response . body ) . toContain ( "required-query" ) ;
1265+ } ) ;
1266+
1267+ it ( "returns 400 when a required header is missing" , async ( ) => {
1268+ const dispatcher = makeDispatcher ( true ) ;
1269+
1270+ const response = await dispatcher . request ( {
1271+ body : { name : "sprocket" } ,
1272+ headers : { } ,
1273+ method : "POST" ,
1274+ path : "/widgets" ,
1275+ query : { "required-query" : "yes" } ,
1276+ req : { path : "/widgets" } ,
1277+ } ) ;
1278+
1279+ expect ( response . status ) . toBe ( 400 ) ;
1280+ expect ( response . body ) . toContain ( "x-required-header" ) ;
1281+ } ) ;
1282+
1283+ it ( "returns 400 when the request body is missing a required field" , async ( ) => {
1284+ const dispatcher = makeDispatcher ( true ) ;
1285+
1286+ const response = await dispatcher . request ( {
1287+ body : { } ,
1288+ headers : { "x-required-header" : "yes" } ,
1289+ method : "POST" ,
1290+ path : "/widgets" ,
1291+ query : { "required-query" : "yes" } ,
1292+ req : { path : "/widgets" } ,
1293+ } ) ;
1294+
1295+ expect ( response . status ) . toBe ( 400 ) ;
1296+ expect ( response . body ) . toContain ( "name" ) ;
1297+ } ) ;
1298+
1299+ it ( "returns 200 when all required parameters and body are valid" , async ( ) => {
1300+ const dispatcher = makeDispatcher ( true ) ;
1301+
1302+ const response = await dispatcher . request ( {
1303+ body : { name : "sprocket" } ,
1304+ headers : { "x-required-header" : "yes" } ,
1305+ method : "POST" ,
1306+ path : "/widgets" ,
1307+ query : { "required-query" : "yes" } ,
1308+ req : { path : "/widgets" } ,
1309+ } ) ;
1310+
1311+ expect ( response . status ) . toBe ( 200 ) ;
1312+ } ) ;
1313+
1314+ it ( "skips validation when validateRequests is false" , async ( ) => {
1315+ const dispatcher = makeDispatcher ( false ) ;
1316+
1317+ const response = await dispatcher . request ( {
1318+ body : { } ,
1319+ headers : { } ,
1320+ method : "POST" ,
1321+ path : "/widgets" ,
1322+ query : { } ,
1323+ req : { path : "/widgets" } ,
1324+ } ) ;
1325+
1326+ expect ( response . status ) . toBe ( 200 ) ;
1327+ } ) ;
1328+
1329+ it ( "skips validation when there is no OpenAPI document" , async ( ) => {
1330+ const registry = new Registry ( ) ;
1331+
1332+ registry . add ( "/widgets" , {
1333+ POST ( ) {
1334+ return { body : "ok" , status : 200 } ;
1335+ } ,
1336+ } ) ;
1337+
1338+ const dispatcher = new Dispatcher ( registry , new ContextRegistry ( ) ) ;
1339+
1340+ const response = await dispatcher . request ( {
1341+ body : { } ,
1342+ headers : { } ,
1343+ method : "POST" ,
1344+ path : "/widgets" ,
1345+ query : { } ,
1346+ req : { path : "/widgets" } ,
1347+ } ) ;
1348+
1349+ expect ( response . status ) . toBe ( 200 ) ;
1350+ } ) ;
1351+ } ) ;
11891352} ) ;
0 commit comments