From 60fca6101d542a6f557d51d44d2cee016900177b Mon Sep 17 00:00:00 2001 From: Day Dreamer <87640074+HugoXK@users.noreply.github.com> Date: Sun, 1 Nov 2026 11:02:38 +0800 Subject: [PATCH] test: pin the three settings silences updateRoom keeps (#1965) Three settings behaviours #1964 made correct have no test: an unnamed room's title follows its members (_patch.name_ absent, so no settings line), a seat change that empties the task lead says nothing on top of the left line, and a save writing back the mode the room already holds is a no-op. Removing any of those guards leaves the whole core suite green, so a refactor could turn each of them into a line in every room and nothing would go red. Each case drives the public updateRoom with the fixtures of the settings case beside it, and each was checked against its guard removed: only that case fails (95 of 96 still pass). Docs-not-needed: test-only pin of behaviour docs/subsystems/core-host.md already describes; no source file changes (#1965). --- core/test/remote-room.test.js | 64 +++++++++++++++++++ patches/ds-bot/files/test/remote-room.test.js | 64 +++++++++++++++++++ 2 files changed, 128 insertions(+) diff --git a/core/test/remote-room.test.js b/core/test/remote-room.test.js index 2afae16aa..b136cd255 100644 --- a/core/test/remote-room.test.js +++ b/core/test/remote-room.test.js @@ -2503,6 +2503,70 @@ test('a settings patch leaves one visible line in the room, and only when a valu assert.equal(last().text, 'Chief A updated the group notice') }) +// #1965: an unnamed group follows its members, so a seat change moves the title without anyone asking +// for a rename. The seat change already draws its own joined line; the room must not also say it was +// renamed, which is what a settings save looks like. +test('an added member retitles an unnamed room without a settings line', async (t) => { + const { rt } = host(t, TEAM) + installRoster(rt) + await rt.load() + const invite = await rt.createInvitation({ createdByMemberId: 'owner-1' }) + const alice = await rt.redeemInvitation(invite.token, { displayName: 'Alice' }) + const actorA = await rt.authenticate(alice.sessionToken) + const events = [] + rt.postToRoom = async (room, sender, text, extra) => { events.push({ room: room.id, sender, text, extra }) } + const room = rt.state.rooms.war + delete room.named + room.name = rt.autoRoomName(room.members, room) + + await rt.updateRoom('war', { add: ['spec-a'] }, undefined, actorA) + + assert.equal(room.name, 'Alice, Chief A, Chief B, Writer A', 'the unnamed group follows its members') + assert.deepEqual(events, [], 'a title that follows the members is not a settings line') +}) + +// #1965: losing the task lead is a consequence of the seat change, not something the patch asked for. +// The left line already says the Bot is gone, so the room says nothing else. +test('a member leaving with the task lead clears it without a settings line', async (t) => { + const { rt } = host(t, TEAM) + installRoster(rt) + await rt.load() + const invite = await rt.createInvitation({ createdByMemberId: 'owner-1' }) + const alice = await rt.redeemInvitation(invite.token, { displayName: 'Alice' }) + const actorA = await rt.authenticate(alice.sessionToken) + const events = [] + rt.postToRoom = async (room, sender, text, extra) => { events.push({ room: room.id, sender, text, extra }) } + await rt.updateRoom('war', { add: ['spec-a'] }, undefined, actorA) + await rt.updateRoom('war', { taskLead: 'chief-b' }, undefined, actorA) + assert.equal(events.length, 1, 'naming the task lead is a settings line') + events.length = 0 + + await rt.updateRoom('war', { remove: ['chief-b'] }, undefined, actorA) + + assert.equal(rt.state.rooms.war.taskLead, undefined, 'the lead left, so the admin plans the board again') + assert.deepEqual(events, [], 'the left line already says it; the cleared lead is not a second line') +}) + +// #1965: a save that writes back the mode the room already holds changes nothing, so it says nothing. +test('writing back the reply mode the room already holds says nothing', async (t) => { + const { rt } = host(t, TEAM) + installRoster(rt) + await rt.load() + const invite = await rt.createInvitation({ createdByMemberId: 'owner-1' }) + const alice = await rt.redeemInvitation(invite.token, { displayName: 'Alice' }) + const actorA = await rt.authenticate(alice.sessionToken) + const events = [] + rt.postToRoom = async (room, sender, text, extra) => { events.push({ room: room.id, sender, text, extra }) } + await rt.updateRoom('war', { mode: 'admin' }, undefined, actorA) + assert.equal(events.length, 1, 'switching the mode is a settings line') + events.length = 0 + + await rt.updateRoom('war', { mode: 'admin' }, undefined, actorA) + + assert.equal(rt.state.rooms.war.mode, 'admin', 'the room keeps the mode it already had') + assert.deepEqual(events, [], 'a save that writes the same mode back says nothing') +}) + // #904: a roster is stranger-written text, so the envelope that carries one is gated: only the room's // own inviter, only the room this mirror is bound to, and nothing it says widens reach by itself. test('#904: a roster from another home or for another room changes no participant', async (t) => { diff --git a/patches/ds-bot/files/test/remote-room.test.js b/patches/ds-bot/files/test/remote-room.test.js index 2afae16aa..b136cd255 100644 --- a/patches/ds-bot/files/test/remote-room.test.js +++ b/patches/ds-bot/files/test/remote-room.test.js @@ -2503,6 +2503,70 @@ test('a settings patch leaves one visible line in the room, and only when a valu assert.equal(last().text, 'Chief A updated the group notice') }) +// #1965: an unnamed group follows its members, so a seat change moves the title without anyone asking +// for a rename. The seat change already draws its own joined line; the room must not also say it was +// renamed, which is what a settings save looks like. +test('an added member retitles an unnamed room without a settings line', async (t) => { + const { rt } = host(t, TEAM) + installRoster(rt) + await rt.load() + const invite = await rt.createInvitation({ createdByMemberId: 'owner-1' }) + const alice = await rt.redeemInvitation(invite.token, { displayName: 'Alice' }) + const actorA = await rt.authenticate(alice.sessionToken) + const events = [] + rt.postToRoom = async (room, sender, text, extra) => { events.push({ room: room.id, sender, text, extra }) } + const room = rt.state.rooms.war + delete room.named + room.name = rt.autoRoomName(room.members, room) + + await rt.updateRoom('war', { add: ['spec-a'] }, undefined, actorA) + + assert.equal(room.name, 'Alice, Chief A, Chief B, Writer A', 'the unnamed group follows its members') + assert.deepEqual(events, [], 'a title that follows the members is not a settings line') +}) + +// #1965: losing the task lead is a consequence of the seat change, not something the patch asked for. +// The left line already says the Bot is gone, so the room says nothing else. +test('a member leaving with the task lead clears it without a settings line', async (t) => { + const { rt } = host(t, TEAM) + installRoster(rt) + await rt.load() + const invite = await rt.createInvitation({ createdByMemberId: 'owner-1' }) + const alice = await rt.redeemInvitation(invite.token, { displayName: 'Alice' }) + const actorA = await rt.authenticate(alice.sessionToken) + const events = [] + rt.postToRoom = async (room, sender, text, extra) => { events.push({ room: room.id, sender, text, extra }) } + await rt.updateRoom('war', { add: ['spec-a'] }, undefined, actorA) + await rt.updateRoom('war', { taskLead: 'chief-b' }, undefined, actorA) + assert.equal(events.length, 1, 'naming the task lead is a settings line') + events.length = 0 + + await rt.updateRoom('war', { remove: ['chief-b'] }, undefined, actorA) + + assert.equal(rt.state.rooms.war.taskLead, undefined, 'the lead left, so the admin plans the board again') + assert.deepEqual(events, [], 'the left line already says it; the cleared lead is not a second line') +}) + +// #1965: a save that writes back the mode the room already holds changes nothing, so it says nothing. +test('writing back the reply mode the room already holds says nothing', async (t) => { + const { rt } = host(t, TEAM) + installRoster(rt) + await rt.load() + const invite = await rt.createInvitation({ createdByMemberId: 'owner-1' }) + const alice = await rt.redeemInvitation(invite.token, { displayName: 'Alice' }) + const actorA = await rt.authenticate(alice.sessionToken) + const events = [] + rt.postToRoom = async (room, sender, text, extra) => { events.push({ room: room.id, sender, text, extra }) } + await rt.updateRoom('war', { mode: 'admin' }, undefined, actorA) + assert.equal(events.length, 1, 'switching the mode is a settings line') + events.length = 0 + + await rt.updateRoom('war', { mode: 'admin' }, undefined, actorA) + + assert.equal(rt.state.rooms.war.mode, 'admin', 'the room keeps the mode it already had') + assert.deepEqual(events, [], 'a save that writes the same mode back says nothing') +}) + // #904: a roster is stranger-written text, so the envelope that carries one is gated: only the room's // own inviter, only the room this mirror is bound to, and nothing it says widens reach by itself. test('#904: a roster from another home or for another room changes no participant', async (t) => {