2020-11-18 15:18:55 +01:00
import { of , throwError } from 'rxjs' ;
import { take } from 'rxjs/operators' ;
2020-11-20 11:12:34 +01:00
import { AnnotationQueryRequest , CoreApp , DataFrame , dateTime , FieldCache , TimeRange , TimeSeries } from '@grafana/data' ;
2020-09-01 06:21:21 +02:00
import { BackendSrvRequest , FetchResponse } from '@grafana/runtime' ;
2020-07-16 14:00:28 +01:00
import LokiDatasource from './datasource' ;
2020-04-22 12:59:06 +01:00
import { LokiQuery , LokiResponse , LokiResultType } from './types' ;
2019-01-18 18:59:32 +01:00
import { getQueryOptions } from 'test/helpers/getQueryOptions' ;
2019-05-10 02:37:43 -07:00
import { TemplateSrv } from 'app/features/templating/template_srv' ;
2020-06-04 13:44:48 +02:00
import { backendSrv } from 'app/core/services/backend_srv' ;
import { CustomVariableModel } from '../../../features/variables/types' ;
2020-09-01 06:21:21 +02:00
import { initialCustomVariableModelState } from '../../../features/variables/custom/reducer' ;
import { makeMockLokiDatasource } from './mocks' ;
2020-11-26 10:32:02 +01:00
import { createFetchResponse } from 'test/helpers/createFetchResponse' ;
2020-01-21 09:08:07 +00:00
jest . mock ( '@grafana/runtime' , ( ) = > ( {
2020-09-01 06:21:21 +02:00
// @ts-ignore
2020-07-09 16:14:55 +02:00
. . . jest . requireActual ( '@grafana/runtime' ) ,
2020-01-21 09:08:07 +00:00
getBackendSrv : ( ) = > backendSrv ,
} ) ) ;
2020-10-01 18:51:23 +01:00
const timeSrvStub = {
timeRange : ( ) = > ( {
from : new Date ( 0 ) ,
to : new Date ( 1 ) ,
} ) ,
} ;
2020-08-14 10:33:37 +02:00
2020-11-20 11:12:34 +01:00
const testLogsResponse : FetchResponse < LokiResponse > = {
2020-10-16 17:24:23 +02:00
data : {
data : {
resultType : LokiResultType.Stream ,
result : [
{
stream : { } ,
values : [ [ '1573646419522934000' , 'hello' ] ] ,
} ,
] ,
} ,
status : 'success' ,
} ,
ok : true ,
headers : ( { } as unknown ) as Headers ,
redirected : false ,
status : 200 ,
statusText : 'Success' ,
type : 'default' ,
url : '' ,
config : ( { } as unknown ) as BackendSrvRequest ,
} ;
2020-11-20 11:12:34 +01:00
const testMetricsResponse : FetchResponse < LokiResponse > = {
data : {
data : {
resultType : LokiResultType.Matrix ,
result : [
{
metric : { } ,
values : [ [ 1605715380 , '1.1' ] ] ,
} ,
] ,
} ,
status : 'success' ,
} ,
ok : true ,
headers : ( { } as unknown ) as Headers ,
redirected : false ,
status : 200 ,
statusText : 'OK' ,
type : 'basic' ,
url : '' ,
config : ( { } as unknown ) as BackendSrvRequest ,
} ;
2018-12-06 00:19:55 +01:00
describe ( 'LokiDatasource' , ( ) = > {
2020-09-01 06:21:21 +02:00
const fetchMock = jest . spyOn ( backendSrv , 'fetch' ) ;
2018-12-06 00:19:55 +01:00
2020-01-21 09:08:07 +00:00
beforeEach ( ( ) = > {
jest . clearAllMocks ( ) ;
2020-11-18 15:18:55 +01:00
fetchMock . mockImplementation ( ( ) = > of ( createFetchResponse ( { } ) ) ) ;
2020-01-21 09:08:07 +00:00
} ) ;
2018-12-31 11:25:28 +00:00
2019-12-04 12:43:22 +00:00
describe ( 'when creating range query' , ( ) = > {
let ds : LokiDatasource ;
let adjustIntervalSpy : jest.SpyInstance ;
2020-09-01 06:21:21 +02:00
2019-12-04 12:43:22 +00:00
beforeEach ( ( ) = > {
2020-09-01 06:21:21 +02:00
ds = createLokiDSForTests ( ) ;
2019-12-04 12:43:22 +00:00
adjustIntervalSpy = jest . spyOn ( ds , 'adjustInterval' ) ;
} ) ;
it ( 'should use default intervalMs if one is not provided' , ( ) = > {
const target = { expr : '{job="grafana"}' , refId : 'B' } ;
const raw = { from : 'now' , to : 'now-1h' } ;
const range = { from : dateTime ( ) , to : dateTime ( ) , raw : raw } ;
const options = {
range ,
} ;
2020-10-16 13:30:02 +02:00
const req = ds . createRangeQuery ( target , options as any , 1000 ) ;
2019-12-04 12:43:22 +00:00
expect ( req . start ) . toBeDefined ( ) ;
expect ( req . end ) . toBeDefined ( ) ;
expect ( adjustIntervalSpy ) . toHaveBeenCalledWith ( 1000 , expect . anything ( ) ) ;
} ) ;
it ( 'should use provided intervalMs' , ( ) = > {
const target = { expr : '{job="grafana"}' , refId : 'B' } ;
const raw = { from : 'now' , to : 'now-1h' } ;
const range = { from : dateTime ( ) , to : dateTime ( ) , raw : raw } ;
const options = {
range ,
intervalMs : 2000 ,
} ;
2020-10-16 13:30:02 +02:00
const req = ds . createRangeQuery ( target , options as any , 1000 ) ;
2019-12-04 12:43:22 +00:00
expect ( req . start ) . toBeDefined ( ) ;
expect ( req . end ) . toBeDefined ( ) ;
expect ( adjustIntervalSpy ) . toHaveBeenCalledWith ( 2000 , expect . anything ( ) ) ;
} ) ;
} ) ;
2020-11-20 11:12:34 +01:00
describe ( 'when doing logs queries with limits' , ( ) = > {
2020-11-18 15:18:55 +01:00
const runLimitTest = async ( {
2020-10-16 13:30:02 +02:00
maxDataPoints = 123 ,
queryMaxLines ,
dsMaxLines = 456 ,
expectedLimit ,
expr = '{label="val"}' ,
} : any ) = > {
2020-09-01 06:21:21 +02:00
let settings : any = {
url : 'myloggingurl' ,
2020-10-16 13:30:02 +02:00
jsonData : {
maxLines : dsMaxLines ,
} ,
2020-09-01 06:21:21 +02:00
} ;
2020-01-21 09:08:07 +00:00
2020-09-01 06:21:21 +02:00
const templateSrvMock = ( {
getAdhocFilters : ( ) : any [ ] = > [ ] ,
replace : ( a : string ) = > a ,
} as unknown ) as TemplateSrv ;
2019-11-15 15:38:25 +00:00
2020-10-01 18:51:23 +01:00
const ds = new LokiDatasource ( settings , templateSrvMock , timeSrvStub as any ) ;
2019-11-15 15:38:25 +00:00
2020-10-16 13:30:02 +02:00
const options = getQueryOptions < LokiQuery > ( { targets : [ { expr , refId : 'B' , maxLines : queryMaxLines } ] } ) ;
options . maxDataPoints = maxDataPoints ;
2020-09-01 06:21:21 +02:00
2020-11-20 11:12:34 +01:00
fetchMock . mockImplementation ( ( ) = > of ( testLogsResponse ) ) ;
2020-09-01 06:21:21 +02:00
2020-11-18 15:18:55 +01:00
await expect ( ds . query ( options ) . pipe ( take ( 1 ) ) ) . toEmitValuesWith ( ( ) = > {
expect ( fetchMock . mock . calls . length ) . toBe ( 1 ) ;
expect ( fetchMock . mock . calls [ 0 ] [ 0 ] . url ) . toContain ( ` limit= ${ expectedLimit } ` ) ;
} ) ;
2020-09-01 06:21:21 +02:00
} ;
2019-01-18 18:59:32 +01:00
2020-11-18 15:18:55 +01:00
it ( 'should use datasource max lines when no limit given and it is log query' , async ( ) = > {
await runLimitTest ( { expectedLimit : 456 } ) ;
2018-12-31 11:25:28 +00:00
} ) ;
2020-11-18 15:18:55 +01:00
it ( 'should use custom max lines from query if set and it is logs query' , async ( ) = > {
await runLimitTest ( { queryMaxLines : 20 , expectedLimit : 20 } ) ;
2019-09-05 08:04:01 -04:00
} ) ;
2019-01-18 18:59:32 +01:00
2020-11-18 15:18:55 +01:00
it ( 'should use custom max lines from query if set and it is logs query even if it is higher than data source limit' , async ( ) = > {
await runLimitTest ( { queryMaxLines : 500 , expectedLimit : 500 } ) ;
2019-09-05 08:04:01 -04:00
} ) ;
2019-01-18 18:59:32 +01:00
2020-11-18 15:18:55 +01:00
it ( 'should use maxDataPoints if it is metrics query' , async ( ) = > {
await runLimitTest ( { expr : 'rate({label="val"}[10m])' , expectedLimit : 123 } ) ;
} ) ;
it ( 'should use maxDataPoints if it is metrics query and using search' , async ( ) = > {
await runLimitTest ( { expr : 'rate({label="val"}[10m])' , expectedLimit : 123 } ) ;
2018-12-31 11:25:28 +00:00
} ) ;
2020-09-01 06:21:21 +02:00
} ) ;
2019-02-01 13:30:15 +01:00
2020-09-01 06:21:21 +02:00
describe ( 'when querying' , ( ) = > {
2020-11-20 11:12:34 +01:00
function setup ( expr : string , app : CoreApp , instant? : boolean , range? : boolean ) {
2020-09-01 06:21:21 +02:00
const ds = createLokiDSForTests ( ) ;
const options = getQueryOptions < LokiQuery > ( {
2020-11-20 11:12:34 +01:00
targets : [ { expr , refId : 'B' , instant , range } ] ,
2020-10-16 17:24:23 +02:00
app ,
2020-09-01 06:21:21 +02:00
} ) ;
ds . runInstantQuery = jest . fn ( ( ) = > of ( { data : [ ] } ) ) ;
ds . runRangeQuery = jest . fn ( ( ) = > of ( { data : [ ] } ) ) ;
2020-10-16 17:24:23 +02:00
return { ds , options } ;
}
2020-11-20 11:12:34 +01:00
const metricsQuery = 'rate({job="grafana"}[10m])' ;
const logsQuery = '{job="grafana"} |= "foo"' ;
it ( 'should run logs instant if only instant is selected' , async ( ) = > {
const { ds , options } = setup ( logsQuery , CoreApp . Explore , true , false ) ;
await ds . query ( options ) . toPromise ( ) ;
expect ( ds . runInstantQuery ) . toBeCalled ( ) ;
expect ( ds . runRangeQuery ) . not . toBeCalled ( ) ;
} ) ;
it ( 'should run metrics instant if only instant is selected' , async ( ) = > {
const { ds , options } = setup ( metricsQuery , CoreApp . Explore , true , false ) ;
2020-10-16 17:24:23 +02:00
await ds . query ( options ) . toPromise ( ) ;
expect ( ds . runInstantQuery ) . toBeCalled ( ) ;
2020-11-20 11:12:34 +01:00
expect ( ds . runRangeQuery ) . not . toBeCalled ( ) ;
} ) ;
it ( 'should run only logs range query if only range is selected' , async ( ) = > {
const { ds , options } = setup ( logsQuery , CoreApp . Explore , false , true ) ;
await ds . query ( options ) . toPromise ( ) ;
expect ( ds . runInstantQuery ) . not . toBeCalled ( ) ;
expect ( ds . runRangeQuery ) . toBeCalled ( ) ;
} ) ;
it ( 'should run only metrics range query if only range is selected' , async ( ) = > {
const { ds , options } = setup ( metricsQuery , CoreApp . Explore , false , true ) ;
await ds . query ( options ) . toPromise ( ) ;
expect ( ds . runInstantQuery ) . not . toBeCalled ( ) ;
expect ( ds . runRangeQuery ) . toBeCalled ( ) ;
} ) ;
it ( 'should run only logs range query if no query type is selected in Explore' , async ( ) = > {
const { ds , options } = setup ( logsQuery , CoreApp . Explore ) ;
await ds . query ( options ) . toPromise ( ) ;
expect ( ds . runInstantQuery ) . not . toBeCalled ( ) ;
2020-10-16 17:24:23 +02:00
expect ( ds . runRangeQuery ) . toBeCalled ( ) ;
2020-09-01 06:21:21 +02:00
} ) ;
2019-02-01 13:30:15 +01:00
2020-11-20 11:12:34 +01:00
it ( 'should run only metrics range query if no query type is selected in Explore' , async ( ) = > {
const { ds , options } = setup ( metricsQuery , CoreApp . Explore ) ;
2020-10-16 17:24:23 +02:00
await ds . query ( options ) . toPromise ( ) ;
expect ( ds . runInstantQuery ) . not . toBeCalled ( ) ;
expect ( ds . runRangeQuery ) . toBeCalled ( ) ;
} ) ;
2020-10-04 21:41:12 +02:00
2020-11-20 11:12:34 +01:00
it ( 'should run only logs range query in Dashboard' , async ( ) = > {
const { ds , options } = setup ( logsQuery , CoreApp . Dashboard ) ;
2020-10-16 17:24:23 +02:00
await ds . query ( options ) . toPromise ( ) ;
expect ( ds . runInstantQuery ) . not . toBeCalled ( ) ;
expect ( ds . runRangeQuery ) . toBeCalled ( ) ;
2020-10-04 21:41:12 +02:00
} ) ;
2020-11-20 11:12:34 +01:00
it ( 'should run only metrics range query in Dashboard' , async ( ) = > {
const { ds , options } = setup ( metricsQuery , CoreApp . Dashboard ) ;
await ds . query ( options ) . toPromise ( ) ;
expect ( ds . runInstantQuery ) . not . toBeCalled ( ) ;
expect ( ds . runRangeQuery ) . toBeCalled ( ) ;
} ) ;
it ( 'should return series data for metrics range queries' , async ( ) = > {
2020-09-01 06:21:21 +02:00
const ds = createLokiDSForTests ( ) ;
2019-02-01 13:30:15 +01:00
const options = getQueryOptions < LokiQuery > ( {
2020-11-20 11:12:34 +01:00
targets : [ { expr : metricsQuery , refId : 'B' , range : true } ] ,
2020-10-04 21:41:12 +02:00
app : CoreApp.Explore ,
2019-02-01 13:30:15 +01:00
} ) ;
2020-11-20 11:12:34 +01:00
fetchMock . mockImplementation ( ( ) = > of ( testMetricsResponse ) ) ;
2020-09-01 06:21:21 +02:00
2020-11-18 15:18:55 +01:00
await expect ( ds . query ( options ) ) . toEmitValuesWith ( received = > {
2020-11-20 11:12:34 +01:00
const result = received [ 0 ] ;
const timeSeries = result . data [ 0 ] as TimeSeries ;
2020-11-18 15:18:55 +01:00
2020-11-20 11:12:34 +01:00
expect ( timeSeries . meta ? . preferredVisualisationType ) . toBe ( 'graph' ) ;
expect ( timeSeries . refId ) . toBe ( 'B' ) ;
expect ( timeSeries . datapoints [ 0 ] ) . toEqual ( [ 1.1 , 1605715380000 ] ) ;
2020-11-18 15:18:55 +01:00
} ) ;
2019-02-01 13:30:15 +01:00
} ) ;
2020-07-09 10:13:41 +02:00
2020-11-20 11:12:34 +01:00
it ( 'should return series data for logs range query' , async ( ) = > {
2020-10-04 21:41:12 +02:00
const ds = createLokiDSForTests ( ) ;
const options = getQueryOptions < LokiQuery > ( {
2020-11-20 11:12:34 +01:00
targets : [ { expr : logsQuery , refId : 'B' } ] ,
2020-10-04 21:41:12 +02:00
} ) ;
2020-11-20 11:12:34 +01:00
fetchMock . mockImplementation ( ( ) = > of ( testLogsResponse ) ) ;
2020-11-18 15:18:55 +01:00
await expect ( ds . query ( options ) ) . toEmitValuesWith ( received = > {
2020-11-20 11:12:34 +01:00
const result = received [ 0 ] ;
const dataFrame = result . data [ 0 ] as DataFrame ;
2020-11-18 15:18:55 +01:00
const fieldCache = new FieldCache ( dataFrame ) ;
2020-10-04 21:41:12 +02:00
2020-11-18 15:18:55 +01:00
expect ( fieldCache . getFieldByName ( 'line' ) ? . values . get ( 0 ) ) . toBe ( 'hello' ) ;
expect ( dataFrame . meta ? . limit ) . toBe ( 20 ) ;
expect ( dataFrame . meta ? . searchWords ) . toEqual ( [ 'foo' ] ) ;
} ) ;
2020-10-04 21:41:12 +02:00
} ) ;
2020-11-18 15:18:55 +01:00
it ( 'should return custom error message when Loki returns escaping error' , async ( ) = > {
2020-09-01 06:21:21 +02:00
const ds = createLokiDSForTests ( ) ;
2020-07-09 10:13:41 +02:00
const options = getQueryOptions < LokiQuery > ( {
targets : [ { expr : '{job="gra\\fana"}' , refId : 'B' } ] ,
} ) ;
2020-11-18 15:18:55 +01:00
fetchMock . mockImplementation ( ( ) = >
throwError ( {
data : {
message : 'parse error at line 1, col 6: invalid char escape' ,
} ,
status : 400 ,
statusText : 'Bad Request' ,
} )
) ;
await expect ( ds . query ( options ) ) . toEmitValuesWith ( received = > {
const err : any = received [ 0 ] ;
expect ( err . data . message ) . toBe (
'Error: parse error at line 1, col 6: invalid char escape. Make sure that all special characters are escaped with \\. For more information on escaping of special characters visit LogQL documentation at https://grafana.com/docs/loki/latest/logql/.'
) ;
2020-09-01 06:21:21 +02:00
} ) ;
2020-07-09 10:13:41 +02:00
} ) ;
2018-12-31 11:25:28 +00:00
} ) ;
2020-09-01 06:21:21 +02:00
describe ( 'when interpolating variables' , ( ) = > {
2019-11-15 15:38:25 +00:00
let ds : LokiDatasource ;
2020-06-04 13:44:48 +02:00
let variable : CustomVariableModel ;
2019-11-06 17:29:44 +01:00
beforeEach ( ( ) = > {
2020-09-01 06:21:21 +02:00
ds = createLokiDSForTests ( ) ;
2020-06-04 13:44:48 +02:00
variable = { . . . initialCustomVariableModelState } ;
2019-11-06 17:29:44 +01:00
} ) ;
it ( 'should only escape single quotes' , ( ) = > {
expect ( ds . interpolateQueryExpr ( "abc'$^*{}[]+?.()|" , variable ) ) . toEqual ( "abc\\\\'$^*{}[]+?.()|" ) ;
} ) ;
it ( 'should return a number' , ( ) = > {
expect ( ds . interpolateQueryExpr ( 1000 , variable ) ) . toEqual ( 1000 ) ;
} ) ;
describe ( 'and variable allows multi-value' , ( ) = > {
beforeEach ( ( ) = > {
variable . multi = true ;
} ) ;
it ( 'should regex escape values if the value is a string' , ( ) = > {
expect ( ds . interpolateQueryExpr ( 'looking*glass' , variable ) ) . toEqual ( 'looking\\\\*glass' ) ;
} ) ;
it ( 'should return pipe separated values if the value is an array of strings' , ( ) = > {
expect ( ds . interpolateQueryExpr ( [ 'a|bc' , 'de|f' ] , variable ) ) . toEqual ( 'a\\\\|bc|de\\\\|f' ) ;
} ) ;
} ) ;
describe ( 'and variable allows all' , ( ) = > {
beforeEach ( ( ) = > {
variable . includeAll = true ;
} ) ;
it ( 'should regex escape values if the array is a string' , ( ) = > {
expect ( ds . interpolateQueryExpr ( 'looking*glass' , variable ) ) . toEqual ( 'looking\\\\*glass' ) ;
} ) ;
it ( 'should return pipe separated values if the value is an array of strings' , ( ) = > {
expect ( ds . interpolateQueryExpr ( [ 'a|bc' , 'de|f' ] , variable ) ) . toEqual ( 'a\\\\|bc|de\\\\|f' ) ;
} ) ;
} ) ;
} ) ;
2018-12-06 00:19:55 +01:00
describe ( 'when performing testDataSource' , ( ) = > {
describe ( 'and call succeeds' , ( ) = > {
2020-09-01 06:21:21 +02:00
it ( 'should return successfully' , async ( ) = > {
2020-11-18 15:18:55 +01:00
fetchMock . mockImplementation ( ( ) = > of ( createFetchResponse ( { values : [ 'avalue' ] } ) ) ) ;
const ds = createLokiDSForTests ( { } as TemplateSrv ) ;
2020-09-01 06:21:21 +02:00
2020-11-18 15:18:55 +01:00
const result = await ds . testDatasource ( ) ;
2018-12-06 00:19:55 +01:00
expect ( result . status ) . toBe ( 'success' ) ;
} ) ;
} ) ;
describe ( 'and call fails with 401 error' , ( ) = > {
2019-11-15 15:38:25 +00:00
it ( 'should return error status and a detailed error message' , async ( ) = > {
2020-11-18 15:18:55 +01:00
fetchMock . mockImplementation ( ( ) = >
throwError ( {
statusText : 'Unauthorized' ,
status : 401 ,
data : {
message : 'Unauthorized' ,
} ,
} )
) ;
const ds = createLokiDSForTests ( { } as TemplateSrv ) ;
const result = await ds . testDatasource ( ) ;
2020-09-01 06:21:21 +02:00
2018-12-06 00:19:55 +01:00
expect ( result . status ) . toEqual ( 'error' ) ;
expect ( result . message ) . toBe ( 'Loki: Unauthorized. 401. Unauthorized' ) ;
} ) ;
} ) ;
describe ( 'and call fails with 404 error' , ( ) = > {
2020-09-01 06:21:21 +02:00
it ( 'should return error status and a detailed error message' , async ( ) = > {
2020-11-18 15:18:55 +01:00
fetchMock . mockImplementation ( ( ) = >
throwError ( {
statusText : 'Not found' ,
status : 404 ,
data : {
message : '404 page not found' ,
} ,
} )
) ;
2020-09-01 06:21:21 +02:00
2020-11-18 15:18:55 +01:00
const ds = createLokiDSForTests ( { } as TemplateSrv ) ;
2020-09-01 06:21:21 +02:00
2020-11-18 15:18:55 +01:00
const result = await ds . testDatasource ( ) ;
2018-12-06 00:19:55 +01:00
expect ( result . status ) . toEqual ( 'error' ) ;
expect ( result . message ) . toBe ( 'Loki: Not found. 404. 404 page not found' ) ;
} ) ;
} ) ;
describe ( 'and call fails with 502 error' , ( ) = > {
2020-09-01 06:21:21 +02:00
it ( 'should return error status and a detailed error message' , async ( ) = > {
2020-11-18 15:18:55 +01:00
fetchMock . mockImplementation ( ( ) = >
throwError ( {
statusText : 'Bad Gateway' ,
status : 502 ,
data : '' ,
} )
) ;
2020-09-01 06:21:21 +02:00
2020-11-18 15:18:55 +01:00
const ds = createLokiDSForTests ( { } as TemplateSrv ) ;
2020-09-01 06:21:21 +02:00
2020-11-18 15:18:55 +01:00
const result = await ds . testDatasource ( ) ;
2018-12-06 00:19:55 +01:00
expect ( result . status ) . toEqual ( 'error' ) ;
expect ( result . message ) . toBe ( 'Loki: Bad Gateway. 502' ) ;
} ) ;
} ) ;
} ) ;
2019-09-10 11:04:44 +02:00
2019-11-26 05:43:24 -08:00
describe ( 'when creating a range query' , ( ) = > {
// Loki v1 API has an issue with float step parameters, can be removed when API is fixed
it ( 'should produce an integer step parameter' , ( ) = > {
2020-09-01 06:21:21 +02:00
const ds = createLokiDSForTests ( ) ;
const query : LokiQuery = { expr : 'foo' , refId : 'bar' } ;
2019-11-26 05:43:24 -08:00
const range : TimeRange = {
from : dateTime ( 0 ) ,
to : dateTime ( 1 e9 + 1 ) ,
raw : { from : '0' , to : '1000000001' } ,
} ;
2020-09-01 06:21:21 +02:00
2019-11-26 05:43:24 -08:00
// Odd timerange/interval combination that would lead to a float step
2020-07-16 14:00:28 +01:00
const options = { range , intervalMs : 2000 } ;
2020-09-01 06:21:21 +02:00
2020-10-16 13:30:02 +02:00
expect ( Number . isInteger ( ds . createRangeQuery ( query , options as any , 1000 ) . step ! ) ) . toBeTruthy ( ) ;
2019-11-26 05:43:24 -08:00
} ) ;
} ) ;
2020-09-01 06:21:21 +02:00
describe ( 'when calling annotationQuery' , ( ) = > {
2020-11-18 15:18:55 +01:00
const getTestContext = ( response : any ) = > {
2020-09-01 06:21:21 +02:00
const query = makeAnnotationQueryRequest ( ) ;
2020-11-18 15:18:55 +01:00
fetchMock . mockImplementation ( ( ) = > of ( response ) ) ;
2020-09-01 06:21:21 +02:00
const ds = createLokiDSForTests ( ) ;
const promise = ds . annotationQuery ( query ) ;
return { promise } ;
} ;
2019-11-15 15:38:25 +00:00
it ( 'should transform the loki data to annotation response' , async ( ) = > {
2020-09-01 06:21:21 +02:00
const response : FetchResponse = ( {
data : {
data : {
resultType : LokiResultType.Stream ,
result : [
{
stream : {
label : 'value' ,
label2 : 'value ' ,
} ,
values : [ [ '1549016857498000000' , 'hello' ] ] ,
2020-01-21 09:08:07 +00:00
} ,
2020-09-01 06:21:21 +02:00
{
stream : {
label2 : 'value2' ,
} ,
values : [ [ '1549024057498000000' , 'hello 2' ] ] ,
} ,
] ,
} ,
status : 'success' ,
} ,
} as unknown ) as FetchResponse ;
2020-11-18 15:18:55 +01:00
const { promise } = getTestContext ( response ) ;
2020-09-01 06:21:21 +02:00
const res = await promise ;
2019-09-10 11:04:44 +02:00
expect ( res . length ) . toBe ( 2 ) ;
expect ( res [ 0 ] . text ) . toBe ( 'hello' ) ;
expect ( res [ 0 ] . tags ) . toEqual ( [ 'value' ] ) ;
expect ( res [ 1 ] . text ) . toBe ( 'hello 2' ) ;
expect ( res [ 1 ] . tags ) . toEqual ( [ 'value2' ] ) ;
} ) ;
} ) ;
2019-12-03 09:40:22 +01:00
describe ( 'metricFindQuery' , ( ) = > {
2020-09-01 06:21:21 +02:00
const getTestContext = ( mock : LokiDatasource ) = > {
const ds = createLokiDSForTests ( ) ;
ds . getVersion = mock . getVersion ;
ds . metadataRequest = mock . metadataRequest ;
return { ds } ;
} ;
2019-12-03 09:40:22 +01:00
const mocks = makeMetadataAndVersionsMocks ( ) ;
mocks . forEach ( ( mock , index ) = > {
it ( ` should return label names for Loki v ${ index } ` , async ( ) = > {
2020-09-01 06:21:21 +02:00
const { ds } = getTestContext ( mock ) ;
2019-12-03 09:40:22 +01:00
2020-09-01 06:21:21 +02:00
const res = await ds . metricFindQuery ( 'label_names()' ) ;
expect ( res ) . toEqual ( [ { text : 'label1' } , { text : 'label2' } ] ) ;
2019-12-03 09:40:22 +01:00
} ) ;
} ) ;
mocks . forEach ( ( mock , index ) = > {
it ( ` should return label values for Loki v ${ index } ` , async ( ) = > {
2020-09-01 06:21:21 +02:00
const { ds } = getTestContext ( mock ) ;
const res = await ds . metricFindQuery ( 'label_values(label1)' ) ;
expect ( res ) . toEqual ( [ { text : 'value1' } , { text : 'value2' } ] ) ;
2019-12-03 09:40:22 +01:00
} ) ;
} ) ;
mocks . forEach ( ( mock , index ) = > {
it ( ` should return empty array when incorrect query for Loki v ${ index } ` , async ( ) = > {
2020-09-01 06:21:21 +02:00
const { ds } = getTestContext ( mock ) ;
const res = await ds . metricFindQuery ( 'incorrect_query' ) ;
expect ( res ) . toEqual ( [ ] ) ;
2019-12-03 09:40:22 +01:00
} ) ;
} ) ;
2020-08-14 10:33:37 +02:00
mocks . forEach ( ( mock , index ) = > {
2020-09-01 06:21:21 +02:00
it ( ` should return label names according to provided rangefor Loki v ${ index } ` , async ( ) = > {
const { ds } = getTestContext ( mock ) ;
const res = await ds . metricFindQuery ( 'label_names()' , { range : { from : new Date ( 2 ) , to : new Date ( 3 ) } } ) ;
expect ( res ) . toEqual ( [ { text : 'label1' } ] ) ;
2020-08-14 10:33:37 +02:00
} ) ;
} ) ;
2019-12-03 09:40:22 +01:00
} ) ;
2018-12-06 00:19:55 +01:00
} ) ;
2019-09-05 08:04:01 -04:00
2020-09-01 06:21:21 +02:00
function createLokiDSForTests (
templateSrvMock = ( {
getAdhocFilters : ( ) : any [ ] = > [ ] ,
replace : ( a : string ) = > a ,
} as unknown ) as TemplateSrv
) : LokiDatasource {
const instanceSettings : any = {
url : 'myloggingurl' ,
2019-09-05 08:04:01 -04:00
} ;
2020-09-01 06:21:21 +02:00
const customData = { . . . ( instanceSettings . jsonData || { } ) , maxLines : 20 } ;
const customSettings = { . . . instanceSettings , jsonData : customData } ;
2020-10-01 18:51:23 +01:00
return new LokiDatasource ( customSettings , templateSrvMock , timeSrvStub as any ) ;
2019-09-05 08:04:01 -04:00
}
2019-09-10 11:04:44 +02:00
function makeAnnotationQueryRequest ( ) : AnnotationQueryRequest < LokiQuery > {
const timeRange = {
from : dateTime ( ) ,
to : dateTime ( ) ,
} ;
return {
annotation : {
expr : '{test=test}' ,
refId : '' ,
datasource : 'loki' ,
enable : true ,
name : 'test-annotation' ,
} ,
dashboard : {
id : 1 ,
} as any ,
range : {
. . . timeRange ,
raw : timeRange ,
} ,
rangeRaw : timeRange ,
} ;
}
2019-12-03 09:40:22 +01:00
function makeMetadataAndVersionsMocks() {
const mocks = [ ] ;
for ( let i = 0 ; i <= 1 ; i ++ ) {
const mock : LokiDatasource = makeMockLokiDatasource ( { label1 : [ 'value1' , 'value2' ] , label2 : [ 'value3' , 'value4' ] } ) ;
mock . getVersion = jest . fn ( ) . mockReturnValue ( ` v ${ i } ` ) ;
mocks . push ( mock ) ;
}
return mocks ;
}