@frontmcp/testing 0.5.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.
- package/LICENSE +201 -0
- package/README.md +1358 -0
- package/jest-preset.js +61 -0
- package/package.json +94 -0
- package/src/assertions/index.d.ts +5 -0
- package/src/assertions/index.js +18 -0
- package/src/assertions/index.js.map +1 -0
- package/src/assertions/mcp-assertions.d.ts +81 -0
- package/src/assertions/mcp-assertions.js +220 -0
- package/src/assertions/mcp-assertions.js.map +1 -0
- package/src/auth/auth-headers.d.ts +29 -0
- package/src/auth/auth-headers.js +62 -0
- package/src/auth/auth-headers.js.map +1 -0
- package/src/auth/index.d.ts +9 -0
- package/src/auth/index.js +15 -0
- package/src/auth/index.js.map +1 -0
- package/src/auth/token-factory.d.ts +94 -0
- package/src/auth/token-factory.js +181 -0
- package/src/auth/token-factory.js.map +1 -0
- package/src/auth/user-fixtures.d.ts +26 -0
- package/src/auth/user-fixtures.js +92 -0
- package/src/auth/user-fixtures.js.map +1 -0
- package/src/client/index.d.ts +7 -0
- package/src/client/index.js +12 -0
- package/src/client/index.js.map +1 -0
- package/src/client/mcp-test-client.builder.d.ts +72 -0
- package/src/client/mcp-test-client.builder.js +111 -0
- package/src/client/mcp-test-client.builder.js.map +1 -0
- package/src/client/mcp-test-client.d.ts +360 -0
- package/src/client/mcp-test-client.js +929 -0
- package/src/client/mcp-test-client.js.map +1 -0
- package/src/client/mcp-test-client.types.d.ts +216 -0
- package/src/client/mcp-test-client.types.js +7 -0
- package/src/client/mcp-test-client.types.js.map +1 -0
- package/src/errors/index.d.ts +45 -0
- package/src/errors/index.js +85 -0
- package/src/errors/index.js.map +1 -0
- package/src/expect.d.ts +67 -0
- package/src/expect.js +31 -0
- package/src/expect.js.map +1 -0
- package/src/fixtures/fixture-types.d.ts +166 -0
- package/src/fixtures/fixture-types.js +7 -0
- package/src/fixtures/fixture-types.js.map +1 -0
- package/src/fixtures/index.d.ts +7 -0
- package/src/fixtures/index.js +16 -0
- package/src/fixtures/index.js.map +1 -0
- package/src/fixtures/test-fixture.d.ts +41 -0
- package/src/fixtures/test-fixture.js +280 -0
- package/src/fixtures/test-fixture.js.map +1 -0
- package/src/http-mock/http-mock.d.ts +84 -0
- package/src/http-mock/http-mock.js +544 -0
- package/src/http-mock/http-mock.js.map +1 -0
- package/src/http-mock/http-mock.types.d.ts +124 -0
- package/src/http-mock/http-mock.types.js +10 -0
- package/src/http-mock/http-mock.types.js.map +1 -0
- package/src/http-mock/index.d.ts +6 -0
- package/src/http-mock/index.js +11 -0
- package/src/http-mock/index.js.map +1 -0
- package/src/index.d.ts +65 -0
- package/src/index.js +128 -0
- package/src/index.js.map +1 -0
- package/src/interceptor/index.d.ts +7 -0
- package/src/interceptor/index.js +15 -0
- package/src/interceptor/index.js.map +1 -0
- package/src/interceptor/interceptor-chain.d.ts +77 -0
- package/src/interceptor/interceptor-chain.js +207 -0
- package/src/interceptor/interceptor-chain.js.map +1 -0
- package/src/interceptor/interceptor.types.d.ts +131 -0
- package/src/interceptor/interceptor.types.js +7 -0
- package/src/interceptor/interceptor.types.js.map +1 -0
- package/src/interceptor/mock-registry.d.ts +82 -0
- package/src/interceptor/mock-registry.js +189 -0
- package/src/interceptor/mock-registry.js.map +1 -0
- package/src/matchers/index.d.ts +7 -0
- package/src/matchers/index.js +12 -0
- package/src/matchers/index.js.map +1 -0
- package/src/matchers/matcher-types.d.ts +266 -0
- package/src/matchers/matcher-types.js +10 -0
- package/src/matchers/matcher-types.js.map +1 -0
- package/src/matchers/mcp-matchers.d.ts +47 -0
- package/src/matchers/mcp-matchers.js +391 -0
- package/src/matchers/mcp-matchers.js.map +1 -0
- package/src/playwright/index.d.ts +37 -0
- package/src/playwright/index.js +49 -0
- package/src/playwright/index.js.map +1 -0
- package/src/server/index.d.ts +6 -0
- package/src/server/index.js +10 -0
- package/src/server/index.js.map +1 -0
- package/src/server/test-server.d.ts +99 -0
- package/src/server/test-server.js +286 -0
- package/src/server/test-server.js.map +1 -0
- package/src/setup.d.ts +22 -0
- package/src/setup.js +30 -0
- package/src/setup.js.map +1 -0
- package/src/transport/index.d.ts +6 -0
- package/src/transport/index.js +10 -0
- package/src/transport/index.js.map +1 -0
- package/src/transport/streamable-http.transport.d.ts +65 -0
- package/src/transport/streamable-http.transport.js +432 -0
- package/src/transport/streamable-http.transport.js.map +1 -0
- package/src/transport/transport.interface.d.ts +124 -0
- package/src/transport/transport.interface.js +7 -0
- package/src/transport/transport.interface.js.map +1 -0
- package/src/ui/index.d.ts +17 -0
- package/src/ui/index.js +23 -0
- package/src/ui/index.js.map +1 -0
- package/src/ui/ui-assertions.d.ts +94 -0
- package/src/ui/ui-assertions.js +215 -0
- package/src/ui/ui-assertions.js.map +1 -0
- package/src/ui/ui-matchers.d.ts +39 -0
- package/src/ui/ui-matchers.js +275 -0
- package/src/ui/ui-matchers.js.map +1 -0
package/jest-preset.js
ADDED
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file jest-preset.js
|
|
3
|
+
* @description Jest preset for @frontmcp/testing
|
|
4
|
+
*
|
|
5
|
+
* Usage in jest.config.ts or jest.e2e.config.ts:
|
|
6
|
+
* ```typescript
|
|
7
|
+
* export default {
|
|
8
|
+
* preset: '@frontmcp/testing/jest-preset',
|
|
9
|
+
* // Your additional config...
|
|
10
|
+
* };
|
|
11
|
+
* ```
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
module.exports = {
|
|
15
|
+
// Use Node.js environment for E2E tests
|
|
16
|
+
testEnvironment: 'node',
|
|
17
|
+
|
|
18
|
+
// Transform TypeScript files
|
|
19
|
+
transform: {
|
|
20
|
+
'^.+\\.tsx?$': [
|
|
21
|
+
'ts-jest',
|
|
22
|
+
{
|
|
23
|
+
useESM: false,
|
|
24
|
+
tsconfig: {
|
|
25
|
+
// Allow importing .js extensions for ESM compatibility
|
|
26
|
+
moduleResolution: 'node',
|
|
27
|
+
},
|
|
28
|
+
},
|
|
29
|
+
],
|
|
30
|
+
},
|
|
31
|
+
|
|
32
|
+
// File extensions to consider
|
|
33
|
+
moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx', 'json', 'node'],
|
|
34
|
+
|
|
35
|
+
// Default test timeout (30 seconds for E2E)
|
|
36
|
+
testTimeout: 30000,
|
|
37
|
+
|
|
38
|
+
// Setup files that run after Jest is initialized
|
|
39
|
+
// Path resolves to the compiled output when used from node_modules/@frontmcp/testing
|
|
40
|
+
setupFilesAfterEnv: [require.resolve('@frontmcp/testing/setup')],
|
|
41
|
+
|
|
42
|
+
// Test file patterns for E2E tests
|
|
43
|
+
testMatch: ['**/*.e2e.ts', '**/*.e2e.js', '**/e2e/**/*.test.ts', '**/e2e/**/*.test.js'],
|
|
44
|
+
|
|
45
|
+
// Module name mapping for path aliases
|
|
46
|
+
// Note: These point to dist/src/ since the package exports declare dist/src/index.js
|
|
47
|
+
moduleNameMapper: {
|
|
48
|
+
// Map @frontmcp/testing to the installed package
|
|
49
|
+
'^@frontmcp/testing$': '<rootDir>/node_modules/@frontmcp/testing/dist/src/index.js',
|
|
50
|
+
'^@frontmcp/testing/(.*)$': '<rootDir>/node_modules/@frontmcp/testing/dist/src/$1',
|
|
51
|
+
},
|
|
52
|
+
|
|
53
|
+
// Ignore patterns
|
|
54
|
+
testPathIgnorePatterns: ['/node_modules/', '/dist/'],
|
|
55
|
+
|
|
56
|
+
// Coverage settings (optional, disabled by default for E2E)
|
|
57
|
+
collectCoverage: false,
|
|
58
|
+
|
|
59
|
+
// Verbose output
|
|
60
|
+
verbose: true,
|
|
61
|
+
};
|
package/package.json
ADDED
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@frontmcp/testing",
|
|
3
|
+
"version": "0.5.0",
|
|
4
|
+
"description": "E2E testing framework for FrontMCP servers - MCP client, auth mocks, Playwright integration",
|
|
5
|
+
"author": "AgentFront <info@agentfront.dev>",
|
|
6
|
+
"homepage": "https://docs.agentfront.dev",
|
|
7
|
+
"license": "Apache-2.0",
|
|
8
|
+
"keywords": [
|
|
9
|
+
"mcp",
|
|
10
|
+
"testing",
|
|
11
|
+
"e2e",
|
|
12
|
+
"integration-testing",
|
|
13
|
+
"mcp-client",
|
|
14
|
+
"playwright",
|
|
15
|
+
"agentfront",
|
|
16
|
+
"frontmcp",
|
|
17
|
+
"framework",
|
|
18
|
+
"typescript"
|
|
19
|
+
],
|
|
20
|
+
"repository": {
|
|
21
|
+
"type": "git",
|
|
22
|
+
"url": "git+https://github.com/agentfront/frontmcp.git"
|
|
23
|
+
},
|
|
24
|
+
"bugs": {
|
|
25
|
+
"url": "https://github.com/agentfront/frontmcp/issues"
|
|
26
|
+
},
|
|
27
|
+
"main": "./src/index.js",
|
|
28
|
+
"types": "./src/index.d.ts",
|
|
29
|
+
"exports": {
|
|
30
|
+
"./package.json": "./package.json",
|
|
31
|
+
".": {
|
|
32
|
+
"types": "./src/index.d.ts",
|
|
33
|
+
"import": "./src/index.js",
|
|
34
|
+
"default": "./src/index.js"
|
|
35
|
+
},
|
|
36
|
+
"./setup": {
|
|
37
|
+
"types": "./src/setup.d.ts",
|
|
38
|
+
"import": "./src/setup.js",
|
|
39
|
+
"default": "./src/setup.js"
|
|
40
|
+
},
|
|
41
|
+
"./jest-preset": {
|
|
42
|
+
"default": "./jest-preset.js"
|
|
43
|
+
},
|
|
44
|
+
"./fixtures": {
|
|
45
|
+
"types": "./src/fixtures/index.d.ts",
|
|
46
|
+
"import": "./src/fixtures/index.js",
|
|
47
|
+
"default": "./src/fixtures/index.js"
|
|
48
|
+
},
|
|
49
|
+
"./matchers": {
|
|
50
|
+
"types": "./src/matchers/index.d.ts",
|
|
51
|
+
"import": "./src/matchers/index.js",
|
|
52
|
+
"default": "./src/matchers/index.js"
|
|
53
|
+
},
|
|
54
|
+
"./playwright": {
|
|
55
|
+
"types": "./src/playwright/index.d.ts",
|
|
56
|
+
"import": "./src/playwright/index.js",
|
|
57
|
+
"default": "./src/playwright/index.js"
|
|
58
|
+
}
|
|
59
|
+
},
|
|
60
|
+
"peerDependencies": {
|
|
61
|
+
"@frontmcp/sdk": "0.5.0",
|
|
62
|
+
"@frontmcp/ui": "0.5.0",
|
|
63
|
+
"@playwright/test": "^1.40.0",
|
|
64
|
+
"jest": "^29.0.0",
|
|
65
|
+
"@jest/globals": "^29.0.0"
|
|
66
|
+
},
|
|
67
|
+
"peerDependenciesMeta": {
|
|
68
|
+
"@frontmcp/ui": {
|
|
69
|
+
"optional": true
|
|
70
|
+
},
|
|
71
|
+
"@playwright/test": {
|
|
72
|
+
"optional": true
|
|
73
|
+
},
|
|
74
|
+
"jest": {
|
|
75
|
+
"optional": true
|
|
76
|
+
},
|
|
77
|
+
"@jest/globals": {
|
|
78
|
+
"optional": true
|
|
79
|
+
}
|
|
80
|
+
},
|
|
81
|
+
"engines": {
|
|
82
|
+
"node": ">=22.0.0"
|
|
83
|
+
},
|
|
84
|
+
"dependencies": {
|
|
85
|
+
"@modelcontextprotocol/sdk": "1.23.0",
|
|
86
|
+
"jose": "^6.0.11",
|
|
87
|
+
"tslib": "^2.3.0"
|
|
88
|
+
},
|
|
89
|
+
"devDependencies": {
|
|
90
|
+
"@types/jest": "^29.5.14",
|
|
91
|
+
"typescript": "^5.9.3"
|
|
92
|
+
},
|
|
93
|
+
"type": "commonjs"
|
|
94
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* @file assertions/index.ts
|
|
4
|
+
* @description MCP assertions exports
|
|
5
|
+
*/
|
|
6
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
|
+
exports.hasMimeType = exports.hasTextContent = exports.isError = exports.isSuccessful = exports.containsPrompt = exports.containsResourceTemplate = exports.containsResource = exports.containsTool = exports.McpAssertions = void 0;
|
|
8
|
+
var mcp_assertions_1 = require("./mcp-assertions");
|
|
9
|
+
Object.defineProperty(exports, "McpAssertions", { enumerable: true, get: function () { return mcp_assertions_1.McpAssertions; } });
|
|
10
|
+
Object.defineProperty(exports, "containsTool", { enumerable: true, get: function () { return mcp_assertions_1.containsTool; } });
|
|
11
|
+
Object.defineProperty(exports, "containsResource", { enumerable: true, get: function () { return mcp_assertions_1.containsResource; } });
|
|
12
|
+
Object.defineProperty(exports, "containsResourceTemplate", { enumerable: true, get: function () { return mcp_assertions_1.containsResourceTemplate; } });
|
|
13
|
+
Object.defineProperty(exports, "containsPrompt", { enumerable: true, get: function () { return mcp_assertions_1.containsPrompt; } });
|
|
14
|
+
Object.defineProperty(exports, "isSuccessful", { enumerable: true, get: function () { return mcp_assertions_1.isSuccessful; } });
|
|
15
|
+
Object.defineProperty(exports, "isError", { enumerable: true, get: function () { return mcp_assertions_1.isError; } });
|
|
16
|
+
Object.defineProperty(exports, "hasTextContent", { enumerable: true, get: function () { return mcp_assertions_1.hasTextContent; } });
|
|
17
|
+
Object.defineProperty(exports, "hasMimeType", { enumerable: true, get: function () { return mcp_assertions_1.hasMimeType; } });
|
|
18
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/assertions/index.ts"],"names":[],"mappings":";AAAA;;;GAGG;;;AAEH,mDAU0B;AATxB,+GAAA,aAAa,OAAA;AACb,8GAAA,YAAY,OAAA;AACZ,kHAAA,gBAAgB,OAAA;AAChB,0HAAA,wBAAwB,OAAA;AACxB,gHAAA,cAAc,OAAA;AACd,8GAAA,YAAY,OAAA;AACZ,yGAAA,OAAO,OAAA;AACP,gHAAA,cAAc,OAAA;AACd,6GAAA,WAAW,OAAA","sourcesContent":["/**\n * @file assertions/index.ts\n * @description MCP assertions exports\n */\n\nexport {\n McpAssertions,\n containsTool,\n containsResource,\n containsResourceTemplate,\n containsPrompt,\n isSuccessful,\n isError,\n hasTextContent,\n hasMimeType,\n} from './mcp-assertions';\n"]}
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file mcp-assertions.ts
|
|
3
|
+
* @description MCP-specific test assertions
|
|
4
|
+
*/
|
|
5
|
+
import type { McpResponse, ToolResultWrapper, ResourceContentWrapper, McpErrorInfo } from '../client';
|
|
6
|
+
import type { Tool, Resource, ResourceTemplate, Prompt, CallToolResult, ReadResourceResult } from '@modelcontextprotocol/sdk/types.js';
|
|
7
|
+
/**
|
|
8
|
+
* MCP-specific assertion helpers
|
|
9
|
+
*/
|
|
10
|
+
export declare const McpAssertions: {
|
|
11
|
+
/**
|
|
12
|
+
* Assert that an MCP response was successful and return the data
|
|
13
|
+
* @throws Error if response was not successful
|
|
14
|
+
*/
|
|
15
|
+
assertSuccess<T>(response: McpResponse<T>, message?: string): T;
|
|
16
|
+
/**
|
|
17
|
+
* Assert that an MCP response was an error
|
|
18
|
+
* @param expectedCode Optional expected error code
|
|
19
|
+
*/
|
|
20
|
+
assertError<T>(response: McpResponse<T>, expectedCode?: number): McpErrorInfo;
|
|
21
|
+
/**
|
|
22
|
+
* Assert that a tool call was successful (not isError)
|
|
23
|
+
*/
|
|
24
|
+
assertToolSuccess(result: ToolResultWrapper | McpResponse<CallToolResult>): void;
|
|
25
|
+
/**
|
|
26
|
+
* Assert that a tool result has specific content type
|
|
27
|
+
*/
|
|
28
|
+
assertToolContent(result: ToolResultWrapper | McpResponse<CallToolResult>, type: "text" | "image" | "resource"): void;
|
|
29
|
+
/**
|
|
30
|
+
* Assert that a resource read was successful and return the text content
|
|
31
|
+
*/
|
|
32
|
+
assertTextResource(response: ResourceContentWrapper | McpResponse<ReadResourceResult>): string;
|
|
33
|
+
/**
|
|
34
|
+
* Assert that tools array contains a tool with given name
|
|
35
|
+
*/
|
|
36
|
+
assertContainsTool(tools: Tool[], name: string): Tool;
|
|
37
|
+
/**
|
|
38
|
+
* Assert that resources array contains a resource with given URI
|
|
39
|
+
*/
|
|
40
|
+
assertContainsResource(resources: Resource[], uri: string): Resource;
|
|
41
|
+
/**
|
|
42
|
+
* Assert that resource templates array contains a template with given URI template
|
|
43
|
+
*/
|
|
44
|
+
assertContainsResourceTemplate(templates: ResourceTemplate[], uriTemplate: string): ResourceTemplate;
|
|
45
|
+
/**
|
|
46
|
+
* Assert that prompts array contains a prompt with given name
|
|
47
|
+
*/
|
|
48
|
+
assertContainsPrompt(prompts: Prompt[], name: string): Prompt;
|
|
49
|
+
};
|
|
50
|
+
/**
|
|
51
|
+
* Check if tools array contains a tool with given name
|
|
52
|
+
*/
|
|
53
|
+
export declare function containsTool(tools: Tool[], name: string): boolean;
|
|
54
|
+
/**
|
|
55
|
+
* Check if resources array contains a resource with given URI
|
|
56
|
+
*/
|
|
57
|
+
export declare function containsResource(resources: Resource[], uri: string): boolean;
|
|
58
|
+
/**
|
|
59
|
+
* Check if resource templates array contains a template with given URI template
|
|
60
|
+
*/
|
|
61
|
+
export declare function containsResourceTemplate(templates: ResourceTemplate[], uriTemplate: string): boolean;
|
|
62
|
+
/**
|
|
63
|
+
* Check if prompts array contains a prompt with given name
|
|
64
|
+
*/
|
|
65
|
+
export declare function containsPrompt(prompts: Prompt[], name: string): boolean;
|
|
66
|
+
/**
|
|
67
|
+
* Check if result is successful
|
|
68
|
+
*/
|
|
69
|
+
export declare function isSuccessful(result: ToolResultWrapper | ResourceContentWrapper): boolean;
|
|
70
|
+
/**
|
|
71
|
+
* Check if result is an error
|
|
72
|
+
*/
|
|
73
|
+
export declare function isError(result: ToolResultWrapper | ResourceContentWrapper, expectedCode?: number): boolean;
|
|
74
|
+
/**
|
|
75
|
+
* Check if result has text content
|
|
76
|
+
*/
|
|
77
|
+
export declare function hasTextContent(result: ToolResultWrapper): boolean;
|
|
78
|
+
/**
|
|
79
|
+
* Check if resource has specific MIME type
|
|
80
|
+
*/
|
|
81
|
+
export declare function hasMimeType(result: ResourceContentWrapper, mimeType: string): boolean;
|
|
@@ -0,0 +1,220 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* @file mcp-assertions.ts
|
|
4
|
+
* @description MCP-specific test assertions
|
|
5
|
+
*/
|
|
6
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
|
+
exports.McpAssertions = void 0;
|
|
8
|
+
exports.containsTool = containsTool;
|
|
9
|
+
exports.containsResource = containsResource;
|
|
10
|
+
exports.containsResourceTemplate = containsResourceTemplate;
|
|
11
|
+
exports.containsPrompt = containsPrompt;
|
|
12
|
+
exports.isSuccessful = isSuccessful;
|
|
13
|
+
exports.isError = isError;
|
|
14
|
+
exports.hasTextContent = hasTextContent;
|
|
15
|
+
exports.hasMimeType = hasMimeType;
|
|
16
|
+
// ═══════════════════════════════════════════════════════════════════
|
|
17
|
+
// ASSERTION FUNCTIONS
|
|
18
|
+
// ═══════════════════════════════════════════════════════════════════
|
|
19
|
+
/**
|
|
20
|
+
* MCP-specific assertion helpers
|
|
21
|
+
*/
|
|
22
|
+
exports.McpAssertions = {
|
|
23
|
+
/**
|
|
24
|
+
* Assert that an MCP response was successful and return the data
|
|
25
|
+
* @throws Error if response was not successful
|
|
26
|
+
*/
|
|
27
|
+
assertSuccess(response, message) {
|
|
28
|
+
if (!response.success) {
|
|
29
|
+
const errorMsg = response.error?.message ?? 'Unknown error';
|
|
30
|
+
throw new Error(message ?? `Expected success but got error: ${errorMsg} (code: ${response.error?.code})`);
|
|
31
|
+
}
|
|
32
|
+
if (response.data === undefined) {
|
|
33
|
+
throw new Error(message ?? 'Expected data but got undefined');
|
|
34
|
+
}
|
|
35
|
+
return response.data;
|
|
36
|
+
},
|
|
37
|
+
/**
|
|
38
|
+
* Assert that an MCP response was an error
|
|
39
|
+
* @param expectedCode Optional expected error code
|
|
40
|
+
*/
|
|
41
|
+
assertError(response, expectedCode) {
|
|
42
|
+
if (response.success) {
|
|
43
|
+
throw new Error('Expected error but got success');
|
|
44
|
+
}
|
|
45
|
+
if (!response.error) {
|
|
46
|
+
throw new Error('Expected error info but got undefined');
|
|
47
|
+
}
|
|
48
|
+
if (expectedCode !== undefined && response.error.code !== expectedCode) {
|
|
49
|
+
throw new Error(`Expected error code ${expectedCode} but got ${response.error.code}: ${response.error.message}`);
|
|
50
|
+
}
|
|
51
|
+
return response.error;
|
|
52
|
+
},
|
|
53
|
+
/**
|
|
54
|
+
* Assert that a tool call was successful (not isError)
|
|
55
|
+
*/
|
|
56
|
+
assertToolSuccess(result) {
|
|
57
|
+
if ('raw' in result) {
|
|
58
|
+
// ToolResultWrapper
|
|
59
|
+
if (result.isError) {
|
|
60
|
+
throw new Error(`Tool call failed: ${result.error?.message ?? 'Unknown error'}`);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
else {
|
|
64
|
+
// McpResponse<CallToolResult>
|
|
65
|
+
if (!result.success) {
|
|
66
|
+
throw new Error(`Tool call failed: ${result.error?.message ?? 'Unknown error'}`);
|
|
67
|
+
}
|
|
68
|
+
if (result.data?.isError) {
|
|
69
|
+
throw new Error('Tool returned isError=true');
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
},
|
|
73
|
+
/**
|
|
74
|
+
* Assert that a tool result has specific content type
|
|
75
|
+
*/
|
|
76
|
+
assertToolContent(result, type) {
|
|
77
|
+
let content;
|
|
78
|
+
if ('raw' in result) {
|
|
79
|
+
content = result.raw.content;
|
|
80
|
+
}
|
|
81
|
+
else {
|
|
82
|
+
if (!result.success || !result.data) {
|
|
83
|
+
throw new Error('Tool call was not successful');
|
|
84
|
+
}
|
|
85
|
+
content = result.data.content;
|
|
86
|
+
}
|
|
87
|
+
const hasContent = content?.some((c) => c.type === type);
|
|
88
|
+
if (!hasContent) {
|
|
89
|
+
throw new Error(`Expected tool result to have ${type} content`);
|
|
90
|
+
}
|
|
91
|
+
},
|
|
92
|
+
/**
|
|
93
|
+
* Assert that a resource read was successful and return the text content
|
|
94
|
+
*/
|
|
95
|
+
assertTextResource(response) {
|
|
96
|
+
if ('raw' in response) {
|
|
97
|
+
// ResourceContentWrapper
|
|
98
|
+
if (response.isError) {
|
|
99
|
+
throw new Error(`Resource read failed: ${response.error?.message ?? 'Unknown error'}`);
|
|
100
|
+
}
|
|
101
|
+
const text = response.text();
|
|
102
|
+
if (text === undefined) {
|
|
103
|
+
throw new Error('Expected text content but got undefined');
|
|
104
|
+
}
|
|
105
|
+
return text;
|
|
106
|
+
}
|
|
107
|
+
else {
|
|
108
|
+
// McpResponse<ReadResourceResult>
|
|
109
|
+
if (!response.success || !response.data) {
|
|
110
|
+
throw new Error(`Resource read failed: ${response.error?.message ?? 'Unknown error'}`);
|
|
111
|
+
}
|
|
112
|
+
const content = response.data.contents?.[0];
|
|
113
|
+
if (!content || !('text' in content)) {
|
|
114
|
+
throw new Error('Expected text content but got undefined');
|
|
115
|
+
}
|
|
116
|
+
return content.text;
|
|
117
|
+
}
|
|
118
|
+
},
|
|
119
|
+
/**
|
|
120
|
+
* Assert that tools array contains a tool with given name
|
|
121
|
+
*/
|
|
122
|
+
assertContainsTool(tools, name) {
|
|
123
|
+
const tool = tools.find((t) => t.name === name);
|
|
124
|
+
if (!tool) {
|
|
125
|
+
const available = tools.map((t) => t.name).join(', ');
|
|
126
|
+
throw new Error(`Expected to find tool "${name}" but got: [${available}]`);
|
|
127
|
+
}
|
|
128
|
+
return tool;
|
|
129
|
+
},
|
|
130
|
+
/**
|
|
131
|
+
* Assert that resources array contains a resource with given URI
|
|
132
|
+
*/
|
|
133
|
+
assertContainsResource(resources, uri) {
|
|
134
|
+
const resource = resources.find((r) => r.uri === uri);
|
|
135
|
+
if (!resource) {
|
|
136
|
+
const available = resources.map((r) => r.uri).join(', ');
|
|
137
|
+
throw new Error(`Expected to find resource "${uri}" but got: [${available}]`);
|
|
138
|
+
}
|
|
139
|
+
return resource;
|
|
140
|
+
},
|
|
141
|
+
/**
|
|
142
|
+
* Assert that resource templates array contains a template with given URI template
|
|
143
|
+
*/
|
|
144
|
+
assertContainsResourceTemplate(templates, uriTemplate) {
|
|
145
|
+
const template = templates.find((t) => t.uriTemplate === uriTemplate);
|
|
146
|
+
if (!template) {
|
|
147
|
+
const available = templates.map((t) => t.uriTemplate).join(', ');
|
|
148
|
+
throw new Error(`Expected to find resource template "${uriTemplate}" but got: [${available}]`);
|
|
149
|
+
}
|
|
150
|
+
return template;
|
|
151
|
+
},
|
|
152
|
+
/**
|
|
153
|
+
* Assert that prompts array contains a prompt with given name
|
|
154
|
+
*/
|
|
155
|
+
assertContainsPrompt(prompts, name) {
|
|
156
|
+
const prompt = prompts.find((p) => p.name === name);
|
|
157
|
+
if (!prompt) {
|
|
158
|
+
const available = prompts.map((p) => p.name).join(', ');
|
|
159
|
+
throw new Error(`Expected to find prompt "${name}" but got: [${available}]`);
|
|
160
|
+
}
|
|
161
|
+
return prompt;
|
|
162
|
+
},
|
|
163
|
+
};
|
|
164
|
+
// ═══════════════════════════════════════════════════════════════════
|
|
165
|
+
// HELPER FUNCTIONS FOR CUSTOM MATCHERS
|
|
166
|
+
// ═══════════════════════════════════════════════════════════════════
|
|
167
|
+
/**
|
|
168
|
+
* Check if tools array contains a tool with given name
|
|
169
|
+
*/
|
|
170
|
+
function containsTool(tools, name) {
|
|
171
|
+
return tools.some((t) => t.name === name);
|
|
172
|
+
}
|
|
173
|
+
/**
|
|
174
|
+
* Check if resources array contains a resource with given URI
|
|
175
|
+
*/
|
|
176
|
+
function containsResource(resources, uri) {
|
|
177
|
+
return resources.some((r) => r.uri === uri);
|
|
178
|
+
}
|
|
179
|
+
/**
|
|
180
|
+
* Check if resource templates array contains a template with given URI template
|
|
181
|
+
*/
|
|
182
|
+
function containsResourceTemplate(templates, uriTemplate) {
|
|
183
|
+
return templates.some((t) => t.uriTemplate === uriTemplate);
|
|
184
|
+
}
|
|
185
|
+
/**
|
|
186
|
+
* Check if prompts array contains a prompt with given name
|
|
187
|
+
*/
|
|
188
|
+
function containsPrompt(prompts, name) {
|
|
189
|
+
return prompts.some((p) => p.name === name);
|
|
190
|
+
}
|
|
191
|
+
/**
|
|
192
|
+
* Check if result is successful
|
|
193
|
+
*/
|
|
194
|
+
function isSuccessful(result) {
|
|
195
|
+
return result.isSuccess;
|
|
196
|
+
}
|
|
197
|
+
/**
|
|
198
|
+
* Check if result is an error
|
|
199
|
+
*/
|
|
200
|
+
function isError(result, expectedCode) {
|
|
201
|
+
if (!result.isError)
|
|
202
|
+
return false;
|
|
203
|
+
if (expectedCode !== undefined) {
|
|
204
|
+
return result.error?.code === expectedCode;
|
|
205
|
+
}
|
|
206
|
+
return true;
|
|
207
|
+
}
|
|
208
|
+
/**
|
|
209
|
+
* Check if result has text content
|
|
210
|
+
*/
|
|
211
|
+
function hasTextContent(result) {
|
|
212
|
+
return result.hasTextContent();
|
|
213
|
+
}
|
|
214
|
+
/**
|
|
215
|
+
* Check if resource has specific MIME type
|
|
216
|
+
*/
|
|
217
|
+
function hasMimeType(result, mimeType) {
|
|
218
|
+
return result.hasMimeType(mimeType);
|
|
219
|
+
}
|
|
220
|
+
//# sourceMappingURL=mcp-assertions.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mcp-assertions.js","sourceRoot":"","sources":["../../../src/assertions/mcp-assertions.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * @file mcp-assertions.ts\n * @description MCP-specific test assertions\n */\n\nimport type { McpResponse, ToolResultWrapper, ResourceContentWrapper, McpErrorInfo } from '../client';\nimport type {\n Tool,\n Resource,\n ResourceTemplate,\n Prompt,\n CallToolResult,\n ReadResourceResult,\n} from '@modelcontextprotocol/sdk/types.js';\n\n// ═══════════════════════════════════════════════════════════════════\n// ASSERTION FUNCTIONS\n// ═══════════════════════════════════════════════════════════════════\n\n/**\n * MCP-specific assertion helpers\n */\nexport const McpAssertions = {\n /**\n * Assert that an MCP response was successful and return the data\n * @throws Error if response was not successful\n */\n assertSuccess<T>(response: McpResponse<T>, message?: string): T {\n if (!response.success) {\n const errorMsg = response.error?.message ?? 'Unknown error';\n throw new Error(message ?? `Expected success but got error: ${errorMsg} (code: ${response.error?.code})`);\n }\n if (response.data === undefined) {\n throw new Error(message ?? 'Expected data but got undefined');\n }\n return response.data;\n },\n\n /**\n * Assert that an MCP response was an error\n * @param expectedCode Optional expected error code\n */\n assertError<T>(response: McpResponse<T>, expectedCode?: number): McpErrorInfo {\n if (response.success) {\n throw new Error('Expected error but got success');\n }\n if (!response.error) {\n throw new Error('Expected error info but got undefined');\n }\n if (expectedCode !== undefined && response.error.code !== expectedCode) {\n throw new Error(`Expected error code ${expectedCode} but got ${response.error.code}: ${response.error.message}`);\n }\n return response.error;\n },\n\n /**\n * Assert that a tool call was successful (not isError)\n */\n assertToolSuccess(result: ToolResultWrapper | McpResponse<CallToolResult>): void {\n if ('raw' in result) {\n // ToolResultWrapper\n if (result.isError) {\n throw new Error(`Tool call failed: ${result.error?.message ?? 'Unknown error'}`);\n }\n } else {\n // McpResponse<CallToolResult>\n if (!result.success) {\n throw new Error(`Tool call failed: ${result.error?.message ?? 'Unknown error'}`);\n }\n if (result.data?.isError) {\n throw new Error('Tool returned isError=true');\n }\n }\n },\n\n /**\n * Assert that a tool result has specific content type\n */\n assertToolContent(\n result: ToolResultWrapper | McpResponse<CallToolResult>,\n type: 'text' | 'image' | 'resource',\n ): void {\n let content: CallToolResult['content'];\n\n if ('raw' in result) {\n content = result.raw.content;\n } else {\n if (!result.success || !result.data) {\n throw new Error('Tool call was not successful');\n }\n content = result.data.content;\n }\n\n const hasContent = content?.some((c) => c.type === type);\n if (!hasContent) {\n throw new Error(`Expected tool result to have ${type} content`);\n }\n },\n\n /**\n * Assert that a resource read was successful and return the text content\n */\n assertTextResource(response: ResourceContentWrapper | McpResponse<ReadResourceResult>): string {\n if ('raw' in response) {\n // ResourceContentWrapper\n if (response.isError) {\n throw new Error(`Resource read failed: ${response.error?.message ?? 'Unknown error'}`);\n }\n const text = response.text();\n if (text === undefined) {\n throw new Error('Expected text content but got undefined');\n }\n return text;\n } else {\n // McpResponse<ReadResourceResult>\n if (!response.success || !response.data) {\n throw new Error(`Resource read failed: ${response.error?.message ?? 'Unknown error'}`);\n }\n const content = response.data.contents?.[0];\n if (!content || !('text' in content)) {\n throw new Error('Expected text content but got undefined');\n }\n return content.text;\n }\n },\n\n /**\n * Assert that tools array contains a tool with given name\n */\n assertContainsTool(tools: Tool[], name: string): Tool {\n const tool = tools.find((t) => t.name === name);\n if (!tool) {\n const available = tools.map((t) => t.name).join(', ');\n throw new Error(`Expected to find tool \"${name}\" but got: [${available}]`);\n }\n return tool;\n },\n\n /**\n * Assert that resources array contains a resource with given URI\n */\n assertContainsResource(resources: Resource[], uri: string): Resource {\n const resource = resources.find((r) => r.uri === uri);\n if (!resource) {\n const available = resources.map((r) => r.uri).join(', ');\n throw new Error(`Expected to find resource \"${uri}\" but got: [${available}]`);\n }\n return resource;\n },\n\n /**\n * Assert that resource templates array contains a template with given URI template\n */\n assertContainsResourceTemplate(templates: ResourceTemplate[], uriTemplate: string): ResourceTemplate {\n const template = templates.find((t) => t.uriTemplate === uriTemplate);\n if (!template) {\n const available = templates.map((t) => t.uriTemplate).join(', ');\n throw new Error(`Expected to find resource template \"${uriTemplate}\" but got: [${available}]`);\n }\n return template;\n },\n\n /**\n * Assert that prompts array contains a prompt with given name\n */\n assertContainsPrompt(prompts: Prompt[], name: string): Prompt {\n const prompt = prompts.find((p) => p.name === name);\n if (!prompt) {\n const available = prompts.map((p) => p.name).join(', ');\n throw new Error(`Expected to find prompt \"${name}\" but got: [${available}]`);\n }\n return prompt;\n },\n};\n\n// ═══════════════════════════════════════════════════════════════════\n// HELPER FUNCTIONS FOR CUSTOM MATCHERS\n// ═══════════════════════════════════════════════════════════════════\n\n/**\n * Check if tools array contains a tool with given name\n */\nexport function containsTool(tools: Tool[], name: string): boolean {\n return tools.some((t) => t.name === name);\n}\n\n/**\n * Check if resources array contains a resource with given URI\n */\nexport function containsResource(resources: Resource[], uri: string): boolean {\n return resources.some((r) => r.uri === uri);\n}\n\n/**\n * Check if resource templates array contains a template with given URI template\n */\nexport function containsResourceTemplate(templates: ResourceTemplate[], uriTemplate: string): boolean {\n return templates.some((t) => t.uriTemplate === uriTemplate);\n}\n\n/**\n * Check if prompts array contains a prompt with given name\n */\nexport function containsPrompt(prompts: Prompt[], name: string): boolean {\n return prompts.some((p) => p.name === name);\n}\n\n/**\n * Check if result is successful\n */\nexport function isSuccessful(result: ToolResultWrapper | ResourceContentWrapper): boolean {\n return result.isSuccess;\n}\n\n/**\n * Check if result is an error\n */\nexport function isError(result: ToolResultWrapper | ResourceContentWrapper, expectedCode?: number): boolean {\n if (!result.isError) return false;\n if (expectedCode !== undefined) {\n return result.error?.code === expectedCode;\n }\n return true;\n}\n\n/**\n * Check if result has text content\n */\nexport function hasTextContent(result: ToolResultWrapper): boolean {\n return result.hasTextContent();\n}\n\n/**\n * Check if resource has specific MIME type\n */\nexport function hasMimeType(result: ResourceContentWrapper, mimeType: string): boolean {\n return result.hasMimeType(mimeType);\n}\n"]}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file auth-headers.ts
|
|
3
|
+
* @description Helper functions for building authentication headers
|
|
4
|
+
*/
|
|
5
|
+
/**
|
|
6
|
+
* Helper functions for building authentication headers
|
|
7
|
+
*/
|
|
8
|
+
export declare const AuthHeaders: {
|
|
9
|
+
/**
|
|
10
|
+
* Create Authorization header with Bearer token
|
|
11
|
+
*/
|
|
12
|
+
bearer(token: string): Record<string, string>;
|
|
13
|
+
/**
|
|
14
|
+
* Create headers with no authentication
|
|
15
|
+
*/
|
|
16
|
+
noAuth(): Record<string, string>;
|
|
17
|
+
/**
|
|
18
|
+
* Create full MCP request headers with auth and session
|
|
19
|
+
*/
|
|
20
|
+
mcpRequest(token: string, sessionId?: string): Record<string, string>;
|
|
21
|
+
/**
|
|
22
|
+
* Create headers for public mode (no auth required)
|
|
23
|
+
*/
|
|
24
|
+
publicMode(sessionId?: string): Record<string, string>;
|
|
25
|
+
/**
|
|
26
|
+
* Create headers with custom auth header value
|
|
27
|
+
*/
|
|
28
|
+
custom(headerName: string, headerValue: string): Record<string, string>;
|
|
29
|
+
};
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* @file auth-headers.ts
|
|
4
|
+
* @description Helper functions for building authentication headers
|
|
5
|
+
*/
|
|
6
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
|
+
exports.AuthHeaders = void 0;
|
|
8
|
+
/**
|
|
9
|
+
* Helper functions for building authentication headers
|
|
10
|
+
*/
|
|
11
|
+
exports.AuthHeaders = {
|
|
12
|
+
/**
|
|
13
|
+
* Create Authorization header with Bearer token
|
|
14
|
+
*/
|
|
15
|
+
bearer(token) {
|
|
16
|
+
return {
|
|
17
|
+
Authorization: `Bearer ${token}`,
|
|
18
|
+
};
|
|
19
|
+
},
|
|
20
|
+
/**
|
|
21
|
+
* Create headers with no authentication
|
|
22
|
+
*/
|
|
23
|
+
noAuth() {
|
|
24
|
+
return {};
|
|
25
|
+
},
|
|
26
|
+
/**
|
|
27
|
+
* Create full MCP request headers with auth and session
|
|
28
|
+
*/
|
|
29
|
+
mcpRequest(token, sessionId) {
|
|
30
|
+
const headers = {
|
|
31
|
+
'Content-Type': 'application/json',
|
|
32
|
+
Accept: 'application/json',
|
|
33
|
+
Authorization: `Bearer ${token}`,
|
|
34
|
+
};
|
|
35
|
+
if (sessionId) {
|
|
36
|
+
headers['mcp-session-id'] = sessionId;
|
|
37
|
+
}
|
|
38
|
+
return headers;
|
|
39
|
+
},
|
|
40
|
+
/**
|
|
41
|
+
* Create headers for public mode (no auth required)
|
|
42
|
+
*/
|
|
43
|
+
publicMode(sessionId) {
|
|
44
|
+
const headers = {
|
|
45
|
+
'Content-Type': 'application/json',
|
|
46
|
+
Accept: 'application/json',
|
|
47
|
+
};
|
|
48
|
+
if (sessionId) {
|
|
49
|
+
headers['mcp-session-id'] = sessionId;
|
|
50
|
+
}
|
|
51
|
+
return headers;
|
|
52
|
+
},
|
|
53
|
+
/**
|
|
54
|
+
* Create headers with custom auth header value
|
|
55
|
+
*/
|
|
56
|
+
custom(headerName, headerValue) {
|
|
57
|
+
return {
|
|
58
|
+
[headerName]: headerValue,
|
|
59
|
+
};
|
|
60
|
+
},
|
|
61
|
+
};
|
|
62
|
+
//# sourceMappingURL=auth-headers.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"auth-headers.js","sourceRoot":"","sources":["../../../src/auth/auth-headers.ts"],"names":[],"mappings":";AAAA;;;GAGG;;;AAEH;;GAEG;AACU,QAAA,WAAW,GAAG;IACzB;;OAEG;IACH,MAAM,CAAC,KAAa;QAClB,OAAO;YACL,aAAa,EAAE,UAAU,KAAK,EAAE;SACjC,CAAC;IACJ,CAAC;IAED;;OAEG;IACH,MAAM;QACJ,OAAO,EAAE,CAAC;IACZ,CAAC;IAED;;OAEG;IACH,UAAU,CAAC,KAAa,EAAE,SAAkB;QAC1C,MAAM,OAAO,GAA2B;YACtC,cAAc,EAAE,kBAAkB;YAClC,MAAM,EAAE,kBAAkB;YAC1B,aAAa,EAAE,UAAU,KAAK,EAAE;SACjC,CAAC;QAEF,IAAI,SAAS,EAAE,CAAC;YACd,OAAO,CAAC,gBAAgB,CAAC,GAAG,SAAS,CAAC;QACxC,CAAC;QAED,OAAO,OAAO,CAAC;IACjB,CAAC;IAED;;OAEG;IACH,UAAU,CAAC,SAAkB;QAC3B,MAAM,OAAO,GAA2B;YACtC,cAAc,EAAE,kBAAkB;YAClC,MAAM,EAAE,kBAAkB;SAC3B,CAAC;QAEF,IAAI,SAAS,EAAE,CAAC;YACd,OAAO,CAAC,gBAAgB,CAAC,GAAG,SAAS,CAAC;QACxC,CAAC;QAED,OAAO,OAAO,CAAC;IACjB,CAAC;IAED;;OAEG;IACH,MAAM,CAAC,UAAkB,EAAE,WAAmB;QAC5C,OAAO;YACL,CAAC,UAAU,CAAC,EAAE,WAAW;SAC1B,CAAC;IACJ,CAAC;CACF,CAAC","sourcesContent":["/**\n * @file auth-headers.ts\n * @description Helper functions for building authentication headers\n */\n\n/**\n * Helper functions for building authentication headers\n */\nexport const AuthHeaders = {\n /**\n * Create Authorization header with Bearer token\n */\n bearer(token: string): Record<string, string> {\n return {\n Authorization: `Bearer ${token}`,\n };\n },\n\n /**\n * Create headers with no authentication\n */\n noAuth(): Record<string, string> {\n return {};\n },\n\n /**\n * Create full MCP request headers with auth and session\n */\n mcpRequest(token: string, sessionId?: string): Record<string, string> {\n const headers: Record<string, string> = {\n 'Content-Type': 'application/json',\n Accept: 'application/json',\n Authorization: `Bearer ${token}`,\n };\n\n if (sessionId) {\n headers['mcp-session-id'] = sessionId;\n }\n\n return headers;\n },\n\n /**\n * Create headers for public mode (no auth required)\n */\n publicMode(sessionId?: string): Record<string, string> {\n const headers: Record<string, string> = {\n 'Content-Type': 'application/json',\n Accept: 'application/json',\n };\n\n if (sessionId) {\n headers['mcp-session-id'] = sessionId;\n }\n\n return headers;\n },\n\n /**\n * Create headers with custom auth header value\n */\n custom(headerName: string, headerValue: string): Record<string, string> {\n return {\n [headerName]: headerValue,\n };\n },\n};\n"]}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file auth/index.ts
|
|
3
|
+
* @description Auth testing utilities exports
|
|
4
|
+
*/
|
|
5
|
+
export { TestTokenFactory } from './token-factory';
|
|
6
|
+
export type { CreateTokenOptions, TokenFactoryOptions } from './token-factory';
|
|
7
|
+
export { AuthHeaders } from './auth-headers';
|
|
8
|
+
export { TestUsers, createTestUser } from './user-fixtures';
|
|
9
|
+
export type { TestUserFixture } from './user-fixtures';
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* @file auth/index.ts
|
|
4
|
+
* @description Auth testing utilities exports
|
|
5
|
+
*/
|
|
6
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
|
+
exports.createTestUser = exports.TestUsers = exports.AuthHeaders = exports.TestTokenFactory = void 0;
|
|
8
|
+
var token_factory_1 = require("./token-factory");
|
|
9
|
+
Object.defineProperty(exports, "TestTokenFactory", { enumerable: true, get: function () { return token_factory_1.TestTokenFactory; } });
|
|
10
|
+
var auth_headers_1 = require("./auth-headers");
|
|
11
|
+
Object.defineProperty(exports, "AuthHeaders", { enumerable: true, get: function () { return auth_headers_1.AuthHeaders; } });
|
|
12
|
+
var user_fixtures_1 = require("./user-fixtures");
|
|
13
|
+
Object.defineProperty(exports, "TestUsers", { enumerable: true, get: function () { return user_fixtures_1.TestUsers; } });
|
|
14
|
+
Object.defineProperty(exports, "createTestUser", { enumerable: true, get: function () { return user_fixtures_1.createTestUser; } });
|
|
15
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/auth/index.ts"],"names":[],"mappings":";AAAA;;;GAGG;;;AAEH,iDAAmD;AAA1C,iHAAA,gBAAgB,OAAA;AAGzB,+CAA6C;AAApC,2GAAA,WAAW,OAAA;AAEpB,iDAA4D;AAAnD,0GAAA,SAAS,OAAA;AAAE,+GAAA,cAAc,OAAA","sourcesContent":["/**\n * @file auth/index.ts\n * @description Auth testing utilities exports\n */\n\nexport { TestTokenFactory } from './token-factory';\nexport type { CreateTokenOptions, TokenFactoryOptions } from './token-factory';\n\nexport { AuthHeaders } from './auth-headers';\n\nexport { TestUsers, createTestUser } from './user-fixtures';\nexport type { TestUserFixture } from './user-fixtures';\n"]}
|