@pay-skill/sdk 0.1.1 → 0.1.3

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,154 +1,154 @@
1
- /**
2
- * Auth rejection tests — proves that:
3
- * 1. Requests without auth headers are rejected with 401
4
- * 2. Requests with invalid/wrong signatures are rejected with 401
5
- * 3. The SDK surfaces auth errors as PayServerError with correct statusCode
6
- */
7
-
8
- import { describe, it, beforeEach, afterEach } from "node:test";
9
- import assert from "node:assert/strict";
10
- import { createServer, type Server, type IncomingMessage, type ServerResponse } from "node:http";
11
- import { once } from "node:events";
12
-
13
- import { PayClient, PayServerError, CallbackSigner, RawKeySigner } from "../src/index.js";
14
- import type { Hex, Address } from "viem";
15
-
16
- const ANVIL_PK =
17
- "0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80" as Hex;
18
- const WRONG_PK =
19
- "0x59c6995e998f97a5a0044966f0945389dc9e86dae88c7a8412f4603b6b78690d" as Hex;
20
-
21
- const TEST_ROUTER = "0x5FbDB2315678afecb367f032d93F642f64180aa3" as Address;
22
- const TEST_CHAIN_ID = 8453;
23
- const VALID_ADDR = "0x" + "a1".repeat(20);
24
-
25
- /**
26
- * Minimal HTTP server that enforces X-Pay-* auth headers.
27
- * Returns 401 if any required header is missing, 200 otherwise.
28
- */
29
- function createAuthServer(): Server {
30
- return createServer((req: IncomingMessage, res: ServerResponse) => {
31
- const agent = req.headers["x-pay-agent"];
32
- const sig = req.headers["x-pay-signature"];
33
- const ts = req.headers["x-pay-timestamp"];
34
- const nonce = req.headers["x-pay-nonce"];
35
-
36
- if (!agent || !sig || !ts || !nonce) {
37
- res.writeHead(401, { "Content-Type": "application/json" });
38
- res.end(JSON.stringify({ error: "Missing auth headers" }));
39
- return;
40
- }
41
-
42
- // Check that sig looks like a real 65-byte hex signature
43
- const sigStr = Array.isArray(sig) ? sig[0] : sig;
44
- if (!sigStr.startsWith("0x") || sigStr.length !== 132) {
45
- res.writeHead(401, { "Content-Type": "application/json" });
46
- res.end(JSON.stringify({ error: "Invalid signature format" }));
47
- return;
48
- }
49
-
50
- // Check that sig is not all zeros (stub detection)
51
- if (sigStr === "0x" + "0".repeat(130)) {
52
- res.writeHead(401, { "Content-Type": "application/json" });
53
- res.end(JSON.stringify({ error: "Stub signature rejected" }));
54
- return;
55
- }
56
-
57
- // Auth passed — return mock data
58
- res.writeHead(200, { "Content-Type": "application/json" });
59
- res.end(
60
- JSON.stringify({
61
- address: agent,
62
- balance: 100_000_000,
63
- open_tabs: [],
64
- })
65
- );
66
- });
67
- }
68
-
69
- let server: Server;
70
- let baseUrl: string;
71
-
72
- describe("Auth rejection", () => {
73
- beforeEach(async () => {
74
- server = createAuthServer();
75
- server.listen(0); // random port
76
- await once(server, "listening");
77
- const addr = server.address();
78
- if (typeof addr === "object" && addr) {
79
- baseUrl = `http://127.0.0.1:${addr.port}`;
80
- }
81
- });
82
-
83
- afterEach(async () => {
84
- server.close();
85
- await once(server, "close");
86
- });
87
-
88
- it("rejects request without auth headers (no private key configured)", async () => {
89
- // Client with no auth config — sends no X-Pay-* headers
90
- const client = new PayClient({
91
- apiUrl: baseUrl,
92
- signer: new CallbackSigner((_h: Uint8Array) => new Uint8Array(65)),
93
- });
94
-
95
- await assert.rejects(
96
- () => client.getStatus(),
97
- (err: unknown) => {
98
- assert.ok(err instanceof PayServerError);
99
- assert.equal(err.statusCode, 401);
100
- assert.ok(err.message.includes("Missing auth headers"));
101
- return true;
102
- }
103
- );
104
- });
105
-
106
- it("rejects request with stub signer (all-zero signature)", async () => {
107
- // Client with a stub signer that returns zeros — server should reject
108
- const client = new PayClient({
109
- apiUrl: baseUrl,
110
- signer: new CallbackSigner((_h: Uint8Array) => new Uint8Array(65)),
111
- chainId: TEST_CHAIN_ID,
112
- routerAddress: TEST_ROUTER,
113
- });
114
-
115
- await assert.rejects(
116
- () => client.getStatus(),
117
- (err: unknown) => {
118
- assert.ok(err instanceof PayServerError);
119
- assert.equal(err.statusCode, 401);
120
- return true;
121
- }
122
- );
123
- });
124
-
125
- it("accepts request with valid auth headers (real signing)", async () => {
126
- const client = new PayClient({
127
- apiUrl: baseUrl,
128
- privateKey: ANVIL_PK,
129
- chainId: TEST_CHAIN_ID,
130
- routerAddress: TEST_ROUTER,
131
- });
132
-
133
- // Should NOT throw — server accepts valid auth
134
- const status = await client.getStatus();
135
- assert.ok(status.balance >= 0);
136
- });
137
-
138
- it("PayServerError has statusCode 401 for auth failures", async () => {
139
- // Directly verify error structure
140
- const client = new PayClient({
141
- apiUrl: baseUrl,
142
- signer: new CallbackSigner((_h: Uint8Array) => new Uint8Array(65)),
143
- });
144
-
145
- try {
146
- await client.getStatus();
147
- assert.fail("Should have thrown PayServerError");
148
- } catch (err) {
149
- assert.ok(err instanceof PayServerError, "must be PayServerError");
150
- assert.equal(err.statusCode, 401, "statusCode must be 401");
151
- assert.equal(err.code, "server_error");
152
- }
153
- });
154
- });
1
+ /**
2
+ * Auth rejection tests — proves that:
3
+ * 1. Requests without auth headers are rejected with 401
4
+ * 2. Requests with invalid/wrong signatures are rejected with 401
5
+ * 3. The SDK surfaces auth errors as PayServerError with correct statusCode
6
+ */
7
+
8
+ import { describe, it, beforeEach, afterEach } from "node:test";
9
+ import assert from "node:assert/strict";
10
+ import { createServer, type Server, type IncomingMessage, type ServerResponse } from "node:http";
11
+ import { once } from "node:events";
12
+
13
+ import { PayClient, PayServerError, CallbackSigner, RawKeySigner } from "../src/index.js";
14
+ import type { Hex, Address } from "viem";
15
+
16
+ const ANVIL_PK =
17
+ "0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80" as Hex;
18
+ const WRONG_PK =
19
+ "0x59c6995e998f97a5a0044966f0945389dc9e86dae88c7a8412f4603b6b78690d" as Hex;
20
+
21
+ const TEST_ROUTER = "0x5FbDB2315678afecb367f032d93F642f64180aa3" as Address;
22
+ const TEST_CHAIN_ID = 8453;
23
+ const VALID_ADDR = "0x" + "a1".repeat(20);
24
+
25
+ /**
26
+ * Minimal HTTP server that enforces X-Pay-* auth headers.
27
+ * Returns 401 if any required header is missing, 200 otherwise.
28
+ */
29
+ function createAuthServer(): Server {
30
+ return createServer((req: IncomingMessage, res: ServerResponse) => {
31
+ const agent = req.headers["x-pay-agent"];
32
+ const sig = req.headers["x-pay-signature"];
33
+ const ts = req.headers["x-pay-timestamp"];
34
+ const nonce = req.headers["x-pay-nonce"];
35
+
36
+ if (!agent || !sig || !ts || !nonce) {
37
+ res.writeHead(401, { "Content-Type": "application/json" });
38
+ res.end(JSON.stringify({ error: "Missing auth headers" }));
39
+ return;
40
+ }
41
+
42
+ // Check that sig looks like a real 65-byte hex signature
43
+ const sigStr = Array.isArray(sig) ? sig[0] : sig;
44
+ if (!sigStr.startsWith("0x") || sigStr.length !== 132) {
45
+ res.writeHead(401, { "Content-Type": "application/json" });
46
+ res.end(JSON.stringify({ error: "Invalid signature format" }));
47
+ return;
48
+ }
49
+
50
+ // Check that sig is not all zeros (stub detection)
51
+ if (sigStr === "0x" + "0".repeat(130)) {
52
+ res.writeHead(401, { "Content-Type": "application/json" });
53
+ res.end(JSON.stringify({ error: "Stub signature rejected" }));
54
+ return;
55
+ }
56
+
57
+ // Auth passed — return mock data
58
+ res.writeHead(200, { "Content-Type": "application/json" });
59
+ res.end(
60
+ JSON.stringify({
61
+ address: agent,
62
+ balance: 100_000_000,
63
+ open_tabs: [],
64
+ })
65
+ );
66
+ });
67
+ }
68
+
69
+ let server: Server;
70
+ let baseUrl: string;
71
+
72
+ describe("Auth rejection", () => {
73
+ beforeEach(async () => {
74
+ server = createAuthServer();
75
+ server.listen(0); // random port
76
+ await once(server, "listening");
77
+ const addr = server.address();
78
+ if (typeof addr === "object" && addr) {
79
+ baseUrl = `http://127.0.0.1:${addr.port}`;
80
+ }
81
+ });
82
+
83
+ afterEach(async () => {
84
+ server.close();
85
+ await once(server, "close");
86
+ });
87
+
88
+ it("rejects request without auth headers (no private key configured)", async () => {
89
+ // Client with no auth config — sends no X-Pay-* headers
90
+ const client = new PayClient({
91
+ apiUrl: baseUrl,
92
+ signer: new CallbackSigner((_h: Uint8Array) => new Uint8Array(65)),
93
+ });
94
+
95
+ await assert.rejects(
96
+ () => client.getStatus(),
97
+ (err: unknown) => {
98
+ assert.ok(err instanceof PayServerError);
99
+ assert.equal(err.statusCode, 401);
100
+ assert.ok(err.message.includes("Missing auth headers"));
101
+ return true;
102
+ }
103
+ );
104
+ });
105
+
106
+ it("rejects request with stub signer (all-zero signature)", async () => {
107
+ // Client with a stub signer that returns zeros — server should reject
108
+ const client = new PayClient({
109
+ apiUrl: baseUrl,
110
+ signer: new CallbackSigner((_h: Uint8Array) => new Uint8Array(65)),
111
+ chainId: TEST_CHAIN_ID,
112
+ routerAddress: TEST_ROUTER,
113
+ });
114
+
115
+ await assert.rejects(
116
+ () => client.getStatus(),
117
+ (err: unknown) => {
118
+ assert.ok(err instanceof PayServerError);
119
+ assert.equal(err.statusCode, 401);
120
+ return true;
121
+ }
122
+ );
123
+ });
124
+
125
+ it("accepts request with valid auth headers (real signing)", async () => {
126
+ const client = new PayClient({
127
+ apiUrl: baseUrl,
128
+ privateKey: ANVIL_PK,
129
+ chainId: TEST_CHAIN_ID,
130
+ routerAddress: TEST_ROUTER,
131
+ });
132
+
133
+ // Should NOT throw — server accepts valid auth
134
+ const status = await client.getStatus();
135
+ assert.ok(status.balance >= 0);
136
+ });
137
+
138
+ it("PayServerError has statusCode 401 for auth failures", async () => {
139
+ // Directly verify error structure
140
+ const client = new PayClient({
141
+ apiUrl: baseUrl,
142
+ signer: new CallbackSigner((_h: Uint8Array) => new Uint8Array(65)),
143
+ });
144
+
145
+ try {
146
+ await client.getStatus();
147
+ assert.fail("Should have thrown PayServerError");
148
+ } catch (err) {
149
+ assert.ok(err instanceof PayServerError, "must be PayServerError");
150
+ assert.equal(err.statusCode, 401, "statusCode must be 401");
151
+ assert.equal(err.code, "server_error");
152
+ }
153
+ });
154
+ });