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  });