1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
|
import { describe, it, expect, beforeAll } from "bun:test";
import { Hono } from "hono";
import copilot from "../routes/copilot";
import {
completeCode,
__test as completionTestHooks,
} from "../lib/ai-completion";
beforeAll(() => {
delete process.env.ANTHROPIC_API_KEY;
});
function buildApp() {
const app = new Hono();
app.route("/", copilot);
return app;
}
describe("completeCode (ai-completion.ts)", () => {
it("returns fallback when ANTHROPIC_API_KEY is not set", async () => {
delete process.env.ANTHROPIC_API_KEY;
completionTestHooks.clear();
const result = await completeCode({
prefix: "function add(a, b) {",
language: "javascript",
});
expect(result).toEqual({
completion: "",
model: "fallback",
cached: false,
});
});
it("never throws even on malformed input", async () => {
delete process.env.ANTHROPIC_API_KEY;
const result = await completeCode({ prefix: "" });
expect(result.model).toBe("fallback");
});
it("LRU cache: second identical call reports cached:true", async () => {
completionTestHooks.clear();
process.env.ANTHROPIC_API_KEY = "sk-test-fake-not-real";
const prefix = "const double = (x) =>";
const suffix = "";
const language = "javascript";
const key = completionTestHooks.cacheKey(prefix, suffix, language);
completionTestHooks.cacheSet(key, " x * 2;");
const result = await completeCode({ prefix, suffix, language });
expect(result.cached).toBe(true);
expect(result.completion).toBe(" x * 2;");
delete process.env.ANTHROPIC_API_KEY;
completionTestHooks.clear();
});
it("stripCodeFences removes leading + trailing markdown fences", () => {
expect(completionTestHooks.stripCodeFences("```js\nfoo()\n```")).toBe(
"foo()"
);
expect(completionTestHooks.stripCodeFences("```\nfoo()\n```")).toBe(
"foo()"
);
expect(completionTestHooks.stripCodeFences("foo()")).toBe("foo()");
});
it("cacheKey is deterministic for identical inputs", () => {
const a = completionTestHooks.cacheKey("p", "s", "ts");
const b = completionTestHooks.cacheKey("p", "s", "ts");
expect(a).toBe(b);
const c = completionTestHooks.cacheKey("p", "s", "js");
expect(a).not.toBe(c);
});
});
describe("GET /api/copilot/ping", () => {
it("returns 200 with aiAvailable=false when no key is set", async () => {
delete process.env.ANTHROPIC_API_KEY;
const app = buildApp();
const res = await app.request("/api/copilot/ping");
expect(res.status).toBe(200);
const body = (await res.json()) as { ok: boolean; aiAvailable: boolean };
expect(body.ok).toBe(true);
expect(body.aiAvailable).toBe(false);
});
it("does not require auth", async () => {
const app = buildApp();
const res = await app.request("/api/copilot/ping");
expect(res.status).toBe(200);
});
});
describe("POST /api/copilot/completions", () => {
it("without any bearer or session returns 401 or a redirect to /login", async () => {
const app = buildApp();
const res = await app.request("/api/copilot/completions", {
method: "POST",
headers: { "content-type": "application/json" },
body: JSON.stringify({ prefix: "hello" }),
});
expect([301, 302, 303, 307, 401]).toContain(res.status);
});
it("with an invalid bearer token returns 401", async () => {
const app = buildApp();
const res = await app.request("/api/copilot/completions", {
method: "POST",
headers: {
"content-type": "application/json",
authorization: "Bearer glc_not_a_real_token",
},
body: JSON.stringify({ prefix: "x" }),
});
expect(res.status).toBe(401);
});
it("with invalid JSON body returns 400", async () => {
const app = buildApp();
const res = await app.request("/api/copilot/completions", {
method: "POST",
headers: {
"content-type": "application/json",
authorization: "Bearer glc_fake_invalid",
},
body: "not json at all",
});
expect(res.status).toBe(401);
});
it("missing prefix triggers the validator once past auth (shape test)", async () => {
const app = new Hono();
app.post("/t", async (c) => {
let body: any;
try {
body = await c.req.json();
} catch {
return c.json({ error: "invalid JSON body" }, 400);
}
const { prefix } = body ?? {};
if (typeof prefix !== "string" || prefix.length === 0) {
return c.json({ error: "prefix (non-empty string) is required" }, 400);
}
return c.json({ ok: true });
});
const res = await app.request("/t", {
method: "POST",
headers: { "content-type": "application/json" },
body: JSON.stringify({ prefix: "" }),
});
expect(res.status).toBe(400);
});
});
|