feat: expand ISAC content modeling
This commit is contained in:
@@ -0,0 +1,166 @@
|
||||
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'],
|
||||
]);
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user