Skip to content

Commit 04e63c6

Browse files
authored
Merge pull request #121 from konard/issue-116-13a7cfc3f309
fix(github-client): use buildHeaders() in graphql() to eliminate duplicated header logic
2 parents 6247b74 + fbc16b1 commit 04e63c6

File tree

2 files changed

+123
-12
lines changed

2 files changed

+123
-12
lines changed

plugins/github-dev-assistant/lib/github-client.js

Lines changed: 1 addition & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -222,20 +222,9 @@ export function createGitHubClient(sdk) {
222222
async graphql(query, variables = {}) {
223223
await rateLimiter.wait();
224224

225-
const token = getAccessToken();
226-
const headers = {
227-
Accept: "application/vnd.github+json",
228-
"X-GitHub-Api-Version": "2022-11-28",
229-
"Content-Type": "application/json",
230-
"User-Agent": "teleton-github-dev-assistant/1.0.0",
231-
};
232-
if (token) {
233-
headers.Authorization = `Bearer ${token}`;
234-
}
235-
236225
const res = await fetch("https://api.github.com/graphql", {
237226
method: "POST",
238-
headers,
227+
headers: buildHeaders(),
239228
body: JSON.stringify({ query, variables }),
240229
signal: AbortSignal.timeout(20000),
241230
});
Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,122 @@
1+
/**
2+
* Unit tests verifying that graphql() reuses buildHeaders() logic.
3+
*
4+
* Regression test for issue #116:
5+
* graphql() was duplicating auth header logic instead of reusing buildHeaders().
6+
*
7+
* Uses Node's built-in test runner (node:test).
8+
* Network is mocked via global fetch override.
9+
*/
10+
11+
import { describe, it, before, after } from "node:test";
12+
import assert from "node:assert/strict";
13+
import { createGitHubClient } from "../lib/github-client.js";
14+
15+
// ─── Minimal mock SDK ─────────────────────────────────────────────────────────
16+
17+
function makeSdk(token = "test-token-abc") {
18+
return {
19+
pluginConfig: {},
20+
secrets: {
21+
get(key) {
22+
if (key === "github_token") return token;
23+
return null;
24+
},
25+
},
26+
log: {
27+
info: () => {},
28+
warn: () => {},
29+
error: () => {},
30+
debug: () => {},
31+
},
32+
};
33+
}
34+
35+
// ─── Fetch mock ───────────────────────────────────────────────────────────────
36+
37+
let capturedRequest = null;
38+
const originalFetch = globalThis.fetch;
39+
40+
function mockFetch(url, opts) {
41+
capturedRequest = { url, opts };
42+
const responseBody = JSON.stringify({ data: { viewer: { login: "testuser" } } });
43+
return Promise.resolve(
44+
new Response(responseBody, {
45+
status: 200,
46+
headers: { "Content-Type": "application/json" },
47+
})
48+
);
49+
}
50+
51+
// ─── Tests ────────────────────────────────────────────────────────────────────
52+
53+
describe("graphql() header construction (issue #116 regression)", () => {
54+
before(() => {
55+
globalThis.fetch = mockFetch;
56+
});
57+
58+
after(() => {
59+
globalThis.fetch = originalFetch;
60+
});
61+
62+
it("sends Authorization header with token from sdk.secrets", async () => {
63+
const sdk = makeSdk("my-secret-token");
64+
const client = createGitHubClient(sdk);
65+
66+
await client.graphql("{ viewer { login } }");
67+
68+
assert.ok(capturedRequest, "fetch should have been called");
69+
assert.equal(
70+
capturedRequest.opts.headers.Authorization,
71+
"Bearer my-secret-token",
72+
"Authorization header should match the token from sdk.secrets"
73+
);
74+
});
75+
76+
it("sends standard GitHub API headers (Accept, X-GitHub-Api-Version, User-Agent)", async () => {
77+
const sdk = makeSdk("some-token");
78+
const client = createGitHubClient(sdk);
79+
80+
await client.graphql("{ viewer { login } }");
81+
82+
const headers = capturedRequest.opts.headers;
83+
assert.equal(headers.Accept, "application/vnd.github+json");
84+
assert.equal(headers["X-GitHub-Api-Version"], "2022-11-28");
85+
assert.equal(headers["User-Agent"], "teleton-github-dev-assistant/1.0.0");
86+
assert.equal(headers["Content-Type"], "application/json");
87+
});
88+
89+
it("omits Authorization header when no token is set", async () => {
90+
const sdk = makeSdk(null);
91+
const client = createGitHubClient(sdk);
92+
93+
await client.graphql("{ viewer { login } }");
94+
95+
assert.equal(
96+
capturedRequest.opts.headers.Authorization,
97+
undefined,
98+
"Authorization header should not be set when no token is present"
99+
);
100+
});
101+
102+
it("posts to the GitHub GraphQL endpoint", async () => {
103+
const sdk = makeSdk("token");
104+
const client = createGitHubClient(sdk);
105+
106+
await client.graphql("{ viewer { login } }");
107+
108+
assert.equal(capturedRequest.url, "https://api.github.com/graphql");
109+
assert.equal(capturedRequest.opts.method, "POST");
110+
});
111+
112+
it("serializes query and variables in request body", async () => {
113+
const sdk = makeSdk("token");
114+
const client = createGitHubClient(sdk);
115+
116+
await client.graphql("{ viewer { login } }", { owner: "test" });
117+
118+
const body = JSON.parse(capturedRequest.opts.body);
119+
assert.equal(body.query, "{ viewer { login } }");
120+
assert.deepEqual(body.variables, { owner: "test" });
121+
});
122+
});

0 commit comments

Comments
 (0)