Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
112 changes: 54 additions & 58 deletions redisinsight/api/src/utils/cli-helper.spec.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
/* eslint-disable @typescript-eslint/quotes */
import { randomBytes } from 'crypto';
import ERROR_MESSAGES from 'src/constants/error-messages';
import { CommandParsingError, RedirectionParsingError } from 'src/modules/cli/constants/errors';
Expand All @@ -20,64 +21,59 @@ import {

describe('Cli helper', () => {
describe('splitCliCommandLine', () => {
it('should correctly split simple command with args', () => {
const input = 'memory usage key';

const output = splitCliCommandLine(input);

expect(output).toEqual(['memory', 'usage', 'key']);
});
it('should correctly split command with special symbols in the args in the double quotes', () => {
const input = 'set test "—"';

const output = splitCliCommandLine(input);
const buffer = Buffer.from('e28094', 'hex');
expect(output).toEqual(['set', 'test', buffer]);
});
// todo: enable after review splitCliCommandLine functionality
xit('should correctly split command with special symbols in the args in the single quotes', () => {
const input = "set test '—'";

const output = splitCliCommandLine(input);

const buffer = Buffer.from('e28094', 'hex');
expect(output).toEqual(['set', 'test', buffer]);
});
it('should correctly split simple command without args', () => {
const input = 'info';

const output = splitCliCommandLine(input);

expect(output).toEqual(['info']);
});
it('should correctly split command with double quotes', () => {
const input = 'get "key name"';

const output = splitCliCommandLine(input);
expect(output).toEqual(['get', Buffer.from('key name')]);
});
it('should correctly split command with single quotes', () => {
const input = "get 'key name'";

const output = splitCliCommandLine(input);

expect(output).toEqual(['get', 'key name']);
});
it('should correctly handle special character', () => {
const input = 'set key "\\a\\b\\t\\n\\r"';
const output = splitCliCommandLine(input);

expect(output).toEqual([
'set',
'key',
Buffer.alloc(5, String.fromCharCode(7, 8, 9, 10, 13)),
]);
});
it('should correctly handle hexadecimal', () => {
const input = 'set key "\\xac\\xed"';
const output = splitCliCommandLine(input);

expect(output).toEqual(['set', 'key', Buffer.from([172, 237])]);
[
{
input: 'memory usage key',
output: ['memory', 'usage', 'key'],
},
{
input: 'set test "—"',
output: ['set', 'test', '—'],
},
{
input: "set test '—'",
output: ['set', 'test', '—'],
},
{
input: 'info',
output: ['info'],
},
{
input: 'get "key name"',
output: ['get', 'key name'],
},
{
input: `get "key ' name"`,
output: ['get', `key ' name`],
},
{
input: `get "key \\" name"`,
output: ['get', `key " name`],
},
{
input: "get 'key name'",
output: ['get', 'key name'],
},
{
input: `s"et" ~\\'\\nk"k "ey' 1`,
output: ['set', `~\\\\nk"k "ey`, '1'],
},
{
input: 'set key "\\a\\b\\t\\n\\r"',
output: ['set', 'key', `\u0007\u0008\u0009\n\r`],
},
{
input: 'set key "\\xac\\xed"',
output: ['set', 'key', Buffer.from([172, 237])],
},
{
input: `ACL SETUSER t on nopass ~'\\x00' &* +@all`,
output: ['ACL', 'SETUSER', 't', 'on', 'nopass', '~\\x00', '&*', '+@all'],
},
].forEach((tc) => {
it(`should return ${JSON.stringify(tc.output)} for command ${tc.input}`, async () => {
expect(splitCliCommandLine(tc.input)).toEqual(tc.output);
});
});
it('should throw [CLI_INVALID_QUOTES_CLOSING] error for command with double quotes', () => {
const input = 'get "key"a';
Expand Down
21 changes: 16 additions & 5 deletions redisinsight/api/src/utils/cli-helper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -63,14 +63,25 @@ function getSpecChar(str: string): string {
return char;
}

export const convertToStringIfPossible = (data: any) => {
if (data instanceof Buffer) {
const str = data.toString();
if (Buffer.compare(data, Buffer.from(str)) === 0) {
return str;
}
}

return data;
};

// todo: review/rewrite this function. Pay attention on handling data inside '' vs ""
// todo: rethink implementation. set key {value} where {value} is string ~500KB take ~15s
export const splitCliCommandLine = (line: string): string[] => {
// Splits a command line into a list of arguments.
// Ported from sdssplitargs() function in sds.c from Redis source code.
// This is the function redis-cli uses to parse command lines.
let i = 0;
let currentArg = null;
let currentArg: any = '';
const args = [];
while (i < line.length) {
/* skip blanks */
Expand Down Expand Up @@ -141,19 +152,19 @@ export const splitCliCommandLine = (line: string): string[] => {
} else if ([' ', '\n', '\r', '\t', '\0'].includes(line[i])) {
done = true;
} else if (line[i] === '"') {
currentArg = Buffer.alloc(0);
currentArg = Buffer.from(currentArg);
inq = true;
} else if (line[i] === "'") {
currentArg = '';
insq = true;
} else {
currentArg = `${currentArg || ''}${line[i]}`;
}
if (i < line.length) i += 1;
}
args.push(currentArg);
currentArg = null;
args.push(convertToStringIfPossible(currentArg));
currentArg = '';
}

return args;
};

Expand Down