// The placement setting: two states, inherited once from the SPONSOR at join, // and an explicit choice that nothing above can overwrite. import { createRequire } from 'node:module'; import fs from 'node:fs'; import os from 'node:os'; import path from 'node:path'; const require = createRequire(import.meta.url); const dir = fs.mkdtempSync(path.join(os.tmpdir(), 'rmc-pl-')); const placement = require('../placement.js'); placement.init({ dataDir: dir }); const ok = [], bad = []; const t = (n, c, extra) => { (c ? ok : bad).push(n + (c || !extra ? '' : ' -> ' + extra)); }; const cfg = { directDefaultIds: '21,137,139,30,840' }; // defaults t('an unknown position defaults to team rotation', placement.get(999, cfg).mode === 'team'); t('and is not marked as an explicit choice', placement.get(999, cfg).explicit === false); t('a position on the admin list reads as direct', placement.get(840, cfg).mode === 'direct'); t('the admin list counts as explicit, so nothing overwrites it', placement.get(840, cfg).explicit === true); // choosing placement.set(500, 'direct'); t('a member can choose direct', placement.get(500, cfg).mode === 'direct'); t('their choice is recorded as explicit', placement.get(500, cfg).explicit === true); placement.set(500, 'team'); t('and can switch back', placement.get(500, cfg).mode === 'team'); t('a nonsense value is refused', !!placement.set(500, 'sideways').error); // inheritance: one-time copy from the sponsor placement.inherit(601, 500, cfg); // sponsor 500 is on team t('joining under a team-rotation sponsor gives team', placement.get(601, cfg).mode === 'team'); placement.inherit(602, 840, cfg); // sponsor 840 is direct t('joining under a direct sponsor gives direct', placement.get(602, cfg).mode === 'direct'); t('inherited settings are marked NOT explicit', placement.get(602, cfg).explicit === false); t('and remember which sponsor they came from', placement.get(602, cfg).from === '840'); // the rule that keeps the tree readable placement.set(602, 'team'); // the member disagrees with their sponsor placement.inherit(602, 840, cfg); // a later re-run must not undo that t('an explicit choice is never overwritten from above', placement.get(602, cfg).mode === 'team'); t('and stays marked as their own', placement.get(602, cfg).explicit === true); // a sponsor changing their mind does NOT move people already placed placement.set(700, 'team'); placement.inherit(701, 700, cfg); placement.set(700, 'direct'); // sponsor switches later t('a sponsor switching later does not move their existing team', placement.get(701, cfg).mode === 'team'); placement.inherit(702, 700, cfg); // but new joins get the new setting t('but people who join after do get the new setting', placement.get(702, cfg).mode === 'direct'); // survives a restart const fresh = fs.readFileSync(path.join(dir, 'placement.json'), 'utf8'); t('settings are persisted, not just in memory', fresh.includes('"602"') && fresh.includes('"700"')); fs.rmSync(dir, { recursive: true, force: true }); console.log('PASS ' + ok.length); for (const b of bad) console.log('FAIL ' + b); process.exit(bad.length ? 1 : 0);