Skip to content

Commit d0bbb6e

Browse files
committed
chore: add emeritus cmd test
1 parent b95994e commit d0bbb6e

File tree

2 files changed

+150
-1
lines changed

2 files changed

+150
-1
lines changed

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@
2121
"lint": "eslint . --ext .js,.ts",
2222
"lint:fix": "eslint . --ext .js,.ts --fix",
2323
"try:emeritus": "node --env-file=.env index.js emeritus --monthsInactiveThreshold 24 --dryRun",
24-
"test": "exit 1"
24+
"test": "node --test test/**/*.test.js"
2525
},
2626
"repository": {
2727
"type": "git",

test/emeritus.test.js

Lines changed: 149 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,149 @@
1+
import test from 'node:test'
2+
import assert from 'node:assert/strict'
3+
4+
import emeritus from '../commands/emeritus.js'
5+
6+
function monthsAgo (n) {
7+
const d = new Date()
8+
d.setMonth(d.getMonth() - n)
9+
return d
10+
}
11+
12+
function createMockLogger () {
13+
const infoMessages = []
14+
const debugMessages = []
15+
return {
16+
info: (msg) => { if (typeof msg === 'string') infoMessages.push(msg) },
17+
debug: (msg) => { if (typeof msg === 'string') debugMessages.push(msg) },
18+
infoMessages,
19+
debugMessages
20+
}
21+
}
22+
23+
function createMockClient (thresholdMonths = 24) {
24+
const calls = []
25+
26+
const orgData = { id: 123, name: 'fastify' }
27+
28+
// Org structure with leads, emeritus, and a core team
29+
const orgChart = [
30+
{ slug: 'leads', members: [{ login: 'lead1' }] },
31+
{ slug: 'emeritus', members: [{ login: 'already_emeritus' }] },
32+
{
33+
slug: 'core',
34+
members: [
35+
{ login: 'active_user' },
36+
{ login: 'inactive_user' },
37+
{ login: 'boundary_user' },
38+
{ login: 'inactive_no_contrib' },
39+
// Users may also be in multiple teams
40+
{ login: 'lead1' },
41+
{ login: 'already_emeritus' }
42+
]
43+
}
44+
]
45+
46+
// Default contribution map; will be expanded per membersList when requested
47+
const contributionsByUser = {
48+
active_user: { lastPR: monthsAgo(1) },
49+
inactive_user: { lastIssue: monthsAgo(thresholdMonths + 1) },
50+
boundary_user: { lastCommit: monthsAgo(thresholdMonths) }, // exactly on the boundary -> NOT emeritus
51+
lead1: { lastPR: monthsAgo(thresholdMonths + 10) }, // would be emeritus but excluded for being a lead
52+
already_emeritus: { lastIssue: monthsAgo(thresholdMonths + 2) }, // would be emeritus but already in emeritus team
53+
inactive_no_contrib: { /* no contributions at all -> emeritus */ }
54+
}
55+
56+
const client = {
57+
calls,
58+
lastYearsToRead: null,
59+
60+
async getOrgData (org) {
61+
calls.push({ method: 'getOrgData', org })
62+
return { ...orgData, name: org }
63+
},
64+
65+
async getOrgChart (orgDataArg) {
66+
calls.push({ method: 'getOrgChart', orgData: orgDataArg })
67+
return orgChart
68+
},
69+
70+
async getUsersContributions (orgDataArg, membersList, yearsToRead) {
71+
calls.push({ method: 'getUsersContributions', orgData: orgDataArg, membersList, yearsToRead })
72+
client.lastYearsToRead = yearsToRead
73+
return membersList.map(user => ({ user, ...contributionsByUser[user] }))
74+
},
75+
76+
async createIssue (owner, repo, title, body, labels) {
77+
calls.push({ method: 'createIssue', owner, repo, title, body, labels })
78+
return { number: 1 }
79+
}
80+
}
81+
82+
return client
83+
}
84+
85+
test('emeritus (dry-run): logs the users to move and does not create an issue', async () => {
86+
const threshold = 24
87+
const client = createMockClient(threshold)
88+
const logger = createMockLogger()
89+
90+
await emeritus({ client, logger }, { org: 'fastify', monthsInactiveThreshold: threshold, dryRun: true })
91+
92+
// Ensure no issue is created in dry run
93+
assert.equal(client.calls.some(c => c.method === 'createIssue'), false)
94+
95+
// It should log the header and each username to be moved
96+
const lines = logger.infoMessages
97+
assert.ok(lines.some(l => l.includes('These users should be added to emeritus team')), 'should log dry-run header')
98+
99+
// Expected users to move: inactive_user and inactive_no_contrib
100+
assert.ok(lines.some(l => l.includes('- @inactive_user')), 'should list inactive_user')
101+
assert.ok(lines.some(l => l.includes('- @inactive_no_contrib')), 'should list inactive_no_contrib')
102+
103+
// Should not list users who are active, leads, or already in emeritus, or on the boundary
104+
assert.equal(lines.some(l => l.includes('- @active_user')), false, 'active_user should not be listed')
105+
assert.equal(lines.some(l => l.includes('- @boundary_user')), false, 'boundary_user (exactly on threshold) should not be listed')
106+
assert.equal(lines.some(l => l.includes('- @lead1')), false, 'lead should not be listed')
107+
assert.equal(lines.some(l => l.includes('- @already_emeritus')), false, 'already emeritus should not be listed')
108+
109+
// Confirm yearsToRead passed to getUsersContributions is ceil(threshold/12)
110+
assert.equal(client.lastYearsToRead, Math.ceil(threshold / 12))
111+
})
112+
113+
test('emeritus (non-dry-run): creates an issue with the list of users to move', async () => {
114+
const threshold = 24
115+
const client = createMockClient(threshold)
116+
const logger = createMockLogger()
117+
118+
await emeritus({ client, logger }, { org: 'fastify', monthsInactiveThreshold: threshold, dryRun: false })
119+
120+
const issueCalls = client.calls.filter(c => c.method === 'createIssue')
121+
assert.equal(issueCalls.length, 1, 'should create exactly one issue')
122+
123+
const [call] = issueCalls
124+
assert.equal(call.owner, 'fastify')
125+
assert.equal(call.repo, 'org-admin')
126+
assert.equal(call.title, 'Move to emeritus members')
127+
assert.deepEqual(call.labels, ['question'])
128+
129+
// Body should mention threshold and include the expected users
130+
assert.ok(call.body.includes(`more than ${threshold} months`), 'body should mention the threshold')
131+
assert.ok(call.body.includes('- @inactive_user'))
132+
assert.ok(call.body.includes('- @inactive_no_contrib'))
133+
134+
// Body should not include excluded users
135+
assert.equal(call.body.includes('- @lead1'), false)
136+
assert.equal(call.body.includes('- @already_emeritus'), false)
137+
assert.equal(call.body.includes('- @boundary_user'), false)
138+
assert.equal(call.body.includes('- @active_user'), false)
139+
})
140+
141+
test('emeritus: yearsToRead is computed with Math.ceil(monthsThreshold/12)', async () => {
142+
const threshold = 13 // should round up to 2 years
143+
const client = createMockClient(threshold)
144+
const logger = createMockLogger()
145+
146+
await emeritus({ client, logger }, { org: 'fastify', monthsInactiveThreshold: threshold, dryRun: true })
147+
148+
assert.equal(client.lastYearsToRead, 2)
149+
})

0 commit comments

Comments
 (0)