@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
@@ -74,6 +74,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
74
74
  Object.defineProperty(exports, "__esModule", { value: true });
75
75
  var bun_test_1 = require("bun:test");
76
76
  var mongoose_1 = __importStar(require("mongoose"));
77
+ var passport_1 = __importDefault(require("passport"));
77
78
  var passport_local_mongoose_1 = __importDefault(require("passport-local-mongoose"));
78
79
  var supertest_1 = __importDefault(require("supertest"));
79
80
  var expressServer_1 = require("./expressServer");
@@ -349,3 +350,382 @@ var connectDb = function () { return __awaiter(void 0, void 0, void 0, function
349
350
  });
350
351
  }); });
351
352
  });
353
+ // Helper to extract the strategy verify callback and invoke it directly
354
+ var invokeGitHubVerify = function (req, accessToken, refreshToken, profile) {
355
+ var _a;
356
+ var strategy = (_a = passport_1.default._strategies) === null || _a === void 0 ? void 0 : _a.github;
357
+ if (!strategy) {
358
+ throw new Error("github strategy not registered");
359
+ }
360
+ return new Promise(function (resolve) {
361
+ var done = function (err, user) { return resolve({ err: err, user: user }); };
362
+ strategy._verify(req, accessToken, refreshToken, profile, done);
363
+ });
364
+ };
365
+ (0, bun_test_1.describe)("GitHub strategy verify callback", function () {
366
+ var testApp = { get: function () { }, use: function () { } };
367
+ (0, bun_test_1.beforeEach)(function () { return __awaiter(void 0, void 0, void 0, function () {
368
+ return __generator(this, function (_a) {
369
+ switch (_a.label) {
370
+ case 0: return [4 /*yield*/, connectDb()];
371
+ case 1:
372
+ _a.sent();
373
+ return [4 /*yield*/, GitHubTestUserModel.deleteMany({})];
374
+ case 2:
375
+ _a.sent();
376
+ return [2 /*return*/];
377
+ }
378
+ });
379
+ }); });
380
+ (0, bun_test_1.it)("uses custom findOrCreateUser when provided", function () { return __awaiter(void 0, void 0, void 0, function () {
381
+ var customUser, result;
382
+ return __generator(this, function (_a) {
383
+ switch (_a.label) {
384
+ case 0:
385
+ customUser = { _id: "custom-user-id", email: "custom@example.com" };
386
+ (0, githubAuth_1.setupGitHubAuth)(testApp, GitHubTestUserModel, {
387
+ callbackURL: "http://localhost:9000/auth/github/callback",
388
+ clientId: "id",
389
+ clientSecret: "secret",
390
+ findOrCreateUser: function () { return __awaiter(void 0, void 0, void 0, function () { return __generator(this, function (_a) {
391
+ return [2 /*return*/, customUser];
392
+ }); }); },
393
+ });
394
+ return [4 /*yield*/, invokeGitHubVerify({}, "access", "refresh", { id: "123" })];
395
+ case 1:
396
+ result = _a.sent();
397
+ (0, bun_test_1.expect)(result.err).toBeNull();
398
+ (0, bun_test_1.expect)(result.user).toEqual(customUser);
399
+ return [2 /*return*/];
400
+ }
401
+ });
402
+ }); });
403
+ (0, bun_test_1.it)("creates a new user when no existing GitHub or email user", function () { return __awaiter(void 0, void 0, void 0, function () {
404
+ var profile, result;
405
+ return __generator(this, function (_a) {
406
+ switch (_a.label) {
407
+ case 0:
408
+ (0, githubAuth_1.setupGitHubAuth)(testApp, GitHubTestUserModel, {
409
+ callbackURL: "http://localhost:9000/auth/github/callback",
410
+ clientId: "id",
411
+ clientSecret: "secret",
412
+ });
413
+ profile = {
414
+ emails: [{ value: "new@example.com" }],
415
+ id: "gh-new-1",
416
+ photos: [{ value: "http://avatar" }],
417
+ profileUrl: "http://profile",
418
+ username: "newghuser",
419
+ };
420
+ return [4 /*yield*/, invokeGitHubVerify({}, "access", "refresh", profile)];
421
+ case 1:
422
+ result = _a.sent();
423
+ (0, bun_test_1.expect)(result.err).toBeNull();
424
+ (0, bun_test_1.expect)(result.user).toBeDefined();
425
+ (0, bun_test_1.expect)(result.user.githubId).toBe("gh-new-1");
426
+ (0, bun_test_1.expect)(result.user.githubUsername).toBe("newghuser");
427
+ (0, bun_test_1.expect)(result.user.email).toBe("new@example.com");
428
+ return [2 /*return*/];
429
+ }
430
+ });
431
+ }); });
432
+ (0, bun_test_1.it)("logs in existing GitHub user", function () { return __awaiter(void 0, void 0, void 0, function () {
433
+ var existingUser, result;
434
+ return __generator(this, function (_a) {
435
+ switch (_a.label) {
436
+ case 0: return [4 /*yield*/, GitHubTestUserModel.create({
437
+ email: "gh@example.com",
438
+ githubId: "gh-existing-1",
439
+ githubUsername: "ghuser",
440
+ name: "GH User",
441
+ })];
442
+ case 1:
443
+ existingUser = _a.sent();
444
+ (0, githubAuth_1.setupGitHubAuth)(testApp, GitHubTestUserModel, {
445
+ callbackURL: "http://localhost:9000/auth/github/callback",
446
+ clientId: "id",
447
+ clientSecret: "secret",
448
+ });
449
+ return [4 /*yield*/, invokeGitHubVerify({}, "access", "refresh", {
450
+ id: "gh-existing-1",
451
+ username: "ghuser",
452
+ })];
453
+ case 2:
454
+ result = _a.sent();
455
+ (0, bun_test_1.expect)(result.err).toBeNull();
456
+ (0, bun_test_1.expect)(result.user._id.toString()).toBe(existingUser._id.toString());
457
+ return [2 /*return*/];
458
+ }
459
+ });
460
+ }); });
461
+ (0, bun_test_1.it)("links GitHub to authenticated user when allowAccountLinking=true", function () { return __awaiter(void 0, void 0, void 0, function () {
462
+ var existingUser, req, result;
463
+ return __generator(this, function (_a) {
464
+ switch (_a.label) {
465
+ case 0: return [4 /*yield*/, GitHubTestUserModel.create({
466
+ email: "link@example.com",
467
+ name: "Link User",
468
+ })];
469
+ case 1:
470
+ existingUser = _a.sent();
471
+ (0, githubAuth_1.setupGitHubAuth)(testApp, GitHubTestUserModel, {
472
+ allowAccountLinking: true,
473
+ callbackURL: "http://localhost:9000/auth/github/callback",
474
+ clientId: "id",
475
+ clientSecret: "secret",
476
+ });
477
+ req = { user: existingUser };
478
+ return [4 /*yield*/, invokeGitHubVerify(req, "access", "refresh", {
479
+ id: "gh-link-1",
480
+ photos: [{ value: "http://avatar" }],
481
+ profileUrl: "http://profile",
482
+ username: "linkedghuser",
483
+ })];
484
+ case 2:
485
+ result = _a.sent();
486
+ (0, bun_test_1.expect)(result.err).toBeNull();
487
+ (0, bun_test_1.expect)(result.user.githubId).toBe("gh-link-1");
488
+ (0, bun_test_1.expect)(result.user.githubUsername).toBe("linkedghuser");
489
+ return [2 /*return*/];
490
+ }
491
+ });
492
+ }); });
493
+ (0, bun_test_1.it)("rejects linking when allowAccountLinking=false", function () { return __awaiter(void 0, void 0, void 0, function () {
494
+ var existingUser, req, result;
495
+ return __generator(this, function (_a) {
496
+ switch (_a.label) {
497
+ case 0: return [4 /*yield*/, GitHubTestUserModel.create({
498
+ email: "nolink@example.com",
499
+ })];
500
+ case 1:
501
+ existingUser = _a.sent();
502
+ (0, githubAuth_1.setupGitHubAuth)(testApp, GitHubTestUserModel, {
503
+ allowAccountLinking: false,
504
+ callbackURL: "http://localhost:9000/auth/github/callback",
505
+ clientId: "id",
506
+ clientSecret: "secret",
507
+ });
508
+ req = { user: existingUser };
509
+ return [4 /*yield*/, invokeGitHubVerify(req, "access", "refresh", { id: "gh-nolink-1" })];
510
+ case 2:
511
+ result = _a.sent();
512
+ (0, bun_test_1.expect)(result.err).toBeDefined();
513
+ (0, bun_test_1.expect)(result.err.status).toBe(400);
514
+ return [2 /*return*/];
515
+ }
516
+ });
517
+ }); });
518
+ (0, bun_test_1.it)("rejects linking when GitHub account belongs to another user", function () { return __awaiter(void 0, void 0, void 0, function () {
519
+ var userA, req, result;
520
+ return __generator(this, function (_a) {
521
+ switch (_a.label) {
522
+ case 0: return [4 /*yield*/, GitHubTestUserModel.create({
523
+ email: "a@example.com",
524
+ })];
525
+ case 1:
526
+ userA = _a.sent();
527
+ return [4 /*yield*/, GitHubTestUserModel.create({
528
+ email: "b@example.com",
529
+ githubId: "gh-other-1",
530
+ })];
531
+ case 2:
532
+ _a.sent();
533
+ (0, githubAuth_1.setupGitHubAuth)(testApp, GitHubTestUserModel, {
534
+ allowAccountLinking: true,
535
+ callbackURL: "http://localhost:9000/auth/github/callback",
536
+ clientId: "id",
537
+ clientSecret: "secret",
538
+ });
539
+ req = { user: userA };
540
+ return [4 /*yield*/, invokeGitHubVerify(req, "access", "refresh", { id: "gh-other-1" })];
541
+ case 3:
542
+ result = _a.sent();
543
+ (0, bun_test_1.expect)(result.err).toBeDefined();
544
+ (0, bun_test_1.expect)(result.err.status).toBe(400);
545
+ return [2 /*return*/];
546
+ }
547
+ });
548
+ }); });
549
+ (0, bun_test_1.it)("links GitHub to existing email user when allowAccountLinking is not false", function () { return __awaiter(void 0, void 0, void 0, function () {
550
+ var result;
551
+ return __generator(this, function (_a) {
552
+ switch (_a.label) {
553
+ case 0: return [4 /*yield*/, GitHubTestUserModel.create({
554
+ email: "emailuser@example.com",
555
+ })];
556
+ case 1:
557
+ _a.sent();
558
+ (0, githubAuth_1.setupGitHubAuth)(testApp, GitHubTestUserModel, {
559
+ callbackURL: "http://localhost:9000/auth/github/callback",
560
+ clientId: "id",
561
+ clientSecret: "secret",
562
+ });
563
+ return [4 /*yield*/, invokeGitHubVerify({}, "access", "refresh", {
564
+ emails: [{ value: "emailuser@example.com" }],
565
+ id: "gh-email-link-1",
566
+ username: "emailuserghuser",
567
+ })];
568
+ case 2:
569
+ result = _a.sent();
570
+ (0, bun_test_1.expect)(result.err).toBeNull();
571
+ (0, bun_test_1.expect)(result.user.githubId).toBe("gh-email-link-1");
572
+ (0, bun_test_1.expect)(result.user.email).toBe("emailuser@example.com");
573
+ return [2 /*return*/];
574
+ }
575
+ });
576
+ }); });
577
+ (0, bun_test_1.it)("rejects email-link when allowAccountLinking=false", function () { return __awaiter(void 0, void 0, void 0, function () {
578
+ var result;
579
+ return __generator(this, function (_a) {
580
+ switch (_a.label) {
581
+ case 0: return [4 /*yield*/, GitHubTestUserModel.create({
582
+ email: "emailnolink@example.com",
583
+ })];
584
+ case 1:
585
+ _a.sent();
586
+ (0, githubAuth_1.setupGitHubAuth)(testApp, GitHubTestUserModel, {
587
+ allowAccountLinking: false,
588
+ callbackURL: "http://localhost:9000/auth/github/callback",
589
+ clientId: "id",
590
+ clientSecret: "secret",
591
+ });
592
+ return [4 /*yield*/, invokeGitHubVerify({}, "access", "refresh", {
593
+ emails: [{ value: "emailnolink@example.com" }],
594
+ id: "gh-email-nolink-1",
595
+ })];
596
+ case 2:
597
+ result = _a.sent();
598
+ (0, bun_test_1.expect)(result.err).toBeDefined();
599
+ (0, bun_test_1.expect)(result.err.status).toBe(400);
600
+ return [2 /*return*/];
601
+ }
602
+ });
603
+ }); });
604
+ (0, bun_test_1.it)("returns error when thrown during lookup", function () { return __awaiter(void 0, void 0, void 0, function () {
605
+ var result;
606
+ return __generator(this, function (_a) {
607
+ switch (_a.label) {
608
+ case 0:
609
+ // Set up strategy with findOrCreateUser that throws
610
+ (0, githubAuth_1.setupGitHubAuth)(testApp, GitHubTestUserModel, {
611
+ callbackURL: "http://localhost:9000/auth/github/callback",
612
+ clientId: "id",
613
+ clientSecret: "secret",
614
+ findOrCreateUser: function () { return __awaiter(void 0, void 0, void 0, function () {
615
+ return __generator(this, function (_a) {
616
+ throw new Error("boom");
617
+ });
618
+ }); },
619
+ });
620
+ return [4 /*yield*/, invokeGitHubVerify({}, "access", "refresh", { id: "gh-err-1" })];
621
+ case 1:
622
+ result = _a.sent();
623
+ (0, bun_test_1.expect)(result.err).toBeDefined();
624
+ (0, bun_test_1.expect)(result.err.message).toBe("boom");
625
+ return [2 /*return*/];
626
+ }
627
+ });
628
+ }); });
629
+ });
630
+ (0, bun_test_1.describe)("addGitHubAuthRoutes link endpoints", function () {
631
+ var app;
632
+ var agent;
633
+ (0, bun_test_1.beforeEach)(function () { return __awaiter(void 0, void 0, void 0, function () {
634
+ function addRoutes(router) {
635
+ router.get("/test", function (_req, res) { return res.json({ ok: true }); });
636
+ }
637
+ return __generator(this, function (_a) {
638
+ switch (_a.label) {
639
+ case 0:
640
+ (0, bun_test_1.setSystemTime)();
641
+ return [4 /*yield*/, connectDb()];
642
+ case 1:
643
+ _a.sent();
644
+ return [4 /*yield*/, GitHubTestUserModel.deleteMany({})];
645
+ case 2:
646
+ _a.sent();
647
+ app = (0, expressServer_1.setupServer)({
648
+ addRoutes: addRoutes,
649
+ githubAuth: {
650
+ allowAccountLinking: true,
651
+ callbackURL: "http://localhost:9000/auth/github/callback",
652
+ clientId: "test-client-id",
653
+ clientSecret: "test-client-secret",
654
+ },
655
+ skipListen: true,
656
+ userModel: GitHubTestUserModel,
657
+ });
658
+ agent = supertest_1.default.agent(app);
659
+ return [2 /*return*/];
660
+ }
661
+ });
662
+ }); });
663
+ (0, bun_test_1.afterEach)(function () {
664
+ (0, bun_test_1.setSystemTime)();
665
+ });
666
+ (0, bun_test_1.it)("GET /auth/github/link requires JWT authentication", function () { return __awaiter(void 0, void 0, void 0, function () {
667
+ var res;
668
+ return __generator(this, function (_a) {
669
+ switch (_a.label) {
670
+ case 0: return [4 /*yield*/, agent.get("/auth/github/link").expect(401)];
671
+ case 1:
672
+ res = _a.sent();
673
+ (0, bun_test_1.expect)(res.body.message).toContain("Authentication required");
674
+ return [2 /*return*/];
675
+ }
676
+ });
677
+ }); });
678
+ (0, bun_test_1.it)("GET /auth/github with returnTo stores it in session", function () { return __awaiter(void 0, void 0, void 0, function () {
679
+ var res;
680
+ return __generator(this, function (_a) {
681
+ switch (_a.label) {
682
+ case 0: return [4 /*yield*/, agent.get("/auth/github?returnTo=https://example.com/cb").expect(302)];
683
+ case 1:
684
+ res = _a.sent();
685
+ (0, bun_test_1.expect)(res.headers.location).toContain("github.com");
686
+ return [2 /*return*/];
687
+ }
688
+ });
689
+ }); });
690
+ (0, bun_test_1.it)("DELETE /auth/github/unlink clears GitHub fields", function () { return __awaiter(void 0, void 0, void 0, function () {
691
+ var user, loginRes, updatedUser;
692
+ return __generator(this, function (_a) {
693
+ switch (_a.label) {
694
+ case 0: return [4 /*yield*/, GitHubTestUserModel.create({
695
+ email: "unlinkme@example.com",
696
+ githubAvatarUrl: "http://avatar",
697
+ githubId: "77777",
698
+ githubProfileUrl: "http://profile",
699
+ githubUsername: "ghunlink",
700
+ })];
701
+ case 1:
702
+ user = _a.sent();
703
+ return [4 /*yield*/, user.setPassword("password123")];
704
+ case 2:
705
+ _a.sent();
706
+ return [4 /*yield*/, user.save()];
707
+ case 3:
708
+ _a.sent();
709
+ return [4 /*yield*/, agent
710
+ .post("/auth/login")
711
+ .send({ email: "unlinkme@example.com", password: "password123" })
712
+ .expect(200)];
713
+ case 4:
714
+ loginRes = _a.sent();
715
+ return [4 /*yield*/, agent
716
+ .delete("/auth/github/unlink")
717
+ .set("authorization", "Bearer ".concat(loginRes.body.data.token))
718
+ .expect(200)];
719
+ case 5:
720
+ _a.sent();
721
+ return [4 /*yield*/, GitHubTestUserModel.findOne({ email: "unlinkme@example.com" })];
722
+ case 6:
723
+ updatedUser = _a.sent();
724
+ (0, bun_test_1.expect)(updatedUser.githubId).toBeUndefined();
725
+ (0, bun_test_1.expect)(updatedUser.githubAvatarUrl).toBeUndefined();
726
+ (0, bun_test_1.expect)(updatedUser.githubProfileUrl).toBeUndefined();
727
+ return [2 /*return*/];
728
+ }
729
+ });
730
+ }); });
731
+ });
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,143 @@
1
+ "use strict";
2
+ var __assign = (this && this.__assign) || function () {
3
+ __assign = Object.assign || function(t) {
4
+ for (var s, i = 1, n = arguments.length; i < n; i++) {
5
+ s = arguments[i];
6
+ for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
7
+ t[p] = s[p];
8
+ }
9
+ return t;
10
+ };
11
+ return __assign.apply(this, arguments);
12
+ };
13
+ var __importDefault = (this && this.__importDefault) || function (mod) {
14
+ return (mod && mod.__esModule) ? mod : { "default": mod };
15
+ };
16
+ Object.defineProperty(exports, "__esModule", { value: true });
17
+ var bun_test_1 = require("bun:test");
18
+ var node_fs_1 = __importDefault(require("node:fs"));
19
+ var node_os_1 = __importDefault(require("node:os"));
20
+ var node_path_1 = __importDefault(require("node:path"));
21
+ var winston_1 = __importDefault(require("winston"));
22
+ var logger_1 = require("./logger");
23
+ (0, bun_test_1.describe)("logger", function () {
24
+ var OLD_ENV = process.env;
25
+ (0, bun_test_1.beforeEach)(function () {
26
+ process.env = __assign({}, OLD_ENV);
27
+ });
28
+ (0, bun_test_1.afterEach)(function () {
29
+ process.env = OLD_ENV;
30
+ });
31
+ (0, bun_test_1.it)("logger.info writes a log entry", function () {
32
+ (0, bun_test_1.expect)(function () { return logger_1.logger.info("test info message"); }).not.toThrow();
33
+ });
34
+ (0, bun_test_1.it)("logger.warn writes a log entry", function () {
35
+ (0, bun_test_1.expect)(function () { return logger_1.logger.warn("test warn message"); }).not.toThrow();
36
+ });
37
+ (0, bun_test_1.it)("logger.error writes a log entry", function () {
38
+ (0, bun_test_1.expect)(function () { return logger_1.logger.error("test error message"); }).not.toThrow();
39
+ });
40
+ (0, bun_test_1.it)("logger.debug writes a log entry", function () {
41
+ (0, bun_test_1.expect)(function () { return logger_1.logger.debug("test debug message"); }).not.toThrow();
42
+ });
43
+ (0, bun_test_1.it)("logger.catch handles Error instance", function () {
44
+ (0, bun_test_1.expect)(function () { return logger_1.logger.catch(new Error("caught error")); }).not.toThrow();
45
+ });
46
+ (0, bun_test_1.it)("logger.catch handles non-Error value", function () {
47
+ (0, bun_test_1.expect)(function () { return logger_1.logger.catch("string error"); }).not.toThrow();
48
+ });
49
+ (0, bun_test_1.it)("logger.catch with Sentry logging enabled and Error", function () {
50
+ process.env.USE_SENTRY_LOGGING = "true";
51
+ (0, bun_test_1.expect)(function () { return logger_1.logger.catch(new Error("captured")); }).not.toThrow();
52
+ });
53
+ });
54
+ (0, bun_test_1.describe)("setupLogging", function () {
55
+ var tempDir;
56
+ (0, bun_test_1.beforeEach)(function () {
57
+ tempDir = node_fs_1.default.mkdtempSync(node_path_1.default.join(node_os_1.default.tmpdir(), "logger-test-"));
58
+ });
59
+ (0, bun_test_1.afterEach)(function () {
60
+ try {
61
+ node_fs_1.default.rmSync(tempDir, { force: true, recursive: true });
62
+ }
63
+ catch (_a) { }
64
+ // Restore a default logger config
65
+ (0, logger_1.setupLogging)({ disableFileLogging: true });
66
+ });
67
+ (0, bun_test_1.it)("disables console logging when disableConsoleLogging is true", function () {
68
+ (0, bun_test_1.expect)(function () {
69
+ return (0, logger_1.setupLogging)({
70
+ disableConsoleLogging: true,
71
+ disableFileLogging: true,
72
+ });
73
+ }).not.toThrow();
74
+ });
75
+ (0, bun_test_1.it)("disables console colors when disableConsoleColors is true", function () {
76
+ (0, bun_test_1.expect)(function () {
77
+ return (0, logger_1.setupLogging)({
78
+ disableConsoleColors: true,
79
+ disableFileLogging: true,
80
+ });
81
+ }).not.toThrow();
82
+ });
83
+ (0, bun_test_1.it)("adds timestamps when showConsoleTimestamps is true", function () {
84
+ (0, bun_test_1.expect)(function () {
85
+ return (0, logger_1.setupLogging)({
86
+ disableFileLogging: true,
87
+ showConsoleTimestamps: true,
88
+ });
89
+ }).not.toThrow();
90
+ });
91
+ (0, bun_test_1.it)("respects logLevel option", function () {
92
+ (0, bun_test_1.expect)(function () {
93
+ return (0, logger_1.setupLogging)({
94
+ disableFileLogging: true,
95
+ level: "info",
96
+ });
97
+ }).not.toThrow();
98
+ });
99
+ (0, bun_test_1.it)("creates log directory if it does not exist", function () {
100
+ var nonExistentDir = node_path_1.default.join(tempDir, "nested", "logs");
101
+ (0, logger_1.setupLogging)({
102
+ disableConsoleLogging: true,
103
+ logDirectory: nonExistentDir,
104
+ });
105
+ (0, bun_test_1.expect)(node_fs_1.default.existsSync(nonExistentDir)).toBe(true);
106
+ });
107
+ (0, bun_test_1.it)("uses existing log directory if it exists", function () {
108
+ var existingDir = node_path_1.default.join(tempDir, "existing");
109
+ node_fs_1.default.mkdirSync(existingDir);
110
+ (0, bun_test_1.expect)(function () {
111
+ return (0, logger_1.setupLogging)({
112
+ disableConsoleLogging: true,
113
+ logDirectory: existingDir,
114
+ });
115
+ }).not.toThrow();
116
+ });
117
+ (0, bun_test_1.it)("adds custom transports when provided", function () {
118
+ var customTransport = new winston_1.default.transports.Console({ level: "error" });
119
+ (0, bun_test_1.expect)(function () {
120
+ return (0, logger_1.setupLogging)({
121
+ disableConsoleLogging: true,
122
+ disableFileLogging: true,
123
+ transports: [customTransport],
124
+ });
125
+ }).not.toThrow();
126
+ });
127
+ (0, bun_test_1.it)("uses file logging at info level when level is info (no debug file)", function () {
128
+ (0, logger_1.setupLogging)({
129
+ disableConsoleLogging: true,
130
+ level: "info",
131
+ logDirectory: tempDir,
132
+ });
133
+ // No assertion needed - just verifying branch coverage with level=info
134
+ (0, bun_test_1.expect)(true).toBe(true);
135
+ });
136
+ (0, bun_test_1.it)("uses file logging at debug level by default (with debug file)", function () {
137
+ (0, logger_1.setupLogging)({
138
+ disableConsoleLogging: true,
139
+ logDirectory: tempDir,
140
+ });
141
+ (0, bun_test_1.expect)(true).toBe(true);
142
+ });
143
+ });
@@ -257,4 +257,41 @@ var googleChatNotifier_1 = require("./googleChatNotifier");
257
257
  }
258
258
  });
259
259
  }); });
260
+ (0, bun_test_1.it)("returns early when webhook url is missing for channel and no default exists", function () { return __awaiter(void 0, void 0, void 0, function () {
261
+ return __generator(this, function (_a) {
262
+ switch (_a.label) {
263
+ case 0:
264
+ process.env.GOOGLE_CHAT_WEBHOOKS = JSON.stringify({
265
+ ops: "https://chat.example/ops",
266
+ });
267
+ return [4 /*yield*/, (0, googleChatNotifier_1.sendToGoogleChat)("no default", { channel: "missing" })];
268
+ case 1:
269
+ _a.sent();
270
+ (0, bun_test_1.expect)(mockAxiosPost.mock.calls.length).toBe(0);
271
+ (0, bun_test_1.expect)(Sentry.captureException).toHaveBeenCalled();
272
+ return [2 /*return*/];
273
+ }
274
+ });
275
+ }); });
276
+ (0, bun_test_1.it)("prefixes message with [ENV] and posts when channel matches", function () { return __awaiter(void 0, void 0, void 0, function () {
277
+ var callArgs, _a, url, payload;
278
+ return __generator(this, function (_b) {
279
+ switch (_b.label) {
280
+ case 0:
281
+ process.env.GOOGLE_CHAT_WEBHOOKS = JSON.stringify({
282
+ default: "https://chat.example/default",
283
+ ops: "https://chat.example/ops",
284
+ });
285
+ mockAxiosPost.mockResolvedValue({ status: 200 });
286
+ return [4 /*yield*/, (0, googleChatNotifier_1.sendToGoogleChat)("deploy complete", { channel: "ops", env: "staging" })];
287
+ case 1:
288
+ _b.sent();
289
+ callArgs = mockAxiosPost.mock.calls[0];
290
+ _a = __read(callArgs, 2), url = _a[0], payload = _a[1];
291
+ (0, bun_test_1.expect)(url).toBe("https://chat.example/ops");
292
+ (0, bun_test_1.expect)(payload).toEqual({ text: "[STAGING] deploy complete" });
293
+ return [2 /*return*/];
294
+ }
295
+ });
296
+ }); });
260
297
  });
package/dist/openApi.js CHANGED
@@ -150,7 +150,7 @@ function getOpenApiMiddleware(model, options) {
150
150
  createAPIErrorComponent(options.openApi);
151
151
  if (!((_c = options.openApi) === null || _c === void 0 ? void 0 : _c.path)) {
152
152
  // Just log this once rather than for each middleware.
153
- logger_1.logger.debug("No options.openApi provided, skipping *OpenApiMiddleware");
153
+ logger_1.logger.debug("No options.openApi provided for model \"".concat(model.modelName, "\" in getOpenApiMiddleware, skipping *OpenApiMiddleware"));
154
154
  return noop;
155
155
  }
156
156
  if (((_e = (_d = options.permissions) === null || _d === void 0 ? void 0 : _d.read) === null || _e === void 0 ? void 0 : _e.length) === 0) {
@@ -416,7 +416,7 @@ function readOpenApiMiddleware(options, properties, required, queryParameters) {
416
416
  var _c, _d, _e, _f, _g;
417
417
  if (!((_c = options.openApi) === null || _c === void 0 ? void 0 : _c.path)) {
418
418
  // Just log this once rather than for each middleware.
419
- logger_1.logger.debug("No options.openApi provided, skipping *OpenApiMiddleware");
419
+ logger_1.logger.debug("No options.openApi provided in readOpenApiMiddleware, skipping *OpenApiMiddleware");
420
420
  return noop;
421
421
  }
422
422
  if (((_e = (_d = options.permissions) === null || _d === void 0 ? void 0 : _d.read) === null || _e === void 0 ? void 0 : _e.length) === 0) {