239 lines
9.1 KiB
TypeScript
239 lines
9.1 KiB
TypeScript
import { test, expect, suite, it } from 'vitest';
|
|
import * as misc from './misc';
|
|
|
|
|
|
suite('parseSchedule', () => {
|
|
it('should parse a valid schedule', () => {
|
|
const result = misc.parseSchedule(['00:00', '00:15', '1:30', '12:30']);
|
|
expect(result.valid).toEqual([
|
|
{ string: '00:00', hours: 0, minutes: 0 },
|
|
{ string: '00:15', hours: 0, minutes: 15 },
|
|
{ string: '01:30', hours: 1, minutes: 30 },
|
|
{ string: '12:30', hours: 12, minutes: 30 },
|
|
]);
|
|
expect(result.invalid).toEqual([]);
|
|
});
|
|
|
|
it('should let the average american type 24:00', () => {
|
|
const result = misc.parseSchedule(['24:00']);
|
|
expect(result.valid).toEqual([
|
|
{ string: '00:00', hours: 0, minutes: 0 },
|
|
]);
|
|
expect(result.invalid).toEqual([]);
|
|
});
|
|
|
|
it('should handle invalid stuff', () => {
|
|
const result = misc.parseSchedule(['12:34', 'invalid', '1030', '25:00', '1', '01', '']);
|
|
expect(result).toBeTruthy();
|
|
expect(result.valid).toEqual([
|
|
{ string: '12:34', hours: 12, minutes: 34 },
|
|
]);
|
|
expect(result.invalid).toEqual(['invalid', '1030', '25:00', '1', '01']);
|
|
});
|
|
|
|
it('should remove duplicates', () => {
|
|
const result = misc.parseSchedule(['02:00', '02:00', '05:55', '13:55']);
|
|
expect(result.valid).toEqual([
|
|
{ string: '02:00', hours: 2, minutes: 0 },
|
|
{ string: '05:55', hours: 5, minutes: 55 },
|
|
{ string: '13:55', hours: 13, minutes: 55 },
|
|
]);
|
|
expect(result.invalid).toEqual([]);
|
|
});
|
|
|
|
it('should sort the times', () => {
|
|
const result = misc.parseSchedule(['00:00', '00:01', '23:59', '01:01', '01:00']);
|
|
expect(result.valid).toEqual([
|
|
{ string: '00:00', hours: 0, minutes: 0 },
|
|
{ string: '00:01', hours: 0, minutes: 1 },
|
|
{ string: '01:00', hours: 1, minutes: 0 },
|
|
{ string: '01:01', hours: 1, minutes: 1 },
|
|
{ string: '23:59', hours: 23, minutes: 59 },
|
|
]);
|
|
expect(result.invalid).toEqual([]);
|
|
});
|
|
});
|
|
|
|
test('redactApiKeys', () => {
|
|
expect(misc.redactApiKeys('')).toBe('')
|
|
expect(misc.redactApiKeys('abc')).toBe('abc')
|
|
|
|
const example = `
|
|
sv_licenseKey cfxk_NYWn5555555500000000_2TLnnn
|
|
sv_licenseKey "cfxk_NYWn5555555500000000_2TLnnn"
|
|
sv_licenseKey 'cfxk_NYWn5555555500000000_2TLnnn'
|
|
|
|
steam_webApiKey A2FAF8CF83B87E795555555500000000
|
|
sv_tebexSecret 238a98bec4c0353fee20ac865555555500000000
|
|
rcon_password a5555555500000000
|
|
rcon_password "a5555555500000000"
|
|
rcon_password 'a5555555500000000'
|
|
mysql_connection_string "mysql://root:root@localhost:3306/txAdmin"
|
|
https://discord.com/api/webhooks/33335555555500000000/xxxxxxxxxxxxxxxxxxxx5555555500000000`;
|
|
|
|
const result = misc.redactApiKeys(example)
|
|
expect(result).toContain('[REDACTED]');
|
|
expect(result).toContain('2TLnnn');
|
|
expect(result).not.toContain('5555555500000000');
|
|
expect(result).not.toContain('mysql://');
|
|
})
|
|
|
|
|
|
suite('redactStartupSecrets', () => {
|
|
const redactedString = '[REDACTED]';
|
|
it('should return an empty array when given an empty array', () => {
|
|
expect(misc.redactStartupSecrets([])).toEqual([]);
|
|
});
|
|
|
|
it('should return the same array if no redaction keys are present', () => {
|
|
const args = ['node', 'script.js', '--help'];
|
|
expect(misc.redactStartupSecrets(args)).toEqual(args);
|
|
});
|
|
|
|
it('should redact a sv_licenseKey secret correctly', () => {
|
|
const args = ['sv_licenseKey', 'cfxk_12345_secret'];
|
|
// The regex captures "secret" and returns "[REDACTED cfxk...secret]"
|
|
const expected = ['sv_licenseKey', '[REDACTED cfxk...secret]'];
|
|
expect(misc.redactStartupSecrets(args)).toEqual(expected);
|
|
});
|
|
|
|
it('should not redact sv_licenseKey secret if the secret does not match the regex', () => {
|
|
const args = ['sv_licenseKey', 'invalidsecret'];
|
|
const expected = ['sv_licenseKey', 'invalidsecret'];
|
|
expect(misc.redactStartupSecrets(args)).toEqual(expected);
|
|
});
|
|
|
|
it('should redact steam_webApiKey secret correctly', () => {
|
|
const validKey = 'a'.repeat(32);
|
|
const args = ['steam_webApiKey', validKey];
|
|
const expected = ['steam_webApiKey', redactedString];
|
|
expect(misc.redactStartupSecrets(args)).toEqual(expected);
|
|
});
|
|
|
|
it('should redact sv_tebexSecret secret correctly', () => {
|
|
const validSecret = 'b'.repeat(40);
|
|
const args = ['sv_tebexSecret', validSecret];
|
|
const expected = ['sv_tebexSecret', redactedString];
|
|
expect(misc.redactStartupSecrets(args)).toEqual(expected);
|
|
});
|
|
|
|
it('should redact rcon_password secret correctly', () => {
|
|
const args = ['rcon_password', 'mysecretpassword'];
|
|
const expected = ['rcon_password', redactedString];
|
|
expect(misc.redactStartupSecrets(args)).toEqual(expected);
|
|
});
|
|
|
|
it('should redact mysql_connection_string secret correctly', () => {
|
|
const args = [
|
|
'mysql_connection_string',
|
|
'Server=myServerAddress;Database=myDataBase;User Id=myUsername;Password=myPassword;',
|
|
];
|
|
const expected = ['mysql_connection_string', redactedString];
|
|
expect(misc.redactStartupSecrets(args)).toEqual(expected);
|
|
});
|
|
|
|
it('should handle multiple redactions in a single array', () => {
|
|
const validSteamKey = 'c'.repeat(32);
|
|
const args = [
|
|
'sv_licenseKey', 'cfxk_12345_abcdef',
|
|
'someOtherArg', 'value',
|
|
'steam_webApiKey', validSteamKey,
|
|
];
|
|
const expected = [
|
|
'sv_licenseKey', '[REDACTED cfxk...abcdef]',
|
|
'someOtherArg', 'value',
|
|
'steam_webApiKey', redactedString,
|
|
];
|
|
expect(misc.redactStartupSecrets(args)).toEqual(expected);
|
|
});
|
|
|
|
it('should handle case-insensitive key matching', () => {
|
|
const args = ['SV_LICENSEKEY', 'cfxk_12345_SECRET'];
|
|
const expected = ['SV_LICENSEKEY', '[REDACTED cfxk...SECRET]'];
|
|
expect(misc.redactStartupSecrets(args)).toEqual(expected);
|
|
});
|
|
|
|
it('should leave a key unchanged if it is the last element', () => {
|
|
const args = ['sv_licenseKey'];
|
|
const expected = ['sv_licenseKey'];
|
|
expect(misc.redactStartupSecrets(args)).toEqual(expected);
|
|
});
|
|
|
|
it('should handle rules without regex', () => {
|
|
const args = ['rcon_password', 'whatever'];
|
|
const expected = ['rcon_password', redactedString];
|
|
expect(misc.redactStartupSecrets(args)).toEqual(expected);
|
|
});
|
|
|
|
it('should handle a real example', () => {
|
|
const args = [
|
|
"+setr", "txAdmin-debugMode", "true",
|
|
"+set", "tx2faSecret", "whatever",
|
|
"+set", "sv_licenseKey", "cfxk_xxxxxxxxxxxxxxxxxxxx_yyyyy",
|
|
"+set", "onesync", "enabled",
|
|
"+set", "sv_enforceGameBuild", "2545",
|
|
];
|
|
const expected = [
|
|
"+setr", "txAdmin-debugMode", "true",
|
|
"+set", "tx2faSecret", redactedString,
|
|
"+set", "sv_licenseKey", "[REDACTED cfxk...yyyyy]",
|
|
"+set", "onesync", "enabled",
|
|
"+set", "sv_enforceGameBuild", "2545",
|
|
];
|
|
expect(misc.redactStartupSecrets(args)).toEqual(expected);
|
|
});
|
|
|
|
it('should redact discord webhooks', () => {
|
|
const args = [
|
|
"aaa",
|
|
"https://discord.com/api/webhooks/33335555555500000000/xxxxxxxxxxxxxxxxxxxx5555555500000000",
|
|
"bbb",
|
|
];
|
|
const expected = [
|
|
"aaa",
|
|
"https://discord.com/api/webhooks/[REDACTED]/[REDACTED]",
|
|
"bbb",
|
|
];
|
|
expect(misc.redactStartupSecrets(args)).toEqual(expected);
|
|
});
|
|
});
|
|
|
|
|
|
test('now', () => {
|
|
const result = misc.now();
|
|
expect(typeof result).toBe('number');
|
|
expect(result.toString().length).toBe(10);
|
|
expect(result.toString()).not.toContain('.');
|
|
expect(result.toString()).not.toContain('-');
|
|
});
|
|
|
|
test('anyUndefined', () => {
|
|
expect(misc.anyUndefined(undefined, 'test')).toBe(true);
|
|
expect(misc.anyUndefined('test', 'xxxx')).toBe(false);
|
|
expect(misc.anyUndefined(undefined, undefined)).toBe(true);
|
|
});
|
|
|
|
test('calcExpirationFromDuration', () => {
|
|
const currTs = misc.now();
|
|
let result = misc.calcExpirationFromDuration('1 hour');
|
|
expect(result?.duration).toBe(3600);
|
|
expect(result?.expiration).toBe(currTs + 3600);
|
|
|
|
result = misc.calcExpirationFromDuration('1 hours');
|
|
expect(result?.duration).toBe(3600);
|
|
|
|
result = misc.calcExpirationFromDuration('permanent');
|
|
expect(result?.expiration).toBe(false);
|
|
|
|
expect(() => misc.calcExpirationFromDuration('x day')).toThrowError('duration number');
|
|
expect(() => misc.calcExpirationFromDuration('')).toThrowError('duration number');
|
|
expect(() => misc.calcExpirationFromDuration('-1 day')).toThrowError('duration number');
|
|
});
|
|
|
|
test('parseLimitedFloat', () => {
|
|
expect(misc.parseLimitedFloat('123.4567899999')).toBe(123.45679);
|
|
expect(misc.parseLimitedFloat(123.4567899999)).toBe(123.45679);
|
|
expect(misc.parseLimitedFloat(123.4567899999, 2)).toBe(123.46);
|
|
expect(misc.parseLimitedFloat(0.1 + 0.2)).toBe(0.3);
|
|
});
|