@ -1,17 +1,22 @@
import { of , Subject } from 'rxjs' ;
import { first , last , take } from 'rxjs/operators' ;
import { omit } from 'lodash' ;
import { AnnotationQueryRequest , DataFrame , DataQueryResponse , dateTime , FieldCache , TimeRange } from '@grafana/data' ;
import { BackendSrvRequest , FetchResponse } from '@grafana/runtime' ;
import LokiDatasource from './datasource' ;
import LokiDatasource from './datasource' ;
import { LokiQuery , LokiResponse , LokiResultType } from './types' ;
import { LokiQuery , LokiResponse , LokiResultType } from './types' ;
import { getQueryOptions } from 'test/helpers/getQueryOptions' ;
import { getQueryOptions } from 'test/helpers/getQueryOptions' ;
import { AnnotationQueryRequest , DataFrame , DataSourceApi , dateTime , FieldCache , TimeRange } from '@grafana/data' ;
import { TemplateSrv } from 'app/features/templating/template_srv' ;
import { TemplateSrv } from 'app/features/templating/template_srv' ;
import { makeMockLokiDatasource } from './mocks' ;
import { of } from 'rxjs' ;
import omit from 'lodash/omit' ;
import { backendSrv } from 'app/core/services/backend_srv' ;
import { backendSrv } from 'app/core/services/backend_srv' ;
import { CustomVariableModel } from '../../../features/variables/types' ;
import { CustomVariableModel } from '../../../features/variables/types' ;
import { initialCustomVariableModelState } from '../../../features/variables/custom/reducer' ; // will use the version in __mocks__
import { initialCustomVariableModelState } from '../../../features/variables/custom/reducer' ;
import { observableTester } from '../../../../test/helpers/observableTester' ;
import { expect } from '../../../../test/lib/common' ;
import { makeMockLokiDatasource } from './mocks' ;
jest . mock ( '@grafana/runtime' , ( ) = > ( {
jest . mock ( '@grafana/runtime' , ( ) = > ( {
//@ts-ignore
// @ts-ignore
. . . jest . requireActual ( '@grafana/runtime' ) ,
. . . jest . requireActual ( '@grafana/runtime' ) ,
getBackendSrv : ( ) = > backendSrv ,
getBackendSrv : ( ) = > backendSrv ,
} ) ) ;
} ) ) ;
@ -27,14 +32,11 @@ jest.mock('app/features/dashboard/services/TimeSrv', () => {
} ;
} ;
} ) ;
} ) ;
const datasourceRequestMock = jest . spyOn ( backendSrv , 'datasourceRequest' ) ;
describe ( 'LokiDatasource' , ( ) = > {
describe ( 'LokiDatasource' , ( ) = > {
const instanceSettings : any = {
let fetchStream : Subject < FetchResponse > ;
url : 'myloggingurl' ,
const fetchMock = jest . spyOn ( backendSrv , 'fetch' ) ;
} ;
const testResp : { data : LokiResponse } = {
const testResponse : FetchResponse < LokiResponse > = {
data : {
data : {
data : {
data : {
resultType : LokiResultType.Stream ,
resultType : LokiResultType.Stream ,
@ -47,25 +49,28 @@ describe('LokiDatasource', () => {
} ,
} ,
status : 'success' ,
status : 'success' ,
} ,
} ,
ok : true ,
headers : ( { } as unknown ) as Headers ,
redirected : false ,
status : 200 ,
statusText : 'Success' ,
type : 'default' ,
url : '' ,
config : ( { } as unknown ) as BackendSrvRequest ,
} ;
} ;
beforeEach ( ( ) = > {
beforeEach ( ( ) = > {
jest . clearAllMocks ( ) ;
jest . clearAllMocks ( ) ;
datasourceRequestMock . mockImplementation ( ( ) = > Promise . resolve ( ) ) ;
fetchStream = new Subject < FetchResponse > ( ) ;
fetchMock . mockImplementation ( ( ) = > fetchStream . asObservable ( ) ) ;
} ) ;
} ) ;
const templateSrvMock = ( {
getAdhocFilters : ( ) : any [ ] = > [ ] ,
replace : ( a : string ) = > a ,
} as unknown ) as TemplateSrv ;
describe ( 'when creating range query' , ( ) = > {
describe ( 'when creating range query' , ( ) = > {
let ds : LokiDatasource ;
let ds : LokiDatasource ;
let adjustIntervalSpy : jest.SpyInstance ;
let adjustIntervalSpy : jest.SpyInstance ;
beforeEach ( ( ) = > {
beforeEach ( ( ) = > {
const customData = { . . . ( instanceSettings . jsonData || { } ) , maxLines : 20 } ;
ds = createLokiDSForTests ( ) ;
const customSettings = { . . . instanceSettings , jsonData : customData } ;
ds = new LokiDatasource ( customSettings , templateSrvMock ) ;
adjustIntervalSpy = jest . spyOn ( ds , 'adjustInterval' ) ;
adjustIntervalSpy = jest . spyOn ( ds , 'adjustInterval' ) ;
} ) ;
} ) ;
@ -99,124 +104,161 @@ describe('LokiDatasource', () => {
} ) ;
} ) ;
} ) ;
} ) ;
describe ( 'when querying' , ( ) = > {
describe ( 'when querying with limits' , ( ) = > {
let ds : LokiDatasource ;
const runLimitTest = ( { maxDataPoints , maxLines , expectedLimit , done } : any ) = > {
let testLimit : any ;
let settings : any = {
url : 'myloggingurl' ,
} ;
beforeAll ( ( ) = > {
if ( Number . isFinite ( maxLines ! ) ) {
testLimit = makeLimitTest ( instanceSettings , datasourceRequestMock , templateSrvMock , testResp ) ;
const customData = { . . . ( settings . jsonData || { } ) , maxLines : 20 } ;
} ) ;
settings = { . . . settings , jsonData : customData } ;
}
beforeEach ( ( ) = > {
const templateSrvMock = ( {
const customData = { . . . ( instanceSettings . jsonData || { } ) , maxLines : 20 } ;
getAdhocFilters : ( ) : any [ ] = > [ ] ,
const customSettings = { . . . instanceSettings , jsonData : customData } ;
replace : ( a : string ) = > a ,
ds = new LokiDatasource ( customSettings , templateSrvMock ) ;
} as unknown ) as TemplateSrv ;
datasourceRequestMock . mockImplementation ( ( ) = > Promise . resolve ( testResp ) ) ;
} ) ;
test ( 'should run range and instant query' , async ( ) = > {
const ds = new LokiDatasource ( settings , templateSrvMock ) ;
const options = getQueryOptions < LokiQuery > ( {
targets : [ { expr : '{job="grafana"}' , refId : 'B' } ] ,
} ) ;
ds . runInstantQuery = jest . fn ( ( ) = > of ( { data : [ ] } ) ) ;
const options = getQueryOptions < LokiQuery > ( { targets : [ { expr : 'foo' , refId : 'B' , maxLines : maxDataPoints } ] } ) ;
ds . runRangeQuery = jest . fn ( ( ) = > of ( { data : [ ] } ) ) ;
await ds . query ( options ) . toPromise ( ) ;
expect ( ds . runInstantQuery ) . toBeCalled ( ) ;
if ( Number . isFinite ( maxDataPoints ! ) ) {
expect ( ds . runRangeQuery ) . toBeCalled ( ) ;
options . maxDataPoints = maxDataPoints ;
} ) ;
} else {
// By default is 500
delete options . maxDataPoints ;
}
observableTester ( ) . subscribeAndExpectOnComplete < DataQueryResponse > ( {
observable : ds.query ( options ) . pipe ( take ( 1 ) ) ,
expect : ( ) = > {
expect ( fetchMock . mock . calls . length ) . toBe ( 2 ) ;
expect ( fetchMock . mock . calls [ 0 ] [ 0 ] . url ) . toContain ( ` limit= ${ expectedLimit } ` ) ;
} ,
done ,
} ) ;
fetchStream . next ( testResponse ) ;
} ;
test ( 'should use default max lines when no limit given' , ( ) = > {
i t( 'should use default max lines when no limit given' , done = > {
testLimit ( {
runLimitTes t( {
expectedLimit : 1000 ,
expectedLimit : 1000 ,
done ,
} ) ;
} ) ;
} ) ;
} ) ;
test ( 'should use custom max lines if limit is set' , ( ) = > {
i t( 'should use custom max lines if limit is set' , done = > {
testLimit ( {
runLimitTes t( {
maxLines : 20 ,
maxLines : 20 ,
expectedLimit : 20 ,
expectedLimit : 20 ,
done ,
} ) ;
} ) ;
} ) ;
} ) ;
test ( 'should use custom maxDataPoints if set in request' , ( ) = > {
i t( 'should use custom maxDataPoints if set in request' , ( ) = > {
testLimi t( {
runLimitTes t( {
maxDataPoints : 500 ,
maxDataPoints : 500 ,
expectedLimit : 500 ,
expectedLimit : 500 ,
} ) ;
} ) ;
} ) ;
} ) ;
tes t( 'should use datasource maxLimit if maxDataPoints is higher' , ( ) = > {
i t( 'should use datasource maxLimit if maxDataPoints is higher' , ( ) = > {
testLimi t( {
runLimitTes t( {
maxLines : 20 ,
maxLines : 20 ,
maxDataPoints : 500 ,
maxDataPoints : 500 ,
expectedLimit : 20 ,
expectedLimit : 20 ,
} ) ;
} ) ;
} ) ;
} ) ;
} ) ;
test ( 'should return series data' , async ( ) = > {
describe ( 'when querying' , ( ) = > {
const customData = { . . . ( instanceSettings . jsonData || { } ) , maxLines : 20 } ;
it ( 'should run range and instant query' , done = > {
const customSettings = { . . . instanceSettings , jsonData : customData } ;
const ds = createLokiDSForTests ( ) ;
const ds = new LokiDatasource ( customSettings , templateSrvMock ) ;
const options = getQueryOptions < LokiQuery > ( {
datasourceRequestMock . mockImplementation (
targets : [ { expr : '{job="grafana"}' , refId : 'B' } ] ,
jest
} ) ;
. fn ( )
. mockReturnValueOnce ( Promise . resolve ( testResp ) )
ds . runInstantQuery = jest . fn ( ( ) = > of ( { data : [ ] } ) ) ;
. mockReturnValueOnce ( Promise . resolve ( omit ( testResp , 'data.status' ) ) )
ds . runRangeQuery = jest . fn ( ( ) = > of ( { data : [ ] } ) ) ;
) ;
observableTester ( ) . subscribeAndExpectOnComplete < DataQueryResponse > ( {
observable : ds.query ( options ) ,
expect : ( ) = > {
expect ( ds . runInstantQuery ) . toBeCalled ( ) ;
expect ( ds . runRangeQuery ) . toBeCalled ( ) ;
} ,
done ,
} ) ;
} ) ;
it ( 'should return series data' , done = > {
const ds = createLokiDSForTests ( ) ;
const options = getQueryOptions < LokiQuery > ( {
const options = getQueryOptions < LokiQuery > ( {
targets : [ { expr : '{job="grafana"} |= "foo"' , refId : 'B' } ] ,
targets : [ { expr : '{job="grafana"} |= "foo"' , refId : 'B' } ] ,
} ) ;
} ) ;
const res = await ds . query ( options ) . toPromise ( ) ;
observableTester ( ) . subscribeAndExpectOnNext < DataQueryResponse > ( {
observable : ds.query ( options ) . pipe ( first ( ) ) , // first result always comes from runInstantQuery
expect : res = > {
expect ( res ) . toEqual ( {
data : [ ] ,
key : 'B_instant' ,
} ) ;
} ,
done ,
} ) ;
observableTester ( ) . subscribeAndExpectOnNext < DataQueryResponse > ( {
observable : ds.query ( options ) . pipe ( last ( ) ) , // last result always comes from runRangeQuery
expect : res = > {
const dataFrame = res . data [ 0 ] as DataFrame ;
const fieldCache = new FieldCache ( dataFrame ) ;
expect ( fieldCache . getFieldByName ( 'line' ) ? . values . get ( 0 ) ) . toBe ( 'hello' ) ;
expect ( dataFrame . meta ? . limit ) . toBe ( 20 ) ;
expect ( dataFrame . meta ? . searchWords ) . toEqual ( [ 'foo' ] ) ;
} ,
done ,
} ) ;
const dataFrame = res . data [ 0 ] as DataFrame ;
fetchStream . next ( testResponse ) ;
const fieldCache = new FieldCache ( dataFrame ) ;
fetchStream . next ( omit ( testResponse , 'data.status' ) ) ;
expect ( fieldCache . getFieldByName ( 'line' ) ? . values . get ( 0 ) ) . toBe ( 'hello' ) ;
expect ( dataFrame . meta ? . limit ) . toBe ( 20 ) ;
expect ( dataFrame . meta ? . searchWords ) . toEqual ( [ 'foo' ] ) ;
} ) ;
} ) ;
test ( 'should return custom error message when Loki returns escaping error' , async ( ) = > {
it ( 'should return custom error message when Loki returns escaping error' , done = > {
const customData = { . . . ( instanceSettings . jsonData || { } ) , maxLines : 20 } ;
const ds = createLokiDSForTests ( ) ;
const customSettings = { . . . instanceSettings , jsonData : customData } ;
const ds = new LokiDatasource ( customSettings , templateSrvMock ) ;
datasourceRequestMock . mockImplementation (
jest . fn ( ) . mockReturnValue (
Promise . reject ( {
data : {
message : 'parse error at line 1, col 6: invalid char escape' ,
} ,
status : 400 ,
statusText : 'Bad Request' ,
} )
)
) ;
const options = getQueryOptions < LokiQuery > ( {
const options = getQueryOptions < LokiQuery > ( {
targets : [ { expr : '{job="gra\\fana"}' , refId : 'B' } ] ,
targets : [ { expr : '{job="gra\\fana"}' , refId : 'B' } ] ,
} ) ;
} ) ;
try {
observableTester ( ) . subscribeAndExpectOnError < DataQueryResponse > ( {
await ds . query ( options ) . toPromise ( ) ;
observable : ds.query ( options ) ,
} catch ( err ) {
expect : err = > {
expect ( err . data . message ) . toBe (
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://github.com/grafana/loki/blob/master/docs/logql.md.'
'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://github.com/grafana/loki/blob/master/docs/logql.md.'
) ;
) ;
}
} ,
done ,
} ) ;
fetchStream . error ( {
data : {
message : 'parse error at line 1, col 6: invalid char escape' ,
} ,
status : 400 ,
statusText : 'Bad Request' ,
} ) ;
} ) ;
} ) ;
} ) ;
} ) ;
describe ( 'When interpolating variables' , ( ) = > {
describe ( 'w hen interpolating variables' , ( ) = > {
let ds : LokiDatasource ;
let ds : LokiDatasource ;
let variable : CustomVariableModel ;
let variable : CustomVariableModel ;
beforeEach ( ( ) = > {
beforeEach ( ( ) = > {
const customData = { . . . ( instanceSettings . jsonData || { } ) , maxLines : 20 } ;
ds = createLokiDSForTests ( ) ;
const customSettings = { . . . instanceSettings , jsonData : customData } ;
ds = new LokiDatasource ( customSettings , templateSrvMock ) ;
variable = { . . . initialCustomVariableModelState } ;
variable = { . . . initialCustomVariableModelState } ;
} ) ;
} ) ;
@ -258,86 +300,82 @@ describe('LokiDatasource', () => {
} ) ;
} ) ;
describe ( 'when performing testDataSource' , ( ) = > {
describe ( 'when performing testDataSource' , ( ) = > {
let ds : DataSourceApi < any , any > ;
const getTestContext = ( ) = > {
let result : any ;
const ds = createLokiDSForTests ( { } as TemplateSrv ) ;
const promise = ds . testDatasource ( ) ;
return { promise } ;
} ;
describe ( 'and call succeeds' , ( ) = > {
describe ( 'and call succeeds' , ( ) = > {
beforeEach ( async ( ) = > {
it ( 'should return successfully' , async ( ) = > {
datasourceRequestMock . mockImplementation ( async ( ) = > {
const { promise } = getTestContext ( ) ;
return Promise . resolve ( {
status : 200 ,
fetchStream . next ( ( {
data : {
status : 200 ,
values : [ 'avalue' ] ,
data : {
} ,
values : [ 'avalue' ] ,
} ) ;
} ,
} ) ;
} as unknown ) as FetchResponse ) ;
ds = new LokiDatasource ( instanceSettings , { } as TemplateSrv ) ;
result = await ds . testDatasource ( ) ;
fetchStream . complete ( ) ;
} ) ;
const result = await promise ;
it ( 'should return successfully' , ( ) = > {
expect ( result . status ) . toBe ( 'success' ) ;
expect ( result . status ) . toBe ( 'success' ) ;
} ) ;
} ) ;
} ) ;
} ) ;
describe ( 'and call fails with 401 error' , ( ) = > {
describe ( 'and call fails with 401 error' , ( ) = > {
let ds : LokiDatasource ;
beforeEach ( ( ) = > {
datasourceRequestMock . mockImplementation ( ( ) = >
Promise . reject ( {
statusText : 'Unauthorized' ,
status : 401 ,
data : {
message : 'Unauthorized' ,
} ,
} )
) ;
const customData = { . . . ( instanceSettings . jsonData || { } ) , maxLines : 20 } ;
const customSettings = { . . . instanceSettings , jsonData : customData } ;
ds = new LokiDatasource ( customSettings , templateSrvMock ) ;
} ) ;
it ( 'should return error status and a detailed error message' , async ( ) = > {
it ( 'should return error status and a detailed error message' , async ( ) = > {
const result = await ds . testDatasource ( ) ;
const { promise } = getTestContext ( ) ;
fetchStream . error ( {
statusText : 'Unauthorized' ,
status : 401 ,
data : {
message : 'Unauthorized' ,
} ,
} ) ;
const result = await promise ;
expect ( result . status ) . toEqual ( 'error' ) ;
expect ( result . status ) . toEqual ( 'error' ) ;
expect ( result . message ) . toBe ( 'Loki: Unauthorized. 401. Unauthorized' ) ;
expect ( result . message ) . toBe ( 'Loki: Unauthorized. 401. Unauthorized' ) ;
} ) ;
} ) ;
} ) ;
} ) ;
describe ( 'and call fails with 404 error' , ( ) = > {
describe ( 'and call fails with 404 error' , ( ) = > {
beforeEach ( async ( ) = > {
it ( 'should return error status and a detailed error message' , async ( ) = > {
datasourceRequestMock . mockImplementation ( ( ) = >
const { promise } = getTestContext ( ) ;
Promise . reject ( {
statusText : 'Not found' ,
fetchStream . error ( {
status : 404 ,
statusText : 'Not found' ,
data : '404 page not found' ,
status : 404 ,
} )
data : {
) ;
message : '404 page not found' ,
ds = new LokiDatasource ( instanceSettings , { } as TemplateSrv ) ;
} ,
result = await ds . testDatasource ( ) ;
} ) ;
} ) ;
const result = await promise ;
it ( 'should return error status and a detailed error message' , ( ) = > {
expect ( result . status ) . toEqual ( 'error' ) ;
expect ( result . status ) . toEqual ( 'error' ) ;
expect ( result . message ) . toBe ( 'Loki: Not found. 404. 404 page not found' ) ;
expect ( result . message ) . toBe ( 'Loki: Not found. 404. 404 page not found' ) ;
} ) ;
} ) ;
} ) ;
} ) ;
describe ( 'and call fails with 502 error' , ( ) = > {
describe ( 'and call fails with 502 error' , ( ) = > {
beforeEach ( async ( ) = > {
it ( 'should return error status and a detailed error message' , async ( ) = > {
datasourceRequestMock . mockImplementation ( ( ) = >
const { promise } = getTestContext ( ) ;
Promise . reject ( {
statusText : 'Bad Gateway' ,
fetchStream . error ( {
status : 502 ,
statusText : 'Bad Gateway' ,
data : '' ,
status : 502 ,
} )
data : '' ,
) ;
} ) ;
ds = new LokiDatasource ( instanceSettings , { } as TemplateSrv ) ;
result = await ds . testDatasource ( ) ;
const result = await promise ;
} ) ;
it ( 'should return error status and a detailed error message' , ( ) = > {
expect ( result . status ) . toEqual ( 'error' ) ;
expect ( result . status ) . toEqual ( 'error' ) ;
expect ( result . message ) . toBe ( 'Loki: Bad Gateway. 502' ) ;
expect ( result . message ) . toBe ( 'Loki: Bad Gateway. 502' ) ;
} ) ;
} ) ;
@ -345,56 +383,63 @@ describe('LokiDatasource', () => {
} ) ;
} ) ;
describe ( 'when creating a range query' , ( ) = > {
describe ( 'when creating a range query' , ( ) = > {
const ds = new LokiDatasource ( instanceSettings , templateSrvMock ) ;
const query : LokiQuery = { expr : 'foo' , refId : 'bar' } ;
// Loki v1 API has an issue with float step parameters, can be removed when API is fixed
// Loki v1 API has an issue with float step parameters, can be removed when API is fixed
it ( 'should produce an integer step parameter' , ( ) = > {
it ( 'should produce an integer step parameter' , ( ) = > {
const ds = createLokiDSForTests ( ) ;
const query : LokiQuery = { expr : 'foo' , refId : 'bar' } ;
const range : TimeRange = {
const range : TimeRange = {
from : dateTime ( 0 ) ,
from : dateTime ( 0 ) ,
to : dateTime ( 1 e9 + 1 ) ,
to : dateTime ( 1 e9 + 1 ) ,
raw : { from : '0' , to : '1000000001' } ,
raw : { from : '0' , to : '1000000001' } ,
} ;
} ;
// Odd timerange/interval combination that would lead to a float step
// Odd timerange/interval combination that would lead to a float step
const options = { range , intervalMs : 2000 } ;
const options = { range , intervalMs : 2000 } ;
expect ( Number . isInteger ( ds . createRangeQuery ( query , options as any ) . step ! ) ) . toBeTruthy ( ) ;
expect ( Number . isInteger ( ds . createRangeQuery ( query , options as any ) . step ! ) ) . toBeTruthy ( ) ;
} ) ;
} ) ;
} ) ;
} ) ;
describe ( 'annotationQuery' , ( ) = > {
describe ( 'when calling annotationQuery' , ( ) = > {
const getTestContext = ( ) = > {
const query = makeAnnotationQueryRequest ( ) ;
const ds = createLokiDSForTests ( ) ;
const promise = ds . annotationQuery ( query ) ;
return { promise } ;
} ;
it ( 'should transform the loki data to annotation response' , async ( ) = > {
it ( 'should transform the loki data to annotation response' , async ( ) = > {
const ds = new LokiDatasource ( instanceSettings , templateSrvMock ) ;
const { promise } = getTestContext ( ) ;
datasourceRequestMock . mockImplementation (
const response : FetchResponse = ( {
jest . fn ( ) . mockReturnValueOnce (
data : {
Promise . resolve ( {
data : {
data : {
resultType : LokiResultType.Stream ,
data : {
result : [
resultType : LokiResultType.Stream ,
{
result : [
stream : {
{
label : 'value' ,
stream : {
label2 : 'value ' ,
label : 'value' ,
} ,
label2 : 'value ' ,
values : [ [ '1549016857498000000' , 'hello' ] ] ,
} ,
values : [ [ '1549016857498000000' , 'hello' ] ] ,
} ,
{
stream : {
label2 : 'value2' ,
} ,
values : [ [ '1549024057498000000' , 'hello 2' ] ] ,
} ,
] ,
} ,
} ,
status : 'success' ,
{
} ,
stream : {
} )
label2 : 'value2' ,
)
} ,
) ;
values : [ [ '1549024057498000000' , 'hello 2' ] ] ,
} ,
] ,
} ,
status : 'success' ,
} ,
} as unknown ) as FetchResponse ;
const query = makeAnnotationQueryRequest ( ) ;
fetchStream . next ( response ) ;
fetchStream . complete ( ) ;
const res = await promise ;
const res = await ds . annotationQuery ( query ) ;
expect ( res . length ) . toBe ( 2 ) ;
expect ( res . length ) . toBe ( 2 ) ;
expect ( res [ 0 ] . text ) . toBe ( 'hello' ) ;
expect ( res [ 0 ] . text ) . toBe ( 'hello' ) ;
expect ( res [ 0 ] . tags ) . toEqual ( [ 'value' ] ) ;
expect ( res [ 0 ] . tags ) . toEqual ( [ 'value' ] ) ;
@ -405,98 +450,72 @@ describe('LokiDatasource', () => {
} ) ;
} ) ;
describe ( 'metricFindQuery' , ( ) = > {
describe ( 'metricFindQuery' , ( ) = > {
const ds = new LokiDatasource ( instanceSettings , templateSrvMock ) ;
const getTestContext = ( mock : LokiDatasource ) = > {
const ds = createLokiDSForTests ( ) ;
ds . getVersion = mock . getVersion ;
ds . metadataRequest = mock . metadataRequest ;
return { ds } ;
} ;
const mocks = makeMetadataAndVersionsMocks ( ) ;
const mocks = makeMetadataAndVersionsMocks ( ) ;
mocks . forEach ( ( mock , index ) = > {
mocks . forEach ( ( mock , index ) = > {
it ( ` should return label names for Loki v ${ index } ` , async ( ) = > {
it ( ` should return label names for Loki v ${ index } ` , async ( ) = > {
ds . getVersion = mock . getVersion ;
const { ds } = getTestContext ( mock ) ;
ds . metadataRequest = mock . metadataRequest ;
const query = 'label_names()' ;
const res = await ds . metricFindQuery ( query ) ;
expect ( res [ 0 ] . text ) . toEqual ( 'label1' ) ;
expect ( res [ 1 ] . text ) . toEqual ( 'label2' ) ;
expect ( res . length ) . toBe ( 2 ) ;
} ) ;
} ) ;
mocks . forEach ( ( mock , index ) = > {
const res = await ds . metricFindQuery ( 'label_names()' ) ;
it ( ` should return label names for Loki v ${ index } ` , async ( ) = > {
ds . getVersion = mock . getVersion ;
expect ( res ) . toEqual ( [ { text : 'label1' } , { text : 'label2' } ] ) ;
ds . metadataRequest = mock . metadataRequest ;
const query = 'label_names()' ;
const res = await ds . metricFindQuery ( query ) ;
expect ( res [ 0 ] . text ) . toEqual ( 'label1' ) ;
expect ( res [ 1 ] . text ) . toEqual ( 'label2' ) ;
expect ( res . length ) . toBe ( 2 ) ;
} ) ;
} ) ;
} ) ;
} ) ;
mocks . forEach ( ( mock , index ) = > {
mocks . forEach ( ( mock , index ) = > {
it ( ` should return label values for Loki v ${ index } ` , async ( ) = > {
it ( ` should return label values for Loki v ${ index } ` , async ( ) = > {
ds . getVersion = mock . getVersion ;
const { ds } = getTestContext ( mock ) ;
ds . metadataRequest = mock . metadataRequest ;
const query = 'label_values(label1)' ;
const res = await ds . metricFindQuery ( 'label_values(label1)' ) ;
const res = await ds . metricFindQuery ( query ) ;
expect ( res [ 0 ] . text ) . toEqual ( 'value1' ) ;
expect ( res ) . toEqual ( [ { text : 'value1' } , { text : 'value2' } ] ) ;
expect ( res [ 1 ] . text ) . toEqual ( 'value2' ) ;
expect ( res . length ) . toBe ( 2 ) ;
} ) ;
} ) ;
} ) ;
} ) ;
mocks . forEach ( ( mock , index ) = > {
mocks . forEach ( ( mock , index ) = > {
it ( ` should return empty array when incorrect query for Loki v ${ index } ` , async ( ) = > {
it ( ` should return empty array when incorrect query for Loki v ${ index } ` , async ( ) = > {
ds . getVersion = mock . getVersion ;
const { ds } = getTestContext ( mock ) ;
ds . metadataRequest = mock . metadataRequest ;
const query = 'incorrect_query' ;
const res = await ds . metricFindQuery ( 'incorrect_query' ) ;
const res = await ds . metricFindQuery ( query ) ;
expect ( res . length ) . toBe ( 0 ) ;
expect ( res ) . toEqual ( [ ] ) ;
} ) ;
} ) ;
} ) ;
} ) ;
mocks . forEach ( ( mock , index ) = > {
mocks . forEach ( ( mock , index ) = > {
it ( ` should return label names according to provided rangefor Loki v ${ index } ` , async ( ) = > {
it ( ` should return label names according to provided rangefor Loki v ${ index } ` , async ( ) = > {
ds . getVersion = mock . getVersion ;
const { ds } = getTestContext ( mock ) ;
ds . metadataRequest = mock . metadataRequest ;
const query = 'label_names()' ;
const res = await ds . metricFindQuery ( 'label_names()' , { range : { from : new Date ( 2 ) , to : new Date ( 3 ) } } ) ;
const res = await ds . metricFindQuery ( query , {
range : { from : new Date ( 2 ) , to : new Date ( 3 ) } ,
expect ( res ) . toEqual ( [ { text : 'label1' } ] ) ;
} ) ;
expect ( res [ 0 ] . text ) . toEqual ( 'label1' ) ;
expect ( res . length ) . toBe ( 1 ) ;
} ) ;
} ) ;
} ) ;
} ) ;
} ) ;
} ) ;
} ) ;
} ) ;
type LimitTestArgs = {
function createLokiDSForTests (
maxDataPoints? : number ;
templateSrvMock = ( {
maxLines? : number ;
getAdhocFilters : ( ) : any [ ] = > [ ] ,
expectedLimit : number ;
replace : ( a : string ) = > a ,
} ;
} as unknown ) as TemplateSrv
function makeLimitTest ( instanceSettings : any , datasourceRequestMock : any , templateSrvMock : any , testResp : any ) {
) : LokiDatasource {
return ( { maxDataPoints , maxLines , expectedLimit } : LimitTestArgs ) = > {
const instanceSettings : any = {
let settings = instanceSettings ;
url : 'myloggingurl' ,
if ( Number . isFinite ( maxLines ! ) ) {
const customData = { . . . ( instanceSettings . jsonData || { } ) , maxLines : 20 } ;
settings = { . . . instanceSettings , jsonData : customData } ;
}
const ds = new LokiDatasource ( settings , templateSrvMock ) ;
datasourceRequestMock . mockImplementation ( ( ) = > Promise . resolve ( testResp ) ) ;
const options = getQueryOptions < LokiQuery > ( { targets : [ { expr : 'foo' , refId : 'B' , maxLines : maxDataPoints } ] } ) ;
if ( Number . isFinite ( maxDataPoints ! ) ) {
options . maxDataPoints = maxDataPoints ;
} else {
// By default is 500
delete options . maxDataPoints ;
}
ds . query ( options ) ;
expect ( datasourceRequestMock . mock . calls . length ) . toBe ( 2 ) ;
expect ( datasourceRequestMock . mock . calls [ 0 ] [ 0 ] . url ) . toContain ( ` limit= ${ expectedLimit } ` ) ;
} ;
} ;
const customData = { . . . ( instanceSettings . jsonData || { } ) , maxLines : 20 } ;
const customSettings = { . . . instanceSettings , jsonData : customData } ;
return new LokiDatasource ( customSettings , templateSrvMock ) ;
}
}
function makeAnnotationQueryRequest ( ) : AnnotationQueryRequest < LokiQuery > {
function makeAnnotationQueryRequest ( ) : AnnotationQueryRequest < LokiQuery > {