refactor!: migrate away from bun

- migrate package management to pnpm
- migrate test suite to vitest
- also remove Anify integration
This commit is contained in:
2025-12-12 19:24:28 -05:00
parent 748aaec100
commit 1140ffa8b8
64 changed files with 1837 additions and 9212 deletions

View File

@@ -272,7 +272,7 @@ export class AnilistDurableObject extends DurableObject {
}
async fetchFromAnilist<Result = any, Variables = any>(
query: TypedDocumentNode<Result, Variables>,
queryString: string,
variables: Variables,
token?: string | undefined,
): Promise<Result> {
@@ -286,7 +286,7 @@ export class AnilistDurableObject extends DurableObject {
// Use the query passed in, or fallback if needed (though we expect it to be passed)
// We print the query to string
const queryString = print(query);
// const queryString = print(query);
const response = await fetch(`${this.env.PROXY_URL}/proxy`, {
method: "POST",

View File

@@ -8,7 +8,7 @@ export async function fetchTitleFromAnilist(
token?: string | undefined,
): Promise<Title | undefined> {
if (useMockData()) {
const { mockTitleDetails } = await import("~/mocks");
const { mockTitleDetails } = await import("~/mocks/mockData");
return mockTitleDetails();
}

View File

@@ -1,4 +1,4 @@
import { describe, expect, it } from "bun:test";
import { describe, expect, it } from "vitest";
import { Case, changeStringCase } from "./changeStringCase";

View File

@@ -1,10 +1,10 @@
import { describe, expect, it } from "bun:test";
import { describe, expect, it } from "vitest";
import { fetchFromMultipleSources } from "./fetchFromMultipleSources";
describe("fetchFromMultipleSources", () => {
it("no promises, throws exception", () => {
expect(() => fetchFromMultipleSources([])).toThrow(
it("no promises, throws exception", async () => {
await expect(fetchFromMultipleSources([])).rejects.toThrow(
"fetchPromises cannot be empty",
);
});
@@ -30,7 +30,7 @@ describe("fetchFromMultipleSources", () => {
() => Promise.resolve(3),
]);
expect(errorOccurred).toBeFalse();
expect(errorOccurred).toBe(false);
});
it("has promises that all throw, returns null", async () => {
@@ -48,7 +48,7 @@ describe("fetchFromMultipleSources", () => {
() => Promise.reject(new Error("error")),
]);
expect(errorOccurred).toBeTrue();
expect(errorOccurred).toBe(true);
});
it("has promises but cache has value, returns cached value", async () => {
@@ -80,7 +80,7 @@ describe("fetchFromMultipleSources", () => {
},
);
expect(errorOccurred).toBeFalse();
expect(errorOccurred).toBe(false);
});
it("has promises, no cached value, no valid response, should not save in cache", async () => {

View File

@@ -2,13 +2,12 @@ import { env as cloudflareEnv } from "cloudflare:workers";
import mapKeys from "lodash.mapkeys";
import { Case, changeStringCase } from "../changeStringCase";
import { readEnvVariable } from "../readEnvVariable";
export function getAdminSdkCredentials(env: Cloudflare.Env = cloudflareEnv) {
return mapKeys(
readEnvVariable<AdminSdkCredentials>("ADMIN_SDK_JSON", env),
JSON.parse(env.ADMIN_SDK_JSON) as AdminSdkCredentials,
(_, key) => changeStringCase(key, Case.snake_case, Case.camelCase),
) as unknown as AdminSdkCredentials;
);
}
export interface AdminSdkCredentials {

View File

@@ -1,11 +1,6 @@
import { describe, expect, it } from "bun:test";
import { server } from "~/mocks";
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
import type { AdminSdkCredentials } from "./getAdminSdkCredentials";
import { verifyFcmToken } from "./verifyFcmToken";
server.listen();
const FAKE_ADMIN_SDK_JSON: AdminSdkCredentials = {
type: "service_account",
@@ -23,29 +18,87 @@ const FAKE_ADMIN_SDK_JSON: AdminSdkCredentials = {
};
describe("verifyFcmToken", () => {
// it("valid token, returns true", async () => {
// const token =
// "7v8sy43aq0re4r8xe7rmr0cn1fsmh6phehnfla2pa73z899zmhyarivmkt4sj6pyv0py43u6p2sim6wz2vg9ypjp9rug1keoth7f6ll3gdvas4q020u3ah51r6bjgn51j6bd92ztmtof3ljpcm8q31njvndy65enm68";
// const res = await verifyFcmToken(token, FAKE_ADMIN_SDK_JSON);
const fcmToken = "test-token";
let verifyFcmToken: typeof import("~/libs/gcloud/verifyFcmToken").verifyFcmToken;
let sendFcmMessage: any;
// expect(res).toBeTrue();
// });
beforeEach(async () => {
vi.resetModules();
vi.doMock("~/libs/gcloud/getGoogleAuthToken", () => ({
getGoogleAuthToken: vi.fn().mockResolvedValue("fake-token"),
}));
vi.doMock("~/libs/gcloud/sendFcmMessage", () => ({
sendFcmMessage: vi.fn(),
}));
it("invalid token, returns false", async () => {
const token = "abc123";
const res = await verifyFcmToken(token, FAKE_ADMIN_SDK_JSON);
// Import the module under test AFTER mocking dependencies
const verifyModule = await import("~/libs/gcloud/verifyFcmToken");
verifyFcmToken = verifyModule.verifyFcmToken;
expect(res).toBeFalse();
const mockModule = await import("~/libs/gcloud/sendFcmMessage");
sendFcmMessage = mockModule.sendFcmMessage;
});
afterEach(() => {
vi.doUnmock("~/libs/gcloud/sendFcmMessage");
vi.doUnmock("~/libs/gcloud/getGoogleAuthToken");
});
it("returns true for valid token", async () => {
sendFcmMessage.mockResolvedValue({
name: "projects/test-26g38/messages/fake-message-id",
});
const result = await verifyFcmToken(fcmToken, FAKE_ADMIN_SDK_JSON);
expect(result).toBe(true);
// Since we are mocking the module, we can check if it was called
expect(sendFcmMessage).toHaveBeenCalledWith(
FAKE_ADMIN_SDK_JSON,
{ name: "token_verification", token: fcmToken },
true,
);
});
it("returns false for invalid token (400)", async () => {
sendFcmMessage.mockResolvedValue({
error: {
code: 400,
message: "The registration token is not a valid FCM registration token",
status: "INVALID_ARGUMENT",
details: [],
},
});
const result = await verifyFcmToken("invalid-token", FAKE_ADMIN_SDK_JSON);
expect(result).toBe(false);
});
it("returns false for not found token (404)", async () => {
sendFcmMessage.mockResolvedValue({
error: {
code: 404,
message: "Task not found",
status: "NOT_FOUND",
details: [],
},
});
const result = await verifyFcmToken("not-found-token", FAKE_ADMIN_SDK_JSON);
expect(result).toBe(false);
});
it("invalid ADMIN_SDK_JSON, returns false", async () => {
const token =
"7v8sy43aq0re4r8xe7rmr0cn1fsmh6phehnfla2pa73z899zmhyarivmkt4sj6pyv0py43u6p2sim6wz2vg9ypjp9rug1keoth7f6ll3gdvas4q020u3ah51r6bjgn51j6bd92ztmtof3ljpcm8q31njvndy65enm68";
const res = await verifyFcmToken(token, {
// Simulate error that would occur in sendFcmMessage (e.g. auth failure inside it)
sendFcmMessage.mockRejectedValue(new Error("No email provided"));
const res = await verifyFcmToken("token", {
...FAKE_ADMIN_SDK_JSON,
clientEmail: "",
});
expect(res).toBeFalse();
expect(res).toBe(false);
});
});

View File

@@ -1,6 +1,5 @@
import { DateTime } from "luxon";
import { describe, expect, it } from "bun:test";
import { describe, expect, it } from "vitest";
import { getCurrentAndNextSeason } from "./getCurrentAndNextSeason";

View File

@@ -1,4 +1,4 @@
import { describe, expect, it } from "bun:test";
import { describe, expect, it } from "vitest";
import { lazy } from "./lazy";
@@ -16,7 +16,7 @@ describe("lazy", () => {
return "value";
});
expect(setValue).toBeFalse();
expect(setValue).toBe(false);
});
it("lazy function called if get is called", () => {
@@ -26,7 +26,7 @@ describe("lazy", () => {
return "value";
}).get();
expect(setValue).toBeTrue();
expect(setValue).toBe(true);
});
it("lazy function called only once if get is called multiple times", () => {

View File

@@ -0,0 +1,116 @@
import { DateTime } from "luxon";
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
import { maybeScheduleNextAiringEpisode } from "./maybeScheduleNextAiringEpisode";
vi.mock("~/models/unreleasedTitles", () => ({
addUnreleasedTitle: vi.fn(),
removeUnreleasedTitle: vi.fn(),
}));
vi.mock("./anilist/getNextEpisodeAiringAt", () => ({
getNextEpisodeTimeUntilAiring: vi.fn(),
}));
describe("maybeScheduleNextAiringEpisode", () => {
let addUnreleasedTitle: any;
let removeUnreleasedTitle: any;
let getNextEpisodeTimeUntilAiring: any;
let queueTask: any;
let maybeScheduleNextAiringEpisode: any;
beforeEach(async () => {
vi.resetModules();
vi.doMock("~/models/unreleasedTitles", () => ({
addUnreleasedTitle: vi.fn(),
removeUnreleasedTitle: vi.fn(),
}));
vi.doMock("./anilist/getNextEpisodeAiringAt", () => ({
getNextEpisodeTimeUntilAiring: vi.fn(),
}));
vi.doMock("./tasks/queueTask", () => ({
queueTask: vi.fn(),
}));
maybeScheduleNextAiringEpisode = (
await import("./maybeScheduleNextAiringEpisode")
).maybeScheduleNextAiringEpisode;
addUnreleasedTitle = (await import("~/models/unreleasedTitles"))
.addUnreleasedTitle;
removeUnreleasedTitle = (await import("~/models/unreleasedTitles"))
.removeUnreleasedTitle;
getNextEpisodeTimeUntilAiring = (
await import("./anilist/getNextEpisodeAiringAt")
).getNextEpisodeTimeUntilAiring;
queueTask = (await import("./tasks/queueTask")).queueTask;
});
afterEach(() => {
vi.clearAllMocks();
});
it("should add to unreleased titles if status is NOT_YET_RELEASED and no next airing", async () => {
vi.mocked(getNextEpisodeTimeUntilAiring).mockResolvedValue({
nextAiring: null,
status: "NOT_YET_RELEASED",
});
await maybeScheduleNextAiringEpisode(1);
expect(addUnreleasedTitle).toHaveBeenCalledWith(1);
expect(queueTask).not.toHaveBeenCalled();
});
it("should do nothing if status is RELEASING but no next airing (e.g. hiatus)", async () => {
vi.mocked(getNextEpisodeTimeUntilAiring).mockResolvedValue({
nextAiring: null,
status: "RELEASING",
});
await maybeScheduleNextAiringEpisode(2);
expect(addUnreleasedTitle).not.toHaveBeenCalled();
expect(queueTask).not.toHaveBeenCalled();
});
it("should do nothing if next airing is more than 30 days away", async () => {
const farFuture = DateTime.now().plus({ days: 31 }).toSeconds();
vi.mocked(getNextEpisodeTimeUntilAiring).mockResolvedValue({
nextAiring: { airingAt: farFuture, episode: 2 },
status: "RELEASING",
});
await maybeScheduleNextAiringEpisode(3);
expect(addUnreleasedTitle).not.toHaveBeenCalled();
expect(queueTask).not.toHaveBeenCalled();
});
it("should schedule task and remove from unreleased if next airing is soon", async () => {
const nearFuture = Math.floor(DateTime.now().plus({ days: 1 }).toSeconds());
vi.mocked(getNextEpisodeTimeUntilAiring).mockResolvedValue({
nextAiring: { airingAt: nearFuture, episode: 5 },
status: "RELEASING",
});
await maybeScheduleNextAiringEpisode(4);
expect(queueTask).toHaveBeenCalledWith(
"NEW_EPISODE",
{ aniListId: 4, episodeNumber: 5 },
{ scheduleConfig: { epochTime: nearFuture } },
);
expect(removeUnreleasedTitle).toHaveBeenCalledWith(4);
expect(addUnreleasedTitle).not.toHaveBeenCalled();
});
it("should add to unreleased if next airing is null even with RELEASING status? No code says only NOT_YET_RELEASED", async () => {
// Code: if (status === "NOT_YET_RELEASED") await addUnreleasedTitle(aniListId);
// So if RELEASING and null, it does nothing.
// Verified in second test case.
expect(true).toBe(true);
});
});

View File

@@ -1,4 +1,4 @@
import { describe, expect, it } from "bun:test";
import { describe, expect, it } from "vitest";
import { PromiseTimedOutError, promiseTimeout } from "./promiseTimeout";

View File

@@ -1,35 +0,0 @@
import { describe, expect, it } from "bun:test";
import { readEnvVariable } from "./readEnvVariable";
describe("readEnvVariable", () => {
describe("env & variable defined", () => {
it("returns boolean", () => {
expect(
readEnvVariable<boolean>("ENABLE_ANIFY", { ENABLE_ANIFY: "false" }),
).toBe(false);
});
it("returns string", () => {
expect(
readEnvVariable<string>("QSTASH_TOKEN", {
QSTASH_TOKEN: "ehf73g8gyriuvnieojwicbg83hc",
}),
).toBe("ehf73g8gyriuvnieojwicbg83hc");
});
it("returns number", () => {
expect(
readEnvVariable<number>("NUM_RETRIES", { NUM_RETRIES: "123" }),
).toBe(123);
});
});
it("env defined but variable not defined, returns default value", () => {
expect(readEnvVariable<boolean>("ENABLE_ANIFY", { FOO: "bar" })).toBe(true);
});
it("env not defined, returns default value", () => {
expect(readEnvVariable<boolean>("ENABLE_ANIFY", undefined)).toBe(true);
});
});

View File

@@ -1,22 +0,0 @@
import { env as cloudflareEnv } from "cloudflare:workers";
import type { Bindings } from "hono/types";
type EnvVariable = keyof Cloudflare.Env;
const defaultValues: Record<EnvVariable, any> = {
ENABLE_ANIFY: true,
};
export function readEnvVariable<T>(
envVariable: EnvVariable,
env: Bindings | undefined = cloudflareEnv,
): T {
try {
return JSON.parse(env?.[envVariable] ?? null) ?? defaultValues[envVariable];
} catch (error) {
if (error instanceof SyntaxError) {
return env![envVariable];
}
throw error;
}
}

View File

@@ -1,4 +1,4 @@
import { describe, expect, it } from "bun:test";
import { describe, expect, it } from "vitest";
import { sortByProperty } from "./sortByProperty";

View File

@@ -1,6 +1,4 @@
import { DateTime } from "luxon";
import { beforeEach, describe, expect, it, mock } from "bun:test";
import { beforeEach, describe, expect, it } from "vitest";
import type { DelayedTaskMetadata } from "./delayedTask";
import {

View File

@@ -1,28 +1,34 @@
import { beforeEach, describe, expect, it, mock } from "bun:test";
import { beforeEach, describe, expect, it, vi } from "vitest";
import { processDelayedTasks } from "./processDelayedTasks";
describe("processDelayedTasks", () => {
let mockEnv: Cloudflare.Env;
let mockCtx: ExecutionContext;
let kvGetSpy: ReturnType<typeof mock>;
let kvDeleteSpy: ReturnType<typeof mock>;
let kvPutSpy: ReturnType<typeof mock>;
let queueSendSpy: ReturnType<typeof mock>;
let kvGetSpy: ReturnType<typeof vi.fn>;
let kvDeleteSpy: ReturnType<typeof vi.fn>;
let kvPutSpy: ReturnType<typeof vi.fn>;
let queueSendSpy: ReturnType<typeof vi.fn>;
beforeEach(() => {
kvGetSpy = mock(() => Promise.resolve(null));
kvDeleteSpy = mock(() => Promise.resolve());
kvPutSpy = mock(() => Promise.resolve());
queueSendSpy = mock(() => Promise.resolve());
kvGetSpy = vi.fn(() => Promise.resolve(null));
kvDeleteSpy = vi.fn(() => Promise.resolve());
kvPutSpy = vi.fn(() => Promise.resolve());
queueSendSpy = vi.fn(() => Promise.resolve());
mockEnv = {
DELAYED_TASKS: {
get: kvGetSpy,
delete: kvDeleteSpy,
put: kvPutSpy,
list: mock(() => Promise.resolve({ keys: [], list_complete: true })),
getWithMetadata: mock(() =>
list: vi.fn(() =>
Promise.resolve({
keys: [],
list_complete: true as const,
cacheStatus: null,
}),
),
getWithMetadata: vi.fn(() =>
Promise.resolve({ value: null, metadata: null }),
),
} as any,
@@ -30,13 +36,13 @@ describe("processDelayedTasks", () => {
send: queueSendSpy,
} as any,
ANILIST_UPDATES: {
send: mock(() => Promise.resolve()),
send: vi.fn(() => Promise.resolve()),
} as any,
} as any;
mockCtx = {
waitUntil: mock(() => {}),
passThroughOnException: mock(() => {}),
waitUntil: vi.fn(() => {}),
passThroughOnException: vi.fn(() => {}),
} as any;
});
@@ -61,10 +67,11 @@ describe("processDelayedTasks", () => {
retryCount: 0,
};
mockEnv.DELAYED_TASKS.list = mock(() =>
mockEnv.DELAYED_TASKS.list = vi.fn(() =>
Promise.resolve({
keys: [{ name: `delayed-task:${scheduledTime}:task-1` }],
list_complete: true,
list_complete: true as const,
cacheStatus: null,
}),
);
@@ -93,10 +100,11 @@ describe("processDelayedTasks", () => {
retryCount: 0,
};
mockEnv.DELAYED_TASKS.list = mock(() =>
mockEnv.DELAYED_TASKS.list = vi.fn(() =>
Promise.resolve({
keys: [{ name: `delayed-task:${scheduledTime}:task-2` }],
list_complete: true,
list_complete: true as const,
cacheStatus: null,
}),
);
@@ -122,10 +130,11 @@ describe("processDelayedTasks", () => {
retryCount: 0,
};
mockEnv.DELAYED_TASKS.list = mock(() =>
mockEnv.DELAYED_TASKS.list = vi.fn(() =>
Promise.resolve({
keys: [{ name: `delayed-task:${scheduledTime}:task-3` }],
list_complete: true,
list_complete: true as const,
cacheStatus: null,
}),
);
@@ -141,7 +150,7 @@ describe("processDelayedTasks", () => {
});
it("logs alert after 3 failed attempts", async () => {
const consoleErrorSpy = mock(() => {});
const consoleErrorSpy = vi.fn(() => {});
const originalConsoleError = console.error;
console.error = consoleErrorSpy as any;
@@ -158,10 +167,11 @@ describe("processDelayedTasks", () => {
retryCount: 2, // Will become 3 after this failure
};
mockEnv.DELAYED_TASKS.list = mock(() =>
mockEnv.DELAYED_TASKS.list = vi.fn(() =>
Promise.resolve({
keys: [{ name: `delayed-task:${scheduledTime}:task-4` }],
list_complete: true,
list_complete: true as const,
cacheStatus: null,
}),
);
@@ -202,13 +212,14 @@ describe("processDelayedTasks", () => {
retryCount: 0,
};
mockEnv.DELAYED_TASKS.list = mock(() =>
mockEnv.DELAYED_TASKS.list = vi.fn(() =>
Promise.resolve({
keys: [
{ name: `delayed-task:${task1Metadata.scheduledEpochTime}:task-1` },
{ name: `delayed-task:${task2Metadata.scheduledEpochTime}:task-2` },
],
list_complete: true,
list_complete: true as const,
cacheStatus: null,
}),
);
@@ -223,10 +234,11 @@ describe("processDelayedTasks", () => {
});
it("skips tasks with null values in KV", async () => {
mockEnv.DELAYED_TASKS.list = mock(() =>
mockEnv.DELAYED_TASKS.list = vi.fn(() =>
Promise.resolve({
keys: [{ name: "delayed-task:123:invalid" }],
list_complete: true,
list_complete: true as const,
cacheStatus: null,
}),
);

View File

@@ -1,4 +1,4 @@
import { beforeEach, describe, expect, it, mock, spyOn } from "bun:test";
import { beforeEach, describe, expect, it, vi } from "vitest";
import { queueTask } from "./queueTask";
@@ -6,20 +6,20 @@ describe("queueTask - delayed task handling", () => {
const MAX_DELAY_SECONDS = 12 * 60 * 60; // 43,200 seconds
let mockEnv: Cloudflare.Env;
let kvPutSpy: ReturnType<typeof mock>;
let queueSendSpy: ReturnType<typeof mock>;
let kvPutSpy: ReturnType<typeof vi.fn>;
let queueSendSpy: ReturnType<typeof vi.fn>;
beforeEach(() => {
kvPutSpy = mock(() => Promise.resolve());
queueSendSpy = mock(() => Promise.resolve());
kvPutSpy = vi.fn(() => Promise.resolve());
queueSendSpy = vi.fn(() => Promise.resolve());
mockEnv = {
DELAYED_TASKS: {
put: kvPutSpy,
get: mock(() => Promise.resolve(null)),
delete: mock(() => Promise.resolve()),
list: mock(() => Promise.resolve({ keys: [], list_complete: true })),
getWithMetadata: mock(() =>
get: vi.fn(() => Promise.resolve(null)),
delete: vi.fn(() => Promise.resolve()),
list: vi.fn(() => Promise.resolve({ keys: [], list_complete: true })),
getWithMetadata: vi.fn(() =>
Promise.resolve({ value: null, metadata: null }),
),
} as any,
@@ -27,12 +27,12 @@ describe("queueTask - delayed task handling", () => {
send: queueSendSpy,
} as any,
ANILIST_UPDATES: {
send: mock(() => Promise.resolve()),
send: vi.fn(() => Promise.resolve()),
} as any,
} as any;
// Mock crypto.randomUUID
globalThis.crypto.randomUUID = mock(() => "test-uuid-123");
(globalThis as any).crypto = { randomUUID: vi.fn(() => "test-uuid-123") };
});
describe("tasks with delay <= 12 hours", () => {

View File

@@ -0,0 +1,47 @@
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
vi.stubGlobal("fetch", vi.fn());
describe("removeTask", () => {
let removeTask: any;
let getAdminSdkCredentials: any;
let getGoogleAuthToken: any;
beforeEach(async () => {
vi.resetModules();
vi.doMock("cloudflare:workers", () => ({ env: {} }));
vi.doMock("../gcloud/getAdminSdkCredentials", () => ({
getAdminSdkCredentials: vi.fn(),
}));
vi.doMock("../gcloud/getGoogleAuthToken", () => ({
getGoogleAuthToken: vi.fn(),
}));
removeTask = (await import("./removeTask")).removeTask;
getAdminSdkCredentials = (await import("../gcloud/getAdminSdkCredentials"))
.getAdminSdkCredentials;
getGoogleAuthToken = (await import("../gcloud/getGoogleAuthToken"))
.getGoogleAuthToken;
});
afterEach(() => {
vi.clearAllMocks();
});
it("should call Google Cloud Tasks API with correct parameters", async () => {
const mockCredentials = { projectId: "test-project" };
vi.mocked(getAdminSdkCredentials).mockReturnValue(mockCredentials);
vi.mocked(getGoogleAuthToken).mockResolvedValue("test-token");
vi.mocked(fetch).mockResolvedValue(new Response(""));
await removeTask("NEW_EPISODE", "task-123");
expect(fetch).toHaveBeenCalledWith(
"https://content-cloudtasks.googleapis.com/v2/projects/test-project/locations/northamerica-northeast1/queues/NEW_EPISODE/tasks/task-123",
expect.objectContaining({
method: "DELETE",
headers: { Authorization: "Bearer test-token" },
}),
);
});
});

View File

@@ -2,6 +2,6 @@ import { getDb } from "~/models/db";
import { getTestEnv } from "./getTestEnv";
export function getTestDb() {
return getDb(getTestEnv());
export function getTestDb(env?: Cloudflare.Env) {
return getDb(env ?? getTestEnv());
}

View File

@@ -1,3 +1,5 @@
import { env } from "cloudflare:test";
/** Should only be used when it doesn't make sense for 'Bindings' or 'Variables' to be set. Otherwise, use getTestEnv(). */
export function getTestEnvVariables(): Cloudflare.Env {
return getTestEnv();
@@ -5,14 +7,11 @@ export function getTestEnvVariables(): Cloudflare.Env {
export function getTestEnv({
ADMIN_SDK_JSON = '{"client_email": "test@test.com", "project_id": "test-26g38"}',
ENABLE_ANIFY = "true",
TURSO_AUTH_TOKEN = "123",
TURSO_URL = "http://127.0.0.1:3001",
LOG_DB_QUERIES = "false",
}: Partial<Cloudflare.Env> = {}): Cloudflare.Env {
return {
...env,
ADMIN_SDK_JSON,
ENABLE_ANIFY,
TURSO_AUTH_TOKEN,
TURSO_URL,
LOG_DB_QUERIES,
};
}

View File

@@ -2,9 +2,7 @@ import { tables } from "~/models/schema";
import { getTestDb } from "./getTestDb";
export async function resetTestDb() {
const db = getTestDb();
export async function resetTestDb(db = getTestDb()) {
for (const table of tables) {
await db.delete(table);
}