/**
 * Copyright 2019, OpenCensus Authors
 *
 * Licensed under the Apache License, Version 2.0 (the "License")
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */ import { CoreTracer , logger , Span , SpanEventListener , SpanKind , } from '@opencensus/core' ; import * as assert from 'assert' ; import * as redis from 'redis' ; import { plugin } from '../src' ; /** Collects ended root spans to allow for later analysis. */ class SpanVerifier implements SpanEventListener { endedSpans : Span [ ] = [ ] ; onStartSpan ( span ) { return ; } onEndSpan ( span ) { this . endedSpans . push ( span ) ; } } param rootSpanVerifier param expectedName param expectedKind function assertSpan ( rootSpanVerifier , expectedName , expectedKind , verifyAttribute ? ) { assert . strictEqual ( rootSpanVerifier . endedSpans . length , 2 ) ; assert . strictEqual ( rootSpanVerifier . endedSpans [ 1 ] . spans . length , 1 ) ; assert . strictEqual ( rootSpanVerifier . endedSpans [ 1 ] . spans [ 0 ] . name , expectedName ) ; assert . strictEqual ( rootSpanVerifier . endedSpans [ 1 ] . spans [ 0 ] . kind , expectedKind ) ; if ( typeof verifyAttribute === 'function' ) { for ( const span of rootSpanVerifier . endedSpans [ 1 ] . spans ) { assert ( verifyAttribute ( span ) , 'failed to verify attribute' ) ; } } } describe ( 'RedisPlugin' , ( ) => { const OPENCENSUS_REDIS_TESTS = process . env . OPENCENSUS_REDIS_TESTS ; const OPENCENSUS_REDIS_HOST = process . env . OPENCENSUS_REDIS_HOST ; const shouldTest = OPENCENSUS_REDIS_TESTS === '1' ; if ( ! shouldTest ) { console . log ( 'Skipping test-redis. Run REDIS to test' ) ; } const URL = `redis://${ OPENCENSUS_REDIS_HOST || 'localhost' }:6379` ; const VERSION = '2.8.0' ; const REDIS_QUERY_TYPE = SpanKind . CLIENT ; const tracer = new CoreTracer ( ) ; const rootSpanVerifier = new SpanVerifier ( ) ; let client ; before ( done => { tracer . start ( { samplingRate : 1 , logger : logger . logger ( 1 ) } ) ; tracer . registerSpanEventListener ( rootSpanVerifier ) ; plugin . enable ( redis , tracer , VERSION , { } , '' ) ; client = redis . createClient ( { url : URL } ) ; client . on ( 'error' , err => { done ( err ) ; } ) ; client . on ( 'ready' , done ) ; } ) ; beforeEach ( function redisBeforeEach ( done ) { if ( ! shouldTest ) { this . skip ( ) ; } rootSpanVerifier . endedSpans = [ ] ; client . set ( 'test' , 'data' , done ) ; } ) ; afterEach ( done => { client . del ( 'hash' , done ) ; } ) ; after ( ( ) => { if ( client ) { client . quit ( ) ; } } ) ; /** Should intercept query */ describe ( 'Instrumenting query operations' , ( ) => { it ( 'should create a child span for hset' , done => { tracer . startRootSpan ( { name : 'insertRootSpan' } , ( rootSpan ) => { client . hset ( 'hash' , 'random' , 'random' , ( err , result ) => { assert . ifError ( err ) ; assert . strictEqual ( rootSpanVerifier . endedSpans . length , 1 ) ; rootSpan . end ( ) ; assertSpan ( rootSpanVerifier , `redis-hset` , REDIS_QUERY_TYPE , span => { return span . attributes . arguments === undefined ; } ) ; done ( ) ; } ) ; } ) ; } ) ; it ( 'should create a child span for get' , done => { tracer . startRootSpan ( { name : 'getRootSpan' } , ( rootSpan ) => { client . get ( 'test' , ( err , result ) => { assert . ifError ( err ) ; assert . strictEqual ( result , 'data' ) ; assert . strictEqual ( rootSpanVerifier . endedSpans . length , 1 ) ; rootSpan . end ( ) ; assertSpan ( rootSpanVerifier , `redis-get` , REDIS_QUERY_TYPE , span => { return span . attributes . arguments === undefined ; } ) ; done ( ) ; } ) ; } ) ; } ) ; it ( 'should create a child span for del' , done => { tracer . startRootSpan ( { name : 'removeRootSpan' } , ( rootSpan ) => { client . del ( 'test' , ( err , result ) => { assert . ifError ( err ) ; assert . strictEqual ( rootSpanVerifier . endedSpans . length , 1 ) ; rootSpan . end ( ) ; assertSpan ( rootSpanVerifier , `redis-del` , REDIS_QUERY_TYPE , span => { return span . attributes . arguments === undefined ; } ) ; done ( ) ; } ) ; } ) ; } ) ; } ) ; /** Should intercept command */ describe ( 'Removing Instrumentation' , ( ) => { before ( ( ) => { plugin . applyUnpatch ( ) ; } ) ; it ( 'should not create a child span for insert' , done => { tracer . startRootSpan ( { name : 'insertRootSpan' } , ( rootSpan ) => { client . hset ( 'hash' , 'random' , 'random' , ( err , result ) => { assert . ifError ( err ) ; assert . strictEqual ( rootSpanVerifier . endedSpans . length , 0 ) ; rootSpan . end ( ) ; assert . strictEqual ( rootSpanVerifier . endedSpans . length , 1 ) ; assert . strictEqual ( rootSpanVerifier . endedSpans [ 0 ] . spans . length , 0 ) ; done ( ) ; } ) ; } ) ; } ) ; it ( 'should not create a child span for get' , done => { tracer . startRootSpan ( { name : 'getRootSpan' } , ( rootSpan ) => { client . get ( 'test' , ( err , result ) => { assert . ifError ( err ) ; assert . strictEqual ( result , 'data' ) ; rootSpan . end ( ) ; assert . strictEqual ( rootSpanVerifier . endedSpans . length , 1 ) ; assert . strictEqual ( rootSpanVerifier . endedSpans [ 0 ] . spans . length , 0 ) ; done ( ) ; } ) ; } ) ; } ) ; it ( 'should not create a child span for del' , done => { tracer . startRootSpan ( { name : 'removeRootSpan' } , ( rootSpan ) => { client . del ( 'test' , ( err , result ) => { assert . ifError ( err ) ; assert . strictEqual ( rootSpanVerifier . endedSpans . length , 0 ) ; rootSpan . end ( ) ; assert . strictEqual ( rootSpanVerifier . endedSpans . length , 1 ) ; assert . strictEqual ( rootSpanVerifier . endedSpans [ 0 ] . spans . length , 0 ) ; done ( ) ; } ) ; } ) ; } ) ; } ) ; } ) ;