@frontmcp/testing 1.2.1 → 1.4.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/auth/token-factory.d.ts +28 -2
- package/auth/token-factory.d.ts.map +1 -1
- package/esm/fixtures/index.mjs +190 -161
- package/esm/index.mjs +66 -16
- package/esm/package.json +6 -6
- package/esm/perf/index.mjs +40 -12
- package/fixtures/index.js +187 -158
- package/fixtures/test-fixture.d.ts.map +1 -1
- package/index.d.ts +1 -1
- package/index.d.ts.map +1 -1
- package/index.js +63 -12
- package/package.json +6 -6
- package/perf/index.js +39 -11
- package/raw-client/index.d.ts +1 -0
- package/raw-client/index.d.ts.map +1 -1
- package/raw-client/port-probe.d.ts +15 -0
- package/raw-client/port-probe.d.ts.map +1 -0
- package/server/port-registry.d.ts +4 -0
- package/server/port-registry.d.ts.map +1 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@frontmcp/testing",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.4.0",
|
|
4
4
|
"description": "E2E testing framework for FrontMCP servers - MCP client, auth mocks, Playwright integration",
|
|
5
5
|
"author": "AgentFront <info@agentfront.dev>",
|
|
6
6
|
"homepage": "https://docs.agentfront.dev",
|
|
@@ -87,12 +87,12 @@
|
|
|
87
87
|
"./esm": null
|
|
88
88
|
},
|
|
89
89
|
"peerDependencies": {
|
|
90
|
-
"@frontmcp/sdk": "1.
|
|
91
|
-
"@frontmcp/ui": "1.
|
|
90
|
+
"@frontmcp/sdk": "1.4.0",
|
|
91
|
+
"@frontmcp/ui": "1.4.0",
|
|
92
92
|
"@playwright/test": "^1.40.0",
|
|
93
93
|
"jest": "^29.0.0 || ^30.0.0",
|
|
94
94
|
"@jest/globals": "^29.0.0 || ^30.0.0",
|
|
95
|
-
"@frontmcp/lazy-zod": "1.
|
|
95
|
+
"@frontmcp/lazy-zod": "1.4.0"
|
|
96
96
|
},
|
|
97
97
|
"peerDependenciesMeta": {
|
|
98
98
|
"@frontmcp/ui": {
|
|
@@ -112,8 +112,8 @@
|
|
|
112
112
|
"node": ">=24.0.0"
|
|
113
113
|
},
|
|
114
114
|
"dependencies": {
|
|
115
|
-
"@frontmcp/utils": "1.
|
|
116
|
-
"@frontmcp/protocol": "1.
|
|
115
|
+
"@frontmcp/utils": "1.4.0",
|
|
116
|
+
"@frontmcp/protocol": "1.4.0",
|
|
117
117
|
"jose": "^6.0.11",
|
|
118
118
|
"tslib": "^2.3.0"
|
|
119
119
|
},
|
package/perf/index.js
CHANGED
|
@@ -2911,15 +2911,39 @@ var TestTokenFactory = class {
|
|
|
2911
2911
|
publicKey = null;
|
|
2912
2912
|
jwk = null;
|
|
2913
2913
|
keyId;
|
|
2914
|
+
/** HS256 shared secret (gateway mode). Null → RS256 + JWKS (default). */
|
|
2915
|
+
hmacSecret;
|
|
2914
2916
|
constructor(options = {}) {
|
|
2915
2917
|
this.issuer = options.issuer ?? "https://test.frontmcp.local";
|
|
2916
2918
|
this.audience = options.audience ?? "frontmcp-test";
|
|
2917
2919
|
this.keyId = `test-key-${Date.now()}`;
|
|
2920
|
+
this.hmacSecret = options.hmacSecret ? new TextEncoder().encode(options.hmacSecret) : null;
|
|
2921
|
+
}
|
|
2922
|
+
/** True when this factory signs HS256 with a shared secret (gateway mode). */
|
|
2923
|
+
get isHmac() {
|
|
2924
|
+
return this.hmacSecret !== null;
|
|
2925
|
+
}
|
|
2926
|
+
/** The JWS `alg` this factory signs with. */
|
|
2927
|
+
get alg() {
|
|
2928
|
+
return this.isHmac ? "HS256" : "RS256";
|
|
2929
|
+
}
|
|
2930
|
+
/**
|
|
2931
|
+
* The signing key: the shared HMAC secret in gateway mode, otherwise the
|
|
2932
|
+
* generated RSA private key (lazily created via ensureKeys()).
|
|
2933
|
+
*/
|
|
2934
|
+
signingKey() {
|
|
2935
|
+
if (this.hmacSecret) return this.hmacSecret;
|
|
2936
|
+
if (!this.privateKey) {
|
|
2937
|
+
throw new Error("Private key not initialized");
|
|
2938
|
+
}
|
|
2939
|
+
return this.privateKey;
|
|
2918
2940
|
}
|
|
2919
2941
|
/**
|
|
2920
|
-
* Initialize the key pair (called automatically on first use)
|
|
2942
|
+
* Initialize the key pair (called automatically on first use). No-op in HS256
|
|
2943
|
+
* mode — there is no asymmetric keypair, just the shared secret.
|
|
2921
2944
|
*/
|
|
2922
2945
|
async ensureKeys() {
|
|
2946
|
+
if (this.isHmac) return;
|
|
2923
2947
|
if (this.privateKey && this.publicKey) return;
|
|
2924
2948
|
const { publicKey, privateKey } = await (0, import_jose.generateKeyPair)("RS256", {
|
|
2925
2949
|
extractable: true
|
|
@@ -2947,10 +2971,8 @@ var TestTokenFactory = class {
|
|
|
2947
2971
|
scope: options.scopes?.join(" "),
|
|
2948
2972
|
...options.claims
|
|
2949
2973
|
};
|
|
2950
|
-
|
|
2951
|
-
|
|
2952
|
-
}
|
|
2953
|
-
const token = await new import_jose.SignJWT(payload).setProtectedHeader({ alg: "RS256", kid: this.keyId }).sign(this.privateKey);
|
|
2974
|
+
const header = this.isHmac ? { alg: this.alg, typ: "JWT" } : { alg: this.alg, kid: this.keyId };
|
|
2975
|
+
const token = await new import_jose.SignJWT(payload).setProtectedHeader(header).sign(this.signingKey());
|
|
2954
2976
|
return token;
|
|
2955
2977
|
}
|
|
2956
2978
|
/**
|
|
@@ -3009,10 +3031,8 @@ var TestTokenFactory = class {
|
|
|
3009
3031
|
exp: now - 3600
|
|
3010
3032
|
// Expired 1 hour ago
|
|
3011
3033
|
};
|
|
3012
|
-
|
|
3013
|
-
|
|
3014
|
-
}
|
|
3015
|
-
const token = await new import_jose.SignJWT(payload).setProtectedHeader({ alg: "RS256", kid: this.keyId }).sign(this.privateKey);
|
|
3034
|
+
const header = this.isHmac ? { alg: this.alg, typ: "JWT" } : { alg: this.alg, kid: this.keyId };
|
|
3035
|
+
const token = await new import_jose.SignJWT(payload).setProtectedHeader(header).sign(this.signingKey());
|
|
3016
3036
|
return token;
|
|
3017
3037
|
}
|
|
3018
3038
|
/**
|
|
@@ -3020,7 +3040,8 @@ var TestTokenFactory = class {
|
|
|
3020
3040
|
*/
|
|
3021
3041
|
createTokenWithInvalidSignature(options) {
|
|
3022
3042
|
const now = Math.floor(Date.now() / 1e3);
|
|
3023
|
-
const
|
|
3043
|
+
const headerObj = this.isHmac ? { alg: this.alg, typ: "JWT" } : { alg: this.alg, kid: this.keyId };
|
|
3044
|
+
const header = Buffer.from(JSON.stringify(headerObj)).toString("base64url");
|
|
3024
3045
|
const payload = Buffer.from(
|
|
3025
3046
|
JSON.stringify({
|
|
3026
3047
|
iss: this.issuer,
|
|
@@ -3034,9 +3055,15 @@ var TestTokenFactory = class {
|
|
|
3034
3055
|
return `${header}.${payload}.${signature}`;
|
|
3035
3056
|
}
|
|
3036
3057
|
/**
|
|
3037
|
-
* Get the public JWKS for verifying tokens
|
|
3058
|
+
* Get the public JWKS for verifying tokens.
|
|
3059
|
+
*
|
|
3060
|
+
* Only meaningful in RS256 mode — HS256 (gateway) tokens are verified against
|
|
3061
|
+
* the shared secret, not a published public key, so there is no JWKS to serve.
|
|
3038
3062
|
*/
|
|
3039
3063
|
async getPublicJwks() {
|
|
3064
|
+
if (this.isHmac) {
|
|
3065
|
+
throw new Error("getPublicJwks() is not available in HS256 mode (gateway tokens use a shared secret)");
|
|
3066
|
+
}
|
|
3040
3067
|
await this.ensureKeys();
|
|
3041
3068
|
if (!this.jwk) {
|
|
3042
3069
|
throw new Error("JWK not initialized");
|
|
@@ -3098,6 +3125,7 @@ var E2E_PORT_RANGES = {
|
|
|
3098
3125
|
"demo-e2e-orchestrated": { start: 50100, size: 10 },
|
|
3099
3126
|
"demo-e2e-transparent": { start: 50110, size: 10 },
|
|
3100
3127
|
"demo-e2e-cimd": { start: 50120, size: 10 },
|
|
3128
|
+
"demo-e2e-remote-proxy": { start: 50130, size: 10 },
|
|
3101
3129
|
// Feature E2E tests (50200-50299)
|
|
3102
3130
|
"demo-e2e-skills": { start: 50200, size: 10 },
|
|
3103
3131
|
"demo-e2e-remote": { start: 50210, size: 10 },
|
package/raw-client/index.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/raw-client/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,OAAO,EAAE,MAAM,IAAI,SAAS,EAAE,MAAM,oBAAoB,CAAC;AACzD,OAAO,EAAE,oBAAoB,IAAI,uBAAuB,EAAE,MAAM,oBAAoB,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/raw-client/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,OAAO,EAAE,MAAM,IAAI,SAAS,EAAE,MAAM,oBAAoB,CAAC;AACzD,OAAO,EAAE,oBAAoB,IAAI,uBAAuB,EAAE,MAAM,oBAAoB,CAAC;AACrF,OAAO,EAAE,kBAAkB,EAAE,MAAM,cAAc,CAAC"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Resolve `true` iff a TCP server is currently accepting connections on
|
|
3
|
+
* `host:port`, otherwise `false`.
|
|
4
|
+
*
|
|
5
|
+
* A refused connection (`ECONNREFUSED`) or a timeout resolves to `false`; a
|
|
6
|
+
* successful connect resolves to `true` and the probe socket is closed
|
|
7
|
+
* immediately. Probing loopback makes "nothing listening" fail fast, so the
|
|
8
|
+
* timeout only guards against a silently-dropped connection.
|
|
9
|
+
*
|
|
10
|
+
* @param port - TCP port to probe
|
|
11
|
+
* @param host - Host/interface to probe (default `127.0.0.1`)
|
|
12
|
+
* @param timeoutMs - Max time to wait for a connection (default `1500`)
|
|
13
|
+
*/
|
|
14
|
+
export declare function isTcpPortListening(port: number, host?: string, timeoutMs?: number): Promise<boolean>;
|
|
15
|
+
//# sourceMappingURL=port-probe.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"port-probe.d.ts","sourceRoot":"","sources":["../../src/raw-client/port-probe.ts"],"names":[],"mappings":"AAQA;;;;;;;;;;;;GAYG;AACH,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,SAAc,EAAE,SAAS,SAAO,GAAG,OAAO,CAAC,OAAO,CAAC,CAoBvG"}
|
|
@@ -67,6 +67,10 @@ export declare const E2E_PORT_RANGES: {
|
|
|
67
67
|
readonly start: 50120;
|
|
68
68
|
readonly size: 10;
|
|
69
69
|
};
|
|
70
|
+
readonly 'demo-e2e-remote-proxy': {
|
|
71
|
+
readonly start: 50130;
|
|
72
|
+
readonly size: 10;
|
|
73
|
+
};
|
|
70
74
|
readonly 'demo-e2e-skills': {
|
|
71
75
|
readonly start: 50200;
|
|
72
76
|
readonly size: 10;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"port-registry.d.ts","sourceRoot":"","sources":["../../src/server/port-registry.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAQH;;;;;;GAMG;AACH,eAAO,MAAM,eAAe
|
|
1
|
+
{"version":3,"file":"port-registry.d.ts","sourceRoot":"","sources":["../../src/server/port-registry.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAQH;;;;;;GAMG;AACH,eAAO,MAAM,eAAe;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA2DlB,CAAC;AAEX,MAAM,MAAM,UAAU,GAAG,MAAM,OAAO,eAAe,CAAC;AAuBtD;;GAEG;AACH,wBAAgB,YAAY,CAAC,OAAO,EAAE,MAAM,GAAG;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,CAM7E;AAED;;;;;;;;;GASG;AACH,wBAAsB,WAAW,CAC/B,OAAO,EAAE,MAAM,EACf,aAAa,CAAC,EAAE,MAAM,GACrB,OAAO,CAAC;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAA;CAAE,CAAC,CAiEzD;AAkFD;;GAEG;AACH,wBAAgB,cAAc,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAGtD;AAED;;GAEG;AACH,wBAAgB,eAAe,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,EAAE,CAWxE;AAED;;GAEG;AACH,wBAAsB,eAAe,IAAI,OAAO,CAAC,IAAI,CAAC,CAGrD;AAED;;GAEG;AACH,wBAAgB,gBAAgB,IAAI,KAAK,CAAC;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAC;IAAC,UAAU,EAAE,MAAM,CAAA;CAAE,CAAC,CAM/F"}
|