@terreno/api 0.9.3 → 0.11.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (52) hide show
  1. package/bunfig.toml +5 -2
  2. package/bunfig.unit.toml +3 -0
  3. package/dist/auth.test.js +257 -0
  4. package/dist/consentApp.test.js +245 -0
  5. package/dist/expressServer.js +3 -9
  6. package/dist/expressServer.test.js +4 -7
  7. package/dist/githubAuth.test.js +380 -0
  8. package/dist/logger.test.d.ts +1 -0
  9. package/dist/logger.test.js +143 -0
  10. package/dist/notifiers/googleChatNotifier.test.js +37 -0
  11. package/dist/openApi.js +2 -2
  12. package/dist/openApi.test.js +125 -0
  13. package/dist/openApiBuilder.d.ts +1 -0
  14. package/dist/openApiBuilder.js +13 -2
  15. package/dist/openApiBuilder.test.js +66 -0
  16. package/dist/openApiEtag.test.js +8 -0
  17. package/dist/openApiValidator.test.js +309 -0
  18. package/dist/permissions.middleware.test.d.ts +1 -0
  19. package/dist/permissions.middleware.test.js +341 -0
  20. package/dist/plugins.d.ts +8 -8
  21. package/dist/plugins.js +38 -32
  22. package/dist/populate.test.js +99 -0
  23. package/dist/syncConsents.js +2 -2
  24. package/dist/syncConsents.test.js +273 -0
  25. package/dist/tests/bunSetup.js +27 -22
  26. package/dist/tests.d.ts +3 -3
  27. package/dist/tests.js +78 -82
  28. package/dist/utils.d.ts +2 -2
  29. package/dist/utils.js +7 -7
  30. package/package.json +2 -1
  31. package/src/__snapshots__/openApi.test.ts.snap +48 -0
  32. package/src/auth.test.ts +147 -0
  33. package/src/consentApp.test.ts +162 -0
  34. package/src/expressServer.test.ts +4 -11
  35. package/src/expressServer.ts +4 -8
  36. package/src/githubAuth.test.ts +307 -1
  37. package/src/logger.test.ts +149 -0
  38. package/src/notifiers/googleChatNotifier.test.ts +24 -0
  39. package/src/openApi.test.ts +157 -1
  40. package/src/openApi.ts +6 -2
  41. package/src/openApiBuilder.test.ts +81 -0
  42. package/src/openApiBuilder.ts +17 -2
  43. package/src/openApiEtag.test.ts +11 -0
  44. package/src/openApiValidator.test.ts +410 -0
  45. package/src/permissions.middleware.test.ts +197 -0
  46. package/src/plugins.ts +32 -23
  47. package/src/populate.test.ts +78 -2
  48. package/src/syncConsents.test.ts +145 -0
  49. package/src/syncConsents.ts +1 -1
  50. package/src/tests/bunSetup.ts +14 -8
  51. package/src/tests.ts +8 -8
  52. package/src/utils.ts +4 -4
@@ -71,9 +71,13 @@ var supertest_1 = __importDefault(require("supertest"));
71
71
  var api_1 = require("./api");
72
72
  var auth_1 = require("./auth");
73
73
  var expressServer_1 = require("./expressServer");
74
+ var openApi_1 = require("./openApi");
74
75
  var permissions_1 = require("./permissions");
75
76
  var tests_1 = require("./tests");
76
77
  function getMessageSummaryOpenApiMiddleware(options) {
78
+ if (!options.openApi) {
79
+ throw new Error("Expected openApi to be configured for test routes");
80
+ }
77
81
  return options.openApi.path({
78
82
  parameters: [
79
83
  {
@@ -447,3 +451,124 @@ function addRoutesPopulate(router, options) {
447
451
  });
448
452
  }); });
449
453
  });
454
+ (0, bun_test_1.describe)("openApi middleware no-op paths", function () {
455
+ (0, bun_test_1.it)("getOpenApiMiddleware returns noop when openApi not configured", function () {
456
+ var mw = (0, openApi_1.getOpenApiMiddleware)(tests_1.FoodModel, {});
457
+ (0, bun_test_1.expect)(typeof mw).toBe("function");
458
+ (0, bun_test_1.expect)(mw.length).toBe(3);
459
+ });
460
+ (0, bun_test_1.it)("getOpenApiMiddleware returns noop when read permissions are empty", function () {
461
+ var mw = (0, openApi_1.getOpenApiMiddleware)(tests_1.FoodModel, {
462
+ openApi: { component: function () { }, path: function () { return (function () { }); } },
463
+ permissions: {
464
+ create: [],
465
+ delete: [],
466
+ list: [],
467
+ read: [],
468
+ update: [],
469
+ },
470
+ });
471
+ (0, bun_test_1.expect)(typeof mw).toBe("function");
472
+ (0, bun_test_1.expect)(mw.length).toBe(3);
473
+ });
474
+ (0, bun_test_1.it)("listOpenApiMiddleware returns noop when openApi not configured", function () {
475
+ var mw = (0, openApi_1.listOpenApiMiddleware)(tests_1.FoodModel, {});
476
+ (0, bun_test_1.expect)(mw.length).toBe(3);
477
+ });
478
+ (0, bun_test_1.it)("listOpenApiMiddleware returns noop when list permissions are empty", function () {
479
+ var mw = (0, openApi_1.listOpenApiMiddleware)(tests_1.FoodModel, {
480
+ openApi: { path: function () { return (function () { }); } },
481
+ permissions: {
482
+ create: [],
483
+ delete: [],
484
+ list: [],
485
+ read: [],
486
+ update: [],
487
+ },
488
+ });
489
+ (0, bun_test_1.expect)(mw.length).toBe(3);
490
+ });
491
+ (0, bun_test_1.it)("createOpenApiMiddleware returns noop when openApi not configured", function () {
492
+ var mw = (0, openApi_1.createOpenApiMiddleware)(tests_1.FoodModel, {});
493
+ (0, bun_test_1.expect)(mw.length).toBe(3);
494
+ });
495
+ (0, bun_test_1.it)("createOpenApiMiddleware returns noop when create permissions are empty", function () {
496
+ var mw = (0, openApi_1.createOpenApiMiddleware)(tests_1.FoodModel, {
497
+ openApi: { path: function () { return (function () { }); } },
498
+ permissions: {
499
+ create: [],
500
+ delete: [],
501
+ list: [],
502
+ read: [],
503
+ update: [],
504
+ },
505
+ });
506
+ (0, bun_test_1.expect)(mw.length).toBe(3);
507
+ });
508
+ (0, bun_test_1.it)("patchOpenApiMiddleware returns noop when openApi not configured", function () {
509
+ var mw = (0, openApi_1.patchOpenApiMiddleware)(tests_1.FoodModel, {});
510
+ (0, bun_test_1.expect)(mw.length).toBe(3);
511
+ });
512
+ (0, bun_test_1.it)("patchOpenApiMiddleware returns noop when update permissions are empty", function () {
513
+ var mw = (0, openApi_1.patchOpenApiMiddleware)(tests_1.FoodModel, {
514
+ openApi: { path: function () { return (function () { }); } },
515
+ permissions: {
516
+ create: [],
517
+ delete: [],
518
+ list: [],
519
+ read: [],
520
+ update: [],
521
+ },
522
+ });
523
+ (0, bun_test_1.expect)(mw.length).toBe(3);
524
+ });
525
+ (0, bun_test_1.it)("deleteOpenApiMiddleware returns noop when openApi not configured", function () {
526
+ var mw = (0, openApi_1.deleteOpenApiMiddleware)(tests_1.FoodModel, {});
527
+ (0, bun_test_1.expect)(mw.length).toBe(3);
528
+ });
529
+ (0, bun_test_1.it)("deleteOpenApiMiddleware returns noop when delete permissions are empty", function () {
530
+ var mw = (0, openApi_1.deleteOpenApiMiddleware)(tests_1.FoodModel, {
531
+ openApi: { path: function () { return (function () { }); } },
532
+ permissions: {
533
+ create: [],
534
+ delete: [],
535
+ list: [],
536
+ read: [],
537
+ update: [],
538
+ },
539
+ });
540
+ (0, bun_test_1.expect)(mw.length).toBe(3);
541
+ });
542
+ (0, bun_test_1.it)("readOpenApiMiddleware returns noop when openApi not configured", function () {
543
+ var mw = (0, openApi_1.readOpenApiMiddleware)({}, {}, [], []);
544
+ (0, bun_test_1.expect)(mw.length).toBe(3);
545
+ });
546
+ (0, bun_test_1.it)("readOpenApiMiddleware returns noop when read permissions are empty", function () {
547
+ var mw = (0, openApi_1.readOpenApiMiddleware)({
548
+ openApi: { path: function () { return (function () { }); } },
549
+ permissions: {
550
+ create: [],
551
+ delete: [],
552
+ list: [],
553
+ read: [],
554
+ update: [],
555
+ },
556
+ }, { id: { type: "string" } }, ["id"], []);
557
+ (0, bun_test_1.expect)(mw.length).toBe(3);
558
+ });
559
+ (0, bun_test_1.it)("readOpenApiMiddleware returns middleware when configured with permissions", function () {
560
+ // Use a simple path stub that returns a middleware function
561
+ var pathFn = function () { return (function (_req, _res, next) { return next(); }); };
562
+ var mw = (0, openApi_1.readOpenApiMiddleware)({
563
+ openApi: { path: pathFn },
564
+ permissions: {
565
+ create: [],
566
+ delete: [],
567
+ list: [],
568
+ read: [permissions_1.Permissions.IsAny],
569
+ update: [],
570
+ },
571
+ }, { name: { type: "string" } }, ["name"], [{ in: "query", name: "search", schema: { type: "string" } }]);
572
+ (0, bun_test_1.expect)(typeof mw).toBe("function");
573
+ });
574
+ });
@@ -210,6 +210,7 @@ export declare class OpenApiMiddlewareBuilder {
210
210
  * @param options - Router options containing the OpenAPI path configuration
211
211
  */
212
212
  constructor(options: Partial<ModelRouterOptions<unknown>>);
213
+ private describeRoute;
213
214
  /**
214
215
  * Sets the tags for the OpenAPI operation.
215
216
  *
@@ -108,6 +108,17 @@ var OpenApiMiddlewareBuilder = /** @class */ (function () {
108
108
  };
109
109
  this.validationConfig = {};
110
110
  }
111
+ OpenApiMiddlewareBuilder.prototype.describeRoute = function () {
112
+ var _c;
113
+ var parts = [];
114
+ if (this.config.summary) {
115
+ parts.push("\"".concat(this.config.summary, "\""));
116
+ }
117
+ if ((_c = this.config.tags) === null || _c === void 0 ? void 0 : _c.length) {
118
+ parts.push("tags=[".concat(this.config.tags.join(", "), "]"));
119
+ }
120
+ return parts.length > 0 ? parts.join(" ") : "unnamed route";
121
+ };
111
122
  /**
112
123
  * Sets the tags for the OpenAPI operation.
113
124
  *
@@ -423,7 +434,7 @@ var OpenApiMiddlewareBuilder = /** @class */ (function () {
423
434
  openApiMiddleware = this.options.openApi.path((0, merge_1.default)(__assign(__assign({}, this.config), { responses: __assign(__assign({}, this.config.responses), openApi_1.defaultOpenApiErrorResponses) }), (_e = (_d = this.options.openApiOverwrite) === null || _d === void 0 ? void 0 : _d.get) !== null && _e !== void 0 ? _e : {}));
424
435
  }
425
436
  else {
426
- logger_1.logger.debug("No options.openApi provided, skipping OpenApiMiddleware");
437
+ logger_1.logger.debug("No options.openApi provided in buildWithSchemas for ".concat(this.describeRoute(), ", skipping OpenApiMiddleware"));
427
438
  }
428
439
  var globalConfig = (0, openApiValidator_1.getOpenApiValidatorConfig)();
429
440
  var validationEnabled = (_f = this.validationConfig.enabled) !== null && _f !== void 0 ? _f : ((0, openApiValidator_1.isOpenApiValidatorConfigured)() && ((_g = globalConfig.validateRequests) !== null && _g !== void 0 ? _g : false));
@@ -468,7 +479,7 @@ var OpenApiMiddlewareBuilder = /** @class */ (function () {
468
479
  openApiMiddleware = this.options.openApi.path((0, merge_1.default)(__assign(__assign({}, this.config), { responses: __assign(__assign({}, this.config.responses), openApi_1.defaultOpenApiErrorResponses) }), (_e = (_d = this.options.openApiOverwrite) === null || _d === void 0 ? void 0 : _d.get) !== null && _e !== void 0 ? _e : {}));
469
480
  }
470
481
  else {
471
- logger_1.logger.debug("No options.openApi provided, skipping OpenApiMiddleware");
482
+ logger_1.logger.debug("No options.openApi provided in build for ".concat(this.describeRoute(), ", skipping OpenApiMiddleware"));
472
483
  }
473
484
  // Check if validation should be enabled
474
485
  var globalConfig = (0, openApiValidator_1.getOpenApiValidatorConfig)();
@@ -507,3 +507,69 @@ function addRoutesWithBuilder(router, options) {
507
507
  (0, bun_test_1.expect)(builder).toBeInstanceOf(openApiBuilder_1.OpenApiMiddlewareBuilder);
508
508
  });
509
509
  });
510
+ (0, bun_test_1.describe)("OpenApiMiddlewareBuilder withValidation / buildWithSchemas", function () {
511
+ (0, bun_test_1.beforeEach)(function () {
512
+ var resetOpenApiValidatorConfig = require("./openApiValidator").resetOpenApiValidatorConfig;
513
+ resetOpenApiValidatorConfig();
514
+ });
515
+ (0, bun_test_1.it)("buildWithSchemas returns bodySchema and querySchema", function () {
516
+ var _a, _b;
517
+ var result = (0, openApiBuilder_1.createOpenApiBuilder)({})
518
+ .withRequestBody({ name: { required: true, type: "string" } })
519
+ .withQueryParameter("page", { type: "number" })
520
+ .buildWithSchemas();
521
+ (0, bun_test_1.expect)(result.bodySchema).toBeDefined();
522
+ (0, bun_test_1.expect)((_a = result.bodySchema) === null || _a === void 0 ? void 0 : _a.name).toBeDefined();
523
+ (0, bun_test_1.expect)(result.querySchema).toBeDefined();
524
+ (0, bun_test_1.expect)((_b = result.querySchema) === null || _b === void 0 ? void 0 : _b.page).toBeDefined();
525
+ (0, bun_test_1.expect)(typeof result.middleware).toBe("function");
526
+ });
527
+ (0, bun_test_1.it)("buildWithSchemas returns undefined schemas when no body/query defined", function () {
528
+ var result = (0, openApiBuilder_1.createOpenApiBuilder)({}).buildWithSchemas();
529
+ (0, bun_test_1.expect)(result.bodySchema).toBeUndefined();
530
+ (0, bun_test_1.expect)(result.querySchema).toBeUndefined();
531
+ });
532
+ (0, bun_test_1.it)("withValidation with defaults enables body and query validation", function () {
533
+ var result = (0, openApiBuilder_1.createOpenApiBuilder)({})
534
+ .withRequestBody({ name: { required: true, type: "string" } })
535
+ .withQueryParameter("page", { type: "number" })
536
+ .withValidation()
537
+ .buildWithSchemas();
538
+ (0, bun_test_1.expect)(result.validationEnabled).toBe(true);
539
+ });
540
+ (0, bun_test_1.it)("withValidation with enabled=false disables validation", function () {
541
+ var result = (0, openApiBuilder_1.createOpenApiBuilder)({})
542
+ .withRequestBody({ name: { required: true, type: "string" } })
543
+ .withValidation({ enabled: false })
544
+ .buildWithSchemas();
545
+ (0, bun_test_1.expect)(result.validationEnabled).toBe(false);
546
+ });
547
+ (0, bun_test_1.it)("build() returns an array with validators when validation is enabled", function () {
548
+ var result = (0, openApiBuilder_1.createOpenApiBuilder)({})
549
+ .withRequestBody({ name: { required: true, type: "string" } })
550
+ .withQueryParameter("page", { type: "number" })
551
+ .withValidation()
552
+ .build();
553
+ (0, bun_test_1.expect)(Array.isArray(result)).toBe(true);
554
+ (0, bun_test_1.expect)(result.length).toBeGreaterThan(1);
555
+ });
556
+ (0, bun_test_1.it)("build() returns a single middleware when validation is disabled", function () {
557
+ var result = (0, openApiBuilder_1.createOpenApiBuilder)({})
558
+ .withRequestBody({ name: { required: true, type: "string" } })
559
+ .build();
560
+ (0, bun_test_1.expect)(typeof result).toBe("function");
561
+ });
562
+ (0, bun_test_1.it)("build() falls back to single middleware when there is nothing to validate", function () {
563
+ var result = (0, openApiBuilder_1.createOpenApiBuilder)({}).withValidation().build();
564
+ (0, bun_test_1.expect)(typeof result).toBe("function");
565
+ });
566
+ (0, bun_test_1.it)("withValidation options body=false and query=false is respected", function () {
567
+ var result = (0, openApiBuilder_1.createOpenApiBuilder)({})
568
+ .withRequestBody({ name: { required: true, type: "string" } })
569
+ .withQueryParameter("page", { type: "number" })
570
+ .withValidation({ body: false, query: false })
571
+ .build();
572
+ // No body/query validation = just the openApi middleware (single fn)
573
+ (0, bun_test_1.expect)(typeof result).toBe("function");
574
+ });
575
+ });
@@ -45,6 +45,14 @@ var buildResponse = function () {
45
45
  (0, bun_test_1.expect)(next).toHaveBeenCalledTimes(1);
46
46
  (0, bun_test_1.expect)(res.json).toBe(originalJson);
47
47
  });
48
+ (0, bun_test_1.it)("skips GET requests for non-openapi.json paths", function () {
49
+ var req = buildRequest({ method: "GET", path: "/health" });
50
+ var _a = buildResponse(), res = _a.res, originalJson = _a.originalJson;
51
+ var next = (0, bun_test_1.mock)(function () { });
52
+ (0, openApiEtag_1.openApiEtagMiddleware)(req, res, next);
53
+ (0, bun_test_1.expect)(next).toHaveBeenCalledTimes(1);
54
+ (0, bun_test_1.expect)(res.json).toBe(originalJson);
55
+ });
48
56
  (0, bun_test_1.it)("sets ETag and returns json body when no matching If-None-Match header is provided", function () {
49
57
  var req = buildRequest();
50
58
  var _a = buildResponse(), res = _a.res, originalJson = _a.originalJson, set = _a.set, status = _a.status, end = _a.end;
@@ -342,5 +342,314 @@ var setupFreshApp = function () { return __awaiter(void 0, void 0, void 0, funct
342
342
  middleware(req, res, function () { });
343
343
  }).toThrow();
344
344
  });
345
+ (0, bun_test_1.it)("calls onError callback instead of throwing when provided at option level", function () {
346
+ (0, openApiValidator_1.configureOpenApiValidator)();
347
+ var capturedErrors = [];
348
+ var middleware = (0, openApiValidator_1.validateRequestBody)({ name: { required: true, type: "string" } }, {
349
+ onError: function (errors) {
350
+ capturedErrors = errors;
351
+ },
352
+ });
353
+ var nextCalled = false;
354
+ var req = { body: {}, method: "POST", path: "/test" };
355
+ var res = {};
356
+ middleware(req, res, function () {
357
+ nextCalled = true;
358
+ });
359
+ (0, bun_test_1.expect)(nextCalled).toBe(true);
360
+ (0, bun_test_1.expect)(capturedErrors.length).toBeGreaterThan(0);
361
+ });
362
+ (0, bun_test_1.it)("calls global onValidationError when no per-route handler", function () {
363
+ var capturedErrors = [];
364
+ (0, openApiValidator_1.configureOpenApiValidator)({
365
+ onValidationError: function (errors) {
366
+ capturedErrors = errors;
367
+ },
368
+ });
369
+ var middleware = (0, openApiValidator_1.validateRequestBody)({ name: { required: true, type: "string" } });
370
+ var nextCalled = false;
371
+ var req = { body: {}, method: "POST", path: "/test" };
372
+ var res = {};
373
+ middleware(req, res, function () {
374
+ nextCalled = true;
375
+ });
376
+ (0, bun_test_1.expect)(nextCalled).toBe(true);
377
+ (0, bun_test_1.expect)(capturedErrors.length).toBeGreaterThan(0);
378
+ });
379
+ (0, bun_test_1.it)("skips validation when enabled=false on options", function () {
380
+ (0, openApiValidator_1.configureOpenApiValidator)();
381
+ var middleware = (0, openApiValidator_1.validateRequestBody)({ name: { required: true, type: "string" } }, { enabled: false });
382
+ var nextCalled = false;
383
+ var req = { body: {}, method: "POST", path: "/test" };
384
+ var res = {};
385
+ middleware(req, res, function () {
386
+ nextCalled = true;
387
+ });
388
+ (0, bun_test_1.expect)(nextCalled).toBe(true);
389
+ });
390
+ (0, bun_test_1.it)("respects validateRequests=false in global config", function () {
391
+ (0, openApiValidator_1.configureOpenApiValidator)({ validateRequests: false });
392
+ var middleware = (0, openApiValidator_1.validateRequestBody)({ name: { required: true, type: "string" } });
393
+ var nextCalled = false;
394
+ var req = { body: {}, method: "POST", path: "/test" };
395
+ var res = {};
396
+ middleware(req, res, function () {
397
+ nextCalled = true;
398
+ });
399
+ (0, bun_test_1.expect)(nextCalled).toBe(true);
400
+ });
401
+ });
402
+ (0, bun_test_1.describe)("validateQueryParams middleware", function () {
403
+ (0, bun_test_1.it)("is a no-op when not configured", function () {
404
+ (0, openApiValidator_1.resetOpenApiValidatorConfig)();
405
+ var middleware = (0, openApiValidator_1.validateQueryParams)({
406
+ page: { type: "number" },
407
+ });
408
+ var nextCalled = false;
409
+ var req = { method: "GET", path: "/test", query: {} };
410
+ var res = {};
411
+ middleware(req, res, function () {
412
+ nextCalled = true;
413
+ });
414
+ (0, bun_test_1.expect)(nextCalled).toBe(true);
415
+ });
416
+ (0, bun_test_1.it)("coerces types when configured", function () {
417
+ (0, openApiValidator_1.configureOpenApiValidator)({ coerceTypes: true });
418
+ var middleware = (0, openApiValidator_1.validateQueryParams)({
419
+ page: { type: "number" },
420
+ });
421
+ var req = { method: "GET", path: "/test", query: { page: "3" } };
422
+ var res = {};
423
+ middleware(req, res, function () { });
424
+ (0, bun_test_1.expect)(req.query.page).toBe(3);
425
+ });
426
+ (0, bun_test_1.it)("throws validation error when query does not match", function () {
427
+ (0, openApiValidator_1.configureOpenApiValidator)({ coerceTypes: false });
428
+ var middleware = (0, openApiValidator_1.validateQueryParams)({
429
+ page: { required: true, type: "number" },
430
+ });
431
+ var req = { method: "GET", path: "/test", query: {} };
432
+ var res = {};
433
+ // Required top-level property missing triggers an error
434
+ // We need required: [] at schema level via required: true on property. Confirm via calling.
435
+ (0, bun_test_1.expect)(function () {
436
+ middleware(req, res, function () { });
437
+ }).toThrow();
438
+ });
439
+ (0, bun_test_1.it)("uses onError callback for query validation", function () {
440
+ var captured = [];
441
+ (0, openApiValidator_1.configureOpenApiValidator)({ coerceTypes: false });
442
+ var middleware = (0, openApiValidator_1.validateQueryParams)({ page: { required: true, type: "number" } }, {
443
+ onError: function (errors) {
444
+ captured = errors;
445
+ },
446
+ });
447
+ var nextCalled = false;
448
+ var req = { method: "GET", path: "/test", query: {} };
449
+ var res = {};
450
+ middleware(req, res, function () {
451
+ nextCalled = true;
452
+ });
453
+ (0, bun_test_1.expect)(nextCalled).toBe(true);
454
+ (0, bun_test_1.expect)(captured.length).toBeGreaterThan(0);
455
+ });
456
+ (0, bun_test_1.it)("skips query validation when enabled=false", function () {
457
+ (0, openApiValidator_1.configureOpenApiValidator)();
458
+ var middleware = (0, openApiValidator_1.validateQueryParams)({ page: { required: true, type: "number" } }, { enabled: false });
459
+ var nextCalled = false;
460
+ var req = { method: "GET", path: "/test", query: {} };
461
+ var res = {};
462
+ middleware(req, res, function () {
463
+ nextCalled = true;
464
+ });
465
+ (0, bun_test_1.expect)(nextCalled).toBe(true);
466
+ });
467
+ });
468
+ (0, bun_test_1.describe)("validateResponseData", function () {
469
+ (0, bun_test_1.it)("returns valid when validateResponses is disabled", function () {
470
+ (0, openApiValidator_1.configureOpenApiValidator)({ validateResponses: false });
471
+ var result = (0, openApiValidator_1.validateResponseData)({ foo: "bar" }, { name: { type: "string" } });
472
+ (0, bun_test_1.expect)(result.valid).toBe(true);
473
+ });
474
+ (0, bun_test_1.it)("validates response shape when validateResponses is enabled", function () {
475
+ (0, openApiValidator_1.configureOpenApiValidator)({ validateResponses: true });
476
+ var result = (0, openApiValidator_1.validateResponseData)({ name: "Apple" }, { name: { required: true, type: "string" } });
477
+ (0, bun_test_1.expect)(result.valid).toBe(true);
478
+ });
479
+ (0, bun_test_1.it)("returns errors for invalid response shape", function () {
480
+ (0, openApiValidator_1.configureOpenApiValidator)({ coerceTypes: false, validateResponses: true });
481
+ var result = (0, openApiValidator_1.validateResponseData)({ name: 42 }, { name: { required: true, type: "string" } });
482
+ (0, bun_test_1.expect)(result.valid).toBe(false);
483
+ (0, bun_test_1.expect)(result.errors).toBeDefined();
484
+ });
485
+ });
486
+ (0, bun_test_1.describe)("createValidator", function () {
487
+ (0, bun_test_1.it)("runs body then query validation and calls next once both pass", function () {
488
+ (0, openApiValidator_1.configureOpenApiValidator)({ coerceTypes: true });
489
+ var middleware = (0, openApiValidator_1.createValidator)({
490
+ body: { name: { required: true, type: "string" } },
491
+ query: { page: { type: "number" } },
492
+ });
493
+ var nextCalled = false;
494
+ var req = {
495
+ body: { name: "ok" },
496
+ method: "POST",
497
+ path: "/test",
498
+ query: { page: "2" },
499
+ };
500
+ var res = {};
501
+ middleware(req, res, function () {
502
+ nextCalled = true;
503
+ });
504
+ (0, bun_test_1.expect)(nextCalled).toBe(true);
505
+ (0, bun_test_1.expect)(req.query.page).toBe(2);
506
+ });
507
+ (0, bun_test_1.it)("skips when neither body nor query schemas are provided", function () {
508
+ (0, openApiValidator_1.configureOpenApiValidator)();
509
+ var middleware = (0, openApiValidator_1.createValidator)({});
510
+ var nextCalled = false;
511
+ var req = { body: {}, method: "POST", path: "/test", query: {} };
512
+ var res = {};
513
+ middleware(req, res, function () {
514
+ nextCalled = true;
515
+ });
516
+ (0, bun_test_1.expect)(nextCalled).toBe(true);
517
+ });
518
+ (0, bun_test_1.it)("runs only query validation when only query provided", function () {
519
+ (0, openApiValidator_1.configureOpenApiValidator)({ coerceTypes: true });
520
+ var middleware = (0, openApiValidator_1.createValidator)({
521
+ query: { page: { type: "number" } },
522
+ });
523
+ var nextCalled = false;
524
+ var req = { method: "GET", path: "/test", query: { page: "5" } };
525
+ var res = {};
526
+ middleware(req, res, function () {
527
+ nextCalled = true;
528
+ });
529
+ (0, bun_test_1.expect)(nextCalled).toBe(true);
530
+ (0, bun_test_1.expect)(req.query.page).toBe(5);
531
+ });
532
+ (0, bun_test_1.it)("propagates body validation error via next", function () {
533
+ var capturedErrors = [];
534
+ (0, openApiValidator_1.configureOpenApiValidator)({
535
+ onValidationError: function (errors) {
536
+ capturedErrors = errors;
537
+ },
538
+ });
539
+ var middleware = (0, openApiValidator_1.createValidator)({
540
+ body: { name: { required: true, type: "string" } },
541
+ query: { page: { type: "number" } },
542
+ });
543
+ var req = { body: {}, method: "POST", path: "/test", query: {} };
544
+ var res = {};
545
+ var nextCalled = false;
546
+ middleware(req, res, function () {
547
+ nextCalled = true;
548
+ });
549
+ (0, bun_test_1.expect)(capturedErrors.length).toBeGreaterThan(0);
550
+ (0, bun_test_1.expect)(nextCalled).toBe(true);
551
+ });
552
+ });
553
+ (0, bun_test_1.describe)("createModelValidators", function () {
554
+ (0, bun_test_1.beforeEach)(function () {
555
+ (0, openApiValidator_1.configureOpenApiValidator)();
556
+ });
557
+ (0, bun_test_1.it)("returns create and update middleware", function () {
558
+ var validators = (0, openApiValidator_1.createModelValidators)(tests_1.RequiredModel);
559
+ (0, bun_test_1.expect)(typeof validators.create).toBe("function");
560
+ (0, bun_test_1.expect)(typeof validators.update).toBe("function");
561
+ });
562
+ (0, bun_test_1.it)("create validator rejects body with wrong type", function () {
563
+ (0, openApiValidator_1.configureOpenApiValidator)({ coerceTypes: false });
564
+ var create = (0, openApiValidator_1.createModelValidators)(tests_1.RequiredModel).create;
565
+ var req = { body: { name: 123 }, method: "POST", path: "/required" };
566
+ var res = {};
567
+ (0, bun_test_1.expect)(function () {
568
+ create(req, res, function () { });
569
+ }).toThrow();
570
+ });
571
+ (0, bun_test_1.it)("update validator allows a partial body", function () {
572
+ var update = (0, openApiValidator_1.createModelValidators)(tests_1.RequiredModel).update;
573
+ var nextCalled = false;
574
+ var req = { body: { about: "info" }, method: "PATCH", path: "/required" };
575
+ var res = {};
576
+ update(req, res, function () {
577
+ nextCalled = true;
578
+ });
579
+ (0, bun_test_1.expect)(nextCalled).toBe(true);
580
+ });
581
+ (0, bun_test_1.it)("supports onAdditionalPropertiesRemoved option", function () {
582
+ (0, openApiValidator_1.configureOpenApiValidator)({ removeAdditional: true });
583
+ var removedProps = [];
584
+ var create = (0, openApiValidator_1.createModelValidators)(tests_1.RequiredModel, {
585
+ onAdditionalPropertiesRemoved: function (props) {
586
+ removedProps.push.apply(removedProps, __spreadArray([], __read(props), false));
587
+ },
588
+ }).create;
589
+ // Extra property gets stripped and hook is called
590
+ var req = {
591
+ body: { extra: "strip me", name: "Apple" },
592
+ method: "POST",
593
+ path: "/required",
594
+ };
595
+ create(req, {}, function () { });
596
+ (0, bun_test_1.expect)(removedProps).toContain("extra");
597
+ });
598
+ (0, bun_test_1.it)("supports onError option", function () {
599
+ (0, openApiValidator_1.configureOpenApiValidator)({ coerceTypes: false });
600
+ var errorHandled = [];
601
+ var create = (0, openApiValidator_1.createModelValidators)(tests_1.RequiredModel, {
602
+ onError: function (errors) {
603
+ errorHandled = errors;
604
+ },
605
+ }).create;
606
+ // Wrong type triggers error handler
607
+ var req = { body: { name: 42 }, method: "POST", path: "/required" };
608
+ create(req, {}, function () { });
609
+ (0, bun_test_1.expect)(errorHandled.length).toBeGreaterThan(0);
610
+ });
611
+ });
612
+ (0, bun_test_1.describe)("validateModelRequestBody", function () {
613
+ (0, bun_test_1.beforeEach)(function () {
614
+ (0, openApiValidator_1.configureOpenApiValidator)();
615
+ });
616
+ (0, bun_test_1.it)("creates a validator from a Mongoose model", function () {
617
+ var middleware = (0, openApiValidator_1.validateModelRequestBody)(tests_1.RequiredModel);
618
+ var req = { body: {}, method: "POST", path: "/required" };
619
+ var res = {};
620
+ (0, bun_test_1.expect)(function () {
621
+ middleware(req, res, function () { });
622
+ }).toThrow();
623
+ });
624
+ (0, bun_test_1.it)("respects excludeFields option", function () {
625
+ var middleware = (0, openApiValidator_1.validateModelRequestBody)(tests_1.RequiredModel, {
626
+ excludeFields: ["name"],
627
+ });
628
+ // Without 'name' required, empty body passes
629
+ var nextCalled = false;
630
+ var req = { body: {}, method: "POST", path: "/required" };
631
+ var res = {};
632
+ middleware(req, res, function () {
633
+ nextCalled = true;
634
+ });
635
+ (0, bun_test_1.expect)(nextCalled).toBe(true);
636
+ });
637
+ });
638
+ (0, bun_test_1.describe)("getSchemaFromModel", function () {
639
+ (0, bun_test_1.it)("returns properties for a Mongoose model", function () {
640
+ var schema = (0, openApiValidator_1.getSchemaFromModel)(tests_1.RequiredModel);
641
+ (0, bun_test_1.expect)(schema.name).toBeDefined();
642
+ (0, bun_test_1.expect)(schema.about).toBeDefined();
643
+ });
644
+ });
645
+ (0, bun_test_1.describe)("getOpenApiValidatorConfig", function () {
646
+ (0, bun_test_1.it)("returns a shallow copy of the config", function () {
647
+ (0, openApiValidator_1.configureOpenApiValidator)({ removeAdditional: false });
648
+ var config = (0, openApiValidator_1.getOpenApiValidatorConfig)();
649
+ (0, bun_test_1.expect)(config.removeAdditional).toBe(false);
650
+ // Mutating the returned copy should not affect internal state
651
+ config.removeAdditional = true;
652
+ (0, bun_test_1.expect)((0, openApiValidator_1.getOpenApiValidatorConfig)().removeAdditional).toBe(false);
653
+ });
345
654
  });
346
655
  });
@@ -0,0 +1 @@
1
+ export {};