github.com/cockroachdb/cockroach@v20.2.0-alpha.1+incompatible/pkg/ui/src/views/shared/containers/metricDataProvider/metricDataProvider.spec.tsx (about) 1 // Copyright 2018 The Cockroach Authors. 2 // 3 // Use of this software is governed by the Business Source License 4 // included in the file licenses/BSL.txt. 5 // 6 // As of the Change Date specified in that file, in accordance with 7 // the Business Source License, use of this software will be governed 8 // by the Apache License, Version 2.0, included in the file 9 // licenses/APL.txt. 10 11 import { assert } from "chai"; 12 import { shallow } from "enzyme"; 13 import _ from "lodash"; 14 import Long from "long"; 15 import React, { Fragment } from "react"; 16 import * as sinon from "sinon"; 17 import "src/enzymeInit"; 18 import * as protos from "src/js/protos"; 19 import { MetricsQuery, requestMetrics } from "src/redux/metrics"; 20 import { Axis, Metric, MetricsDataComponentProps, QueryTimeInfo } from "src/views/shared/components/metricQuery"; 21 import { MetricsDataProviderUnconnected as MetricsDataProvider } from "src/views/shared/containers/metricDataProvider"; 22 23 // TextGraph is a proof-of-concept component used to demonstrate that 24 // MetricsDataProvider is working correctly. Used in tests. 25 class TextGraph extends React.Component<MetricsDataComponentProps, {}> { 26 render() { 27 return ( 28 <div> 29 { 30 (this.props.data && this.props.data.results) 31 ? this.props.data.results.join(":") 32 : "" 33 } 34 </div> 35 ); 36 } 37 } 38 39 function makeDataProvider( 40 id: string, 41 metrics: MetricsQuery, 42 timeInfo: QueryTimeInfo, 43 rm: typeof requestMetrics, 44 ) { 45 return shallow( 46 <MetricsDataProvider id={id} metrics={metrics} timeInfo={timeInfo} requestMetrics={rm}> 47 <TextGraph> 48 <Axis> 49 <Metric name="test.metric.1" /> 50 <Metric name="test.metric.2" /> 51 </Axis> 52 <Axis> 53 <Metric name="test.metric.3" /> 54 </Axis> 55 </TextGraph> 56 </MetricsDataProvider>, 57 ); 58 } 59 60 function makeMetricsRequest(timeInfo: QueryTimeInfo, sources?: string[]) { 61 return new protos.cockroach.ts.tspb.TimeSeriesQueryRequest({ 62 start_nanos: timeInfo.start, 63 end_nanos: timeInfo.end, 64 sample_nanos: timeInfo.sampleDuration, 65 queries: [ 66 { 67 name: "test.metric.1", 68 sources: sources, 69 downsampler: protos.cockroach.ts.tspb.TimeSeriesQueryAggregator.AVG, 70 source_aggregator: protos.cockroach.ts.tspb.TimeSeriesQueryAggregator.SUM, 71 derivative: protos.cockroach.ts.tspb.TimeSeriesQueryDerivative.NONE, 72 }, 73 { 74 name: "test.metric.2", 75 sources: sources, 76 downsampler: protos.cockroach.ts.tspb.TimeSeriesQueryAggregator.AVG, 77 source_aggregator: protos.cockroach.ts.tspb.TimeSeriesQueryAggregator.SUM, 78 derivative: protos.cockroach.ts.tspb.TimeSeriesQueryDerivative.NONE, 79 }, 80 { 81 name: "test.metric.3", 82 sources: sources, 83 downsampler: protos.cockroach.ts.tspb.TimeSeriesQueryAggregator.AVG, 84 source_aggregator: protos.cockroach.ts.tspb.TimeSeriesQueryAggregator.SUM, 85 derivative: protos.cockroach.ts.tspb.TimeSeriesQueryDerivative.NONE, 86 }, 87 ], 88 }); 89 } 90 91 function makeMetricsQuery(id: string, timeSpan: QueryTimeInfo, sources?: string[]): MetricsQuery { 92 const request = makeMetricsRequest(timeSpan, sources); 93 const data = new protos.cockroach.ts.tspb.TimeSeriesQueryResponse({ 94 results: _.map(request.queries, (q) => { 95 return { 96 query: q, 97 datapoints: [], 98 }; 99 }), 100 }); 101 return { 102 id, 103 request, 104 data, 105 nextRequest: request, 106 error: undefined, 107 }; 108 } 109 110 describe("<MetricsDataProvider>", function() { 111 let spy: sinon.SinonSpy; 112 const timespan1: QueryTimeInfo = { 113 start: Long.fromNumber(0), 114 end: Long.fromNumber(100), 115 sampleDuration: Long.fromNumber(300), 116 }; 117 const timespan2: QueryTimeInfo = { 118 start: Long.fromNumber(100), 119 end: Long.fromNumber(200), 120 sampleDuration: Long.fromNumber(300), 121 }; 122 const graphid = "testgraph"; 123 124 beforeEach(function() { 125 spy = sinon.spy(); 126 }); 127 128 describe("refresh", function() { 129 it("refreshes query data when mounted", function () { 130 makeDataProvider(graphid, null, timespan1, spy); 131 assert.isTrue(spy.called); 132 assert.isTrue(spy.calledWith(graphid, makeMetricsRequest(timespan1))); 133 }); 134 135 it("does nothing when mounted if current request fulfilled", function () { 136 makeDataProvider(graphid, makeMetricsQuery(graphid, timespan1), timespan1, spy); 137 assert.isTrue(spy.notCalled); 138 }); 139 140 it("does nothing when mounted if current request is in flight", function () { 141 const query = makeMetricsQuery(graphid, timespan1); 142 query.request = null; 143 query.data = null; 144 makeDataProvider(graphid, query, timespan1, spy); 145 assert.isTrue(spy.notCalled); 146 }); 147 148 it("refreshes query data when receiving props", function () { 149 const provider = makeDataProvider(graphid, makeMetricsQuery(graphid, timespan1), timespan1, spy); 150 assert.isTrue(spy.notCalled); 151 provider.setProps({ 152 metrics: undefined, 153 }); 154 assert.isTrue(spy.called); 155 assert.isTrue(spy.calledWith(graphid, makeMetricsRequest(timespan1))); 156 }); 157 158 it("refreshes if timespan changes", function () { 159 const provider = makeDataProvider(graphid, makeMetricsQuery(graphid, timespan1), timespan1, spy); 160 assert.isTrue(spy.notCalled); 161 provider.setProps({ 162 timeInfo: timespan2, 163 }); 164 assert.isTrue(spy.called); 165 assert.isTrue(spy.calledWith(graphid, makeMetricsRequest(timespan2))); 166 }); 167 168 it("refreshes if query changes", function () { 169 const provider = makeDataProvider(graphid, makeMetricsQuery(graphid, timespan1), timespan1, spy); 170 assert.isTrue(spy.notCalled); 171 // Modify "sources" parameter. 172 provider.setProps({ 173 metrics: makeMetricsQuery(graphid, timespan1, ["1"]), 174 }); 175 assert.isTrue(spy.called); 176 assert.isTrue(spy.calledWith(graphid, makeMetricsRequest(timespan1))); 177 }); 178 179 }); 180 181 describe("attach", function() { 182 it("attaches metrics data to contained component", function() { 183 const provider = makeDataProvider(graphid, makeMetricsQuery(graphid, timespan1), timespan1, spy); 184 const props: any = provider.first().props(); 185 assert.isDefined(props.data); 186 assert.deepEqual(props.data, makeMetricsQuery(graphid, timespan1).data); 187 }); 188 189 it("attaches metrics data if timespan doesn't match", function() { 190 const provider = makeDataProvider(graphid, makeMetricsQuery(graphid, timespan1), timespan2, spy); 191 const props: any = provider.first().props(); 192 assert.isDefined(props.data); 193 assert.deepEqual(props.data, makeMetricsQuery(graphid, timespan1).data); 194 }); 195 196 it("does not attach metrics data if query doesn't match", function() { 197 const provider = makeDataProvider(graphid, makeMetricsQuery(graphid, timespan1, ["1"]), timespan1, spy); 198 const props: any = provider.first().props(); 199 assert.isUndefined(props.data); 200 }); 201 202 it("throws error if it contains multiple graph components", function() { 203 try { 204 shallow( 205 <MetricsDataProvider 206 id="id" 207 metrics={null} 208 timeInfo={timespan1} 209 requestMetrics={spy}> 210 <Fragment> 211 <TextGraph> 212 <Axis> 213 <Metric name="test.metrics.1" /> 214 </Axis> 215 </TextGraph> 216 <TextGraph> 217 <Axis> 218 <Metric name="test.metrics.2" /> 219 </Axis> 220 </TextGraph> 221 </Fragment> 222 </MetricsDataProvider>, 223 ); 224 assert.fail("expected error from MetricsDataProvider"); 225 } catch (e) { 226 // assert.match(e, /Invariant Violation/); 227 } 228 }); 229 }); 230 });