@enterprisestandard/esv 0.0.5-beta.20260115.1 → 0.0.5-beta.20260115.2

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.
@@ -1,503 +1,433 @@
1
- import { createRequire } from "node:module";
2
- var __create = Object.create;
3
- var __getProtoOf = Object.getPrototypeOf;
4
- var __defProp = Object.defineProperty;
5
- var __getOwnPropNames = Object.getOwnPropertyNames;
6
- var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
7
- var __hasOwnProp = Object.prototype.hasOwnProperty;
8
- var __toESM = (mod, isNodeMode, target) => {
9
- target = mod != null ? __create(__getProtoOf(mod)) : {};
10
- const to = isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target;
11
- for (let key of __getOwnPropNames(mod))
12
- if (!__hasOwnProp.call(to, key))
13
- __defProp(to, key, {
14
- get: () => mod[key],
15
- enumerable: true
16
- });
17
- return to;
18
- };
19
- var __moduleCache = /* @__PURE__ */ new WeakMap;
20
- var __toCommonJS = (from) => {
21
- var entry = __moduleCache.get(from), desc;
22
- if (entry)
23
- return entry;
24
- entry = __defProp({}, "__esModule", { value: true });
25
- if (from && typeof from === "object" || typeof from === "function")
26
- __getOwnPropNames(from).map((key) => !__hasOwnProp.call(entry, key) && __defProp(entry, key, {
27
- get: () => from[key],
28
- enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
29
- }));
30
- __moduleCache.set(from, entry);
31
- return entry;
32
- };
33
- var __commonJS = (cb, mod) => () => (mod || cb((mod = { exports: {} }).exports, mod), mod.exports);
34
- var __export = (target, all) => {
35
- for (var name in all)
36
- __defProp(target, name, {
37
- get: all[name],
38
- enumerable: true,
39
- configurable: true,
40
- set: (newValue) => all[name] = () => newValue
41
- });
42
- };
43
- var __esm = (fn, res) => () => (fn && (res = fn(fn = 0)), res);
44
- var __require = /* @__PURE__ */ createRequire(import.meta.url);
45
-
46
- // packages/esv/src/utils.ts
47
- function createFetcher(config) {
48
- const timeout = config.timeout ?? 5000;
49
- const headers = config.headers ?? {};
50
- return async function fetcher(path, options = {}) {
51
- const url = `${config.baseUrl}${path}`;
52
- const controller = new AbortController;
53
- const timeoutId = setTimeout(() => controller.abort(), timeout);
54
- try {
55
- const response = await fetch(url, {
56
- ...options,
57
- signal: controller.signal,
58
- headers: {
59
- ...headers,
60
- ...options.headers
61
- },
62
- redirect: "manual"
63
- });
64
- return response;
65
- } finally {
66
- clearTimeout(timeoutId);
67
- }
68
- };
69
- }
70
- async function runTest(name, testFn) {
71
- const start = performance.now();
72
- try {
73
- const result = await testFn();
74
- return {
75
- name,
76
- passed: true,
77
- duration: performance.now() - start,
78
- details: result?.details
79
- };
80
- } catch (error) {
1
+ /**
2
+ * Workload Validation Tests
3
+ *
4
+ * These tests validate that an application correctly implements
5
+ * Enterprise Standard Workload Identity authentication.
6
+ */
7
+ import { assert, assertValid, createFetcher, runTest, skipTest } from '../utils';
8
+ /**
9
+ * Gets a workload token response validator (workload tokens don't have id_token, only access_token)
10
+ * This is different from OIDC tokenResponse which includes id_token
11
+ */
12
+ function getWorkloadTokenResponseValidator() {
81
13
  return {
82
- name,
83
- passed: false,
84
- error: error instanceof Error ? error.message : String(error),
85
- duration: performance.now() - start
14
+ '~standard': {
15
+ validate: (value) => {
16
+ if (typeof value !== 'object' || value === null) {
17
+ return {
18
+ issues: [{ message: `Expected object, got ${typeof value}` }],
19
+ };
20
+ }
21
+ const token = value;
22
+ const issues = [];
23
+ if (typeof token.access_token !== 'string') {
24
+ issues.push({ message: 'Expected access_token to be string', path: ['access_token'] });
25
+ }
26
+ if (typeof token.token_type !== 'string') {
27
+ issues.push({ message: 'Expected token_type to be string', path: ['token_type'] });
28
+ }
29
+ // expires_in is optional but if present should be a number
30
+ if (token.expires_in !== undefined && typeof token.expires_in !== 'number') {
31
+ issues.push({ message: 'Expected expires_in to be number or undefined', path: ['expires_in'] });
32
+ }
33
+ // scope is optional but if present should be a string
34
+ if (token.scope !== undefined && typeof token.scope !== 'string') {
35
+ issues.push({ message: 'Expected scope to be string or undefined', path: ['scope'] });
36
+ }
37
+ if (issues.length > 0) {
38
+ return { issues };
39
+ }
40
+ return { value };
41
+ },
42
+ },
86
43
  };
87
- }
88
- }
89
- function skipTest(name, reason) {
90
- return {
91
- name,
92
- passed: true,
93
- duration: 0,
94
- details: { skipped: true, reason }
95
- };
96
- }
97
- function assert(condition, message) {
98
- if (!condition) {
99
- throw new Error(message);
100
- }
101
- }
102
- function assertEqual(actual, expected, message) {
103
- if (actual !== expected) {
104
- throw new Error(message ?? `Expected ${JSON.stringify(expected)}, got ${JSON.stringify(actual)}`);
105
- }
106
- }
107
- function assertValid(data, validator, message) {
108
- const result = validator["~standard"].validate(data);
109
- if (result instanceof Promise) {
110
- throw new Error(message ?? "Async validators are not supported in assertValid. Use the validator directly with await.");
111
- }
112
- if ("issues" in result) {
113
- const issues = result.issues;
114
- const errorMessages = issues.map((issue) => {
115
- const path = issue.path ? issue.path.map(String).join(".") : "";
116
- return path ? `${path}: ${issue.message}` : issue.message;
117
- });
118
- throw new Error(message ?? `Validation failed: ${errorMessages.join("; ")}`);
119
- }
120
- }
121
- function parseCookies(headers) {
122
- const cookies = new Map;
123
- const setCookieHeaders = headers.getSetCookie?.() ?? [];
124
- for (const cookie of setCookieHeaders) {
125
- const [pair] = cookie.split(";");
126
- const [name, value] = pair.split("=");
127
- if (name && value !== undefined) {
128
- cookies.set(name.trim(), value.trim());
129
- }
130
- }
131
- return cookies;
132
- }
133
- function buildCookieHeader(cookies) {
134
- return Array.from(cookies.entries()).map(([name, value]) => `${name}=${value}`).join("; ");
135
- }
136
-
137
- // packages/esv/src/workload/index.ts
138
- var exports_workload = {};
139
- __export(exports_workload, {
140
- validateWorkload: () => validateWorkload,
141
- createWorkloadTests: () => createWorkloadTests
142
- });
143
- function getWorkloadTokenResponseValidator() {
144
- return {
145
- "~standard": {
146
- validate: (value) => {
147
- if (typeof value !== "object" || value === null) {
148
- return {
149
- issues: [{ message: `Expected object, got ${typeof value}` }]
150
- };
151
- }
152
- const token = value;
153
- const issues = [];
154
- if (typeof token.access_token !== "string") {
155
- issues.push({ message: "Expected access_token to be string", path: ["access_token"] });
156
- }
157
- if (typeof token.token_type !== "string") {
158
- issues.push({ message: "Expected token_type to be string", path: ["token_type"] });
159
- }
160
- if (token.expires_in !== undefined && typeof token.expires_in !== "number") {
161
- issues.push({ message: "Expected expires_in to be number or undefined", path: ["expires_in"] });
162
- }
163
- if (token.scope !== undefined && typeof token.scope !== "string") {
164
- issues.push({ message: "Expected scope to be string or undefined", path: ["scope"] });
165
- }
166
- if (issues.length > 0) {
167
- return { issues };
168
- }
169
- return { value };
170
- }
171
- }
172
- };
173
44
  }
45
+ /**
46
+ * Gets a JWKS key validator (simple inline validator for standard JWKS key structure)
47
+ */
174
48
  function getJwksKeyValidator() {
175
- return {
176
- "~standard": {
177
- validate: (value) => {
178
- if (typeof value !== "object" || value === null) {
179
- return {
180
- issues: [{ message: `Expected object, got ${typeof value}` }]
181
- };
182
- }
183
- const key = value;
184
- const issues = [];
185
- if (typeof key.kty !== "string") {
186
- issues.push({ message: "Expected kty to be string", path: ["kty"] });
187
- }
188
- if (typeof key.kid !== "string") {
189
- issues.push({ message: "Expected kid to be string", path: ["kid"] });
190
- }
191
- if (issues.length > 0) {
192
- return { issues };
193
- }
194
- return { value };
195
- }
196
- }
197
- };
49
+ // JWKS keys have a standard structure - create a simple validator inline
50
+ // This validates the minimal required fields: kty and kid
51
+ return {
52
+ '~standard': {
53
+ validate: (value) => {
54
+ if (typeof value !== 'object' || value === null) {
55
+ return {
56
+ issues: [{ message: `Expected object, got ${typeof value}` }],
57
+ };
58
+ }
59
+ const key = value;
60
+ const issues = [];
61
+ if (typeof key.kty !== 'string') {
62
+ issues.push({ message: 'Expected kty to be string', path: ['kty'] });
63
+ }
64
+ if (typeof key.kid !== 'string') {
65
+ issues.push({ message: 'Expected kid to be string', path: ['kid'] });
66
+ }
67
+ if (issues.length > 0) {
68
+ return { issues };
69
+ }
70
+ return { value };
71
+ },
72
+ },
73
+ };
198
74
  }
75
+ /**
76
+ * Gets a token validation result validator (simple inline validator)
77
+ */
199
78
  function getTokenValidationResultValidator() {
200
- return {
201
- "~standard": {
202
- validate: (value) => {
203
- if (typeof value !== "object" || value === null) {
204
- return {
205
- issues: [{ message: `Expected object, got ${typeof value}` }]
206
- };
207
- }
208
- const result = value;
209
- const issues = [];
210
- if (typeof result.valid !== "boolean") {
211
- issues.push({ message: "Expected valid to be boolean", path: ["valid"] });
212
- }
213
- if (issues.length > 0) {
214
- return { issues };
215
- }
216
- return { value };
217
- }
218
- }
219
- };
220
- }
221
- async function testJwksEndpoint(config, fetch2) {
222
- return runTest("Workload JWKS Endpoint", async () => {
223
- const response = await fetch2(config.jwksPath);
224
- assert(response.status === 200, `Expected 200 OK, got ${response.status}`);
225
- const data = await response.json();
226
- assert(Array.isArray(data.keys), "JWKS response missing keys array");
227
- const keyValidator = getJwksKeyValidator();
228
- for (const key of data.keys) {
229
- assertValid(key, keyValidator);
230
- }
231
79
  return {
232
- details: {
233
- keyCount: data.keys.length,
234
- algorithms: data.keys.map((k) => k.alg).filter(Boolean)
235
- }
80
+ '~standard': {
81
+ validate: (value) => {
82
+ if (typeof value !== 'object' || value === null) {
83
+ return {
84
+ issues: [{ message: `Expected object, got ${typeof value}` }],
85
+ };
86
+ }
87
+ const result = value;
88
+ const issues = [];
89
+ if (typeof result.valid !== 'boolean') {
90
+ issues.push({ message: 'Expected valid to be boolean', path: ['valid'] });
91
+ }
92
+ if (issues.length > 0) {
93
+ return { issues };
94
+ }
95
+ return { value };
96
+ },
97
+ },
236
98
  };
237
- });
238
99
  }
239
- async function testTokenEndpoint(config, fetch2) {
240
- const result = await runTest("Workload Token Endpoint", async () => {
241
- const url = config.testScopes ? `${config.tokenPath}?scope=${encodeURIComponent(config.testScopes)}` : config.tokenPath;
242
- const response = await fetch2(url);
243
- if (response.status === 503) {
244
- return {
245
- details: {
246
- skipped: true,
247
- reason: "Workload authentication not configured"
100
+ /**
101
+ * Default configuration for Workload validation
102
+ */
103
+ const DEFAULT_CONFIG = {
104
+ tokenPath: '/api/workload/token',
105
+ validatePath: '/api/workload/validate',
106
+ jwksPath: '/api/workload/jwks',
107
+ refreshPath: '/api/workload/refresh',
108
+ esvUrl: 'http://localhost:3555',
109
+ };
110
+ /**
111
+ * Test: JWKS endpoint returns valid JWKS structure
112
+ */
113
+ async function testJwksEndpoint(config, fetch) {
114
+ return runTest('Workload JWKS Endpoint', async () => {
115
+ const response = await fetch(config.jwksPath);
116
+ // Should return 200 OK
117
+ assert(response.status === 200, `Expected 200 OK, got ${response.status}`);
118
+ const data = await response.json();
119
+ // Should have JWKS structure
120
+ assert(Array.isArray(data.keys), 'JWKS response missing keys array');
121
+ // Each key should have required fields - validate using validator
122
+ const keyValidator = getJwksKeyValidator();
123
+ for (const key of data.keys) {
124
+ assertValid(key, keyValidator);
248
125
  }
249
- };
250
- }
251
- assert(response.status === 200, `Expected 200 OK, got ${response.status}`);
252
- const data = await response.json();
253
- const validator = getWorkloadTokenResponseValidator();
254
- assertValid(data, validator);
255
- return {
256
- details: {
257
- tokenType: data.token_type,
258
- hasToken: !!data.access_token,
259
- token: data.access_token
260
- }
261
- };
262
- });
263
- return {
264
- ...result,
265
- token: result.details?.token
266
- };
126
+ return {
127
+ details: {
128
+ keyCount: data.keys.length,
129
+ algorithms: data.keys.map((k) => k.alg).filter(Boolean),
130
+ },
131
+ };
132
+ });
267
133
  }
268
- async function testValidateEndpoint(config, fetch2, token) {
269
- return runTest("Workload Validate Endpoint", async () => {
270
- const response = await fetch2(config.validatePath, {
271
- method: "POST",
272
- headers: {
273
- Authorization: `Bearer ${token}`
274
- }
134
+ /**
135
+ * Test: Token endpoint returns access token
136
+ */
137
+ async function testTokenEndpoint(config, fetch) {
138
+ const result = await runTest('Workload Token Endpoint', async () => {
139
+ const url = config.testScopes
140
+ ? `${config.tokenPath}?scope=${encodeURIComponent(config.testScopes)}`
141
+ : config.tokenPath;
142
+ const response = await fetch(url);
143
+ // Should return 200 OK (or 503 if workload not configured)
144
+ if (response.status === 503) {
145
+ return {
146
+ details: {
147
+ skipped: true,
148
+ reason: 'Workload authentication not configured',
149
+ },
150
+ };
151
+ }
152
+ assert(response.status === 200, `Expected 200 OK, got ${response.status}`);
153
+ const data = await response.json();
154
+ // Should have token response structure - validate using workload-specific validator
155
+ // (workload tokens don't have id_token, only access_token)
156
+ const validator = getWorkloadTokenResponseValidator();
157
+ assertValid(data, validator);
158
+ return {
159
+ details: {
160
+ tokenType: data.token_type,
161
+ hasToken: !!data.access_token,
162
+ token: data.access_token,
163
+ },
164
+ };
275
165
  });
276
- assert(response.status === 200, `Expected 200 OK for valid token, got ${response.status}`);
277
- const data = await response.json();
278
- const validationResultValidator = getTokenValidationResultValidator();
279
- assertValid(data, validationResultValidator);
280
- assert(data.valid === true, "Token should be valid");
281
166
  return {
282
- details: {
283
- valid: data.valid,
284
- claims: data.claims
285
- }
167
+ ...result,
168
+ token: result.details?.token,
286
169
  };
287
- });
288
170
  }
289
- async function testValidateEndpointInvalid(config, fetch2) {
290
- return runTest("Workload Validate Endpoint (Invalid Token)", async () => {
291
- const response = await fetch2(config.validatePath, {
292
- method: "POST",
293
- headers: {
294
- Authorization: "Bearer invalid.token.here"
295
- }
171
+ /**
172
+ * Test: Token validation endpoint works
173
+ */
174
+ async function testValidateEndpoint(config, fetch, token) {
175
+ return runTest('Workload Validate Endpoint', async () => {
176
+ const response = await fetch(config.validatePath, {
177
+ method: 'POST',
178
+ headers: {
179
+ Authorization: `Bearer ${token}`,
180
+ },
181
+ });
182
+ // Should return 200 OK for valid token
183
+ assert(response.status === 200, `Expected 200 OK for valid token, got ${response.status}`);
184
+ const data = await response.json();
185
+ // Should have validation result structure - validate using validator
186
+ const validationResultValidator = getTokenValidationResultValidator();
187
+ assertValid(data, validationResultValidator);
188
+ assert(data.valid === true, 'Token should be valid');
189
+ return {
190
+ details: {
191
+ valid: data.valid,
192
+ claims: data.claims,
193
+ },
194
+ };
296
195
  });
297
- assert(response.status === 401, `Expected 401 Unauthorized for invalid token, got ${response.status}`);
298
- const data = await response.json();
299
- const validationResultValidator = getTokenValidationResultValidator();
300
- assertValid(data, validationResultValidator);
301
- assert(data.valid === false, "Invalid token should not validate");
302
- return {
303
- details: {
304
- valid: data.valid,
305
- error: data.error
306
- }
307
- };
308
- });
309
196
  }
310
- async function testValidateEndpointNoAuth(config, fetch2) {
311
- return runTest("Workload Validate Endpoint (No Auth)", async () => {
312
- const response = await fetch2(config.validatePath, {
313
- method: "POST"
197
+ /**
198
+ * Test: Token validation rejects invalid tokens
199
+ */
200
+ async function testValidateEndpointInvalid(config, fetch) {
201
+ return runTest('Workload Validate Endpoint (Invalid Token)', async () => {
202
+ const response = await fetch(config.validatePath, {
203
+ method: 'POST',
204
+ headers: {
205
+ Authorization: 'Bearer invalid.token.here',
206
+ },
207
+ });
208
+ // Should return 401 for invalid token
209
+ assert(response.status === 401, `Expected 401 Unauthorized for invalid token, got ${response.status}`);
210
+ const data = await response.json();
211
+ // Should indicate token is invalid - validate using validator
212
+ const validationResultValidator = getTokenValidationResultValidator();
213
+ assertValid(data, validationResultValidator);
214
+ assert(data.valid === false, 'Invalid token should not validate');
215
+ return {
216
+ details: {
217
+ valid: data.valid,
218
+ error: data.error,
219
+ },
220
+ };
314
221
  });
315
- assert(response.status === 401, `Expected 401 Unauthorized for missing auth, got ${response.status}`);
316
- return {
317
- details: {
318
- status: response.status,
319
- requiresAuth: true
320
- }
321
- };
322
- });
323
222
  }
324
- async function testRefreshEndpoint(config, fetch2) {
325
- return runTest("Workload Refresh Endpoint", async () => {
326
- const response = await fetch2(config.refreshPath, {
327
- method: "POST"
223
+ /**
224
+ * Test: Token validation rejects missing authorization header
225
+ */
226
+ async function testValidateEndpointNoAuth(config, fetch) {
227
+ return runTest('Workload Validate Endpoint (No Auth)', async () => {
228
+ const response = await fetch(config.validatePath, {
229
+ method: 'POST',
230
+ });
231
+ // Should return 401 for missing auth
232
+ assert(response.status === 401, `Expected 401 Unauthorized for missing auth, got ${response.status}`);
233
+ return {
234
+ details: {
235
+ status: response.status,
236
+ requiresAuth: true,
237
+ },
238
+ };
328
239
  });
329
- if (response.status === 503) {
330
- return {
331
- details: {
332
- skipped: true,
333
- reason: "Workload authentication not configured"
240
+ }
241
+ /**
242
+ * Test: Refresh endpoint works
243
+ */
244
+ async function testRefreshEndpoint(config, fetch) {
245
+ return runTest('Workload Refresh Endpoint', async () => {
246
+ const response = await fetch(config.refreshPath, {
247
+ method: 'POST',
248
+ });
249
+ // Should return 200 OK or 503 if not configured
250
+ if (response.status === 503) {
251
+ return {
252
+ details: {
253
+ skipped: true,
254
+ reason: 'Workload authentication not configured',
255
+ },
256
+ };
334
257
  }
335
- };
336
- }
337
- assert(response.status === 200, `Expected 200 OK, got ${response.status}`);
338
- const data = await response.json();
339
- const validator = getWorkloadTokenResponseValidator();
340
- assertValid(data, validator);
341
- return {
342
- details: {
343
- tokenType: data.token_type,
344
- hasToken: !!data.access_token
345
- }
346
- };
347
- });
258
+ assert(response.status === 200, `Expected 200 OK, got ${response.status}`);
259
+ const data = await response.json();
260
+ // Should have token response structure - validate using workload-specific validator
261
+ // (workload tokens don't have id_token, only access_token)
262
+ const validator = getWorkloadTokenResponseValidator();
263
+ assertValid(data, validator);
264
+ return {
265
+ details: {
266
+ tokenType: data.token_type,
267
+ hasToken: !!data.access_token,
268
+ },
269
+ };
270
+ });
348
271
  }
272
+ /**
273
+ * Test: Whoami endpoint with workload authentication
274
+ *
275
+ * This test calls the ESV mock server's /api/whoami endpoint to validate
276
+ * that the workload token issued by the application is valid and can be
277
+ * used for cross-service authentication. This allows each application to
278
+ * be tested independently without requiring other applications to be running.
279
+ */
349
280
  async function testWhoamiWithWorkload(config, token) {
350
- return runTest("Workload Authentication (Whoami)", async () => {
351
- const esvWhoamiUrl = `${config.esvUrl}/api/whoami`;
352
- const response = await globalThis.fetch(esvWhoamiUrl, {
353
- headers: {
354
- Authorization: `Bearer ${token}`
355
- }
356
- });
357
- if (response.status === 404) {
358
- return {
359
- details: {
360
- skipped: true,
361
- reason: "ESV whoami endpoint not available"
281
+ return runTest('Workload Authentication (Whoami)', async () => {
282
+ // Call the ESV mock server's whoami endpoint with the workload token
283
+ const esvWhoamiUrl = `${config.esvUrl}/api/whoami`;
284
+ const response = await globalThis.fetch(esvWhoamiUrl, {
285
+ headers: {
286
+ Authorization: `Bearer ${token}`,
287
+ },
288
+ });
289
+ // Should return 200 OK
290
+ if (response.status === 404) {
291
+ return {
292
+ details: {
293
+ skipped: true,
294
+ reason: 'ESV whoami endpoint not available',
295
+ },
296
+ };
362
297
  }
363
- };
298
+ assert(response.status === 200, `Expected 200 OK, got ${response.status}`);
299
+ const data = await response.json();
300
+ // Should have workload identity in response
301
+ assert(data.workload !== undefined, 'Response should include workload identity');
302
+ return {
303
+ details: {
304
+ workloadId: data.workload?.workload_id,
305
+ clientId: data.workload?.client_id,
306
+ },
307
+ };
308
+ });
309
+ }
310
+ /**
311
+ * Runs all Workload validation tests
312
+ */
313
+ export async function validateWorkload(config) {
314
+ const startTime = performance.now();
315
+ const mergedConfig = { ...DEFAULT_CONFIG, ...config };
316
+ const fetch = createFetcher(mergedConfig);
317
+ const tests = [];
318
+ // Test JWKS endpoint
319
+ tests.push(await testJwksEndpoint(mergedConfig, fetch));
320
+ // Test token endpoint and get a token for further tests
321
+ const tokenResult = await testTokenEndpoint(mergedConfig, fetch);
322
+ tests.push(tokenResult);
323
+ // Test validation endpoints
324
+ tests.push(await testValidateEndpointNoAuth(mergedConfig, fetch));
325
+ tests.push(await testValidateEndpointInvalid(mergedConfig, fetch));
326
+ // If we have a token, test validation with valid token
327
+ const token = tokenResult.token ?? config.validToken;
328
+ if (token) {
329
+ tests.push(await testValidateEndpoint(mergedConfig, fetch, token));
330
+ tests.push(await testWhoamiWithWorkload(mergedConfig, token));
331
+ }
332
+ else {
333
+ tests.push(skipTest('Workload Validate Endpoint', 'No valid token available'));
334
+ tests.push(skipTest('Workload Authentication (Whoami)', 'No valid token available'));
364
335
  }
365
- assert(response.status === 200, `Expected 200 OK, got ${response.status}`);
366
- const data = await response.json();
367
- assert(data.workload !== undefined, "Response should include workload identity");
336
+ // Test refresh endpoint
337
+ tests.push(await testRefreshEndpoint(mergedConfig, fetch));
338
+ const duration = performance.now() - startTime;
339
+ const passed = tests.filter((t) => t.passed).length;
340
+ const failed = tests.filter((t) => !t.passed).length;
341
+ const skipped = tests.filter((t) => t.details?.skipped).length;
368
342
  return {
369
- details: {
370
- workloadId: data.workload?.workload_id,
371
- clientId: data.workload?.client_id
372
- }
343
+ suite: 'Workload',
344
+ passed: failed === 0,
345
+ tests,
346
+ duration,
347
+ summary: {
348
+ total: tests.length,
349
+ passed: passed - skipped,
350
+ failed,
351
+ skipped,
352
+ },
373
353
  };
374
- });
375
- }
376
- async function validateWorkload(config) {
377
- const startTime = performance.now();
378
- const mergedConfig = { ...DEFAULT_CONFIG, ...config };
379
- const fetch2 = createFetcher(mergedConfig);
380
- const tests = [];
381
- tests.push(await testJwksEndpoint(mergedConfig, fetch2));
382
- const tokenResult = await testTokenEndpoint(mergedConfig, fetch2);
383
- tests.push(tokenResult);
384
- tests.push(await testValidateEndpointNoAuth(mergedConfig, fetch2));
385
- tests.push(await testValidateEndpointInvalid(mergedConfig, fetch2));
386
- const token = tokenResult.token ?? config.validToken;
387
- if (token) {
388
- tests.push(await testValidateEndpoint(mergedConfig, fetch2, token));
389
- tests.push(await testWhoamiWithWorkload(mergedConfig, token));
390
- } else {
391
- tests.push(skipTest("Workload Validate Endpoint", "No valid token available"));
392
- tests.push(skipTest("Workload Authentication (Whoami)", "No valid token available"));
393
- }
394
- tests.push(await testRefreshEndpoint(mergedConfig, fetch2));
395
- const duration = performance.now() - startTime;
396
- const passed = tests.filter((t) => t.passed).length;
397
- const failed = tests.filter((t) => !t.passed).length;
398
- const skipped = tests.filter((t) => t.details?.skipped).length;
399
- return {
400
- suite: "Workload",
401
- passed: failed === 0,
402
- tests,
403
- duration,
404
- summary: {
405
- total: tests.length,
406
- passed: passed - skipped,
407
- failed,
408
- skipped
409
- }
410
- };
411
354
  }
412
- function createWorkloadTests(config) {
413
- const mergedConfig = { ...DEFAULT_CONFIG, ...config };
414
- const fetch2 = createFetcher(mergedConfig);
415
- let acquiredToken;
416
- return [
417
- {
418
- name: "JWKS endpoint returns valid keys",
419
- fn: async () => {
420
- const result = await testJwksEndpoint(mergedConfig, fetch2);
421
- if (!result.passed)
422
- throw new Error(result.error);
423
- }
424
- },
425
- {
426
- name: "token endpoint returns access token",
427
- fn: async () => {
428
- const result = await testTokenEndpoint(mergedConfig, fetch2);
429
- if (!result.passed && !result.details?.skipped)
430
- throw new Error(result.error);
431
- acquiredToken = result.token;
432
- }
433
- },
434
- {
435
- name: "validate endpoint rejects missing auth",
436
- fn: async () => {
437
- const result = await testValidateEndpointNoAuth(mergedConfig, fetch2);
438
- if (!result.passed)
439
- throw new Error(result.error);
440
- }
441
- },
442
- {
443
- name: "validate endpoint rejects invalid tokens",
444
- fn: async () => {
445
- const result = await testValidateEndpointInvalid(mergedConfig, fetch2);
446
- if (!result.passed)
447
- throw new Error(result.error);
448
- }
449
- },
450
- {
451
- name: "validate endpoint accepts valid token",
452
- fn: async () => {
453
- const token = acquiredToken ?? config.validToken;
454
- if (!token) {
455
- console.warn("Skipping: No valid token available");
456
- return;
457
- }
458
- const result = await testValidateEndpoint(mergedConfig, fetch2, token);
459
- if (!result.passed)
460
- throw new Error(result.error);
461
- }
462
- },
463
- {
464
- name: "whoami endpoint works with workload authentication",
465
- fn: async () => {
466
- const token = acquiredToken ?? config.validToken;
467
- if (!token) {
468
- console.warn("Skipping: No valid token available");
469
- return;
470
- }
471
- const result = await testWhoamiWithWorkload(mergedConfig, token);
472
- if (!result.passed && !result.details?.skipped)
473
- throw new Error(result.error);
474
- }
475
- },
476
- {
477
- name: "refresh endpoint works",
478
- fn: async () => {
479
- const result = await testRefreshEndpoint(mergedConfig, fetch2);
480
- if (!result.passed && !result.details?.skipped)
481
- throw new Error(result.error);
482
- }
483
- }
484
- ];
355
+ /**
356
+ * Creates Vitest-compatible test suite for Workload validation
357
+ */
358
+ export function createWorkloadTests(config) {
359
+ const mergedConfig = { ...DEFAULT_CONFIG, ...config };
360
+ const fetch = createFetcher(mergedConfig);
361
+ // Store token for dependent tests
362
+ let acquiredToken;
363
+ return [
364
+ {
365
+ name: 'JWKS endpoint returns valid keys',
366
+ fn: async () => {
367
+ const result = await testJwksEndpoint(mergedConfig, fetch);
368
+ if (!result.passed)
369
+ throw new Error(result.error);
370
+ },
371
+ },
372
+ {
373
+ name: 'token endpoint returns access token',
374
+ fn: async () => {
375
+ const result = await testTokenEndpoint(mergedConfig, fetch);
376
+ if (!result.passed && !result.details?.skipped)
377
+ throw new Error(result.error);
378
+ acquiredToken = result.token;
379
+ },
380
+ },
381
+ {
382
+ name: 'validate endpoint rejects missing auth',
383
+ fn: async () => {
384
+ const result = await testValidateEndpointNoAuth(mergedConfig, fetch);
385
+ if (!result.passed)
386
+ throw new Error(result.error);
387
+ },
388
+ },
389
+ {
390
+ name: 'validate endpoint rejects invalid tokens',
391
+ fn: async () => {
392
+ const result = await testValidateEndpointInvalid(mergedConfig, fetch);
393
+ if (!result.passed)
394
+ throw new Error(result.error);
395
+ },
396
+ },
397
+ {
398
+ name: 'validate endpoint accepts valid token',
399
+ fn: async () => {
400
+ const token = acquiredToken ?? config.validToken;
401
+ if (!token) {
402
+ console.warn('Skipping: No valid token available');
403
+ return;
404
+ }
405
+ const result = await testValidateEndpoint(mergedConfig, fetch, token);
406
+ if (!result.passed)
407
+ throw new Error(result.error);
408
+ },
409
+ },
410
+ {
411
+ name: 'whoami endpoint works with workload authentication',
412
+ fn: async () => {
413
+ const token = acquiredToken ?? config.validToken;
414
+ if (!token) {
415
+ console.warn('Skipping: No valid token available');
416
+ return;
417
+ }
418
+ const result = await testWhoamiWithWorkload(mergedConfig, token);
419
+ if (!result.passed && !result.details?.skipped)
420
+ throw new Error(result.error);
421
+ },
422
+ },
423
+ {
424
+ name: 'refresh endpoint works',
425
+ fn: async () => {
426
+ const result = await testRefreshEndpoint(mergedConfig, fetch);
427
+ if (!result.passed && !result.details?.skipped)
428
+ throw new Error(result.error);
429
+ },
430
+ },
431
+ ];
485
432
  }
486
- var DEFAULT_CONFIG;
487
- var init_workload = __esm(() => {
488
- DEFAULT_CONFIG = {
489
- tokenPath: "/api/workload/token",
490
- validatePath: "/api/workload/validate",
491
- jwksPath: "/api/workload/jwks",
492
- refreshPath: "/api/workload/refresh",
493
- esvUrl: "http://localhost:3555"
494
- };
495
- });
496
- init_workload();
497
-
498
- export {
499
- validateWorkload,
500
- createWorkloadTests
501
- };
502
-
503
- //# debugId=A73B69B4545ADC4664756E2164756E21
433
+ //# sourceMappingURL=index.js.map