mirror of
https://github.com/apache/skywalking-booster-ui.git
synced 2026-05-25 08:18:37 +00:00
542 lines
16 KiB
TypeScript
542 lines
16 KiB
TypeScript
/**
|
|
* Licensed to the Apache Software Foundation (ASF) under one or more
|
|
* contributor license agreements. See the NOTICE file distributed with
|
|
* this work for additional information regarding copyright ownership.
|
|
* The ASF licenses this file to You 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 { describe, it, expect, vi, beforeEach } from "vitest";
|
|
import useAssociateProcessor from "../useAssociateProcessor";
|
|
import type { EventParams } from "@/types/app";
|
|
import type { AssociateProcessorProps, FilterOption } from "@/types/dashboard";
|
|
|
|
// Mock the store
|
|
let mockAppStore: any;
|
|
vi.mock("@/store/modules/app", () => ({
|
|
useAppStoreWithOut: () => mockAppStore,
|
|
}));
|
|
|
|
// Mock utility functions
|
|
vi.mock("@/utils/dateFormat", () => ({
|
|
default: vi.fn((date: Date, step: string, monthDayDiff?: boolean) => {
|
|
if (step === "HOUR" && monthDayDiff) {
|
|
return "2023-01-01 12";
|
|
}
|
|
return "2023-01-01 12:00:00";
|
|
}),
|
|
}));
|
|
|
|
vi.mock("@/utils/localtime", () => ({
|
|
default: vi.fn((utc: boolean, date: Date) => new Date(date)),
|
|
}));
|
|
|
|
// Mock structuredClone
|
|
const structuredCloneMock = vi.fn((obj: any) => JSON.parse(JSON.stringify(obj)));
|
|
Object.defineProperty(window, "structuredClone", {
|
|
value: structuredCloneMock,
|
|
writable: true,
|
|
});
|
|
|
|
// Helper function to create mock legend options
|
|
const createMockLegendOptions = () => ({
|
|
show: false,
|
|
total: false,
|
|
min: false,
|
|
max: false,
|
|
mean: false,
|
|
asTable: false,
|
|
toTheRight: false,
|
|
width: 0,
|
|
asSelector: false,
|
|
});
|
|
|
|
describe("useAssociateProcessor", () => {
|
|
beforeEach(() => {
|
|
vi.clearAllMocks();
|
|
mockAppStore = {
|
|
utc: false,
|
|
intervalUnix: [1640995200000, 1640998800000, 1641002400000], // Sample timestamps
|
|
durationRow: { step: "HOUR" },
|
|
};
|
|
});
|
|
|
|
describe("eventAssociate", () => {
|
|
it("returns undefined when no filters provided", () => {
|
|
const mockProps: AssociateProcessorProps = {
|
|
filters: { dataIndex: 0, sourceId: "test" },
|
|
option: { series: [], type: "line", legend: createMockLegendOptions() },
|
|
relatedTrace: {
|
|
duration: { start: "0", end: "0", step: "HOUR" },
|
|
refIdType: "",
|
|
status: "",
|
|
queryOrder: "",
|
|
latency: false,
|
|
enableRelate: false,
|
|
},
|
|
};
|
|
const { eventAssociate } = useAssociateProcessor(mockProps);
|
|
const result = eventAssociate();
|
|
expect(result).toEqual({ series: [], type: "line", legend: createMockLegendOptions() });
|
|
});
|
|
|
|
it("returns option when no duration in filters", () => {
|
|
const option: FilterOption = {
|
|
series: [
|
|
{
|
|
name: "test",
|
|
data: [[1, 2]] as (number | string)[][],
|
|
},
|
|
],
|
|
type: "line",
|
|
legend: createMockLegendOptions(),
|
|
};
|
|
const mockProps: AssociateProcessorProps = {
|
|
filters: { dataIndex: 0, sourceId: "test" },
|
|
option,
|
|
relatedTrace: {
|
|
duration: { start: "0", end: "0", step: "HOUR" },
|
|
refIdType: "",
|
|
status: "",
|
|
queryOrder: "",
|
|
latency: false,
|
|
enableRelate: false,
|
|
},
|
|
};
|
|
const { eventAssociate } = useAssociateProcessor(mockProps);
|
|
const result = eventAssociate();
|
|
expect(result).toBe(option);
|
|
});
|
|
|
|
it("returns undefined when no series data", () => {
|
|
const option: FilterOption = { series: [], type: "line", legend: createMockLegendOptions() };
|
|
const mockProps: AssociateProcessorProps = {
|
|
filters: {
|
|
dataIndex: 0,
|
|
sourceId: "test",
|
|
duration: { startTime: "1000", endTime: "2000", step: "HOUR" },
|
|
},
|
|
option,
|
|
relatedTrace: {
|
|
duration: { start: "0", end: "0", step: "HOUR" },
|
|
refIdType: "",
|
|
status: "",
|
|
queryOrder: "",
|
|
latency: false,
|
|
enableRelate: false,
|
|
},
|
|
};
|
|
const { eventAssociate } = useAssociateProcessor(mockProps);
|
|
const result = eventAssociate();
|
|
expect(result).toBeUndefined();
|
|
});
|
|
|
|
it("returns undefined when endTime not in series data", () => {
|
|
const option: FilterOption = {
|
|
series: [
|
|
{
|
|
name: "test",
|
|
data: [
|
|
[1000, 1],
|
|
[1500, 2],
|
|
] as (number | string)[][],
|
|
},
|
|
],
|
|
type: "line",
|
|
legend: createMockLegendOptions(),
|
|
};
|
|
const mockProps: AssociateProcessorProps = {
|
|
filters: {
|
|
dataIndex: 0,
|
|
sourceId: "test",
|
|
duration: { startTime: "1000", endTime: "3000", step: "HOUR" },
|
|
},
|
|
option,
|
|
relatedTrace: {
|
|
duration: { start: "0", end: "0", step: "HOUR" },
|
|
refIdType: "",
|
|
status: "",
|
|
queryOrder: "",
|
|
latency: false,
|
|
enableRelate: false,
|
|
},
|
|
};
|
|
const { eventAssociate } = useAssociateProcessor(mockProps);
|
|
const result = eventAssociate();
|
|
expect(result).toBeUndefined();
|
|
});
|
|
|
|
it("adds markArea when endTime exists in series data", () => {
|
|
const option: FilterOption = {
|
|
series: [
|
|
{
|
|
name: "test",
|
|
data: [
|
|
["1000", 1],
|
|
["2000", 2],
|
|
["3000", 3],
|
|
] as (number | string)[][],
|
|
},
|
|
],
|
|
type: "line",
|
|
legend: createMockLegendOptions(),
|
|
};
|
|
const mockProps: AssociateProcessorProps = {
|
|
filters: {
|
|
dataIndex: 0,
|
|
sourceId: "test",
|
|
duration: { startTime: "1000", endTime: "2000", step: "HOUR" },
|
|
},
|
|
option,
|
|
relatedTrace: {
|
|
duration: { start: "0", end: "0", step: "HOUR" },
|
|
refIdType: "",
|
|
status: "",
|
|
queryOrder: "",
|
|
latency: false,
|
|
enableRelate: false,
|
|
},
|
|
};
|
|
const { eventAssociate } = useAssociateProcessor(mockProps);
|
|
const result = eventAssociate();
|
|
|
|
expect(result).toBeDefined();
|
|
expect(result?.series[0].markArea).toEqual({
|
|
silent: true,
|
|
itemStyle: { opacity: 0.3 },
|
|
data: [[{ xAxis: "1000" }, { xAxis: "2000" }]],
|
|
});
|
|
expect(structuredCloneMock).toHaveBeenCalledWith(option.series);
|
|
});
|
|
|
|
it("preserves other series properties when adding markArea", () => {
|
|
const option: FilterOption = {
|
|
series: [
|
|
{
|
|
name: "Series1",
|
|
data: [
|
|
["1000", 1],
|
|
["2000", 2],
|
|
] as (number | string)[][],
|
|
},
|
|
{
|
|
name: "Series2",
|
|
data: [
|
|
["1000", 3],
|
|
["2000", 4],
|
|
] as (number | string)[][],
|
|
},
|
|
],
|
|
type: "line",
|
|
legend: createMockLegendOptions(),
|
|
};
|
|
const mockProps: AssociateProcessorProps = {
|
|
filters: {
|
|
dataIndex: 0,
|
|
sourceId: "test",
|
|
duration: { startTime: "1000", endTime: "2000", step: "HOUR" },
|
|
},
|
|
option,
|
|
relatedTrace: {
|
|
duration: { start: "0", end: "0", step: "HOUR" },
|
|
refIdType: "",
|
|
status: "",
|
|
queryOrder: "",
|
|
latency: false,
|
|
enableRelate: false,
|
|
},
|
|
};
|
|
const { eventAssociate } = useAssociateProcessor(mockProps);
|
|
const result = eventAssociate();
|
|
|
|
expect(result?.series).toHaveLength(2);
|
|
expect(result?.series[0].name).toBe("Series1");
|
|
expect(result?.series[0].markArea).toBeDefined();
|
|
expect(result?.series[1].name).toBe("Series2");
|
|
expect(result?.series[1].markArea).toBeUndefined();
|
|
});
|
|
});
|
|
|
|
describe("traceFilters", () => {
|
|
it("returns undefined when no currentParams provided", () => {
|
|
const mockProps: AssociateProcessorProps = {
|
|
filters: { dataIndex: 0, sourceId: "test" },
|
|
option: { series: [], type: "line", legend: createMockLegendOptions() },
|
|
relatedTrace: {
|
|
duration: { start: "0", end: "0", step: "HOUR" },
|
|
refIdType: "",
|
|
status: "",
|
|
queryOrder: "",
|
|
latency: false,
|
|
enableRelate: false,
|
|
},
|
|
};
|
|
const { traceFilters } = useAssociateProcessor(mockProps);
|
|
const result = traceFilters(null);
|
|
expect(result).toBeUndefined();
|
|
});
|
|
|
|
it("returns object with undefined duration when no start time in intervalUnix", () => {
|
|
mockAppStore.intervalUnix = [];
|
|
const mockProps: AssociateProcessorProps = {
|
|
filters: { dataIndex: 0, sourceId: "test" },
|
|
option: { series: [], type: "line", legend: createMockLegendOptions() },
|
|
relatedTrace: {
|
|
duration: { start: "0", end: "0", step: "HOUR" },
|
|
refIdType: "",
|
|
status: "",
|
|
queryOrder: "",
|
|
latency: false,
|
|
enableRelate: false,
|
|
},
|
|
};
|
|
const { traceFilters } = useAssociateProcessor(mockProps);
|
|
const currentParams: EventParams = {
|
|
componentType: "chart",
|
|
seriesType: "line",
|
|
seriesIndex: 0,
|
|
seriesName: "test",
|
|
name: "test",
|
|
data: [1000, 1],
|
|
dataType: "number",
|
|
value: 1,
|
|
color: "#000",
|
|
event: {},
|
|
dataIndex: 0,
|
|
};
|
|
const result = traceFilters(currentParams);
|
|
expect(result).toBeDefined();
|
|
expect(result?.duration).toBeUndefined();
|
|
expect(result?.metricValue).toEqual([]);
|
|
});
|
|
|
|
it("returns trace filters with duration when start time exists", () => {
|
|
const mockProps: AssociateProcessorProps = {
|
|
filters: { dataIndex: 0, sourceId: "test" },
|
|
option: { series: [], type: "line", legend: createMockLegendOptions() },
|
|
relatedTrace: {
|
|
duration: { start: "0", end: "0", step: "HOUR" },
|
|
refIdType: "",
|
|
status: "",
|
|
queryOrder: "",
|
|
latency: false,
|
|
enableRelate: false,
|
|
},
|
|
};
|
|
const { traceFilters } = useAssociateProcessor(mockProps);
|
|
const currentParams: EventParams = {
|
|
componentType: "chart",
|
|
seriesType: "line",
|
|
seriesIndex: 0,
|
|
seriesName: "test",
|
|
name: "test",
|
|
data: [1000, 1],
|
|
dataType: "number",
|
|
value: 1,
|
|
color: "#000",
|
|
event: {},
|
|
dataIndex: 0,
|
|
};
|
|
const result = traceFilters(currentParams);
|
|
|
|
expect(result).toBeDefined();
|
|
expect(result?.duration).toEqual({
|
|
startTime: "2023-01-01 12",
|
|
endTime: "2023-01-01 12",
|
|
step: "HOUR",
|
|
});
|
|
expect(result?.queryOrder).toBe("");
|
|
expect(result?.status).toBe("");
|
|
});
|
|
|
|
it("includes relatedTrace properties when provided", () => {
|
|
const mockProps: AssociateProcessorProps = {
|
|
filters: { dataIndex: 0, sourceId: "test" },
|
|
option: { series: [], type: "line", legend: createMockLegendOptions() },
|
|
relatedTrace: {
|
|
duration: { start: "0", end: "0", step: "HOUR" },
|
|
refIdType: "",
|
|
status: "SUCCESS",
|
|
queryOrder: "BY_START_TIME",
|
|
latency: true,
|
|
enableRelate: true,
|
|
},
|
|
};
|
|
const { traceFilters } = useAssociateProcessor(mockProps);
|
|
const currentParams: EventParams = {
|
|
componentType: "chart",
|
|
seriesType: "line",
|
|
seriesIndex: 0,
|
|
seriesName: "test",
|
|
name: "test",
|
|
data: [1000, 1],
|
|
dataType: "number",
|
|
value: 1,
|
|
color: "#000",
|
|
event: {},
|
|
dataIndex: 0,
|
|
};
|
|
const result = traceFilters(currentParams);
|
|
|
|
expect(result?.status).toBe("SUCCESS");
|
|
expect(result?.queryOrder).toBe("BY_START_TIME");
|
|
});
|
|
|
|
it("generates latency list when latency is enabled", () => {
|
|
const option: FilterOption = {
|
|
series: [
|
|
{
|
|
name: "Service1",
|
|
data: [[1000, 100] as (number | string)[], [2000, 200] as (number | string)[]],
|
|
},
|
|
{
|
|
name: "Service2",
|
|
data: [[1000, 150] as (number | string)[], [2000, 250] as (number | string)[]],
|
|
},
|
|
],
|
|
type: "line",
|
|
legend: createMockLegendOptions(),
|
|
};
|
|
const mockProps: AssociateProcessorProps = {
|
|
filters: { dataIndex: 0, sourceId: "test" },
|
|
option,
|
|
relatedTrace: {
|
|
duration: { start: "0", end: "0", step: "HOUR" },
|
|
refIdType: "",
|
|
status: "",
|
|
queryOrder: "",
|
|
latency: true,
|
|
enableRelate: false,
|
|
},
|
|
};
|
|
const { traceFilters } = useAssociateProcessor(mockProps);
|
|
const currentParams: EventParams = {
|
|
componentType: "chart",
|
|
seriesType: "line",
|
|
seriesIndex: 0,
|
|
seriesName: "test",
|
|
name: "test",
|
|
data: [1000, 1],
|
|
dataType: "number",
|
|
value: 1,
|
|
color: "#000",
|
|
event: {},
|
|
dataIndex: 0,
|
|
};
|
|
const result = traceFilters(currentParams);
|
|
|
|
expect(result?.latency).toHaveLength(2);
|
|
expect(result?.latency[0]).toEqual({
|
|
label: "Service1--Service2",
|
|
value: "0",
|
|
data: [100, 150],
|
|
});
|
|
expect(result?.latency[1]).toEqual({
|
|
label: "Service2--Infinity",
|
|
value: "1",
|
|
data: [150, Infinity],
|
|
});
|
|
});
|
|
|
|
it("generates metricValue for all series", () => {
|
|
const option: FilterOption = {
|
|
series: [
|
|
{
|
|
name: "Service1",
|
|
data: [[1000, 100] as (number | string)[], [2000, 200] as (number | string)[]],
|
|
},
|
|
{
|
|
name: "Service2",
|
|
data: [[1000, 150] as (number | string)[], [2000, 250] as (number | string)[]],
|
|
},
|
|
],
|
|
type: "line",
|
|
legend: createMockLegendOptions(),
|
|
};
|
|
const mockProps: AssociateProcessorProps = {
|
|
filters: { dataIndex: 0, sourceId: "test" },
|
|
option,
|
|
relatedTrace: {
|
|
duration: { start: "0", end: "0", step: "HOUR" },
|
|
refIdType: "",
|
|
status: "",
|
|
queryOrder: "",
|
|
latency: false,
|
|
enableRelate: false,
|
|
},
|
|
};
|
|
const { traceFilters } = useAssociateProcessor(mockProps);
|
|
const currentParams: EventParams = {
|
|
componentType: "chart",
|
|
seriesType: "line",
|
|
seriesIndex: 0,
|
|
seriesName: "test",
|
|
name: "test",
|
|
data: [1000, 1],
|
|
dataType: "number",
|
|
value: 1,
|
|
color: "#000",
|
|
event: {},
|
|
dataIndex: 0,
|
|
};
|
|
const result = traceFilters(currentParams);
|
|
|
|
expect(result?.metricValue).toHaveLength(2);
|
|
expect(result?.metricValue[0]).toEqual({
|
|
label: "Service1",
|
|
value: "0",
|
|
data: 100,
|
|
date: 1000,
|
|
});
|
|
expect(result?.metricValue[1]).toEqual({
|
|
label: "Service2",
|
|
value: "1",
|
|
data: 150,
|
|
date: 1000,
|
|
});
|
|
});
|
|
|
|
it("handles empty series gracefully", () => {
|
|
const mockProps: AssociateProcessorProps = {
|
|
filters: { dataIndex: 0, sourceId: "test" },
|
|
option: { series: [], type: "line", legend: createMockLegendOptions() },
|
|
relatedTrace: {
|
|
duration: { start: "0", end: "0", step: "HOUR" },
|
|
refIdType: "",
|
|
status: "",
|
|
queryOrder: "",
|
|
latency: false,
|
|
enableRelate: false,
|
|
},
|
|
};
|
|
const { traceFilters } = useAssociateProcessor(mockProps);
|
|
const currentParams: EventParams = {
|
|
componentType: "chart",
|
|
seriesType: "line",
|
|
seriesIndex: 0,
|
|
seriesName: "test",
|
|
name: "test",
|
|
data: [1000, 1],
|
|
dataType: "number",
|
|
value: 1,
|
|
color: "#000",
|
|
event: {},
|
|
dataIndex: 0,
|
|
};
|
|
const result = traceFilters(currentParams);
|
|
|
|
expect(result?.metricValue).toEqual([]);
|
|
expect(result?.latency).toBeUndefined();
|
|
});
|
|
});
|
|
});
|