Skip to content

Commit 1844a49

Browse files
committed
✅ add test for ideal TLAs
1 parent f68e0b4 commit 1844a49

File tree

2 files changed

+80
-3
lines changed

2 files changed

+80
-3
lines changed

scripts/check-delegates-test.mjs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,16 @@ const twoLetter = `Chris de Almeida (CDA)\nRob Palmer (RP)\nUjjwal Sharma (USA)`
88
const threeLetter = `Chris de Almeida (CDA)\nRob Palmer (ROBPALMER)\nUjjwal Sharma (USA)`;
99
const duplicate = `Chris de Almeida (CDA)\nRob Palmer (RPR)\nUjjwal Sharma (USA)\nUjjwal Sharma (USA)`;
1010
const valid = `Chris de Almeida (CDA)\nMichael Ficarra (MF)\nRob Palmer (RPR)\nUjjwal Sharma (USA)`;
11+
const mononymous = `Chris de Almeida (CDA)\nYee (YEE)\nRob Palmer (RPR)\nUjjwal Sharma (USA)`;
12+
const idealTLA = `Chris de Almeida (CAA)\nMichael Ficarra (MF)\nRob Palmer (RPR)\nUjjwal Sharma (USA)`;
1113

1214
assert.throws(() => checkDelegates(lex), { message: 'Line 3: Not in lexicographic order.' }); // also validates expected line number
1315
assert.throws(() => checkDelegates(missing), { message: /Missing abbreviation for/ });
1416
assert.throws(() => checkDelegates(uppercaseLatin), { message: /Abbreviations must be all uppercase Latin letters/ });
1517
assert.throws(() => checkDelegates(twoLetter), { message: /not in allowlist. New delegate abbreviations must be three letters/ });
1618
assert.throws(() => checkDelegates(threeLetter), { message: /New delegate abbreviations must be three letters/ });
1719
assert.throws(() => checkDelegates(duplicate), { message: /Conflicting usage on line/ });
20+
assert.throws(() => checkDelegates(mononymous), { message: /Unexpected mononymous delegate/ });
21+
assert.throws(() => checkDelegates(idealTLA), { message: /Should be using ideal TLA \(CDA\)/ });
1822

1923
assert.doesNotThrow(() => checkDelegates(valid));

scripts/check-delegates.mjs

Lines changed: 76 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,16 @@
22

33
import fs from 'fs';
44

5-
export function checkDelegates(contents = fs.readFileSync('./delegates.txt', 'utf8').toString()) {
5+
export function checkDelegates(contents) {
6+
7+
const DELEGATES_FILE_PATH = './delegates.txt';
8+
9+
let isContentsDelegatesFile = false;
10+
11+
if (!contents) {
12+
contents = fs.readFileSync(DELEGATES_FILE_PATH, 'utf8').toString();
13+
isContentsDelegatesFile = true;
14+
}
615

716
console.debug('checking delegates...');
817

@@ -19,8 +28,36 @@ export function checkDelegates(contents = fs.readFileSync('./delegates.txt', 'ut
1928
'VM', 'WH', 'YK', 'ZB',
2029
]);
2130

31+
// list of abbreviations that are not ideal.
32+
// most of these are here because they are grandfathered in.
33+
// new elements should only be false positives.
34+
const NON_IDEAL_ABBRS = new Set([
35+
'ABU', 'ACB', 'AEC', 'AEH', 'ALH', 'ARB', 'ASH', 'AVC', 'AVK',
36+
'AVP', 'AWB', 'AYS', 'BNG', 'BRK', 'CCN', 'CHU', 'CJI', 'CJR',
37+
'CJT', 'CZW', 'DAS', 'DDC', 'DEN', 'DFS', 'DFV', 'DHC', 'DJF',
38+
'DJW', 'DLM', 'DMM', 'DMP', 'DTL', 'DVE', 'EDB', 'FED', 'FRT',
39+
'FYT', 'GCW', 'GKZ', 'GPT', 'GRS', 'HUG', 'HUX', 'IOA', 'IVH',
40+
'JAN', 'JAZ', 'JBS', 'JDD', 'JFP', 'JHJ', 'JHL', 'JMN', 'JPB',
41+
'JPG', 'JRB', 'JSL', 'JSW', 'JTO', 'JXF', 'JXZ', 'JZY', 'KBK',
42+
'KCD', 'KGO', 'KHG', 'KOT', 'KZM', 'LCA', 'LCP', 'LEO', 'LFP',
43+
'LGY', 'LIU', 'LWT', 'LWW', 'LZH', 'LZJ', 'LZM', 'MAG', 'MAR',
44+
'MCM', 'MCW', 'MED', 'MGR', 'MHA', 'MJN', 'MJS', 'MLS', 'MPC',
45+
'MQW', 'MWS', 'MYC', 'MZG', 'NLY', 'PFC', 'PFM', 'PLH', 'PMD',
46+
'PZE', 'REK', 'ROF', 'RTM', 'SFC', 'SJL', 'SJY', 'SMK', 'SNS',
47+
'SRK', 'SRL', 'SSA', 'STH', 'SYH', 'SYP', 'SZH', 'SZT', 'TAB',
48+
'TEK', 'TJC', 'TOC', 'TVC', 'WES', 'WMM', 'WWW', 'WXK', 'WYJ',
49+
'XAX', 'XTY', 'XWC', 'YIY', 'YJM', 'YKL', 'YKZ', 'YRL', 'YTX',
50+
'YYC', 'ZJL', 'ZRJ', 'ZYY',
51+
]);
52+
53+
// delegates with only one name. this list should be as close to zero as possible...
54+
const MONONYMOUS = new Set([
55+
'Surma',
56+
]);
57+
58+
const allAbbrs = new Set(contents.match(/(?<=\()[^)]*(?=\))/g));
2259

23-
const re = /^(?<name>[^(]+)(?: \((?<abbr>[^)]*)\))?$/;
60+
const re = /^(?<firstName>[^( ]+) ?(?<lastName>[^(]+)?(?: \((?<abbr>[^)]*)\))?$/;
2461
const abbrs = new Map;
2562
const lines = contents.split('\n');
2663

@@ -30,7 +67,7 @@ export function checkDelegates(contents = fs.readFileSync('./delegates.txt', 'ut
3067
for (const line of lines) {
3168
if (line.length === 0) continue;
3269
const match = re.exec(line);
33-
const { abbr } = match.groups;
70+
const { abbr, firstName, lastName } = match.groups;
3471

3572
if (previousLine.localeCompare(line, 'en') > 0) {
3673
throw new Error(`Line ${lineNumber}: Not in lexicographic order.`);
@@ -57,9 +94,45 @@ export function checkDelegates(contents = fs.readFileSync('./delegates.txt', 'ut
5794
}
5895
abbrs.set(abbr, lineNumber);
5996

97+
const idealTLA = getIdealTLA(firstName, lastName);
98+
99+
if (idealTLA){
100+
if (!allAbbrs.has(idealTLA) && !NON_IDEAL_ABBRS.has(abbr) && !TWO_LETTER_ABBRS.has(abbr)){ // duplicates the 2-letter check, but helpful to distinguish these issues
101+
throw new Error(`Line ${lineNumber}: Should be using ideal TLA (${idealTLA}). Note: because code cannot distinguish between a middle name vs a two-part last name, this may be a false positive.`);
102+
}
103+
} else if (!MONONYMOUS.has(firstName)) {
104+
throw new Error(`Line ${lineNumber}: Unexpected mononymous delegate.`);
105+
}
106+
60107
previousLine = line;
61108
++lineNumber;
62109
}
63110

111+
if (isContentsDelegatesFile) {
112+
113+
for (const abbr of TWO_LETTER_ABBRS) {
114+
if (!allAbbrs.has(abbr)){
115+
throw new Error(`abbreviation ${abbr} is included in TWO_LETTER_ABBRS, but does not exist in ${DELEGATES_FILE_PATH}`);
116+
}
117+
}
118+
119+
for (const abbr of NON_IDEAL_ABBRS) {
120+
if (!allAbbrs.has(abbr)){
121+
throw new Error(`abbreviation ${abbr} is included in NON_IDEAL_ABBRS, but does not exist in ${DELEGATES_FILE_PATH}`);
122+
}
123+
}
124+
125+
}
126+
64127
console.debug('...delegates are valid\n');
65128
}
129+
130+
function getIdealTLA(firstName, lastName) {
131+
132+
if (lastName) {
133+
return `${firstName.slice(0, 1)}${lastName.slice(0, 1)}${lastName.slice(lastName.length - 1)}`.toUpperCase();
134+
}
135+
136+
return null;
137+
138+
}

0 commit comments

Comments
 (0)