2026-09-22 09:15:22 -04:00
|
|
|
'use strict';
|
|
|
|
|
const assert = require('assert');
|
|
|
|
|
const { assignMembersToSlots, isAllowedTeamName, slotRoleText, scoreMatch } = require('./matching');
|
|
|
|
|
|
|
|
|
|
const SLOTS = [
|
|
|
|
|
'[LEADER, VOX 1, ACOUSTIC] MIX 1', '[VOX 2] MIX 2', '[VOX 3] MIX 3', '[VOX 4] MIX 4',
|
|
|
|
|
'[GUITAR, LEAD] MIX 5', '[BASS] MIX 6', '[VOX 5] MIX 7', '[PIANO]', '[DRUMS]',
|
|
|
|
|
'[KEYS 2]', '[CALL TO WORSHIP] MC2', 'STAGE MANAGER', '[MC] MC1', '[SPEAKER] HEADSET',
|
|
|
|
|
];
|
|
|
|
|
const M = (label) => ({ label, name: label });
|
|
|
|
|
const members = [
|
|
|
|
|
M('Vox 3'), M('Vox 2'), M('Vox 1'), M('Guitar / Lead'), M('Bass'), M('Piano'), M('Drums'), M('MC'), M('Keys 2'),
|
|
|
|
|
];
|
|
|
|
|
const out = assignMembersToSlots(SLOTS, members).map((m) => (m ? m.label : null));
|
|
|
|
|
|
|
|
|
|
// Numbered vocals land in the RIGHT slot (this is the swap bug we fixed).
|
|
|
|
|
assert.strictEqual(out[0], 'Vox 1', 'slot 0 → Vox 1');
|
|
|
|
|
assert.strictEqual(out[1], 'Vox 2', 'slot 1 → Vox 2');
|
|
|
|
|
assert.strictEqual(out[2], 'Vox 3', 'slot 2 → Vox 3');
|
|
|
|
|
assert.strictEqual(out[3], null, 'slot 3 (Vox 4) unfilled — nobody scheduled');
|
|
|
|
|
assert.strictEqual(out[4], 'Guitar / Lead', 'slot 4 → Guitar/Lead');
|
|
|
|
|
assert.strictEqual(out[5], 'Bass', 'slot 5 → Bass');
|
|
|
|
|
assert.strictEqual(out[6], null, 'slot 6 (Vox 5) unfilled');
|
|
|
|
|
assert.strictEqual(out[7], 'Piano', 'slot 7 → Piano');
|
|
|
|
|
assert.strictEqual(out[8], 'Drums', 'slot 8 → Drums');
|
|
|
|
|
assert.strictEqual(out[9], 'Keys 2', 'slot 9 → Keys 2');
|
|
|
|
|
assert.strictEqual(out[12], 'MC', 'slot 12 → MC');
|
|
|
|
|
|
|
|
|
|
// A different number never matches.
|
|
|
|
|
assert.strictEqual(scoreMatch(slotRoleText('[VOX 2] MIX 2'), 'Vox 3'), 0, 'Vox 3 must not match Vox 2 slot');
|
|
|
|
|
// Mic number no longer pollutes: Vox 7 must not grab "[VOX 5] MIX 7".
|
|
|
|
|
assert.strictEqual(scoreMatch(slotRoleText('[VOX 5] MIX 7'), 'Vox 7'), 0, 'Vox 7 must not match the MIX 7 slot');
|
|
|
|
|
|
|
|
|
|
// Team allow-list (singular stems match plurals/variants via substring).
|
|
|
|
|
assert.ok(isAllowedTeamName('Band', ['band', 'vocal']));
|
|
|
|
|
assert.ok(isAllowedTeamName('Vocals', ['band', 'vocal']));
|
|
|
|
|
assert.ok(isAllowedTeamName('Vocal Team', ['band', 'vocal']));
|
|
|
|
|
assert.ok(!isAllowedTeamName('Video', ['band', 'vocal', 'production']));
|
|
|
|
|
|
|
|
|
|
console.log('matching.test.js: all assertions passed ✓');
|
|
|
|
|
console.log('assignment:', JSON.stringify(out));
|
|
|
|
|
|
|
|
|
|
// --- bestLibraryMatch: folder-photo → PCO person by name ---
|
|
|
|
|
{
|
|
|
|
|
const { bestLibraryMatch } = require('./matching');
|
|
|
|
|
const name = (n) => (e) => (e ? e.name : null);
|
|
|
|
|
const lib = [
|
|
|
|
|
{ id: 'a', name: 'Alex' }, { id: 'g', name: 'AlexG' }, { id: 'bm', name: 'Brian M' },
|
|
|
|
|
{ id: 'c', name: 'Courtney' }, { id: 'gn', name: 'Gaitan' }, { id: 'wes', name: 'Wes' },
|
|
|
|
|
];
|
|
|
|
|
const pick = (n, list = lib) => { const e = bestLibraryMatch(n, list); return e ? e.name : null; };
|
|
|
|
|
assert.strictEqual(pick('Alex Gaitan'), 'Alex', 'first name matches "Alex"');
|
|
|
|
|
assert.strictEqual(pick('Brian Moniz'), 'Brian M', '"Brian M" matches "Brian Moniz"');
|
|
|
|
|
assert.strictEqual(pick('Courtney Maranda'), 'Courtney', 'first name matches');
|
|
|
|
|
assert.strictEqual(pick('Alexander Zinni'), null, '"Alex" must NOT grab "Alexander Zinni"');
|
|
|
|
|
assert.strictEqual(pick('Wes Carter'), 'Wes', 'first name matches "Wes"');
|
|
|
|
|
// surname-named photo
|
|
|
|
|
assert.strictEqual(pick('Someone Gaitan'), 'Gaitan', 'surname photo matches by last name');
|
|
|
|
|
// two people share a first name + only a first-name photo → ambiguous only if two matches tie;
|
|
|
|
|
// here a single "Alex" photo still maps to whoever's first name is exactly Alex.
|
|
|
|
|
assert.strictEqual(pick('Alex Johnson'), 'Alex', 'first-name photo maps to any exact-first-name person');
|
|
|
|
|
// exact full name beats a first-name photo
|
|
|
|
|
assert.strictEqual(pick('Alex Gaitan', [{ id: 'x', name: 'Alex Gaitan' }, { id: 'a', name: 'Alex' }]), 'Alex Gaitan', 'exact full name wins');
|
|
|
|
|
// tie → no match (two identical-scoring photos)
|
|
|
|
|
assert.strictEqual(bestLibraryMatch('Chris Smith', [{ id: '1', name: 'Chris' }, { id: '2', name: 'Chris' }]), null, 'ambiguous tie → null');
|
|
|
|
|
console.log('bestLibraryMatch: all assertions passed ✓');
|
|
|
|
|
}
|
2026-10-20 13:11:56 -04:00
|
|
|
|
|
|
|
|
// --- one spot per person: someone scheduled twice (Leader + Acoustic) fills one spot only ---
|
|
|
|
|
{
|
|
|
|
|
const P = (label, name, personId) => ({ label, name, personId });
|
|
|
|
|
const slots = ['[LEADER, VOX 1, ACOUSTIC] MIX 1', '[GUITAR, 2] MIX 7'];
|
|
|
|
|
const got = assignMembersToSlots(slots, [P('[Leader]', 'Mike', 'mp'), P('Guitar [Acoustic]', 'Mike', 'mp'), P('Guitar [Rhythm]', 'Craig', 'cs')]).map((m) => (m ? m.name : null));
|
|
|
|
|
assert.deepStrictEqual(got, ['Mike', 'Craig'], 'Mike once, Craig gets the guitar spot');
|
|
|
|
|
console.log('one spot per person: all assertions passed ✓');
|
|
|
|
|
}
|