NEO-70: Fix Bruno craft tests for shared Postgres CI state.

Clear refined stock for insufficient_materials deny; spine tolerates
node_depleted gathers, reuses persisted scrap, and asserts refine XP delta.
pull/104/head
VinPropane 2026-05-24 18:06:26 -04:00
parent 8ebe83d701
commit db7c443b50
4 changed files with 72 additions and 27 deletions

View File

@ -5,20 +5,30 @@ meta {
}
docs {
NEO-70: craft without seeded materials denies with insufficient_materials. Requires empty bag (run before spine or on fresh server).
NEO-70: make_field_stim_mk0 without refined_plate_stock denies with insufficient_materials. Pre-request clears refined stock (inventory POST setup only) so the deny is stable after dotnet test / prior Bruno requests.
}
script:pre-request {
const { getInventory, sumItemQuantity } = require("./scripts/inventory-api-helper.js");
const inventory = await getInventory(bru);
const scrap = sumItemQuantity(inventory, "scrap_metal_bulk");
const refined = sumItemQuantity(inventory, "refined_plate_stock");
if (scrap >= 5 || refined > 0) {
throw new Error(
`expected empty craft inputs before insufficient_materials test; scrap=${scrap} refined=${refined} — restart server or run this request before spine`,
);
const { getInventory, sumItemQuantity, postMutation } = require("./scripts/inventory-api-helper.js");
async function clearItem(itemId) {
let qty = sumItemQuantity(await getInventory(bru), itemId);
while (qty > 0) {
const removeQty = Math.min(qty, 99);
const response = await postMutation(bru, {
schemaVersion: 1,
mutationKind: "remove",
itemId,
quantity: removeQty,
});
if (response.status !== 200 || response.data?.applied !== true) {
throw new Error(`clear ${itemId} failed: ${response.status} ${JSON.stringify(response.data)}`);
}
qty = sumItemQuantity(await getInventory(bru), itemId);
}
}
bru.setVar("scrapBeforeInsufficientDeny", scrap);
await clearItem("refined_plate_stock");
}
post {
@ -34,7 +44,7 @@ headers {
body:json {
{
"schemaVersion": 1,
"recipeId": "refine_scrap_standard"
"recipeId": "make_field_stim_mk0"
}
}

View File

@ -1,7 +1,7 @@
meta {
name: POST craft deny inventory full
type: http
seq: 5
seq: 3
}
docs {

View File

@ -5,15 +5,28 @@ meta {
}
docs {
NEO-70 prototype spine: gather scrap from four prototype resource nodes (delta 5 + gamma 3 + beta 2 + alpha 1 = 11 scrap), craft refine_scrap_efficient (10→2 refined_plate_stock), then make_field_stim_mk0. No inventory POST shortcuts.
NEO-70 prototype spine: accumulate >=11 scrap_metal_bulk (gather when nodes allow, reuse persisted inventory after dotnet test), refine_scrap_efficient when refined_plate_stock < 2, then make_field_stim_mk0. No inventory POST shortcuts for spine materials.
}
script:pre-request {
const axios = require("axios");
const { getInventory, sumItemQuantity } = require("./scripts/inventory-api-helper.js");
const baseUrl = bru.getEnvVar("baseUrl") || bru.getVar("baseUrl");
const playerId = bru.getEnvVar("playerId") || bru.getVar("playerId");
const jsonHeaders = { headers: { "Content-Type": "application/json" } };
const progressionBefore = await axios.get(`${baseUrl}/game/players/${playerId}/skill-progression`);
const refineBefore = progressionBefore.data.skills.find((row) => row.id === "refine");
bru.setVar("refineXpBeforeSpine", refineBefore ? refineBefore.xp : 0);
async function currentScrap() {
return sumItemQuantity(await getInventory(bru), "scrap_metal_bulk");
}
async function currentRefined() {
return sumItemQuantity(await getInventory(bru), "refined_plate_stock");
}
async function moveNear(x, z) {
await axios.post(
`${baseUrl}/game/players/${playerId}/move`,
@ -22,15 +35,22 @@ script:pre-request {
);
}
async function gather(interactableId) {
async function tryGather(interactableId) {
const response = await axios.post(
`${baseUrl}/game/players/${playerId}/interact`,
{ schemaVersion: 1, interactableId },
jsonHeaders,
);
if (response.status !== 200 || response.data?.allowed !== true) {
throw new Error(`gather ${interactableId} failed: ${response.status} ${JSON.stringify(response.data)}`);
if (response.status !== 200) {
throw new Error(`gather ${interactableId} HTTP ${response.status}`);
}
if (response.data?.allowed === true) {
return;
}
if (response.data?.reasonCode === "node_depleted") {
return;
}
throw new Error(`gather ${interactableId} denied: ${JSON.stringify(response.data)}`);
}
async function craft(recipeId) {
@ -45,16 +65,30 @@ script:pre-request {
return response.data;
}
await moveNear(16, -2);
await gather("prototype_urban_bulk_delta");
await moveNear(10, -2);
await gather("prototype_bio_mat_gamma");
await moveNear(16, -6);
await gather("prototype_subsurface_vein_beta");
await moveNear(10, -6);
await gather("prototype_resource_node_alpha");
const gatherPlan = [
{ id: "prototype_urban_bulk_delta", x: 16, z: -2 },
{ id: "prototype_bio_mat_gamma", x: 10, z: -2 },
{ id: "prototype_subsurface_vein_beta", x: 16, z: -6 },
{ id: "prototype_resource_node_alpha", x: 10, z: -6 },
];
await craft("refine_scrap_efficient");
let scrap = await currentScrap();
for (const node of gatherPlan) {
if (scrap >= 11) {
break;
}
await moveNear(node.x, node.z);
await tryGather(node.id);
scrap = await currentScrap();
}
if (scrap < 11) {
throw new Error(`spine needs >=11 scrap_metal_bulk, have ${scrap} after optional gathers`);
}
if ((await currentRefined()) < 2) {
await craft("refine_scrap_efficient");
}
}
post {
@ -111,7 +145,8 @@ tests {
test("refine skill XP increased after spine crafts", function () {
const progression = bru.getVar("spineProgression");
const refine = progression.skills.find((row) => row.id === "refine");
const before = bru.getVar("refineXpBeforeSpine");
expect(refine).to.be.an("object");
expect(refine.xp).to.be.at.least(20);
expect(refine.xp - before).to.be.at.least(20);
});
}

View File

@ -3,5 +3,5 @@ meta {
}
docs {
NEO-70 craft HTTP. Run order: deny requests first (seq 14), then spine last (seq 5). Spine uses gather interact (no inventory POST shortcuts). Deny setup requests may use inventory POST only where noted. On persistent Postgres dev stores, run insufficient_materials before spine or restart the server so dev-local-1 bag is empty.
NEO-70 craft HTTP. Run order within folder: deny requests (seq 14) then spine (seq 5). Full collection runs after dotnet test on shared Postgres — insufficient-materials clears refined stock; spine reuses persisted scrap and tolerates node_depleted gathers. Deny setup requests may use inventory POST only where noted.
}