fix(housekeeping): restore people partial compatibility
CI / check (pull_request) Successful in 42s
CI / release (pull_request) Skipped
CI / deploy (pull_request) Skipped

This commit is contained in:
Simo committed 2026-08-29 21:03:43 +02:00
1 parent fcd1dfd96e
commit a6a288d9ff
6 files changed
+412 -11

No files matched your search

+43
View File
@@ -54,3 +54,46 @@ it("keeps one ACL check while delegating a positive bulk adjustment", async () =
{ userIds: [7, 8], amount: 25, type: "credits" },
);
});
it("keeps the pre-Task12 positive-adjust result when currency RCON throws after the database commit", async () => {
execute.mockResolvedValueOnce({
ok: true,
data: {
before: { completed: 0, total: 1, failedIds: [] },
after: {
completed: 1,
total: 1,
failedIds: [],
externalSyncFailures: [
{
userId: 7,
reason:
"Database applied; emulator sync failed. Do not retry automatically.",
},
],
},
completion: {
status: "partial",
external: "failed",
audit: "persisted",
},
},
correlationId: "legacy-positive-adjust",
completion: {
status: "partial",
external: "failed",
audit: "persisted",
},
});
await expect(
bulkAdjustCurrency({ userIds: [7], amount: 25, type: "credits" }),
).resolves.toEqual({
ok: true,
data: {
adjusted: 0,
total: 1,
failedIds: [{ userId: 7, reason: "Database error" }],
},
});
});
+60
View File
@@ -91,4 +91,64 @@ describe("legacy bulk user wrappers", () => {
{ userId: 9, untilUnix: 1234 },
);
});
it.each([
[
"currency",
"users.bulk-currency",
() => bulkGiveCurrency({ userIds: [7], amount: 100, type: "credits" }),
],
[
"badge",
"users.bulk-badge",
() => bulkGiveBadge({ userIds: [7], badgeCode: "ADM" }),
],
] as const)(
"restores the pre-Task12 legacy result when %s RCON throws after the database commit",
async (_kind, operation, invoke) => {
execute.mockResolvedValueOnce({
ok: true,
data: {
before: { completed: 0, total: 1, failedIds: [] },
after: {
completed: 1,
total: 1,
failedIds: [],
externalSyncFailures: [
{
userId: 7,
reason:
"Database applied; emulator sync failed. Do not retry automatically.",
},
],
},
completion: {
status: "partial",
external: "failed",
audit: "persisted",
},
},
correlationId: "legacy-external-sync-failure",
completion: {
status: "partial",
external: "failed",
audit: "persisted",
},
});
await expect(invoke()).resolves.toEqual({
ok: true,
data: {
given: 0,
total: 1,
failedIds: [{ userId: 7, reason: "Database error" }],
},
});
expect(execute).toHaveBeenCalledWith(
expect.anything(),
operation,
expect.anything(),
);
},
);
});
+41 -9
View File
@@ -52,6 +52,35 @@ function failedIds(value: unknown): Array<{ userId: number; reason: string }> {
: [];
}
function legacyBulkOutcome(
after: Readonly<Record<string, unknown>> | null,
userIds: readonly number[],
): {
readonly completed: number;
readonly total: number;
readonly failedIds: Array<{ userId: number; reason: string }>;
} {
const databaseFailures = failedIds(after?.failedIds);
const externalSyncFailures = failedIds(after?.externalSyncFailures).map(
({ userId }) => ({ userId, reason: "Database error" }),
);
const pendingFailures = [...databaseFailures, ...externalSyncFailures];
const orderedFailures = userIds.flatMap((userId) => {
const index = pendingFailures.findIndex(
(failure) => failure.userId === userId,
);
return index === -1 ? [] : pendingFailures.splice(index, 1);
});
return {
completed: Math.max(
0,
numberValue(after?.completed) - externalSyncFailures.length,
),
total: numberValue(after?.total),
failedIds: [...orderedFailures, ...pendingFailures],
};
}
export async function bulkUnban({
userIds,
}: {
@@ -113,12 +142,13 @@ export async function bulkGiveCurrency({
type,
});
if (!result.ok) return { ok: false, error: "Bulk currency failed" };
const outcome = legacyBulkOutcome(result.data.after, userIds);
return {
ok: true,
data: {
given: numberValue(result.data.after?.completed),
total: numberValue(result.data.after?.total),
failedIds: failedIds(result.data.after?.failedIds),
given: outcome.completed,
total: outcome.total,
failedIds: outcome.failedIds,
},
};
}
@@ -142,12 +172,13 @@ export async function bulkGiveBadge({
badgeCode,
});
if (!result.ok) return { ok: false, error: "Bulk badge failed" };
const outcome = legacyBulkOutcome(result.data.after, userIds);
return {
ok: true,
data: {
given: numberValue(result.data.after?.completed),
total: numberValue(result.data.after?.total),
failedIds: failedIds(result.data.after?.failedIds),
given: outcome.completed,
total: outcome.total,
failedIds: outcome.failedIds,
},
};
}
@@ -178,12 +209,13 @@ export async function bulkAdjustCurrency({
type,
});
if (!result.ok) return { ok: false, error: "Currency adjustment failed" };
const outcome = legacyBulkOutcome(result.data.after, userIds);
return {
ok: true,
data: {
adjusted: numberValue(result.data.after?.completed),
total: numberValue(result.data.after?.total),
failedIds: failedIds(result.data.after?.failedIds),
adjusted: outcome.completed,
total: outcome.total,
failedIds: outcome.failedIds,
},
};
}