fix: remove nested .git folders, re-add as normal directories

This commit is contained in:
2026-03-22 17:50:47 -05:00
parent f55c7e47c9
commit 6b7eec67b8
1870 changed files with 4170168 additions and 3 deletions
+241
View File
@@ -0,0 +1,241 @@
import { describe, test, expect, beforeEach } from "bun:test";
import { Hono } from "hono";
import { apiResponse } from "../../src/modules/api-utils/apiResponse";
import type { ContentfulStatusCode } from "hono/utils/http-status";
/**
* Tests for the CW callback route handler.
*
* We import the route handler and mount it on a Hono app to test via
* the app.request() convenience method.
*/
// We need to test the internal helper functions. Since they are not
// exported, we test them through the route handler's observable behavior.
import callbackRoute from "../../src/api/cw/callback";
describe("CW callback route handler", () => {
let app: Hono;
beforeEach(() => {
app = new Hono();
// Replicate the error handling from server.ts
app.onError((err, c) => {
if ((err as any).status) {
const body = apiResponse.error(err);
return c.json(body, body.status as ContentfulStatusCode);
}
return c.json(apiResponse.internalError(), 500);
});
app.route("/", callbackRoute);
// Clear the env var before each test
delete (process.env as Record<string, any>).CW_CALLBACK_SECRET;
});
// -------------------------------------------------------------------
// Secret validation
// -------------------------------------------------------------------
test("rejects when secret does not match CW_CALLBACK_SECRET", async () => {
process.env.CW_CALLBACK_SECRET = "correct-secret";
const res = await app.request("/callback/wrong-secret/opportunity", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({}),
});
expect(res.status).toBe(401);
const body = await res.json();
expect(body.message).toContain("Invalid callback secret");
});
test("accepts when secret matches CW_CALLBACK_SECRET", async () => {
process.env.CW_CALLBACK_SECRET = "correct-secret";
const res = await app.request("/callback/correct-secret/opportunity", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ Action: "updated", ID: 123 }),
});
expect(res.status).toBe(200);
});
test("accepts any secret when CW_CALLBACK_SECRET is not configured", async () => {
const res = await app.request("/callback/any-secret/opportunity", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ Action: "created" }),
});
expect(res.status).toBe(200);
});
// -------------------------------------------------------------------
// Resource validation
// -------------------------------------------------------------------
test("accepts 'opportunity' resource", async () => {
const res = await app.request("/callback/test/opportunity", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({}),
});
expect(res.status).toBe(200);
const body = await res.json();
expect(body.data.resource).toBe("opportunity");
});
test("accepts 'ticket' resource", async () => {
const res = await app.request("/callback/test/ticket", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({}),
});
expect(res.status).toBe(200);
const body = await res.json();
expect(body.data.resource).toBe("ticket");
});
test("accepts 'company' resource", async () => {
const res = await app.request("/callback/test/company", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({}),
});
expect(res.status).toBe(200);
const body = await res.json();
expect(body.data.resource).toBe("company");
});
test("accepts 'activity' resource", async () => {
const res = await app.request("/callback/test/activity", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({}),
});
expect(res.status).toBe(200);
const body = await res.json();
expect(body.data.resource).toBe("activity");
});
test("rejects invalid resource type", async () => {
const res = await app.request("/callback/test/invalidtype", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({}),
});
// Zod validation should fail
expect(res.status).toBeGreaterThanOrEqual(400);
});
// -------------------------------------------------------------------
// Body parsing
// -------------------------------------------------------------------
test("parses JSON body fields", async () => {
const res = await app.request("/callback/test/opportunity", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
Action: "updated",
Type: "opportunity",
ID: 42,
MemberId: "jroberts",
MessageId: "msg-123",
}),
});
expect(res.status).toBe(200);
const body = await res.json();
expect(body.data.summary.action).toBe("updated");
expect(body.data.summary.type).toBe("opportunity");
expect(body.data.summary.id).toBe(42);
expect(body.data.summary.memberId).toBe("jroberts");
expect(body.data.summary.messageId).toBe("msg-123");
});
test("parses Entity field from JSON string", async () => {
const entity = {
CompanyName: "Acme Corp",
StatusName: "Active",
UpdatedBy: "admin",
};
const res = await app.request("/callback/test/company", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
Action: "updated",
Entity: JSON.stringify(entity),
}),
});
expect(res.status).toBe(200);
const body = await res.json();
expect(body.data.summary.entitySummary).toBe("Acme Corp");
expect(body.data.summary.entityStatus).toBe("Active");
expect(body.data.summary.entityUpdatedBy).toBe("admin");
});
test("handles Entity as inline object", async () => {
const res = await app.request("/callback/test/company", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
Action: "created",
Entity: { CompanyName: "Direct Corp", Status: "New" },
}),
});
expect(res.status).toBe(200);
const body = await res.json();
expect(body.data.summary.entitySummary).toBe("Direct Corp");
expect(body.data.summary.entityStatus).toBe("New");
});
test("returns secretValidated field based on env presence", async () => {
delete (process.env as Record<string, any>).CW_CALLBACK_SECRET;
const res = await app.request("/callback/test/opportunity", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({}),
});
const body = await res.json();
expect(body.data.secretValidated).toBe(false);
process.env.CW_CALLBACK_SECRET = "secret";
const res2 = await app.request("/callback/secret/opportunity", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({}),
});
const body2 = await res2.json();
expect(body2.data.secretValidated).toBe(true);
});
test("returns receivedAt timestamp", async () => {
const res = await app.request("/callback/test/opportunity", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({}),
});
const body = await res.json();
expect(body.data.receivedAt).toBeDefined();
// Should be a valid ISO date string
expect(new Date(body.data.receivedAt).toISOString()).toBe(
body.data.receivedAt,
);
});
test("handles non-JSON body gracefully", async () => {
const res = await app.request("/callback/test/opportunity", {
method: "POST",
headers: { "Content-Type": "text/plain" },
body: "this is not json",
});
expect(res.status).toBe(200);
const body = await res.json();
expect(body.data.summary).toBeNull();
});
test("handles empty body gracefully", async () => {
const res = await app.request("/callback/test/opportunity", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: "",
});
expect(res.status).toBe(200);
const body = await res.json();
expect(body.data.summary).toBeNull();
});
});