fix(teams): the two defects the phase 9 rig walk found
All checks were successful
PR Checks / client-build (pull_request) Successful in 30s
PR Checks / bot-tests (pull_request) Successful in 30s
PR Checks / server-tests (pull_request) Successful in 2m33s

Walked against real MariaDB, the real app, and a fake standing in for Discord
that mounts the bot's real internal routes — everything up to the Discord API
call was production code. 47 assertions, and it found two things every unit
test in the phase had passed over.

1. **Every query failed: two result columns named `team_id`.** `desiredTeams`
   and `holdersWithoutClaim` both select `t.id AS team_id`, and the shared
   column list added `i.team_id` beside it. The `mariadb` driver refuses a
   result set with a repeated field name outright, so the pass died at its
   first query with "Error in results, duplicate field name `team_id`" — on the
   one code path every unit test stubs.

   It was also the wrong column: `desiredTeams` LEFT JOINs, so `i.team_id` is
   NULL for exactly the Teams that have no channel yet, which is the create
   case. The two queries that do not join `teams` now ask for it by name.

   The regression test checks the INTERPOLATED sql captured from a fake
   `query`, not the source text — in the source the shared list is still a
   `${COLUMNS}` placeholder, and a first attempt that read the file passed
   happily with the bug reintroduced.

2. **"Sync now" said "Nothing was done" while it was doing it.** Saving the
   settings with voice switched on asks for a pass. An operator who then
   presses Sync now — the obvious next thing — hit `running` and got back
   `ran: false, reason: "a pass is already running"`, which the panel renders
   as nothing having happened, while the pass they triggered was busy creating
   their channels. A pass in flight is now JOINED and its real outcome
   returned, the same choice `teamSync.reconcileNow` makes for the same reason.

Tests: 1162 server (+2), 53 bot, 284 client.

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
2026-08-19 00:07:27 -05:00
parent 61abb3ec89
commit f72c92ffbe
4 changed files with 125 additions and 11 deletions

View File

@@ -307,6 +307,29 @@ test('a pass that throws is reported, not raised — it hangs off a background t
assert.equal(sync.lastPass().ran, false)
})
test('a pass already in flight is JOINED, not refused', async () => {
// Found on the live rig. Saving the settings with voice switched on asks for a
// pass; an operator who then presses "Sync now" — the obvious next thing to do —
// got `ran: false, reason: "a pass is already running"`, and the panel told them
// **"Nothing was done"** while the pass they had just triggered was creating
// their channels. `teamSync.reconcileNow` joins for the same reason: the caller
// wants "Discord now matches", and a pass that started a moment ago delivers it.
let release
const gate = new Promise((resolve) => { release = resolve })
let passes = 0
patch(voice, 'plan', async () => { passes += 1; await gate; return plan })
const first = sync.passNow('first')
const second = sync.passNow('second')
release()
const [a, b] = await Promise.all([first, second])
assert.equal(passes, 1, 'the work was done once')
assert.equal(a.ran, true)
assert.equal(b.ran, true, 'the second caller got the real outcome, not a refusal')
assert.deepEqual(a, b)
})
test('a pass records what it concluded, for the panel', async () => {
plan.provision = [item()]
await sync.passNow('test')