@tellescope/sdk 1.247.0 → 1.249.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 (126) hide show
  1. package/.env +3 -0
  2. package/lib/cjs/sdk.d.ts +7 -1
  3. package/lib/cjs/sdk.d.ts.map +1 -1
  4. package/lib/cjs/sdk.js +3 -0
  5. package/lib/cjs/sdk.js.map +1 -1
  6. package/lib/cjs/tests/api_tests/chats_analytics.test.d.ts +6 -0
  7. package/lib/cjs/tests/api_tests/chats_analytics.test.d.ts.map +1 -0
  8. package/lib/cjs/tests/api_tests/chats_analytics.test.js +256 -0
  9. package/lib/cjs/tests/api_tests/chats_analytics.test.js.map +1 -0
  10. package/lib/cjs/tests/api_tests/cross_org_api_key.test.d.ts +6 -0
  11. package/lib/cjs/tests/api_tests/cross_org_api_key.test.d.ts.map +1 -0
  12. package/lib/cjs/tests/api_tests/cross_org_api_key.test.js +748 -0
  13. package/lib/cjs/tests/api_tests/cross_org_api_key.test.js.map +1 -0
  14. package/lib/cjs/tests/api_tests/date_string_validation.test.d.ts +6 -0
  15. package/lib/cjs/tests/api_tests/date_string_validation.test.d.ts.map +1 -0
  16. package/lib/cjs/tests/api_tests/date_string_validation.test.js +142 -0
  17. package/lib/cjs/tests/api_tests/date_string_validation.test.js.map +1 -0
  18. package/lib/cjs/tests/api_tests/enduser_session_invalidation.test.d.ts +6 -0
  19. package/lib/cjs/tests/api_tests/enduser_session_invalidation.test.d.ts.map +1 -0
  20. package/lib/cjs/tests/api_tests/enduser_session_invalidation.test.js +667 -0
  21. package/lib/cjs/tests/api_tests/enduser_session_invalidation.test.js.map +1 -0
  22. package/lib/cjs/tests/api_tests/eom_billing_codes.test.d.ts +6 -0
  23. package/lib/cjs/tests/api_tests/eom_billing_codes.test.d.ts.map +1 -0
  24. package/lib/cjs/tests/api_tests/eom_billing_codes.test.js +162 -0
  25. package/lib/cjs/tests/api_tests/eom_billing_codes.test.js.map +1 -0
  26. package/lib/cjs/tests/api_tests/eom_procedure_codes.test.d.ts +6 -0
  27. package/lib/cjs/tests/api_tests/eom_procedure_codes.test.d.ts.map +1 -0
  28. package/lib/cjs/tests/api_tests/eom_procedure_codes.test.js +339 -0
  29. package/lib/cjs/tests/api_tests/eom_procedure_codes.test.js.map +1 -0
  30. package/lib/cjs/tests/api_tests/field_redaction.test.d.ts +13 -0
  31. package/lib/cjs/tests/api_tests/field_redaction.test.d.ts.map +1 -0
  32. package/lib/cjs/tests/api_tests/field_redaction.test.js +818 -0
  33. package/lib/cjs/tests/api_tests/field_redaction.test.js.map +1 -0
  34. package/lib/cjs/tests/api_tests/form_submitted_trigger.test.d.ts +6 -0
  35. package/lib/cjs/tests/api_tests/form_submitted_trigger.test.d.ts.map +1 -0
  36. package/lib/cjs/tests/api_tests/form_submitted_trigger.test.js +429 -0
  37. package/lib/cjs/tests/api_tests/form_submitted_trigger.test.js.map +1 -0
  38. package/lib/cjs/tests/api_tests/integrations_redacted.test.d.ts +6 -0
  39. package/lib/cjs/tests/api_tests/integrations_redacted.test.d.ts.map +1 -0
  40. package/lib/cjs/tests/api_tests/integrations_redacted.test.js +273 -0
  41. package/lib/cjs/tests/api_tests/integrations_redacted.test.js.map +1 -0
  42. package/lib/cjs/tests/api_tests/managed_content_file_access.test.d.ts +13 -0
  43. package/lib/cjs/tests/api_tests/managed_content_file_access.test.d.ts.map +1 -0
  44. package/lib/cjs/tests/api_tests/managed_content_file_access.test.js +385 -0
  45. package/lib/cjs/tests/api_tests/managed_content_file_access.test.js.map +1 -0
  46. package/lib/cjs/tests/api_tests/openloop_webhooks.test.d.ts.map +1 -1
  47. package/lib/cjs/tests/api_tests/openloop_webhooks.test.js +108 -24
  48. package/lib/cjs/tests/api_tests/openloop_webhooks.test.js.map +1 -1
  49. package/lib/cjs/tests/api_tests/organization_settings_duplicates.test.d.ts.map +1 -1
  50. package/lib/cjs/tests/api_tests/organization_settings_duplicates.test.js +25 -2
  51. package/lib/cjs/tests/api_tests/organization_settings_duplicates.test.js.map +1 -1
  52. package/lib/cjs/tests/tests.d.ts.map +1 -1
  53. package/lib/cjs/tests/tests.js +310 -183
  54. package/lib/cjs/tests/tests.js.map +1 -1
  55. package/lib/esm/sdk.d.ts +9 -3
  56. package/lib/esm/sdk.d.ts.map +1 -1
  57. package/lib/esm/sdk.js +3 -0
  58. package/lib/esm/sdk.js.map +1 -1
  59. package/lib/esm/session.d.ts +1 -0
  60. package/lib/esm/session.d.ts.map +1 -1
  61. package/lib/esm/tests/api_tests/chats_analytics.test.d.ts +6 -0
  62. package/lib/esm/tests/api_tests/chats_analytics.test.d.ts.map +1 -0
  63. package/lib/esm/tests/api_tests/chats_analytics.test.js +252 -0
  64. package/lib/esm/tests/api_tests/chats_analytics.test.js.map +1 -0
  65. package/lib/esm/tests/api_tests/cross_org_api_key.test.d.ts +6 -0
  66. package/lib/esm/tests/api_tests/cross_org_api_key.test.d.ts.map +1 -0
  67. package/lib/esm/tests/api_tests/cross_org_api_key.test.js +744 -0
  68. package/lib/esm/tests/api_tests/cross_org_api_key.test.js.map +1 -0
  69. package/lib/esm/tests/api_tests/date_string_validation.test.d.ts +6 -0
  70. package/lib/esm/tests/api_tests/date_string_validation.test.d.ts.map +1 -0
  71. package/lib/esm/tests/api_tests/date_string_validation.test.js +138 -0
  72. package/lib/esm/tests/api_tests/date_string_validation.test.js.map +1 -0
  73. package/lib/esm/tests/api_tests/enduser_session_invalidation.test.d.ts +6 -0
  74. package/lib/esm/tests/api_tests/enduser_session_invalidation.test.d.ts.map +1 -0
  75. package/lib/esm/tests/api_tests/enduser_session_invalidation.test.js +663 -0
  76. package/lib/esm/tests/api_tests/enduser_session_invalidation.test.js.map +1 -0
  77. package/lib/esm/tests/api_tests/eom_billing_codes.test.d.ts +6 -0
  78. package/lib/esm/tests/api_tests/eom_billing_codes.test.d.ts.map +1 -0
  79. package/lib/esm/tests/api_tests/eom_billing_codes.test.js +158 -0
  80. package/lib/esm/tests/api_tests/eom_billing_codes.test.js.map +1 -0
  81. package/lib/esm/tests/api_tests/eom_procedure_codes.test.d.ts +6 -0
  82. package/lib/esm/tests/api_tests/eom_procedure_codes.test.d.ts.map +1 -0
  83. package/lib/esm/tests/api_tests/eom_procedure_codes.test.js +335 -0
  84. package/lib/esm/tests/api_tests/eom_procedure_codes.test.js.map +1 -0
  85. package/lib/esm/tests/api_tests/field_redaction.test.d.ts +13 -0
  86. package/lib/esm/tests/api_tests/field_redaction.test.d.ts.map +1 -0
  87. package/lib/esm/tests/api_tests/field_redaction.test.js +814 -0
  88. package/lib/esm/tests/api_tests/field_redaction.test.js.map +1 -0
  89. package/lib/esm/tests/api_tests/form_submitted_trigger.test.d.ts +6 -0
  90. package/lib/esm/tests/api_tests/form_submitted_trigger.test.d.ts.map +1 -0
  91. package/lib/esm/tests/api_tests/form_submitted_trigger.test.js +425 -0
  92. package/lib/esm/tests/api_tests/form_submitted_trigger.test.js.map +1 -0
  93. package/lib/esm/tests/api_tests/integrations_redacted.test.d.ts +6 -0
  94. package/lib/esm/tests/api_tests/integrations_redacted.test.d.ts.map +1 -0
  95. package/lib/esm/tests/api_tests/integrations_redacted.test.js +269 -0
  96. package/lib/esm/tests/api_tests/integrations_redacted.test.js.map +1 -0
  97. package/lib/esm/tests/api_tests/managed_content_file_access.test.d.ts +13 -0
  98. package/lib/esm/tests/api_tests/managed_content_file_access.test.d.ts.map +1 -0
  99. package/lib/esm/tests/api_tests/managed_content_file_access.test.js +358 -0
  100. package/lib/esm/tests/api_tests/managed_content_file_access.test.js.map +1 -0
  101. package/lib/esm/tests/api_tests/openloop_webhooks.test.d.ts.map +1 -1
  102. package/lib/esm/tests/api_tests/openloop_webhooks.test.js +108 -24
  103. package/lib/esm/tests/api_tests/openloop_webhooks.test.js.map +1 -1
  104. package/lib/esm/tests/api_tests/organization_settings_duplicates.test.d.ts.map +1 -1
  105. package/lib/esm/tests/api_tests/organization_settings_duplicates.test.js +25 -2
  106. package/lib/esm/tests/api_tests/organization_settings_duplicates.test.js.map +1 -1
  107. package/lib/esm/tests/tests.d.ts.map +1 -1
  108. package/lib/esm/tests/tests.js +310 -183
  109. package/lib/esm/tests/tests.js.map +1 -1
  110. package/lib/tsconfig.tsbuildinfo +1 -1
  111. package/package.json +10 -10
  112. package/src/sdk.ts +14 -0
  113. package/src/tests/api_tests/chats_analytics.test.ts +182 -0
  114. package/src/tests/api_tests/cross_org_api_key.test.ts +665 -0
  115. package/src/tests/api_tests/date_string_validation.test.ts +107 -0
  116. package/src/tests/api_tests/enduser_session_invalidation.test.ts +361 -0
  117. package/src/tests/api_tests/eom_procedure_codes.test.ts +296 -0
  118. package/src/tests/api_tests/field_redaction.test.ts +669 -0
  119. package/src/tests/api_tests/form_started_trigger.test.ts +1 -1
  120. package/src/tests/api_tests/form_submitted_trigger.test.ts +281 -0
  121. package/src/tests/api_tests/integrations_redacted.test.ts +245 -0
  122. package/src/tests/api_tests/managed_content_file_access.test.ts +214 -0
  123. package/src/tests/api_tests/openloop_webhooks.test.ts +64 -0
  124. package/src/tests/api_tests/organization_settings_duplicates.test.ts +14 -0
  125. package/src/tests/tests.ts +95 -7
  126. package/test_generated.pdf +0 -0
@@ -0,0 +1,107 @@
1
+ require('source-map-support').install();
2
+
3
+ import { Session } from "../../sdk"
4
+ import {
5
+ assert,
6
+ log_header,
7
+ } from "@tellescope/testing"
8
+ import { setup_tests } from "../setup"
9
+ import { is_valid_mm_dd_yyyy } from "@tellescope/validation"
10
+
11
+ const host = process.env.API_URL || 'http://localhost:8080' as const
12
+
13
+ export const date_string_validation_tests = async ({ sdk, sdkNonAdmin } : { sdk: Session, sdkNonAdmin: Session }) => {
14
+ log_header("Date String Validation Tests (is_valid_mm_dd_yyyy)")
15
+
16
+ // --- Valid standard dates ---
17
+ assert(is_valid_mm_dd_yyyy('01-15-1990') === true, 'Expected 01-15-1990 to be valid', 'valid: 01-15-1990')
18
+ assert(is_valid_mm_dd_yyyy('12-31-2024') === true, 'Expected 12-31-2024 to be valid', 'valid: 12-31-2024')
19
+ assert(is_valid_mm_dd_yyyy('06-15-2000') === true, 'Expected 06-15-2000 to be valid', 'valid: 06-15-2000')
20
+ assert(is_valid_mm_dd_yyyy('01-01-2000') === true, 'Expected 01-01-2000 to be valid', 'valid: 01-01-2000')
21
+
22
+ // --- Leap year: Feb 29 accepted ---
23
+ assert(is_valid_mm_dd_yyyy('02-29-2024') === true, 'Expected 02-29-2024 to be valid (2024 is leap year, div by 4)', 'valid: 02-29-2024 (leap year)')
24
+ assert(is_valid_mm_dd_yyyy('02-29-2000') === true, 'Expected 02-29-2000 to be valid (2000 is leap year, div by 400)', 'valid: 02-29-2000 (leap year)')
25
+ assert(is_valid_mm_dd_yyyy('02-29-2400') === true, 'Expected 02-29-2400 to be valid (2400 is leap year, div by 400)', 'valid: 02-29-2400 (leap year)')
26
+
27
+ // --- Non-leap year: Feb 29 rejected ---
28
+ assert(is_valid_mm_dd_yyyy('02-29-2023') === false, 'Expected 02-29-2023 to be invalid (2023 is not a leap year)', 'invalid: 02-29-2023 (not leap year)')
29
+ assert(is_valid_mm_dd_yyyy('02-29-1900') === false, 'Expected 02-29-1900 to be invalid (1900 div by 100 but not 400)', 'invalid: 02-29-1900 (not leap year)')
30
+ assert(is_valid_mm_dd_yyyy('02-29-2100') === false, 'Expected 02-29-2100 to be invalid (2100 div by 100 but not 400)', 'invalid: 02-29-2100 (not leap year)')
31
+ assert(is_valid_mm_dd_yyyy('02-29-2025') === false, 'Expected 02-29-2025 to be invalid (2025 is not a leap year)', 'invalid: 02-29-2025 (not leap year)')
32
+
33
+ // --- Feb 30 and Feb 31 always rejected (the original bug) ---
34
+ assert(is_valid_mm_dd_yyyy('02-30-2024') === false, 'Expected 02-30-2024 to be invalid (Feb never has 30 days)', 'invalid: 02-30-2024 (Feb 30 leap year)')
35
+ assert(is_valid_mm_dd_yyyy('02-31-2024') === false, 'Expected 02-31-2024 to be invalid (Feb never has 31 days)', 'invalid: 02-31-2024 (Feb 31 leap year)')
36
+ assert(is_valid_mm_dd_yyyy('02-30-2023') === false, 'Expected 02-30-2023 to be invalid (Feb never has 30 days)', 'invalid: 02-30-2023 (Feb 30 non-leap)')
37
+ assert(is_valid_mm_dd_yyyy('02-31-2023') === false, 'Expected 02-31-2023 to be invalid (Feb never has 31 days)', 'invalid: 02-31-2023 (Feb 31 non-leap)')
38
+
39
+ // --- Feb 28 always valid ---
40
+ assert(is_valid_mm_dd_yyyy('02-28-2024') === true, 'Expected 02-28-2024 to be valid', 'valid: 02-28-2024')
41
+ assert(is_valid_mm_dd_yyyy('02-28-2023') === true, 'Expected 02-28-2023 to be valid', 'valid: 02-28-2023')
42
+
43
+ // --- 30-day months: day 31 rejected ---
44
+ assert(is_valid_mm_dd_yyyy('04-31-2024') === false, 'Expected 04-31-2024 to be invalid (April has 30 days)', 'invalid: 04-31 (April)')
45
+ assert(is_valid_mm_dd_yyyy('06-31-2024') === false, 'Expected 06-31-2024 to be invalid (June has 30 days)', 'invalid: 06-31 (June)')
46
+ assert(is_valid_mm_dd_yyyy('09-31-2024') === false, 'Expected 09-31-2024 to be invalid (September has 30 days)', 'invalid: 09-31 (September)')
47
+ assert(is_valid_mm_dd_yyyy('11-31-2024') === false, 'Expected 11-31-2024 to be invalid (November has 30 days)', 'invalid: 11-31 (November)')
48
+
49
+ // --- 30-day months: day 30 accepted ---
50
+ assert(is_valid_mm_dd_yyyy('04-30-2024') === true, 'Expected 04-30-2024 to be valid', 'valid: 04-30 (April)')
51
+ assert(is_valid_mm_dd_yyyy('06-30-2024') === true, 'Expected 06-30-2024 to be valid', 'valid: 06-30 (June)')
52
+ assert(is_valid_mm_dd_yyyy('09-30-2024') === true, 'Expected 09-30-2024 to be valid', 'valid: 09-30 (September)')
53
+ assert(is_valid_mm_dd_yyyy('11-30-2024') === true, 'Expected 11-30-2024 to be valid', 'valid: 11-30 (November)')
54
+
55
+ // --- 31-day months: day 31 accepted ---
56
+ assert(is_valid_mm_dd_yyyy('01-31-2024') === true, 'Expected 01-31-2024 to be valid', 'valid: 01-31 (January)')
57
+ assert(is_valid_mm_dd_yyyy('03-31-2024') === true, 'Expected 03-31-2024 to be valid', 'valid: 03-31 (March)')
58
+ assert(is_valid_mm_dd_yyyy('05-31-2024') === true, 'Expected 05-31-2024 to be valid', 'valid: 05-31 (May)')
59
+ assert(is_valid_mm_dd_yyyy('07-31-2024') === true, 'Expected 07-31-2024 to be valid', 'valid: 07-31 (July)')
60
+ assert(is_valid_mm_dd_yyyy('08-31-2024') === true, 'Expected 08-31-2024 to be valid', 'valid: 08-31 (August)')
61
+ assert(is_valid_mm_dd_yyyy('10-31-2024') === true, 'Expected 10-31-2024 to be valid', 'valid: 10-31 (October)')
62
+ assert(is_valid_mm_dd_yyyy('12-31-2024') === true, 'Expected 12-31-2024 to be valid', 'valid: 12-31 (December)')
63
+
64
+ // --- Invalid month values ---
65
+ assert(is_valid_mm_dd_yyyy('00-15-2024') === false, 'Expected month 00 to be invalid', 'invalid: month 00')
66
+ assert(is_valid_mm_dd_yyyy('13-15-2024') === false, 'Expected month 13 to be invalid', 'invalid: month 13')
67
+
68
+ // --- Invalid day values ---
69
+ assert(is_valid_mm_dd_yyyy('01-00-2024') === false, 'Expected day 00 to be invalid', 'invalid: day 00')
70
+ assert(is_valid_mm_dd_yyyy('01-32-2024') === false, 'Expected day 32 to be invalid', 'invalid: day 32')
71
+
72
+ // --- Malformed strings ---
73
+ assert(is_valid_mm_dd_yyyy('') === false, 'Expected empty string to be invalid', 'invalid: empty string')
74
+ assert(is_valid_mm_dd_yyyy('not-a-date') === false, 'Expected text to be invalid', 'invalid: text')
75
+ assert(is_valid_mm_dd_yyyy('2024-01-15') === false, 'Expected YYYY-MM-DD to be invalid', 'invalid: YYYY-MM-DD format')
76
+ assert(is_valid_mm_dd_yyyy('1-15-2024') === false, 'Expected single-digit month to be invalid', 'invalid: single-digit month')
77
+ assert(is_valid_mm_dd_yyyy('01/15/2024') === false, 'Expected slashes to be invalid', 'invalid: slashes')
78
+ assert(is_valid_mm_dd_yyyy('01-15-24') === false, 'Expected 2-digit year to be invalid', 'invalid: 2-digit year')
79
+ assert(is_valid_mm_dd_yyyy('01-5-2024') === false, 'Expected single-digit day to be invalid', 'invalid: single-digit day')
80
+ assert(is_valid_mm_dd_yyyy('01-15-20240') === false, 'Expected 5-digit year to be invalid', 'invalid: 5-digit year')
81
+
82
+ // --- Whitespace trimming ---
83
+ assert(is_valid_mm_dd_yyyy(' 01-15-1990 ') === true, 'Expected trimmed whitespace to be valid', 'valid: whitespace trimmed')
84
+ assert(is_valid_mm_dd_yyyy(' 02-28-2024 ') === true, 'Expected trimmed whitespace to be valid', 'valid: whitespace trimmed (2)')
85
+ }
86
+
87
+ // Allow running this test file independently
88
+ if (require.main === module) {
89
+ console.log(`Using API URL: ${host}`)
90
+ const sdk = new Session({ host })
91
+ const sdkNonAdmin = new Session({ host })
92
+
93
+ const runTests = async () => {
94
+ await setup_tests(sdk, sdkNonAdmin)
95
+ await date_string_validation_tests({ sdk, sdkNonAdmin })
96
+ }
97
+
98
+ runTests()
99
+ .then(() => {
100
+ console.log("Date string validation test suite completed successfully")
101
+ process.exit(0)
102
+ })
103
+ .catch((error) => {
104
+ console.error("Date string validation test suite failed:", error)
105
+ process.exit(1)
106
+ })
107
+ }
@@ -0,0 +1,361 @@
1
+ require('source-map-support').install();
2
+
3
+ import { Session, EnduserSession } from "../../sdk"
4
+ import {
5
+ async_test,
6
+ handleAnyError,
7
+ log_header,
8
+ wait,
9
+ } from "@tellescope/testing"
10
+ import { setup_tests } from "../setup"
11
+
12
+ const host = process.env.API_URL || 'http://localhost:8080' as const
13
+ const businessId = '60398b1131a295e64f084ff6'
14
+
15
+ // Main test function that can be called independently
16
+ export const enduser_session_invalidation_tests = async ({ sdk, sdkNonAdmin } : { sdk: Session, sdkNonAdmin: Session }) => {
17
+ log_header("Enduser Session Invalidation Tests")
18
+
19
+ // Create test enduser
20
+ const testEnduser = await sdk.api.endusers.createOne({ email: `session-invalidation-test-${Date.now()}@tellescope.com` })
21
+
22
+ try {
23
+ // Generate auth token for the enduser
24
+ const { authToken } = await sdk.api.endusers.generate_auth_token({ id: testEnduser.id })
25
+ const enduserSDK = new EnduserSession({ host, authToken, businessId: sdk.userInfo.businessId })
26
+
27
+ // Test 1: Enduser authenticated before invalidation
28
+ await async_test(
29
+ 'enduser authenticated before invalidation',
30
+ () => enduserSDK.test_authenticated(),
31
+ { expectedResult: 'Authenticated!' }
32
+ )
33
+
34
+ // Wait to ensure time separation between token creation and invalidation
35
+ await wait(undefined, 2000)
36
+
37
+ // Test 2: Setting invalidateSessionsBefore rejects old token (401)
38
+ await async_test(
39
+ 'setting invalidateSessionsBefore rejects old token',
40
+ async () => {
41
+ await sdk.api.endusers.updateOne(testEnduser.id, { invalidateSessionsBefore: new Date() })
42
+
43
+ // Old token should now be rejected
44
+ try {
45
+ await enduserSDK.test_authenticated()
46
+ return 'should have thrown'
47
+ } catch (e) {
48
+ return 'rejected'
49
+ }
50
+ },
51
+ { expectedResult: 'rejected' }
52
+ )
53
+
54
+ // Test 3: New token after invalidation works
55
+ await async_test(
56
+ 'new token after invalidation works',
57
+ async () => {
58
+ // Wait to ensure new token iat is after invalidateSessionsBefore
59
+ await wait(undefined, 2000)
60
+
61
+ const { authToken: newAuthToken } = await sdk.api.endusers.generate_auth_token({ id: testEnduser.id })
62
+ const newEnduserSDK = new EnduserSession({ host, authToken: newAuthToken, businessId: sdk.userInfo.businessId })
63
+
64
+ return await newEnduserSDK.test_authenticated()
65
+ },
66
+ { expectedResult: 'Authenticated!' }
67
+ )
68
+
69
+ // Test 4: Cannot set invalidateSessionsBefore backwards (constraint error)
70
+ await async_test(
71
+ 'cannot set invalidateSessionsBefore backwards',
72
+ async () => {
73
+ // Try to set invalidateSessionsBefore to a date in the past (before current value)
74
+ const pastDate = new Date(Date.now() - 24 * 60 * 60 * 1000) // 1 day ago
75
+ await sdk.api.endusers.updateOne(testEnduser.id, { invalidateSessionsBefore: pastDate })
76
+ },
77
+ handleAnyError
78
+ )
79
+
80
+ // Test 5: Deleted enduser token rejected (401)
81
+ await async_test(
82
+ 'deleted enduser token rejected',
83
+ async () => {
84
+ // Create a separate enduser, get token, then delete enduser
85
+ const tempEnduser = await sdk.api.endusers.createOne({ email: `temp-session-test-${Date.now()}@tellescope.com` })
86
+ const { authToken: tempAuthToken } = await sdk.api.endusers.generate_auth_token({ id: tempEnduser.id })
87
+ const tempEnduserSDK = new EnduserSession({ host, authToken: tempAuthToken, businessId: sdk.userInfo.businessId })
88
+
89
+ // Verify token works before deletion
90
+ await tempEnduserSDK.test_authenticated()
91
+
92
+ // Delete the enduser
93
+ await sdk.api.endusers.deleteOne(tempEnduser.id)
94
+
95
+ // Token should now be rejected
96
+ try {
97
+ await tempEnduserSDK.test_authenticated()
98
+ return 'should have thrown'
99
+ } catch (e) {
100
+ return 'rejected'
101
+ }
102
+ },
103
+ { expectedResult: 'rejected' }
104
+ )
105
+
106
+ console.log("✅ All Enduser Session Invalidation tests passed!")
107
+
108
+ } finally {
109
+ // Cleanup: Delete test enduser
110
+ try {
111
+ await sdk.api.endusers.deleteOne(testEnduser.id)
112
+ } catch (error) {
113
+ console.error('Cleanup error:', error)
114
+ }
115
+ }
116
+
117
+ // --- OTP enablement invalidation tests ---
118
+
119
+ log_header("OTP Enablement Session Invalidation Tests")
120
+
121
+ // Helper to reset OTP settings
122
+ const resetOTPSettings = async (s: Session) => {
123
+ await s.api.organizations.updateOne(s.userInfo.businessId, {
124
+ portalSettings: { authentication: { requireOTP: false, requireOTPAfterPassword: false } },
125
+ }, { replaceObjectFields: true })
126
+ }
127
+
128
+ // Ensure OTP is off and db is clean before starting
129
+ await sdk.reset_db()
130
+ await wait(undefined, 500)
131
+ await resetOTPSettings(sdk)
132
+
133
+ try {
134
+
135
+ // Test 6: Enabling requireOTP invalidates multiple enduser sessions at once
136
+ await async_test(
137
+ 'enabling requireOTP invalidates multiple enduser sessions',
138
+ async () => {
139
+ const endusers = await Promise.all([
140
+ sdk.api.endusers.createOne({ email: `otp-bulk-1-${Date.now()}@tellescope.com` }),
141
+ sdk.api.endusers.createOne({ email: `otp-bulk-2-${Date.now()}@tellescope.com` }),
142
+ sdk.api.endusers.createOne({ email: `otp-bulk-3-${Date.now()}@tellescope.com` }),
143
+ ])
144
+ try {
145
+ const tokens = await Promise.all(
146
+ endusers.map(e => sdk.api.endusers.generate_auth_token({ id: e.id, overrideOTP: true }))
147
+ )
148
+ const sessions = tokens.map(t => new EnduserSession({ host, authToken: t.authToken, businessId: sdk.userInfo.businessId }))
149
+
150
+ // Verify all tokens work
151
+ for (const s of sessions) {
152
+ await s.test_authenticated()
153
+ }
154
+
155
+ await wait(undefined, 2000)
156
+
157
+ // Enable OTP
158
+ await sdk.api.organizations.updateOne(sdk.userInfo.businessId, {
159
+ portalSettings: { authentication: { requireOTP: true } },
160
+ })
161
+
162
+ // Wait for side effect to process
163
+ await wait(undefined, 1000)
164
+
165
+ // All old tokens should be rejected
166
+ for (const s of sessions) {
167
+ try {
168
+ await s.test_authenticated()
169
+ return 'should have thrown'
170
+ } catch (e) { /* expected */ }
171
+ }
172
+
173
+ // New tokens should work
174
+ await wait(undefined, 2000)
175
+ for (const e of endusers) {
176
+ const { authToken: newToken } = await sdk.api.endusers.generate_auth_token({ id: e.id, overrideOTP: true })
177
+ const newSession = new EnduserSession({ host, authToken: newToken, businessId: sdk.userInfo.businessId })
178
+ await newSession.test_authenticated()
179
+ }
180
+
181
+ return 'passed'
182
+ } finally {
183
+ await resetOTPSettings(sdk)
184
+ for (const e of endusers) {
185
+ await sdk.api.endusers.deleteOne(e.id).catch(console.error)
186
+ }
187
+ }
188
+ },
189
+ { expectedResult: 'passed' }
190
+ )
191
+
192
+ // Test 7: Enabling requireOTPAfterPassword invalidates multiple enduser sessions
193
+ await async_test(
194
+ 'enabling requireOTPAfterPassword invalidates multiple enduser sessions',
195
+ async () => {
196
+ const endusers = await Promise.all([
197
+ sdk.api.endusers.createOne({ email: `otp-mfa-1-${Date.now()}@tellescope.com` }),
198
+ sdk.api.endusers.createOne({ email: `otp-mfa-2-${Date.now()}@tellescope.com` }),
199
+ ])
200
+ try {
201
+ const tokens = await Promise.all(
202
+ endusers.map(e => sdk.api.endusers.generate_auth_token({ id: e.id, overrideOTP: true }))
203
+ )
204
+ const sessions = tokens.map(t => new EnduserSession({ host, authToken: t.authToken, businessId: sdk.userInfo.businessId }))
205
+
206
+ for (const s of sessions) {
207
+ await s.test_authenticated()
208
+ }
209
+
210
+ await wait(undefined, 2000)
211
+
212
+ await sdk.api.organizations.updateOne(sdk.userInfo.businessId, {
213
+ portalSettings: { authentication: { requireOTPAfterPassword: true } },
214
+ })
215
+
216
+ await wait(undefined, 1000)
217
+
218
+ for (const s of sessions) {
219
+ try {
220
+ await s.test_authenticated()
221
+ return 'should have thrown'
222
+ } catch (e) { /* expected */ }
223
+ }
224
+
225
+ // New tokens work
226
+ await wait(undefined, 2000)
227
+ for (const e of endusers) {
228
+ const { authToken: newToken } = await sdk.api.endusers.generate_auth_token({ id: e.id, overrideOTP: true })
229
+ const newSession = new EnduserSession({ host, authToken: newToken, businessId: sdk.userInfo.businessId })
230
+ await newSession.test_authenticated()
231
+ }
232
+
233
+ return 'passed'
234
+ } finally {
235
+ await resetOTPSettings(sdk)
236
+ for (const e of endusers) {
237
+ await sdk.api.endusers.deleteOne(e.id).catch(console.error)
238
+ }
239
+ }
240
+ },
241
+ { expectedResult: 'passed' }
242
+ )
243
+
244
+ // Reset rate limiting state before continuing
245
+ await sdk.reset_db()
246
+ await wait(undefined, 500)
247
+
248
+ // Test 8: Disabling OTP does NOT invalidate sessions
249
+ await async_test(
250
+ 'disabling OTP does not invalidate sessions',
251
+ async () => {
252
+ // Enable OTP first
253
+ await sdk.api.organizations.updateOne(sdk.userInfo.businessId, {
254
+ portalSettings: { authentication: { requireOTP: true } },
255
+ })
256
+
257
+ await wait(undefined, 2000)
258
+
259
+ // Create enduser and token AFTER OTP is enabled (so token is valid)
260
+ const enduser = await sdk.api.endusers.createOne({ email: `otp-disable-${Date.now()}@tellescope.com` })
261
+ try {
262
+ const { authToken } = await sdk.api.endusers.generate_auth_token({ id: enduser.id, overrideOTP: true })
263
+ const enduserSession = new EnduserSession({ host, authToken, businessId: sdk.userInfo.businessId })
264
+ await enduserSession.test_authenticated()
265
+
266
+ await wait(undefined, 2000)
267
+
268
+ // Disable OTP
269
+ await sdk.api.organizations.updateOne(sdk.userInfo.businessId, {
270
+ portalSettings: { authentication: { requireOTP: false } },
271
+ }, { replaceObjectFields: true })
272
+
273
+ await wait(undefined, 1000)
274
+
275
+ // Old token should still work (disabling OTP should not invalidate)
276
+ return await enduserSession.test_authenticated()
277
+ } finally {
278
+ await resetOTPSettings(sdk)
279
+ await sdk.api.endusers.deleteOne(enduser.id).catch(console.error)
280
+ }
281
+ },
282
+ { expectedResult: 'Authenticated!' }
283
+ )
284
+
285
+ // Test 9: OTP invalidation is scoped to the updated organization only (multi-tenant)
286
+ await async_test(
287
+ 'OTP invalidation is scoped to the updated organization only',
288
+ async () => {
289
+ const sdkOther = new Session({ host, apiKey: "ba745e25162bb95a795c5fa1af70df188d93c4d3aac9c48b34a5c8c9dd7b80f7" })
290
+
291
+ const otherEnduser = await sdkOther.api.endusers.createOne({ email: `otp-other-tenant-${Date.now()}@tellescope.com` })
292
+ const mainEnduser = await sdk.api.endusers.createOne({ email: `otp-main-tenant-${Date.now()}@tellescope.com` })
293
+
294
+ try {
295
+ const { authToken: otherToken } = await sdkOther.api.endusers.generate_auth_token({ id: otherEnduser.id, overrideOTP: true })
296
+ const otherEnduserSession = new EnduserSession({ host, authToken: otherToken, businessId: sdkOther.userInfo.businessId })
297
+
298
+ const { authToken: mainToken } = await sdk.api.endusers.generate_auth_token({ id: mainEnduser.id, overrideOTP: true })
299
+ const mainEnduserSession = new EnduserSession({ host, authToken: mainToken, businessId: sdk.userInfo.businessId })
300
+
301
+ // Both tokens work
302
+ await otherEnduserSession.test_authenticated()
303
+ await mainEnduserSession.test_authenticated()
304
+
305
+ await wait(undefined, 2000)
306
+
307
+ // Enable OTP on main tenant only
308
+ await sdk.api.organizations.updateOne(sdk.userInfo.businessId, {
309
+ portalSettings: { authentication: { requireOTP: true } },
310
+ })
311
+
312
+ await wait(undefined, 1000)
313
+
314
+ // Main tenant enduser's old token should be rejected
315
+ try {
316
+ await mainEnduserSession.test_authenticated()
317
+ return 'should have thrown'
318
+ } catch (e) { /* expected */ }
319
+
320
+ // Other tenant enduser's token should still work
321
+ await otherEnduserSession.test_authenticated()
322
+
323
+ return 'passed'
324
+ } finally {
325
+ await resetOTPSettings(sdk)
326
+ await sdk.api.endusers.deleteOne(mainEnduser.id).catch(console.error)
327
+ await sdkOther.api.endusers.deleteOne(otherEnduser.id).catch(console.error)
328
+ }
329
+ },
330
+ { expectedResult: 'passed' }
331
+ )
332
+
333
+ console.log("✅ All OTP Enablement Session Invalidation tests passed!")
334
+
335
+ } finally {
336
+ // Always reset OTP settings to prevent impacting other tests
337
+ await resetOTPSettings(sdk).catch(console.error)
338
+ }
339
+ }
340
+
341
+ // Allow running this test file independently
342
+ if (require.main === module) {
343
+ console.log(`🌐 Using API URL: ${host}`)
344
+ const sdk = new Session({ host })
345
+ const sdkNonAdmin = new Session({ host })
346
+
347
+ const runTests = async () => {
348
+ await setup_tests(sdk, sdkNonAdmin)
349
+ await enduser_session_invalidation_tests({ sdk, sdkNonAdmin })
350
+ }
351
+
352
+ runTests()
353
+ .then(() => {
354
+ console.log("✅ Enduser session invalidation test suite completed successfully")
355
+ process.exit(0)
356
+ })
357
+ .catch((error) => {
358
+ console.error("❌ Enduser session invalidation test suite failed:", error)
359
+ process.exit(1)
360
+ })
361
+ }