<!-- Add the related story/sub-task/bug number, like Resolves #123, or remove if NA --> **Related issue:** Resolves #45700 Not a product change. This PR will allow us to run the workflow manually. After ~2 weeks, if there are no issues, we'll make it automatic. ## Testing - [x] Added/updated automated tests - [x] QA'd all new/changed functionality manually <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **New Features** * Added automated workflow to identify and close stale issues created by Fleet team members, with dry-run capability and operation limits. * Added system to build and maintain a deduplicated list of Fleet team member handles from GitHub organization and repository history. * **Tests** * Added comprehensive test suites for stale issue management and handle list generation with mock GitHub API interactions and boundary condition coverage. <!-- review_stack_entry_start --> [](https://app.coderabbit.ai/change-stack/fleetdm/fleet/pull/45530?utm_source=github_walkthrough&utm_medium=github&utm_campaign=change_stack) <!-- review_stack_entry_end --> <!-- end of auto-generated comment: release notes by coderabbit.ai -->
74 lines
2.6 KiB
JavaScript
74 lines
2.6 KiB
JavaScript
'use strict';
|
|
|
|
const test = require('node:test');
|
|
const assert = require('node:assert');
|
|
const { execFileSync } = require('node:child_process');
|
|
const path = require('node:path');
|
|
|
|
const SCRIPT = path.join(__dirname, 'build-fleetie-handles.js');
|
|
|
|
function runScript() {
|
|
// Force handbook-only mode so the test is hermetic (no network calls, no token required).
|
|
const env = { ...process.env, READ_ORG_TOKEN: '' };
|
|
delete env.FLEETIE_HANDLES_OUT;
|
|
const out = execFileSync('node', [SCRIPT], {
|
|
env,
|
|
encoding: 'utf8',
|
|
maxBuffer: 64 * 1024 * 1024,
|
|
});
|
|
return out.split('\n').filter(Boolean);
|
|
}
|
|
|
|
let handlesCache = null;
|
|
function getHandles() {
|
|
if (!handlesCache) handlesCache = runScript();
|
|
return handlesCache;
|
|
}
|
|
|
|
test('produces a sane number of handles', () => {
|
|
const handles = getHandles();
|
|
assert.ok(handles.length >= 50, `expected at least 50 handles, got ${handles.length}`);
|
|
});
|
|
|
|
test('output is sorted, lowercased, and deduplicated', () => {
|
|
const handles = getHandles();
|
|
const sorted = [...handles].sort();
|
|
assert.deepStrictEqual(handles, sorted, 'output is not sorted');
|
|
for (const handle of handles) {
|
|
assert.strictEqual(handle, handle.toLowerCase(), `handle not lowercased: ${handle}`);
|
|
}
|
|
assert.strictEqual(new Set(handles).size, handles.length, 'output contains duplicates');
|
|
});
|
|
|
|
test('includes known current Fleeties', () => {
|
|
const set = new Set(getHandles());
|
|
for (const handle of ['getvictor', 'lukeheath', 'mikermcneil', 'noahtalerman', 'eashaw']) {
|
|
assert.ok(set.has(handle), `expected current Fleetie ${handle} in handle list`);
|
|
}
|
|
});
|
|
|
|
test('includes known former Fleeties from git history', () => {
|
|
const set = new Set(getHandles());
|
|
// These handles have all appeared in handbook/company/product-groups.md at some point in git history
|
|
// but are not in the file at HEAD.
|
|
for (const handle of ['iansltx', 'mna', 'roperzh', 'ghernandez345']) {
|
|
assert.ok(set.has(handle), `expected former Fleetie ${handle} in handle list`);
|
|
}
|
|
});
|
|
|
|
test('excludes denylisted path segments and the org handle', () => {
|
|
const set = new Set(getHandles());
|
|
for (const denied of ['fleetdm', 'todo', 'user-attachments', 'orgs', 'issues', 'pull', 'apps']) {
|
|
assert.ok(!set.has(denied), `expected ${denied} to be filtered out`);
|
|
}
|
|
});
|
|
|
|
test('all handles match the GitHub username format', () => {
|
|
const handles = getHandles();
|
|
const githubHandleRe = /^[a-z0-9][a-z0-9-]{0,38}$/;
|
|
for (const handle of handles) {
|
|
assert.ok(githubHandleRe.test(handle), `invalid handle in output: ${handle}`);
|
|
assert.ok(!handle.endsWith('-'), `handle ends with hyphen: ${handle}`);
|
|
}
|
|
});
|