173 lines
7.2 KiB
JavaScript
173 lines
7.2 KiB
JavaScript
// Integration tests for step 10.15: Non-stackable items get one inventory row per unit.
|
|
const { describe, it, before, after } = require('node:test');
|
|
const assert = require('node:assert/strict');
|
|
const { setup, teardown, cleanup } = require('../helpers');
|
|
|
|
describe('step-10.15 non-stackable items: one row per unit', () => {
|
|
let baseUrl, userSeq = 0;
|
|
|
|
before(async () => { baseUrl = await setup(); });
|
|
after(async () => { teardown(); cleanup(); });
|
|
|
|
// ─── helpers ──────────────────────────────────────────────────────────
|
|
|
|
async function registerAndLogin(username, password) {
|
|
await fetch(`${baseUrl}/api/auth/register`, {
|
|
method: 'POST',
|
|
headers: { 'Content-Type': 'application/json' },
|
|
body: JSON.stringify({ username, password }),
|
|
});
|
|
const res = await fetch(`${baseUrl}/api/auth/login`, {
|
|
method: 'POST',
|
|
headers: { 'Content-Type': 'application/json' },
|
|
body: JSON.stringify({ username, password }),
|
|
});
|
|
assert.equal(res.status, 200);
|
|
return res.headers.get('set-cookie') || '';
|
|
}
|
|
|
|
async function createHero(cookie, name, cls) {
|
|
const res = await fetch(`${baseUrl}/api/character`, {
|
|
method: 'POST',
|
|
headers: { 'Content-Type': 'application/json', Cookie: cookie },
|
|
body: JSON.stringify({ name, class: cls }),
|
|
});
|
|
assert.equal(res.status, 201);
|
|
const { data } = await res.json();
|
|
return data.character.id;
|
|
}
|
|
|
|
function countInvRows(characterId, itemId) {
|
|
const db = require('../../src/db').getDb();
|
|
return db.prepare(
|
|
'SELECT COUNT(*) AS cnt FROM inventory WHERE character_id = ? AND item_id = ?'
|
|
).get(characterId, itemId).cnt;
|
|
}
|
|
|
|
function getInvRows(characterId, itemId) {
|
|
const db = require('../../src/db').getDb();
|
|
return db.prepare(
|
|
'SELECT id, quantity FROM inventory WHERE character_id = ? AND item_id = ? ORDER BY id'
|
|
).all(characterId, itemId);
|
|
}
|
|
|
|
// ─── Non-stackable items (weapons) ────────────────────────────────────
|
|
|
|
it('buying 3 non-stackable weapons creates 3 inventory rows', async () => {
|
|
const cookie = await registerAndLogin(`s15b${userSeq++}`, 'pass1234');
|
|
const characterId = await createHero(cookie, 'S15B', 'warrior');
|
|
|
|
// Give hero enough gold for 3 Rusty Swords (60g).
|
|
const db = require('../../src/db').getDb();
|
|
db.prepare('UPDATE characters SET gold = 100 WHERE id = ?').run(characterId);
|
|
|
|
// Buy 3 Rusty Swords (item 1, non-stackable, 20g each = 60g total).
|
|
const res = await fetch(`${baseUrl}/api/shop/buy`, {
|
|
method: 'POST',
|
|
headers: { 'Content-Type': 'application/json', Cookie: cookie },
|
|
body: JSON.stringify({ itemId: 1, quantity: 3 }),
|
|
});
|
|
const body = await res.json();
|
|
assert.equal(res.status, 200);
|
|
assert.equal(body.ok, true);
|
|
assert.equal(body.data.character.gold, 40); // 100 - 60
|
|
|
|
// DB: should be exactly 4 rows (1 starter + 3 bought), each with quantity 1.
|
|
const rows = getInvRows(characterId, 1);
|
|
assert.equal(rows.length, 4, 'should have 4 inventory rows for 1 starter + 3 bought non-stackable items');
|
|
for (const row of rows) {
|
|
assert.equal(row.quantity, 1, 'each row should have quantity 1');
|
|
}
|
|
|
|
// API: each row should show quantity 1.
|
|
const swordItems = body.data.items.filter((i) => i.itemId === 1);
|
|
assert.equal(swordItems.length, 4, 'API should show 4 sword entries');
|
|
for (const item of swordItems) {
|
|
assert.equal(item.quantity, 1);
|
|
}
|
|
});
|
|
|
|
it('buying more non-stackable items adds more rows (not merging)', async () => {
|
|
const cookie = await registerAndLogin(`s15c${userSeq++}`, 'pass1234');
|
|
const characterId = await createHero(cookie, 'S15C', 'warrior');
|
|
const db = require('../../src/db').getDb();
|
|
db.prepare('UPDATE characters SET gold = 100 WHERE id = ?').run(characterId);
|
|
|
|
// Buy 2 Rusty Swords.
|
|
const res1 = await fetch(`${baseUrl}/api/shop/buy`, {
|
|
method: 'POST',
|
|
headers: { 'Content-Type': 'application/json', Cookie: cookie },
|
|
body: JSON.stringify({ itemId: 1, quantity: 2 }),
|
|
});
|
|
const body1 = await res1.json();
|
|
assert.equal(body1.ok, true);
|
|
// 1 starter + 2 bought = 3 rows.
|
|
assert.equal(countInvRows(characterId, 1), 3);
|
|
|
|
// Buy 2 more.
|
|
const res2 = await fetch(`${baseUrl}/api/shop/buy`, {
|
|
method: 'POST',
|
|
headers: { 'Content-Type': 'application/json', Cookie: cookie },
|
|
body: JSON.stringify({ itemId: 1, quantity: 2 }),
|
|
});
|
|
const body2 = await res2.json();
|
|
assert.equal(body2.ok, true);
|
|
|
|
// Should now have 5 rows total (1 starter + 2 + 2 bought), each qty 1.
|
|
assert.equal(countInvRows(characterId, 1), 5, 'should have 5 rows after buying 2+2');
|
|
const rows = getInvRows(characterId, 1);
|
|
for (const row of rows) {
|
|
assert.equal(row.quantity, 1);
|
|
}
|
|
});
|
|
|
|
// ─── Stackable items still merge ──────────────────────────────────────
|
|
|
|
it('buying stackable consumables merges into one row', async () => {
|
|
const cookie = await registerAndLogin(`s15d${userSeq++}`, 'pass1234');
|
|
const characterId = await createHero(cookie, 'S15D', 'warrior');
|
|
const db = require('../../src/db').getDb();
|
|
db.prepare('UPDATE characters SET gold = 100 WHERE id = ?').run(characterId);
|
|
|
|
// Starter kit gives 2 Minor Healing Potions (item 14, stackable).
|
|
// Buy 3 more → should merge to quantity 5 in ONE row.
|
|
const res = await fetch(`${baseUrl}/api/shop/buy`, {
|
|
method: 'POST',
|
|
headers: { 'Content-Type': 'application/json', Cookie: cookie },
|
|
body: JSON.stringify({ itemId: 14, quantity: 3 }),
|
|
});
|
|
const body = await res.json();
|
|
assert.equal(body.ok, true);
|
|
|
|
// Potion rows: should be exactly 1 row with quantity 5.
|
|
const potionRows = getInvRows(characterId, 14);
|
|
assert.equal(potionRows.length, 1, 'stackable items should merge into 1 row');
|
|
assert.equal(potionRows[0].quantity, 5, 'quantity should be 5 (2 starter + 3 bought)');
|
|
});
|
|
|
|
// ─── Non-stackable armor ──────────────────────────────────────────────
|
|
|
|
it('buying 2 non-stackable armor pieces creates 2 rows', async () => {
|
|
const cookie = await registerAndLogin(`s15e${userSeq++}`, 'pass1234');
|
|
const characterId = await createHero(cookie, 'S15E', 'warrior');
|
|
const db = require('../../src/db').getDb();
|
|
db.prepare('UPDATE characters SET gold = 100 WHERE id = ?').run(characterId);
|
|
|
|
// Leather Jerkin (item 8, armor, non-stackable, 25g each).
|
|
const res = await fetch(`${baseUrl}/api/shop/buy`, {
|
|
method: 'POST',
|
|
headers: { 'Content-Type': 'application/json', Cookie: cookie },
|
|
body: JSON.stringify({ itemId: 8, quantity: 2 }),
|
|
});
|
|
const body = await res.json();
|
|
assert.equal(body.ok, true);
|
|
assert.equal(body.data.character.gold, 50); // 100 - 2*25
|
|
|
|
const rows = getInvRows(characterId, 8);
|
|
assert.equal(rows.length, 2, 'should have 2 rows for 2 non-stackable armor');
|
|
for (const row of rows) {
|
|
assert.equal(row.quantity, 1);
|
|
}
|
|
});
|
|
});
|