167 lines
4.8 KiB
TypeScript
167 lines
4.8 KiB
TypeScript
import { beforeEach, describe, expect, it, vi } from 'vitest';
|
|
|
|
const mocks = vi.hoisted(() => ({
|
|
audits: [] as Array<Record<string, unknown>>,
|
|
database: {
|
|
exec: vi.fn(async () => undefined),
|
|
prepare: vi.fn(async () => ({
|
|
get: vi.fn(async (...statIds: unknown[]) => ({ count: statIds.length })),
|
|
})),
|
|
},
|
|
getModSecondaryPool: vi.fn(),
|
|
recordIsacAudit: vi.fn(),
|
|
replaceModSecondaryPool: vi.fn(),
|
|
requirePermission: vi.fn(async (user) => user),
|
|
}));
|
|
|
|
vi.mock('$lib/server/auth/session', () => ({
|
|
getSessionUser: vi.fn(async () => ({ id: 'operator-1' })),
|
|
}));
|
|
|
|
vi.mock('$lib/server/auth/authorization', () => ({
|
|
requireDataView: vi.fn(async (user) => user),
|
|
requirePermission: mocks.requirePermission,
|
|
}));
|
|
|
|
vi.mock('$lib/server/database', () => ({
|
|
getDatabase: vi.fn(async () => mocks.database),
|
|
}));
|
|
|
|
vi.mock('$lib/server/audit', () => ({
|
|
auditRecord: vi.fn(),
|
|
recordIsacAudit: mocks.recordIsacAudit,
|
|
}));
|
|
|
|
vi.mock('$lib/server/data-moderation', () => ({
|
|
getPendingEntities: vi.fn(async () => []),
|
|
}));
|
|
|
|
vi.mock('$lib/server/managed-images', () => ({
|
|
applyGameDataRender: vi.fn(),
|
|
ManagedImageValidationError: class extends Error {},
|
|
managedImageUrl: vi.fn(() => ''),
|
|
processManagedImage: vi.fn(),
|
|
readGameDataRenderMutation: vi.fn(),
|
|
}));
|
|
|
|
vi.mock('$lib/server/mod-secondary-pools', () => ({
|
|
getModSecondaryPool: mocks.getModSecondaryPool,
|
|
replaceModSecondaryPool: mocks.replaceModSecondaryPool,
|
|
}));
|
|
|
|
import { actions } from './+page.server';
|
|
|
|
const endpoints = (
|
|
level1Min: number,
|
|
level1Max: number,
|
|
level20Min: number,
|
|
level20Max: number,
|
|
) => ({ level1Min, level1Max, level20Min, level20Max });
|
|
|
|
const pool = (suffix: string, offset = 0) => [
|
|
{
|
|
statId: `critical-hit-chance-${suffix}`,
|
|
values: {
|
|
Specialized: endpoints(1 + offset, 2 + offset, 3 + offset, 4 + offset),
|
|
Superior: endpoints(2 + offset, 3 + offset, 4 + offset, 5 + offset),
|
|
'High-end': endpoints(3 + offset, 4 + offset, 5 + offset, 6 + offset),
|
|
},
|
|
},
|
|
{
|
|
statId: `critical-hit-damage-${suffix}`,
|
|
values: {
|
|
Specialized: endpoints(4 + offset, 5 + offset, 6 + offset, 7 + offset),
|
|
Superior: endpoints(5 + offset, 6 + offset, 7 + offset, 8 + offset),
|
|
'High-end': endpoints(6 + offset, 7 + offset, 8 + offset, 9 + offset),
|
|
},
|
|
},
|
|
];
|
|
|
|
const request = (secondaryRanges: ReturnType<typeof pool>) => {
|
|
const form = new FormData();
|
|
form.set('id', 'weapon-mod-secondary-pool');
|
|
form.set('secondaryRanges', JSON.stringify(secondaryRanges));
|
|
return new Request('http://localhost/isac/weaponmods?/saveSecondaryPool', {
|
|
method: 'POST',
|
|
body: form,
|
|
});
|
|
};
|
|
|
|
describe('Weapon Mod shared secondary pool action', () => {
|
|
beforeEach(() => {
|
|
vi.clearAllMocks();
|
|
mocks.audits.length = 0;
|
|
mocks.recordIsacAudit.mockImplementation(async (event) => {
|
|
mocks.audits.push(event);
|
|
});
|
|
});
|
|
|
|
it('audits the complete contract and commits the transaction', async () => {
|
|
const before = pool('before');
|
|
const after = pool('after', 1);
|
|
mocks.getModSecondaryPool
|
|
.mockResolvedValueOnce(before)
|
|
.mockResolvedValueOnce(after);
|
|
|
|
const result = await actions.saveSecondaryPool?.({
|
|
cookies: {} as never,
|
|
request: request(after),
|
|
} as never);
|
|
|
|
expect(result).toMatchObject({
|
|
weaponModSecondaryPoolSaved: true,
|
|
weaponModSecondaryPoolMode: 'edit',
|
|
resultToken: expect.any(String),
|
|
});
|
|
expect(mocks.requirePermission).toHaveBeenCalledWith(
|
|
{ id: 'operator-1' },
|
|
'data.manage',
|
|
);
|
|
expect(mocks.replaceModSecondaryPool).toHaveBeenCalledWith(
|
|
mocks.database,
|
|
'weapon',
|
|
after,
|
|
);
|
|
expect(mocks.audits).toEqual([
|
|
{
|
|
actorId: 'operator-1',
|
|
category: 'game_data',
|
|
action: 'weapon_mod_secondary_pool.updated',
|
|
entityId: 'weapon-mod-secondary-pool',
|
|
before,
|
|
after,
|
|
database: mocks.database,
|
|
},
|
|
]);
|
|
expect(mocks.database.exec.mock.calls).toEqual([
|
|
['BEGIN IMMEDIATE'],
|
|
['COMMIT'],
|
|
]);
|
|
});
|
|
|
|
it('rolls back without committing when the audit write fails', async () => {
|
|
const before = pool('before');
|
|
const after = pool('after', 1);
|
|
const auditError = new Error('audit insert failed');
|
|
mocks.getModSecondaryPool
|
|
.mockResolvedValueOnce(before)
|
|
.mockResolvedValueOnce(after);
|
|
mocks.recordIsacAudit.mockRejectedValueOnce(auditError);
|
|
|
|
await expect(
|
|
actions.saveSecondaryPool?.({
|
|
cookies: {} as never,
|
|
request: request(after),
|
|
} as never),
|
|
).rejects.toThrow(auditError);
|
|
|
|
expect(mocks.recordIsacAudit).toHaveBeenCalledOnce();
|
|
expect(mocks.database.exec).toHaveBeenCalledWith('ROLLBACK');
|
|
expect(mocks.database.exec).not.toHaveBeenCalledWith('COMMIT');
|
|
expect(mocks.database.exec.mock.calls).toEqual([
|
|
['BEGIN IMMEDIATE'],
|
|
['ROLLBACK'],
|
|
]);
|
|
});
|
|
});
|