Files
skywalking-booster-ui/src/hooks/__tests__/useAssociateProcessor.spec.ts

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