feat: expand ISAC content modeling
Validate and deploy production / validate (push) Canceled after 0s
Validate and deploy production / deploy (push) Canceled after 0s
Validate and deploy staging / validate (push) Successful in 28s
Validate and deploy staging / deploy (push) Successful in 1m3s

This commit is contained in:
jb
2026-08-03 01:23:20 -04:00
parent d9924a95b9
commit 57068b61cd
90 changed files with 9635 additions and 1270 deletions
+186
View File
@@ -0,0 +1,186 @@
import { beforeEach, describe, expect, it, vi } from 'vitest';
import { createDraft, type LoadoutDraft } from '$lib/builder/loadout';
const mocks = vi.hoisted(() => ({
insertBuild: vi.fn(),
getSessionUser: vi.fn(async () => ({
id: 'agent-1',
displayName: 'Test Agent',
})),
database: {
prepare: vi.fn(),
},
}));
vi.mock('$lib/server/auth/session', () => ({
getSessionUser: mocks.getSessionUser,
}));
vi.mock('$lib/server/database', () => ({
getDatabase: vi.fn(async () => mocks.database),
}));
import { actions } from './+page.server';
const primaryStatId = 'skill-duration';
const secondaryStatIds = ['critical-hit-chance', 'critical-hit-damage'];
const validLoadout = (): LoadoutDraft => {
const loadout = createDraft();
loadout.buildName = 'Validated Skill Chips';
loadout.specialization.focusId = 'focus-1';
loadout.chips[0] = {
setId: 'chip-set-1',
quality: 'Superior',
primary: { statId: primaryStatId, value: 15 },
secondary: [
{ statId: secondaryStatIds[0], value: 2 },
{ statId: secondaryStatIds[1], value: 4 },
],
};
return loadout;
};
const saveRequest = (loadout: LoadoutDraft) => {
const form = new FormData();
form.set('loadout', JSON.stringify(loadout));
form.set('visibility', 'private');
return new Request('http://localhost/builder?/save', {
method: 'POST',
body: form,
});
};
const runSave = (loadout: LoadoutDraft) =>
actions.save?.({
cookies: {} as never,
request: saveRequest(loadout),
} as never);
describe('Builder Skill Chip save validation', () => {
beforeEach(() => {
vi.clearAllMocks();
mocks.database.prepare.mockImplementation(async (sql: string) => ({
all: vi.fn(async () => {
if (sql.includes('FROM skill_chip_sets'))
return [
{
id: 'chip-set-1',
specialization_ability_id: 'ability-1',
},
];
if (sql.includes('FROM specialization_focus_abilities'))
return [{ ability_id: 'ability-1', ability_slot: 1 }];
if (sql.includes('FROM skill_chip_primary_options'))
return [{ stat_attribute_id: primaryStatId }];
if (sql.includes('FROM skill_chip_primary_quality_values'))
return [
{
stat_attribute_id: primaryStatId,
quality: 'Superior',
min_value: 10,
max_value: 20,
},
];
if (sql.includes('FROM skill_chip_secondary_options'))
return secondaryStatIds.map((stat_attribute_id) => ({
stat_attribute_id,
}));
if (sql.includes('FROM skill_chip_secondary_quality_values'))
return secondaryStatIds.map((stat_attribute_id) => ({
stat_attribute_id,
quality: 'Superior',
min_value: 1,
max_value: 5,
}));
return [];
}),
run: mocks.insertBuild,
}));
});
it('rejects a selected Skill Chip assigned to a different focus ability', async () => {
mocks.database.prepare.mockImplementation(async (sql: string) => ({
all: vi.fn(async () => {
if (sql.includes('FROM skill_chip_sets'))
return [
{
id: 'chip-set-1',
specialization_ability_id: 'ability-2',
},
];
if (sql.includes('FROM specialization_focus_abilities'))
return [{ ability_id: 'ability-1', ability_slot: 1 }];
return [];
}),
run: mocks.insertBuild,
}));
const result = await runSave(validLoadout());
expect(result).toMatchObject({
status: 400,
data: {
buildError: 'Skill Chip 1 is not compatible with its assigned ability.',
},
});
expect(mocks.insertBuild).not.toHaveBeenCalled();
});
it.each([
{
label: 'primary',
mutate: (loadout: LoadoutDraft) => {
loadout.chips[0].primary.value = 21;
},
message: 'Skill Chip 1 has an invalid primary Attribute value.',
},
{
label: 'secondary',
mutate: (loadout: LoadoutDraft) => {
loadout.chips[0].secondary[1].value = 6;
},
message: 'Skill Chip 1 has an invalid secondary Attribute value.',
},
])('rejects an out-of-range $label value', async ({ mutate, message }) => {
const loadout = validLoadout();
mutate(loadout);
const result = await runSave(loadout);
expect(result).toMatchObject({
status: 400,
data: { buildError: message },
});
expect(mocks.insertBuild).not.toHaveBeenCalled();
});
it('rejects duplicate secondary Attributes', async () => {
const loadout = validLoadout();
loadout.chips[0].secondary[1].statId = secondaryStatIds[0];
const result = await runSave(loadout);
expect(result).toMatchObject({
status: 400,
data: {
buildError: 'Skill Chip 1 must use distinct secondary Attributes.',
},
});
expect(mocks.insertBuild).not.toHaveBeenCalled();
});
it('accepts primary and secondary values within the configured profiles', async () => {
const result = await runSave(validLoadout());
expect(result).toMatchObject({
buildSaved: true,
buildToken: expect.any(String),
savedBuild: {
visibility: 'private',
title: 'Validated Skill Chips',
},
});
expect(mocks.insertBuild).toHaveBeenCalledOnce();
});
});