test: cover the new code paths SonarCloud flagged as unmeasured

The quality gate came in at 79.5% coverage on new code against a floor of 80%.
The gaps were real ones rather than a threshold quibble: three pieces of new
behaviour had no test at all.

- `useBagTotalsPing` was a new file with no test, and it holds the logic that
  stops one write turning into a storm of listBags requests on every connected
  client: the coalescing window, the in-flight guard, and the ping that arrives
  mid-flight and must still be served.

- The Journey Settings tracks switch had no test, including the part the review
  caught: that it refreshes rather than calling onSaved, which would close the
  dialog and drop unsaved edits.

- Neither bag panel had ever been rendered in a test. They now pin what the
  panels actually print: the server total rather than the sum of what the
  viewer can see, the grand total following the same rule as the rows above it,
  and the offline fallback.

Also covers the two ping call sites that were still unmeasured, the MCP
apply-template path and the plugin RPC bag delete.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01S5VwmiBQ9sPCJwfmpv64r3
This commit is contained in:
Claude
2026-09-02 13:55:48 +00:00
committed by Maurice
parent 700918d533
commit 17668e5ff2
5 changed files with 279 additions and 0 deletions
@@ -381,6 +381,26 @@ describe('Tool: set_packing_category_assignees', () => {
// ---------------------------------------------------------------------------
describe('Tool: apply_packing_template', () => {
it('applies a template and pings the bag weights once (#2191)', async () => {
const { user } = createUser(testDb);
const trip = createTrip(testDb, user.id);
const template = testDb.prepare("INSERT INTO packing_templates (name, created_by) VALUES ('Hiking', ?)").run(user.id);
const category = testDb.prepare("INSERT INTO packing_template_categories (template_id, name, sort_order) VALUES (?, 'Gear', 0)")
.run(template.lastInsertRowid);
testDb.prepare("INSERT INTO packing_template_items (category_id, name, sort_order) VALUES (?, 'Boots', 0)")
.run(category.lastInsertRowid);
await withHarness(user.id, async (h) => {
const result = await h.client.callTool({
name: 'apply_packing_template',
arguments: { tripId: trip.id, templateId: Number(template.lastInsertRowid) },
});
expect(result.isError).toBeFalsy();
// Applied items can carry weights, so the room has to re-read the totals.
expect(broadcastMock).toHaveBeenCalledWith(String(trip.id), 'packing:bag-totals', {}, undefined);
});
});
it('returns error for non-existent template', async () => {
const { user } = createUser(testDb);
const trip = createTrip(testDb, user.id);
@@ -144,6 +144,17 @@ describe('PackingRpc through the router', () => {
}
});
it('PACKING-RPC-007f deleting a bag also pings the weights (#2191)', async () => {
// packing_items.bag_id is ON DELETE SET NULL, so the bag's contents land in
// the unassigned pile: both the bag figure and the loose figure move.
const f = build();
await f.host('db:write:packing').dispatch(req('packing.deleteBag', { tripId: 1, bagId: 80 }), 42);
expect(fanout(f.realtime)).toEqual([
['packing:bag-deleted', undefined],
['packing:bag-totals', undefined],
]);
});
it('PACKING-RPC-007e an invalid item payload is BAD_PARAMS on create and update alike', async () => {
const f = build();
const host = f.host('db:write:packing');