test: add action-helper, send-currency, permission-ranks, catalog-tree, and audit test suites (77 files, 405 tests)
This commit is contained in:
1 parent
2e9db75eca
commit
ac3c42f308
4 files changed
+398
-75
No files matched your search
@@ -1,42 +1,54 @@
|
|||||||
import { describe, expect, it, vi, beforeEach } from "vitest";
|
import { describe, expect, it, vi } from "vitest";
|
||||||
|
|
||||||
const mockError = vi.hoisted(() => vi.fn());
|
const loggerError = vi.hoisted(() => vi.fn());
|
||||||
|
|
||||||
vi.mock("@/lib/logger", () => ({
|
vi.mock("@/lib/logger", () => ({
|
||||||
logger: { error: mockError },
|
logger: { error: loggerError },
|
||||||
}));
|
}));
|
||||||
|
|
||||||
import { wrapAction, actionError } from "./action-helper";
|
import { wrapAction, actionError } from "./action-helper";
|
||||||
|
|
||||||
beforeEach(() => {
|
|
||||||
mockError.mockReset();
|
|
||||||
});
|
|
||||||
|
|
||||||
describe("wrapAction", () => {
|
describe("wrapAction", () => {
|
||||||
it("returns success with data when fn resolves", async () => {
|
it("returns success result on happy path", async () => {
|
||||||
const result = await wrapAction(async () => "hello", "testAction");
|
const fn = vi.fn().mockResolvedValue({ id: 1 });
|
||||||
expect(result).toEqual({ success: true, data: "hello" });
|
const result = await wrapAction(fn, "test_action");
|
||||||
|
expect(result.success).toBe(true);
|
||||||
|
expect(result.data).toEqual({ id: 1 });
|
||||||
});
|
});
|
||||||
|
|
||||||
it("returns failure and logs when fn throws", async () => {
|
it("returns error result on thrown exception", async () => {
|
||||||
const result = await wrapAction(async () => {
|
const fn = vi.fn().mockRejectedValue(new Error("DB error"));
|
||||||
throw new Error("boom");
|
const result = await wrapAction(fn, "failing_action");
|
||||||
}, "testAction");
|
expect(result.success).toBe(false);
|
||||||
expect(result).toEqual({ success: false, error: "boom" });
|
expect(result.error).toBe("DB error");
|
||||||
expect(mockError).toHaveBeenCalledWith("Action failed: testAction", {
|
expect(loggerError).toHaveBeenCalled();
|
||||||
action: "testAction",
|
});
|
||||||
error: "boom",
|
|
||||||
});
|
it("catches non-Error values gracefully", async () => {
|
||||||
|
const fn = vi.fn().mockRejectedValue("string error");
|
||||||
|
const result = await wrapAction(fn, "bad_action");
|
||||||
|
expect(result.success).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("returns data returned by fn", async () => {
|
||||||
|
const fn = vi.fn().mockResolvedValue(undefined);
|
||||||
|
const result = await wrapAction(fn, "void_action");
|
||||||
|
expect(result.success).toBe(true);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("actionError", () => {
|
describe("actionError", () => {
|
||||||
it("returns failure result and logs", () => {
|
it("returns failure result with message", () => {
|
||||||
const result = actionError("myAction", "something went wrong");
|
const result = actionError("some_action", "something went wrong");
|
||||||
expect(result).toEqual({ success: false, error: "something went wrong" });
|
expect(result.success).toBe(false);
|
||||||
expect(mockError).toHaveBeenCalledWith("Action failed: myAction", {
|
expect(result.error).toBe("something went wrong");
|
||||||
action: "myAction",
|
|
||||||
error: "something went wrong",
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
});
|
|
||||||
|
it("logs the error", () => {
|
||||||
|
actionError("audit_action", "audit failed");
|
||||||
|
expect(loggerError).toHaveBeenCalledWith(
|
||||||
|
"Action failed: audit_action",
|
||||||
|
expect.objectContaining({ action: "audit_action", error: "audit failed" }),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -0,0 +1,158 @@
|
|||||||
|
import { describe, expect, it, vi, beforeEach } from "vitest";
|
||||||
|
|
||||||
|
const findUnique = vi.hoisted(() => vi.fn());
|
||||||
|
const findMany = vi.hoisted(() => vi.fn());
|
||||||
|
const update = vi.hoisted(() => vi.fn());
|
||||||
|
const create = vi.hoisted(() => vi.fn());
|
||||||
|
const queryRaw = vi.hoisted(() => vi.fn());
|
||||||
|
const executeRaw = vi.hoisted(() => vi.fn());
|
||||||
|
|
||||||
|
vi.mock("@/lib/prisma", () => ({
|
||||||
|
prisma: {
|
||||||
|
catalogPages: { findUnique, findMany, update, create },
|
||||||
|
$queryRaw: queryRaw,
|
||||||
|
$executeRaw: executeRaw,
|
||||||
|
},
|
||||||
|
}));
|
||||||
|
|
||||||
|
vi.mock("@/env", () => ({ env: {} }));
|
||||||
|
|
||||||
|
import { buildNestedTree, toInt, movePage, deletePage, getAncestors, createPage } from "./catalog-tree";
|
||||||
|
import type { TreeNode } from "@/types/catalog";
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
vi.clearAllMocks();
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("toInt", () => {
|
||||||
|
it("converts valid numbers", () => {
|
||||||
|
expect(toInt(42)).toBe(42);
|
||||||
|
expect(toInt("42")).toBe(42);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("uses fallback for invalid", () => {
|
||||||
|
expect(toInt("abc", -1)).toBe(-1);
|
||||||
|
expect(toInt(NaN, 0)).toBe(0);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("defaults fallback to 0", () => {
|
||||||
|
expect(toInt("abc")).toBe(0);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("buildNestedTree", () => {
|
||||||
|
const flat: TreeNode[] = [
|
||||||
|
{ id: 1, caption: "Root", parentId: -1, depth: 0, orderNum: 1, enabled: "1", visible: "1", iconImage: 0, iconColor: 0, pageLayout: "default_3x3", childCount: 2, itemCount: 0 },
|
||||||
|
{ id: 2, caption: "Child", parentId: 1, depth: 1, orderNum: 1, enabled: "1", visible: "1", iconImage: 0, iconColor: 0, pageLayout: "default_3x3", childCount: 1, itemCount: 5 },
|
||||||
|
{ id: 3, caption: "Grandchild", parentId: 2, depth: 2, orderNum: 1, enabled: "1", visible: "1", iconImage: 0, iconColor: 0, pageLayout: "default_3x3", childCount: 0, itemCount: 0 },
|
||||||
|
];
|
||||||
|
|
||||||
|
it("builds nested tree from flat nodes", () => {
|
||||||
|
const tree = buildNestedTree(flat);
|
||||||
|
expect(tree).toHaveLength(1);
|
||||||
|
expect(tree[0].id).toBe(1);
|
||||||
|
expect(tree[0].children).toHaveLength(1);
|
||||||
|
expect(tree[0].children[0].children[0].id).toBe(3);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("returns empty array for empty input", () => {
|
||||||
|
expect(buildNestedTree([])).toEqual([]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("uses custom rootParentId", () => {
|
||||||
|
expect(buildNestedTree(flat, 999)).toHaveLength(0);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("createPage", () => {
|
||||||
|
it("creates page with default values", async () => {
|
||||||
|
create.mockResolvedValue({ id: 1 });
|
||||||
|
await createPage({ parentId: 0, caption: "Test" });
|
||||||
|
|
||||||
|
const data = create.mock.calls[0][0].data;
|
||||||
|
expect(data.caption).toBe("Test");
|
||||||
|
expect(data.pageLayout).toBe("default_3x3");
|
||||||
|
expect(data.visible).toBe("1");
|
||||||
|
expect(data.enabled).toBe("1");
|
||||||
|
expect(data.clubOnly).toBe("0");
|
||||||
|
expect(data.minRank).toBe(1);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("truncates captionSave to 25 chars", async () => {
|
||||||
|
create.mockResolvedValue({ id: 1 });
|
||||||
|
await createPage({ parentId: 0, caption: "a".repeat(50) });
|
||||||
|
|
||||||
|
expect(create.mock.calls[0][0].data.captionSave).toBe("a".repeat(25));
|
||||||
|
});
|
||||||
|
|
||||||
|
it("converts boolean visible/enabled to string", async () => {
|
||||||
|
create.mockResolvedValue({ id: 1 });
|
||||||
|
await createPage({ parentId: 0, caption: "x", visible: false, enabled: false });
|
||||||
|
expect(create.mock.calls[0][0].data.visible).toBe("0");
|
||||||
|
expect(create.mock.calls[0][0].data.enabled).toBe("0");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("movePage", () => {
|
||||||
|
it("throws on circular hierarchy", async () => {
|
||||||
|
findUnique.mockResolvedValue({ parentId: 1 });
|
||||||
|
await expect(movePage(1, 2)).rejects.toThrow("circular hierarchy");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("allows valid move to root", async () => {
|
||||||
|
update.mockResolvedValue({});
|
||||||
|
await movePage(1, 0);
|
||||||
|
expect(update).toHaveBeenCalledWith({
|
||||||
|
where: { id: 1 },
|
||||||
|
data: { parentId: 0 },
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("allows valid move to other parent", async () => {
|
||||||
|
findUnique.mockResolvedValue({ parentId: 0 });
|
||||||
|
update.mockResolvedValue({});
|
||||||
|
await movePage(1, 2);
|
||||||
|
expect(update).toHaveBeenCalledWith({
|
||||||
|
where: { id: 1 },
|
||||||
|
data: { parentId: 2 },
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("getAncestors", () => {
|
||||||
|
it("returns breadcrumb from root to page", async () => {
|
||||||
|
findUnique
|
||||||
|
.mockResolvedValueOnce({ id: 3, caption: "Grandchild", parentId: 2 })
|
||||||
|
.mockResolvedValueOnce({ id: 2, caption: "Child", parentId: 1 })
|
||||||
|
.mockResolvedValueOnce({ id: 1, caption: "Root", parentId: 0 });
|
||||||
|
|
||||||
|
const ancestors = await getAncestors(3);
|
||||||
|
expect(ancestors).toHaveLength(3);
|
||||||
|
expect(ancestors[0].caption).toBe("Root");
|
||||||
|
expect(ancestors[1].caption).toBe("Child");
|
||||||
|
expect(ancestors[2].caption).toBe("Grandchild");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("stops when page not found", async () => {
|
||||||
|
findUnique.mockResolvedValue(null);
|
||||||
|
expect(await getAncestors(999)).toEqual([]);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("deletePage", () => {
|
||||||
|
it("reparent mode moves children to parent", async () => {
|
||||||
|
findUnique.mockResolvedValue({ parentId: 1 });
|
||||||
|
const deleteFn = vi.fn().mockResolvedValue({});
|
||||||
|
const updateMany = vi.fn().mockResolvedValue({ count: 2 });
|
||||||
|
const prisma = await import("@/lib/prisma").then((m) => m.prisma as any);
|
||||||
|
prisma.catalogPages.delete = deleteFn;
|
||||||
|
prisma.catalogPages.updateMany = updateMany;
|
||||||
|
|
||||||
|
const result = await deletePage(5, "reparent");
|
||||||
|
expect(result).toEqual({ deletedPages: 1, movedChildren: 2 });
|
||||||
|
expect(updateMany).toHaveBeenCalledWith({
|
||||||
|
where: { parentId: 5 },
|
||||||
|
data: { parentId: 1 },
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -0,0 +1,78 @@
|
|||||||
|
import { describe, expect, it } from "vitest";
|
||||||
|
|
||||||
|
import {
|
||||||
|
getRankPermissionColumn,
|
||||||
|
toPermissionValue,
|
||||||
|
splitRankUpdateFields,
|
||||||
|
RANK_GENERAL_FIELDS,
|
||||||
|
} from "./permission-ranks";
|
||||||
|
|
||||||
|
describe("getRankPermissionColumn", () => {
|
||||||
|
it("returns rank_N for valid rank", () => {
|
||||||
|
expect(getRankPermissionColumn(1)).toBe("rank_1");
|
||||||
|
expect(getRankPermissionColumn(255)).toBe("rank_255");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("throws for invalid rank", () => {
|
||||||
|
expect(() => getRankPermissionColumn(0)).toThrow("Invalid rank id");
|
||||||
|
expect(() => getRankPermissionColumn(256)).toThrow("Invalid rank id");
|
||||||
|
expect(() => getRankPermissionColumn(1.5)).toThrow("Invalid rank id");
|
||||||
|
expect(() => getRankPermissionColumn(-1)).toThrow("Invalid rank id");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("toPermissionValue", () => {
|
||||||
|
it("returns numeric value for valid input", () => {
|
||||||
|
expect(toPermissionValue(5)).toBe(5);
|
||||||
|
expect(toPermissionValue("10")).toBe(10);
|
||||||
|
expect(toPermissionValue("255")).toBe(255);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("clamps to 0-255 range", () => {
|
||||||
|
expect(toPermissionValue(-1)).toBe(0);
|
||||||
|
expect(toPermissionValue(256)).toBe(255);
|
||||||
|
expect(toPermissionValue(999)).toBe(255);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("returns 0 for non-numeric strings", () => {
|
||||||
|
expect(toPermissionValue("abc")).toBe(0);
|
||||||
|
expect(toPermissionValue(NaN)).toBe(0);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("splitRankUpdateFields", () => {
|
||||||
|
it("splits rank general fields from permission fields", () => {
|
||||||
|
const { rankFields, permissionFields } = splitRankUpdateFields({
|
||||||
|
rank_name: "Moderator",
|
||||||
|
level: 5,
|
||||||
|
cmd_ban: "1",
|
||||||
|
cmd_kick: "1",
|
||||||
|
});
|
||||||
|
expect(rankFields).toEqual({ rank_name: "Moderator", level: 5 });
|
||||||
|
expect(permissionFields).toEqual({ cmd_ban: 1, cmd_kick: 1 });
|
||||||
|
});
|
||||||
|
|
||||||
|
it("skips id field and invalid keys", () => {
|
||||||
|
const { rankFields, permissionFields } = splitRankUpdateFields({
|
||||||
|
id: 5,
|
||||||
|
"": "test",
|
||||||
|
});
|
||||||
|
expect(rankFields).toEqual({});
|
||||||
|
expect(permissionFields).toEqual({});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("returns empty objects for empty input", () => {
|
||||||
|
const { rankFields, permissionFields } = splitRankUpdateFields({});
|
||||||
|
expect(rankFields).toEqual({});
|
||||||
|
expect(permissionFields).toEqual({});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("RANK_GENERAL_FIELDS", () => {
|
||||||
|
it("contains expected fields", () => {
|
||||||
|
expect(RANK_GENERAL_FIELDS.has("rank_name")).toBe(true);
|
||||||
|
expect(RANK_GENERAL_FIELDS.has("badge")).toBe(true);
|
||||||
|
expect(RANK_GENERAL_FIELDS.has("level")).toBe(true);
|
||||||
|
expect(RANK_GENERAL_FIELDS.has("nonexistent")).toBe(false);
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -1,66 +1,141 @@
|
|||||||
import { describe, expect, it, vi } from "vitest";
|
import { describe, expect, it, vi } from "vitest";
|
||||||
|
|
||||||
|
vi.mock("@/lib/services/rcon", () => ({}));
|
||||||
|
|
||||||
import { sendCurrency } from "./send-currency";
|
import { sendCurrency } from "./send-currency";
|
||||||
|
|
||||||
function deps(delivered: boolean) {
|
|
||||||
return {
|
|
||||||
rcon: {
|
|
||||||
giveCredits: vi.fn().mockResolvedValue(delivered),
|
|
||||||
giveDuckets: vi.fn().mockResolvedValue(delivered),
|
|
||||||
giveDiamonds: vi.fn().mockResolvedValue(delivered),
|
|
||||||
givePointsGotw: vi.fn().mockResolvedValue(delivered),
|
|
||||||
},
|
|
||||||
db: {
|
|
||||||
user: { update: vi.fn().mockResolvedValue(undefined) },
|
|
||||||
usersCurrency: { upsert: vi.fn().mockResolvedValue(undefined) },
|
|
||||||
},
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
describe("sendCurrency", () => {
|
describe("sendCurrency", () => {
|
||||||
it("rejects non-positive amounts", async () => {
|
it("returns false for non-positive amount", async () => {
|
||||||
const d = deps(true);
|
expect(await sendCurrency({} as any, 1, "credits", 0)).toBe(false);
|
||||||
expect(await sendCurrency(d, 1, "credits", 0)).toBe(false);
|
expect(await sendCurrency({} as any, 1, "credits", -5)).toBe(false);
|
||||||
expect(await sendCurrency(d, 1, "credits", null)).toBe(false);
|
expect(await sendCurrency({} as any, 1, "credits", null)).toBe(false);
|
||||||
expect(d.rcon.giveCredits).not.toHaveBeenCalled();
|
expect(await sendCurrency({} as any, 1, "credits", undefined)).toBe(false);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("delivers credits via RCON when connected (no DB write)", async () => {
|
it("delivers credits via RCON", async () => {
|
||||||
const d = deps(true);
|
const rcon = {
|
||||||
expect(await sendCurrency(d, 7, "credits", 100)).toBe(true);
|
giveCredits: vi.fn().mockResolvedValue(true),
|
||||||
expect(d.rcon.giveCredits).toHaveBeenCalledWith(7, 100);
|
giveDuckets: vi.fn(),
|
||||||
expect(d.db.user.update).not.toHaveBeenCalled();
|
giveDiamonds: vi.fn(),
|
||||||
|
givePointsGotw: vi.fn(),
|
||||||
|
};
|
||||||
|
const result = await sendCurrency({ rcon, db: {} as any }, 1, "credits", 100);
|
||||||
|
expect(result).toBe(true);
|
||||||
|
expect(rcon.giveCredits).toHaveBeenCalledWith(1, 100);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("falls back to users.credits when RCON fails", async () => {
|
it("falls back to DB update when RCON fails for credits", async () => {
|
||||||
const d = deps(false);
|
const rcon = {
|
||||||
await sendCurrency(d, 7, "credits", 100);
|
giveCredits: vi.fn().mockResolvedValue(false),
|
||||||
expect(d.db.user.update).toHaveBeenCalledWith({
|
giveDuckets: vi.fn(),
|
||||||
where: { id: 7 },
|
giveDiamonds: vi.fn(),
|
||||||
|
givePointsGotw: vi.fn(),
|
||||||
|
};
|
||||||
|
const userUpdate = vi.fn().mockResolvedValue({});
|
||||||
|
const result = await sendCurrency(
|
||||||
|
{
|
||||||
|
rcon,
|
||||||
|
db: { user: { update: userUpdate } } as any,
|
||||||
|
},
|
||||||
|
1,
|
||||||
|
"credits",
|
||||||
|
100,
|
||||||
|
);
|
||||||
|
expect(result).toBe(true);
|
||||||
|
expect(userUpdate).toHaveBeenCalledWith({
|
||||||
|
where: { id: 1 },
|
||||||
data: { credits: { increment: 100 } },
|
data: { credits: { increment: 100 } },
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it("falls back to users_currency by type for diamonds", async () => {
|
it("delivers duckets via RCON", async () => {
|
||||||
const d = deps(false);
|
const rcon = {
|
||||||
await sendCurrency(d, 7, "diamonds", 3);
|
giveCredits: vi.fn(),
|
||||||
expect(d.db.usersCurrency.upsert).toHaveBeenCalledWith({
|
giveDuckets: vi.fn().mockResolvedValue(true),
|
||||||
where: { userId_type: { userId: 7, type: 5 } },
|
giveDiamonds: vi.fn(),
|
||||||
update: { amount: { increment: 3 } },
|
givePointsGotw: vi.fn(),
|
||||||
create: { userId: 7, type: 5, amount: 3 },
|
};
|
||||||
});
|
const result = await sendCurrency({ rcon, db: {} as any }, 1, "duckets", 50);
|
||||||
|
expect(result).toBe(true);
|
||||||
|
expect(rcon.giveDuckets).toHaveBeenCalledWith(1, 50);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("uses the right type values (duckets=0, points=101)", async () => {
|
it("upserts duckets in DB when RCON fails", async () => {
|
||||||
const d = deps(false);
|
const rcon = {
|
||||||
await sendCurrency(d, 1, "duckets", 10);
|
giveCredits: vi.fn(),
|
||||||
await sendCurrency(d, 1, "points", 20);
|
giveDuckets: vi.fn().mockResolvedValue(false),
|
||||||
expect(d.db.usersCurrency.upsert).toHaveBeenNthCalledWith(
|
giveDiamonds: vi.fn(),
|
||||||
|
givePointsGotw: vi.fn(),
|
||||||
|
};
|
||||||
|
const upsert = vi.fn().mockResolvedValue({});
|
||||||
|
await sendCurrency(
|
||||||
|
{
|
||||||
|
rcon,
|
||||||
|
db: { usersCurrency: { upsert } } as any,
|
||||||
|
},
|
||||||
1,
|
1,
|
||||||
expect.objectContaining({ create: { userId: 1, type: 0, amount: 10 } }),
|
"duckets",
|
||||||
|
50,
|
||||||
);
|
);
|
||||||
expect(d.db.usersCurrency.upsert).toHaveBeenNthCalledWith(
|
expect(upsert).toHaveBeenCalledWith(
|
||||||
2,
|
expect.objectContaining({
|
||||||
expect.objectContaining({ create: { userId: 1, type: 101, amount: 20 } }),
|
where: expect.objectContaining({
|
||||||
|
userId_type: expect.objectContaining({ userId: 1, type: 0 }),
|
||||||
|
}),
|
||||||
|
}),
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
});
|
|
||||||
|
it("upserts diamonds in DB when RCON fails", async () => {
|
||||||
|
const rcon = {
|
||||||
|
giveCredits: vi.fn(),
|
||||||
|
giveDuckets: vi.fn(),
|
||||||
|
giveDiamonds: vi.fn().mockResolvedValue(false),
|
||||||
|
givePointsGotw: vi.fn(),
|
||||||
|
};
|
||||||
|
const upsert = vi.fn().mockResolvedValue({});
|
||||||
|
await sendCurrency(
|
||||||
|
{
|
||||||
|
rcon,
|
||||||
|
db: { usersCurrency: { upsert } } as any,
|
||||||
|
},
|
||||||
|
1,
|
||||||
|
"diamonds",
|
||||||
|
10,
|
||||||
|
);
|
||||||
|
expect(upsert).toHaveBeenCalledWith(
|
||||||
|
expect.objectContaining({
|
||||||
|
where: expect.objectContaining({
|
||||||
|
userId_type: expect.objectContaining({ userId: 1, type: 5 }),
|
||||||
|
}),
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("upserts points in DB when RCON fails", async () => {
|
||||||
|
const rcon = {
|
||||||
|
giveCredits: vi.fn(),
|
||||||
|
giveDuckets: vi.fn(),
|
||||||
|
giveDiamonds: vi.fn(),
|
||||||
|
givePointsGotw: vi.fn().mockResolvedValue(false),
|
||||||
|
};
|
||||||
|
const upsert = vi.fn().mockResolvedValue({});
|
||||||
|
await sendCurrency(
|
||||||
|
{
|
||||||
|
rcon,
|
||||||
|
db: { usersCurrency: { upsert } } as any,
|
||||||
|
},
|
||||||
|
1,
|
||||||
|
"points",
|
||||||
|
10,
|
||||||
|
);
|
||||||
|
expect(rcon.givePointsGotw).toHaveBeenCalled();
|
||||||
|
expect(upsert).toHaveBeenCalledWith(
|
||||||
|
expect.objectContaining({
|
||||||
|
where: expect.objectContaining({
|
||||||
|
userId_type: expect.objectContaining({ userId: 1, type: 101 }),
|
||||||
|
}),
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
Reference in new issue
Block a user