blob: 68cb86912044994aa461f9498ba9817f16503958 [file] [log] [blame]
// Copyright 2022 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import chai from 'chai'; // not esm
import { Category, Filter, FilterExpression, Operator, parseFilter } from '../src/filter';
before(() => {
chai.should();
});
describe('Parser', function () {
describe('single item', () => {
it('supports common metadata fields', () => {
const toTest = {
'any': 'foo',
'manifest': 'foo.cm',
'moniker': 'core/foo',
'message': 'hello',
'package-name': 'my-package',
'tag': 'bar',
};
const ops = {
'equal': '=',
'contains': ':'
};
for (const [category, value] of Object.entries(toTest)) {
for (const [op, opSym] of Object.entries(ops)) {
const filters = parseFilter(`${category}${opSym}${value}`);
filters.should.deep.equal({
ok: true,
value: new FilterExpression([
[
new Filter({
category: category as Category,
subCategory: undefined,
operator: op as Operator,
values: [value],
not: false,
})
]
])
});
}
}
});
it('support pid and tid', () => {
const ops = {
'equal': '=',
'contains': ':'
};
const toTest = {
'pid': 1234,
'tid': 5678,
};
for (const [category, value] of Object.entries(toTest)) {
for (const [op, opSym] of Object.entries(ops)) {
const filters = parseFilter(`${category}${opSym}${value}`);
filters.should.deep.equal({
ok: true,
value: new FilterExpression([
[
new Filter({
category: category as Category,
subCategory: undefined,
operator: op as Operator,
values: [value],
not: false,
})
]
])
});
}
}
});
it('supports custom keys', () => {
const ops = {
'equal': ['=', true],
'contains': [':', 'foo'],
'greaterEq': ['>=', 3],
'lessEq': ['<=', 5],
'greater': ['>', 3],
'less': ['<', 5],
};
for (const [op, [opSym, value]] of Object.entries(ops)) {
const filters = parseFilter(`some_key${opSym}${value}`);
filters.should.deep.equal({
ok: true,
value: new FilterExpression([
[
new Filter({
category: 'custom',
subCategory: 'some_key',
operator: op as Operator,
values: [value] as (string[] | number[] | boolean[]),
not: false,
})
]
])
});
}
});
it('supports severity levels and only valid severities', () => {
const severities = ['trace', 'debug', 'info', 'warn', 'error', 'fatal'];
const ops = {
'equal': '=',
'contains': ':',
'greaterEq': '>=',
'lessEq': '<=',
'greater': '>',
'less': '<',
};
for (const severity of severities) {
for (const [op, opSym] of Object.entries(ops)) {
const filters = parseFilter(`severity${opSym}${severity}`);
filters.should.deep.equal({
ok: true,
value: new FilterExpression([
[
new Filter({
category: 'severity',
subCategory: undefined,
operator: op as Operator,
values: [severity],
not: false,
})
]
])
});
}
}
// An invalid severity is treated as a custom key.
parseFilter('severity:foo').should.deep.equal({
ok: true,
value: new FilterExpression([
[
new Filter({
category: 'custom',
subCategory: 'severity',
operator: 'contains',
values: ['foo'],
not: false,
})
]
])
});
});
it('supports negations', () => {
const ops = ['!', 'not '];
for (const op of ops) {
const filters = parseFilter(`${op}severity:info`);
filters.should.deep.equal({
ok: true,
value: new FilterExpression([
[
new Filter({
category: 'severity',
subCategory: undefined,
operator: 'contains',
values: ['info'],
not: true,
})
]
])
});
}
});
it('supports spaces and reserved chars inside quotes', () => {
const filters = parseFilter('message:"this is:some message"');
filters.should.deep.equal({
ok: true,
value: new FilterExpression([
[
new Filter({
category: 'message',
subCategory: undefined,
operator: 'contains',
values: ['this is:some message'],
not: false,
})
]
])
});
});
it('support multiple possible values', () => {
parseFilter('tag=foo|"bar baz"|quux').should.deep.equal({
ok: true,
value: new FilterExpression([
[
new Filter({
category: 'tag',
subCategory: undefined,
operator: 'equal',
values: ['foo', 'bar baz', 'quux'],
not: false,
})
]
])
});
parseFilter('foo=3|4').should.deep.equal({
ok: true,
value: new FilterExpression([
[
new Filter({
category: 'custom',
subCategory: 'foo',
operator: 'equal',
values: [3, 4],
not: false,
})
]
])
});
parseFilter('severity=info|debug').should.deep.equal({
ok: true,
value: new FilterExpression([
[
new Filter({
category: 'severity',
subCategory: undefined,
operator: 'equal',
values: ['info', 'debug'],
not: false,
})
]
])
});
});
it('is case insensitive for reserved keywords', () => {
parseFilter('SeveriTy:info').should.deep.equal({
ok: true,
value: new FilterExpression([
[
new Filter({
category: 'severity',
subCategory: undefined,
operator: 'contains',
values: ['info'],
not: false,
})
]
])
});
});
it('is case senstive for custom keys', () => {
parseFilter('SOME_key=true').should.deep.equal({
ok: true,
value: new FilterExpression([
[
new Filter({
category: 'custom',
subCategory: 'SOME_key',
operator: 'equal',
values: [true],
not: false,
})
]
])
});
});
});
it('accepts ORed items', () => {
parseFilter('(moniker:foo | !tag:bar Or severity=info)').should.deep.equal(
{
ok: true,
value: new FilterExpression([
[
new Filter({
category: 'moniker',
subCategory: undefined,
operator: 'contains',
values: ['foo'],
not: false,
}),
new Filter({
category: 'tag',
subCategory: undefined,
operator: 'contains',
values: ['bar'],
not: true,
}),
new Filter({
category: 'severity',
subCategory: undefined,
operator: 'equal',
values: ['info'],
not: false,
})
]
])
});
});
it('accepts multiple ANDed expressions', () => {
parseFilter('(moniker:foo or !tag:bar|"baz quux") severity:info & tag:foo and message=bar').should.deep.equal(
{
ok: true,
value: new FilterExpression([
[
new Filter({
category: 'moniker',
subCategory: undefined,
operator: 'contains',
values: ['foo'],
not: false,
}),
new Filter({
category: 'tag',
subCategory: undefined,
operator: 'contains',
values: ['bar', 'baz quux'],
not: true,
}),
],
[
new Filter({
category: 'severity',
subCategory: undefined,
operator: 'contains',
values: ['info'],
not: false,
}),
],
[
new Filter({
category: 'tag',
subCategory: undefined,
operator: 'contains',
values: ['foo'],
not: false,
}),
],
[
new Filter({
category: 'message',
subCategory: undefined,
operator: 'equal',
values: ['bar'],
not: false,
}),
]
])
});
});
describe('raw strings', () => {
it('can be used as "any" filters', () => {
parseFilter('foo').should.deep.equal({
ok: true,
value: new FilterExpression([
[
new Filter({
category: 'any',
subCategory: undefined,
operator: 'contains',
values: ['foo'],
not: false,
})
]
])
});
});
it('multiple raw strings form a conjunction', () => {
parseFilter('foo bar').should.deep.equal({
ok: true,
value: new FilterExpression([
[
new Filter({
category: 'any',
subCategory: undefined,
operator: 'contains',
values: ['foo'],
not: false,
}),
],
[
new Filter({
category: 'any',
subCategory: undefined,
operator: 'contains',
values: ['bar'],
not: false,
})
]
]),
});
});
it('multiple strings inside quotes is a single string', () => {
parseFilter('"foo bar"').should.deep.equal({
ok: true,
value: new FilterExpression([
[
new Filter({
category: 'any',
subCategory: undefined,
operator: 'contains',
values: ['foo bar'],
not: false,
})
]
])
});
});
it('parses not before the string', () => {
parseFilter('NOT foo').should.deep.equal({
ok: true,
value: new FilterExpression([
[
new Filter({
category: 'any',
subCategory: undefined,
operator: 'contains',
values: ['foo'],
not: true,
})
]
])
});
parseFilter('!"foo bar"').should.deep.equal({
ok: true,
value: new FilterExpression([
[
new Filter({
category: 'any',
subCategory: undefined,
operator: 'contains',
values: ['foo bar'],
not: true,
})
]
])
});
});
it('can be mixed with other filters', () => {
parseFilter('moniker:core/bar cool').should.deep.equal({
ok: true,
value: new FilterExpression([
[
new Filter({
category: 'moniker',
subCategory: undefined,
operator: 'contains',
values: ['core/bar'],
not: false,
}),
],
[
new Filter({
category: 'any',
subCategory: undefined,
operator: 'contains',
values: ['cool'],
not: false,
})
]
]),
});
});
});
});