Files
openobserve/web/src/composables/dashboard/usePanelDataLoader.spec.ts
T
6df43be42f feat: live chart preview in the dashboard panel editor (#15214)
Co-authored-by: Abhay Padamani <141122205+ktx-abhay@users.noreply.github.com>
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: ktx-mihir <mihir.patel@kiara.tech>
2026-10-13 06:25:12 +00:00

3326 lines
95 KiB
TypeScript

import { describe, expect, it, beforeEach, afterEach, vi } from "vitest";
import { onMounted, ref } from "vue";
import { flushPromises } from "@vue/test-utils";
import { usePanelDataLoader } from "./usePanelDataLoader";
import queryService from "../../services/search";
import config from "@/aws-exports";
const RealAbortController = globalThis.AbortController;
/**
* Lets the NEXT usePanelDataLoader() install its visibility observer.
*
* onMounted is stubbed to a no-op for this whole file, which leaves a loader
* permanently invisible — and every load, cache restore included, is gated on
* visibility. The composable registers the observer hook before the one that
* auto-loads, so running only the first keeps the "no automatic loadData"
* property these tests rely on.
*/
const allowPanelToBecomeVisible = () => {
vi.mocked(onMounted).mockImplementationOnce((cb: any) => cb());
};
// Mock all dependencies with enhanced functionality
let mockSearchResults: any = [];
let mockPartitionResults: any = {
data: { partitions: [], max_query_range: 0 },
};
let mockMetricsResults: any = { data: { status: "success" } };
let shouldSearchThrow = false;
let shouldPartitionThrow = false;
let shouldMetricsThrow = false;
vi.mock("../../services/search", async (importOriginal) => {
const { overlayServiceMock } = await import("@/test/unit/helpers/mockService");
return overlayServiceMock(await importOriginal(), {
default: {
partition: vi.fn().mockImplementation(() => {
if (shouldPartitionThrow) {
return Promise.reject(new Error("Partition service error"));
}
return Promise.resolve(mockPartitionResults);
}),
search: vi.fn().mockImplementation(() => {
if (shouldSearchThrow) {
return Promise.reject(new Error("Search service error"));
}
return Promise.resolve({ data: mockSearchResults });
}),
metrics_query_range: vi.fn().mockImplementation(() => {
if (shouldMetricsThrow) {
return Promise.reject(new Error("Metrics service error"));
}
return Promise.resolve(mockMetricsResults);
}),
delete_running_queries: vi.fn().mockResolvedValue({ status: "success" }),
},
});
});
vi.mock("vuex", () => ({
useStore: () => ({
state: {
selectedOrganization: { identifier: "test-org" },
zoConfig: { sql_base64_enabled: false },
organizationData: {
organizationSettings: { scrape_interval: 15 },
},
},
}),
}));
vi.mock("@/utils/query/promQLUtils", () => ({
addLabelToPromQlQuery: vi.fn((query) => query),
}));
vi.mock("@/utils/query/sqlUtils", () => ({
addLabelsToSQlQuery: vi.fn((query) => Promise.resolve(query)),
getStreamFromQuery: vi.fn(() => Promise.resolve("test_stream")),
}));
vi.mock("@/utils/dashboard/variables/variablesUtils", async (importOriginal) => ({
...(await importOriginal<typeof import("@/utils/dashboard/variables/variablesUtils")>()),
formatInterval: vi.fn(() => ({ value: 5, unit: "m" })),
formatRateInterval: vi.fn(() => "5m"),
getTimeInSecondsBasedOnUnit: vi.fn(() => 300),
}));
vi.mock("@/utils/zincutils", () => ({
b64EncodeUnicode: vi.fn((str) => btoa(str)),
generateTraceContext: vi.fn(() => ({
traceparent: "test-trace",
traceId: "test-trace-id",
})),
isWebSocketEnabled: vi.fn(() => false),
isStreamingEnabled: vi.fn(() => false),
escapeSingleQuotes: vi.fn((str) => str.replace(/\\/g, "\\\\").replace(/'/g, "\\'")),
useLocalWrapContent: vi.fn(() => null),
}));
// Enhanced Panel Cache mocking
let mockCacheData: any = null;
let shouldCacheThrow = false;
let cacheOperationCount = 0;
vi.mock("./usePanelCache", () => ({
usePanelCache: vi.fn(() => ({
getPanelCache: vi.fn().mockImplementation(() => {
cacheOperationCount++;
if (shouldCacheThrow) {
return Promise.reject(new Error("Cache retrieval error"));
}
return Promise.resolve(mockCacheData);
}),
savePanelCache: vi.fn().mockImplementation((key, data, timeRange) => {
cacheOperationCount++;
if (shouldCacheThrow) {
return Promise.reject(new Error("Cache save error"));
}
mockCacheData = { key, value: data, cacheTimeRange: timeRange };
return Promise.resolve();
}),
})),
}));
vi.mock("@/utils/dashboard/dateTimeUtils", () => ({
convertOffsetToSeconds: vi.fn(() => ({ seconds: 3600, periodAsStr: "1h" })),
}));
// Enhanced WebSocket mocking
let shouldWebSocketThrow = false;
vi.mock("@/composables/useSearchWebSocket", () => ({
default: () => ({
fetchQueryDataWithWebSocket: vi.fn().mockImplementation((payload, callbacks) => {
if (shouldWebSocketThrow) {
callbacks?.error?.(payload, { message: "WebSocket error" });
return "error-trace-id";
}
// Simulate successful connection
setTimeout(() => {
callbacks?.open?.(payload);
callbacks?.message?.(payload, {
content: { results: { hits: [] } },
type: "search_response",
});
callbacks?.close?.(payload, { code: 1000 });
}, 10);
return "test-trace-id";
}),
sendSearchMessageBasedOnRequestId: vi.fn(),
cancelSearchQueryBasedOnRequestId: vi.fn(),
cleanUpListeners: vi.fn(),
}),
}));
// Enhanced Annotations mocking
let mockAnnotations: any[] = [];
let shouldAnnotationsThrow = false;
vi.mock("./useAnnotations", () => ({
useAnnotations: vi.fn(() => ({
refreshAnnotations: vi.fn().mockImplementation(() => {
if (shouldAnnotationsThrow) {
return Promise.reject(new Error("Annotations error"));
}
return Promise.resolve(mockAnnotations);
}),
})),
}));
// Enhanced HTTP Streaming mocking
let shouldStreamingThrow = false;
let streamOverride: ((payload: any, callbacks: any) => any) | null = null;
vi.mock("../useStreamingSearch", () => ({
default: () => ({
fetchQueryDataWithHttpStream: vi.fn().mockImplementation((payload, callbacks) => {
if (streamOverride) return streamOverride(payload, callbacks);
if (shouldStreamingThrow) {
callbacks?.error?.(payload, { message: "Streaming error" });
return "error-stream-id";
}
// Simulate streaming response
setTimeout(() => {
callbacks?.data?.(payload, {
content: { results: { hits: [] } },
type: "search_response",
});
callbacks?.complete?.(payload, { status: "complete" });
}, 10);
return "test-stream-id";
}),
cancelStreamQueryBasedOnRequestId: vi.fn(),
closeStreamWithError: vi.fn(),
closeStream: vi.fn(),
resetAuthToken: vi.fn(),
}),
}));
// Enhanced config change detection
let shouldRequireApiCall = true;
vi.mock("@/utils/dashboard/checkConfigChangeApiCall", () => ({
checkIfConfigChangeRequiredApiCallOrNot: vi.fn(() => shouldRequireApiCall),
}));
// Mock logsUtils
vi.mock("@/composables/useLogs/logsUtils", () => ({
default: () => ({
checkTimestampAlias: vi.fn(() => true), // Always return true to allow queries
}),
}));
// Mock Vue's onMounted and onUnmounted to avoid warnings
vi.mock("vue", async () => {
const actual = await vi.importActual("vue");
return {
...actual,
onMounted: vi.fn(() => {}), // Don't execute callback to avoid loadData being called automatically
onUnmounted: vi.fn((cb) => cb?.()),
};
});
// Test Helper Functions
const createMockPanelSchema = (overrides: any = {}) =>
ref({
id: "test-panel",
title: "Test Panel",
type: "line",
queries: [
{
query: "SELECT * FROM test",
fields: { stream_type: "logs" },
config: {},
...overrides.queryConfig,
},
],
...overrides,
});
const createMockSelectedTimeObj = (overrides: any = {}) =>
ref({
start_time: Date.now() - 3600000, // 1 hour ago
end_time: Date.now(),
type: "relative",
period: { value: 1, unit: "h" },
...overrides,
});
const createMockVariablesData = (overrides: any = {}) =>
ref({
isVariablesLoading: false,
values: [],
...overrides,
});
// Helper functions to mock critical internal functions
const mockIfPanelVariablesCompletedLoading = vi.fn(() => true); // Always return true for tests
const mockRestoreFromCache = vi.fn(() => Promise.resolve(false)); // Always return false for tests (no cache restore)
const resetAllMocks = () => {
// Reset service mocks
mockSearchResults = [];
mockPartitionResults = { data: { partitions: [], max_query_range: 0 } };
mockMetricsResults = { data: { status: "success" } };
shouldSearchThrow = false;
shouldPartitionThrow = false;
shouldMetricsThrow = false;
// Reset WebSocket mocks
shouldWebSocketThrow = false;
// Reset streaming mocks
shouldStreamingThrow = false;
streamOverride = null;
// Reset cache mocks
mockCacheData = null;
shouldCacheThrow = false;
cacheOperationCount = 0;
// Reset annotations
mockAnnotations = [];
shouldAnnotationsThrow = false;
// Reset config
shouldRequireApiCall = true;
// Reset helper function mocks
mockIfPanelVariablesCompletedLoading.mockReturnValue(true);
mockRestoreFromCache.mockReturnValue(Promise.resolve(false));
};
describe("usePanelDataLoader", () => {
let consoleErrorSpy: any;
let windowAddEventListenerSpy: any;
let windowRemoveEventListenerSpy: any;
beforeEach(() => {
resetAllMocks();
vi.clearAllMocks();
consoleErrorSpy = vi.spyOn(console, "error").mockImplementation(() => {});
windowAddEventListenerSpy = vi.spyOn(window, "addEventListener").mockImplementation(() => {});
windowRemoveEventListenerSpy = vi
.spyOn(window, "removeEventListener")
.mockImplementation(() => {});
// Mock IntersectionObserver for visibility detection.
// Must be a real (constructible) function — an arrow implementation throws
// "is not a constructor" at `new IntersectionObserver(...)`, which would
// leave every panel permanently invisible.
global.IntersectionObserver = vi.fn(function (this: any, callback: any) {
this.observe = vi.fn(() => {
// Immediately trigger visibility
callback([{ isIntersecting: true }]);
});
this.unobserve = vi.fn();
this.disconnect = vi.fn();
}) as any;
// Mock setTimeout to work with promise-based async flows
vi.spyOn(global, "setTimeout").mockImplementation((fn: any) => {
if (typeof fn === "function") {
// Execute immediately for tests
Promise.resolve().then(() => fn());
}
return 1 as any;
});
// Mock clearTimeout
vi.spyOn(global, "clearTimeout").mockImplementation(() => {});
// Mock AbortController
global.AbortController = vi.fn(function () {
return {
signal: {
addEventListener: vi.fn(),
removeEventListener: vi.fn(),
aborted: false,
},
abort: vi.fn(),
};
}) as any;
// Mock window.addEventListener and removeEventListener
vi.spyOn(window, "addEventListener").mockImplementation(() => {});
vi.spyOn(window, "removeEventListener").mockImplementation(() => {});
});
afterEach(() => {
consoleErrorSpy.mockRestore();
windowAddEventListenerSpy.mockRestore();
windowRemoveEventListenerSpy.mockRestore();
vi.restoreAllMocks();
});
describe("basic functionality", () => {
it("should initialize with correct state", () => {
const panelSchema = ref({
id: "test-panel",
title: "Test Panel",
type: "line",
queryType: "sql",
queries: [
{
query: "SELECT * FROM test",
fields: { stream_type: "logs", x: [{ alias: "timestamp" }] },
config: {},
},
],
config: { step_value: "60s" },
});
const selectedTimeObj = ref({
start_time: new Date(Date.now() - 3600000),
end_time: new Date(),
});
const variablesData = ref({ values: [] });
const chartPanelRef = ref({ offsetWidth: 1000 });
const forceLoad = ref(false);
const searchType = ref("dashboards");
const dashboardId = ref("test-dashboard");
const folderId = ref("test-folder");
const reportId = ref(null);
const loader = usePanelDataLoader(
panelSchema,
selectedTimeObj,
variablesData,
chartPanelRef,
forceLoad,
searchType,
dashboardId,
folderId,
reportId,
ref(null), // runId
ref(null), // tabId
ref(null), // tabName
ref(null), // searchResponse
ref(false), // is_ui_histogram
);
expect(loader).toBeDefined();
expect(loader.data.value).toEqual([]);
expect(loader.loading.value).toBe(false);
expect(loader.errorDetail.value).toEqual({ message: "", code: "" });
expect(loader.metadata.value).toEqual({ queries: [] });
expect(loader.annotations.value).toEqual([]);
});
it("should handle minimal configuration", () => {
const panelSchema = ref({
id: "test-panel",
queries: [],
});
const selectedTimeObj = ref({});
const variablesData = ref({});
const chartPanelRef = ref(null);
const forceLoad = ref(false);
const searchType = ref("dashboards");
const dashboardId = ref("test-dashboard");
const folderId = ref("test-folder");
const reportId = ref(null);
const loader = usePanelDataLoader(
panelSchema,
selectedTimeObj,
variablesData,
chartPanelRef,
forceLoad,
searchType,
dashboardId,
folderId,
reportId,
ref(null), // runId
ref(null), // tabId
ref(null), // tabName
ref(null), // searchResponse
ref(false), // is_ui_histogram
);
expect(loader).toBeDefined();
expect(loader.loading.value).toBe(false);
});
it("should handle loadData with no queries", async () => {
const panelSchema = ref({
id: "test-panel",
queries: [],
});
const selectedTimeObj = ref({
start_time: new Date(Date.now() - 3600000),
end_time: new Date(),
});
const variablesData = ref({ values: [] });
const chartPanelRef = ref({ offsetWidth: 1000 });
const forceLoad = ref(false);
const searchType = ref("dashboards");
const dashboardId = ref("test-dashboard");
const folderId = ref("test-folder");
const reportId = ref(null);
const loader = usePanelDataLoader(
panelSchema,
selectedTimeObj,
variablesData,
chartPanelRef,
forceLoad,
searchType,
dashboardId,
folderId,
reportId,
ref(null), // runId
ref(null), // tabId
ref(null), // tabName
ref(null), // searchResponse
ref(false), // is_ui_histogram
);
await loader.loadData();
expect(loader.loading.value).toBe(false);
expect(loader.data.value).toEqual([]);
});
it("should handle loadData with empty query", async () => {
const panelSchema = ref({
id: "test-panel",
queries: [{ query: "" }],
});
const selectedTimeObj = ref({
start_time: new Date(Date.now() - 3600000),
end_time: new Date(),
});
const variablesData = ref({ values: [] });
const chartPanelRef = ref({ offsetWidth: 1000 });
const forceLoad = ref(false);
const searchType = ref("dashboards");
const dashboardId = ref("test-dashboard");
const folderId = ref("test-folder");
const reportId = ref(null);
const loader = usePanelDataLoader(
panelSchema,
selectedTimeObj,
variablesData,
chartPanelRef,
forceLoad,
searchType,
dashboardId,
folderId,
reportId,
ref(null), // runId
ref(null), // tabId
ref(null), // tabName
ref(null), // searchResponse
ref(false), // is_ui_histogram
);
await loader.loadData();
expect(loader.loading.value).toBe(false);
expect(loader.data.value).toEqual([]);
});
it("renders injected PromQL data instead of firing a query", async () => {
const panelSchema = ref({
id: "test-panel",
queryType: "promql",
queries: [{ query: "up" }],
});
const selectedTimeObj = ref({
start_time: new Date(Date.now() - 3600000),
end_time: new Date(),
});
const variablesData = ref({ values: [] });
const chartPanelRef = ref({ offsetWidth: 1000 });
const injected = ref({
data: [{ result: [{ values: [[1, "1"]] }] }],
metadata: { queries: [{ startTime: 1000, endTime: 2000 }] },
resultMetaData: [],
});
const loader = usePanelDataLoader(
panelSchema,
selectedTimeObj,
variablesData,
chartPanelRef,
ref(false), // forceLoad
ref("dashboards"), // searchType
ref("d"), // dashboardId
ref("f"), // folderId
ref(null), // reportId
ref(null), // runId
ref(null), // tabId
ref(null), // tabName
ref(null), // searchResponse
ref(false), // is_ui_histogram
ref(null), // dashboardName
ref(null), // folderName
ref(false), // shouldRefreshWithoutCache
ref(undefined), // regionClusterParams
ref(true), // allowAnnotationsAPI
injected, // injectedPromqlData
);
await loader.loadData();
// The already-fetched results are rendered directly; no executor runs.
expect(loader.loading.value).toBe(false);
expect(loader.data.value).toEqual(injected.value.data);
expect(loader.metadata.value).toEqual(injected.value.metadata);
});
it("should handle invalid timestamps", () => {
const panelSchema = ref({
id: "test-panel",
queries: [{ query: "SELECT * FROM test" }],
});
const selectedTimeObj = ref({
start_time: new Date("Invalid"),
end_time: new Date("Invalid"),
});
const variablesData = ref({ values: [] });
const chartPanelRef = ref({ offsetWidth: 1000 });
const forceLoad = ref(false);
const searchType = ref("dashboards");
const dashboardId = ref("test-dashboard");
const folderId = ref("test-folder");
const reportId = ref(null);
const loader = usePanelDataLoader(
panelSchema,
selectedTimeObj,
variablesData,
chartPanelRef,
forceLoad,
searchType,
dashboardId,
folderId,
reportId,
ref(null), // runId
ref(null), // tabId
ref(null), // tabName
ref(null), // searchResponse
ref(false), // is_ui_histogram
);
// Test that the loader was created successfully
// The actual invalid timestamp handling is tested through the internal logic
expect(loader).toBeDefined();
expect(loader.loading.value).toBe(false);
});
});
describe("chart types", () => {
const chartTypes = [
"area",
"area-stacked",
"bar",
"h-bar",
"line",
"scatter",
"stacked",
"h-stacked",
"table",
];
chartTypes.forEach((type) => {
it(`should handle ${type} chart type`, () => {
const panelSchema = ref({
id: "test-panel",
type: type,
queries: [
{
query: "SELECT * FROM test",
fields: { stream_type: "logs", x: [{ alias: "timestamp" }] },
},
],
});
const loader = usePanelDataLoader(
panelSchema,
ref({}),
ref({}),
ref(null),
ref(false),
ref("dashboards"),
ref("test-dashboard"),
ref("test-folder"),
ref(null),
);
expect(loader).toBeDefined();
});
});
});
describe("query types", () => {
it("should handle SQL query type", () => {
const panelSchema = ref({
id: "test-panel",
queryType: "sql",
queries: [
{
query: "SELECT * FROM logs",
fields: { stream_type: "logs" },
},
],
});
const loader = usePanelDataLoader(
panelSchema,
ref({}),
ref({}),
ref(null),
ref(false),
ref("dashboards"),
ref("test-dashboard"),
ref("test-folder"),
ref(null),
);
expect(loader).toBeDefined();
});
it("should handle PromQL query type", () => {
const panelSchema = ref({
id: "test-panel",
queryType: "promql",
queries: [
{
query: "rate(http_requests_total[5m])",
},
],
config: { step_value: "60s" },
});
const loader = usePanelDataLoader(
panelSchema,
ref({}),
ref({}),
ref(null),
ref(false),
ref("dashboards"),
ref("test-dashboard"),
ref("test-folder"),
ref(null),
);
expect(loader).toBeDefined();
});
});
describe("variables handling", () => {
it("should handle empty variables", () => {
const variablesData = ref({ values: [] });
const loader = usePanelDataLoader(
ref({ id: "test", queries: [] }),
ref({}),
variablesData,
ref(null),
ref(false),
ref("dashboards"),
ref("test-dashboard"),
ref("test-folder"),
ref(null),
);
expect(loader).toBeDefined();
});
it("should handle constant variables", () => {
const variablesData = ref({
values: [
{
name: "var1",
type: "constant",
value: "test_value",
},
],
});
const loader = usePanelDataLoader(
ref({ id: "test", queries: [] }),
ref({}),
variablesData,
ref(null),
ref(false),
ref("dashboards"),
ref("test-dashboard"),
ref("test-folder"),
ref(null),
);
expect(loader).toBeDefined();
});
it("should handle dynamic filter variables", () => {
const variablesData = ref({
values: [
{
name: "filters",
type: "dynamic_filters",
value: [
{
operator: "=",
name: "field1",
value: "value1",
},
],
},
],
});
const loader = usePanelDataLoader(
ref({ id: "test", queries: [] }),
ref({}),
variablesData,
ref(null),
ref(false),
ref("dashboards"),
ref("test-dashboard"),
ref("test-folder"),
ref(null),
);
expect(loader).toBeDefined();
});
it("should handle loading variables", () => {
const variablesData = ref({
values: [
{
name: "var1",
type: "constant",
value: null,
isLoading: true,
},
],
});
const loader = usePanelDataLoader(
ref({ id: "test", queries: [] }),
ref({}),
variablesData,
ref(null),
ref(false),
ref("dashboards"),
ref("test-dashboard"),
ref("test-folder"),
ref(null),
);
expect(loader).toBeDefined();
});
});
describe("error handling", () => {
it("should initialize with no error", () => {
const loader = usePanelDataLoader(
ref({ id: "test", queries: [] }),
ref({}),
ref({}),
ref(null),
ref(false),
ref("dashboards"),
ref("test-dashboard"),
ref("test-folder"),
ref(null),
);
expect(loader.errorDetail.value).toEqual({ message: "", code: "" });
});
});
describe("loading states", () => {
it("should initialize loading states correctly", () => {
const loader = usePanelDataLoader(
ref({ id: "test", queries: [] }),
ref({}),
ref({}),
ref(null),
ref(false),
ref("dashboards"),
ref("test-dashboard"),
ref("test-folder"),
ref(null),
);
expect(loader.loading.value).toBe(false);
expect(loader.loadingTotal.value).toBe(0);
expect(loader.loadingCompleted.value).toBe(0);
expect(loader.loadingProgressPercentage.value).toBe(0);
// isPartialData might be true initially due to onMounted loadData call, this is expected behavior
expect(typeof loader.isPartialData.value).toBe("boolean");
expect(loader.isOperationCancelled.value).toBe(false);
});
});
describe("cache behavior", () => {
it("should handle cache operations", () => {
const loader = usePanelDataLoader(
ref({ id: "test", queries: [] }),
ref({}),
ref({}),
ref(null),
ref(false),
ref("dashboards"),
ref("test-dashboard"),
ref("test-folder"),
ref(null),
);
expect(loader.isCachedDataDifferWithCurrentTimeRange.value).toBe(false);
});
});
describe("annotations", () => {
it("should initialize annotations as empty", () => {
const loader = usePanelDataLoader(
ref({ id: "test", queries: [] }),
ref({}),
ref({}),
ref(null),
ref(false),
ref("dashboards"),
ref("test-dashboard"),
ref("test-folder"),
ref(null),
);
expect(loader.annotations.value).toEqual([]);
});
});
describe("trace IDs", () => {
it("should initialize trace IDs as empty", () => {
const loader = usePanelDataLoader(
ref({ id: "test", queries: [] }),
ref({}),
ref({}),
ref(null),
ref(false),
ref("dashboards"),
ref("test-dashboard"),
ref("test-folder"),
ref(null),
);
expect(loader.searchRequestTraceIds.value).toEqual([]);
});
});
describe("metadata", () => {
it("should initialize metadata correctly", () => {
const loader = usePanelDataLoader(
ref({ id: "test", queries: [] }),
ref({}),
ref({}),
ref(null),
ref(false),
ref("dashboards"),
ref("test-dashboard"),
ref("test-folder"),
ref(null),
);
expect(loader.metadata.value).toEqual({ queries: [] });
expect(loader.resultMetaData.value).toEqual([]);
expect(loader.lastTriggeredAt.value).toBe(null);
});
});
describe("force load", () => {
it("should handle force load enabled", () => {
const loader = usePanelDataLoader(
ref({ id: "test", queries: [] }),
ref({}),
ref({}),
ref(null),
ref(true), // forceLoad = true
ref("dashboards"),
ref("test-dashboard"),
ref("test-folder"),
ref(null),
);
expect(loader).toBeDefined();
});
});
describe("optional parameters", () => {
it("should handle all optional parameters", () => {
const loader = usePanelDataLoader(
ref({ id: "test", queries: [] }),
ref({}),
ref({}),
ref(null),
ref(false),
ref("dashboards"),
ref("test-dashboard"),
ref("test-folder"),
ref("test-report"),
ref("test-run"),
ref("test-tab"),
ref("Test Tab"),
ref({ hits: [] }),
ref(true),
);
expect(loader).toBeDefined();
});
it("should handle missing optional parameters", () => {
const loader = usePanelDataLoader(
ref({ id: "test", queries: [] }),
ref({}),
ref({}),
ref(null),
ref(false),
ref("dashboards"),
ref("test-dashboard"),
ref("test-folder"),
ref(null),
);
expect(loader).toBeDefined();
});
});
describe("search response", () => {
it("should handle existing search response", () => {
const searchResponse = ref({
hits: [{ field1: "value1" }],
total: 1,
});
const loader = usePanelDataLoader(
ref({ id: "test", queries: [] }),
ref({}),
ref({}),
ref(null),
ref(false),
ref("dashboards"),
ref("test-dashboard"),
ref("test-folder"),
ref(null),
ref(null),
ref(null),
ref(null),
searchResponse,
ref(false),
);
expect(loader).toBeDefined();
});
});
describe("time shift configuration", () => {
it("should handle queries with time shift", () => {
const panelSchema = ref({
id: "test-panel",
queries: [
{
query: "SELECT * FROM test",
config: {
time_shift: [{ offSet: "1h" }, { offSet: "2h" }],
},
fields: { stream_type: "logs" },
},
],
});
const loader = usePanelDataLoader(
panelSchema,
ref({}),
ref({}),
ref(null),
ref(false),
ref("dashboards"),
ref("test-dashboard"),
ref("test-folder"),
ref(null),
);
expect(loader).toBeDefined();
});
it("should handle queries without time shift", () => {
const panelSchema = ref({
id: "test-panel",
queries: [
{
query: "SELECT * FROM test",
config: {},
fields: { stream_type: "logs" },
},
],
});
const loader = usePanelDataLoader(
panelSchema,
ref({}),
ref({}),
ref(null),
ref(false),
ref("dashboards"),
ref("test-dashboard"),
ref("test-folder"),
ref(null),
);
expect(loader).toBeDefined();
});
});
// ==== CORE FUNCTION COVERAGE - DATA LOADING & QUERY EXECUTION ====
describe("Data Loading and Query Execution", () => {
describe("loadData main function", () => {
it("should execute loadData successfully with valid data", async () => {
const panelSchema = createMockPanelSchema();
const selectedTimeObj = createMockSelectedTimeObj();
const variablesData = createMockVariablesData();
mockSearchResults = {
hits: [{ field1: "value1", field2: "value2" }],
total: 1,
took: 10,
};
const loader = usePanelDataLoader(
panelSchema,
selectedTimeObj,
variablesData,
ref(null),
ref(true), // Set forceLoad to true to skip visibility waiting
ref("dashboards"),
ref("test-dashboard"),
ref("test-folder"),
ref(null),
ref(null),
ref(null),
ref(null),
ref(null),
ref(false),
);
await loader.loadData();
expect(loader.data.value).toBeDefined();
expect(loader.loading.value).toBe(false);
});
it("should handle loadData with no queries gracefully", async () => {
const panelSchema = createMockPanelSchema({ queries: [] });
const selectedTimeObj = createMockSelectedTimeObj();
const variablesData = createMockVariablesData();
const loader = usePanelDataLoader(
panelSchema,
selectedTimeObj,
variablesData,
ref(null),
ref(true), // Set forceLoad to true to skip visibility waiting
ref("dashboards"),
ref("test-dashboard"),
ref("test-folder"),
ref(null),
);
await loader.loadData();
expect(loader.data.value).toEqual([]);
expect(loader.loading.value).toBe(false);
});
it("should set loading state during data loading", async () => {
const panelSchema = createMockPanelSchema();
const selectedTimeObj = createMockSelectedTimeObj();
const variablesData = createMockVariablesData();
mockSearchResults = { hits: [], total: 0 };
const loader = usePanelDataLoader(
panelSchema,
selectedTimeObj,
variablesData,
ref(null),
ref(true), // Set forceLoad to true to skip visibility waiting
ref("dashboards"),
ref("test-dashboard"),
ref("test-folder"),
ref(null),
);
// Start the load operation
const loadPromise = loader.loadData();
// Check loading state immediately (should be true if set synchronously)
// Or we can just verify the final state is false
await loadPromise;
expect(loader.loading.value).toBe(false);
});
it("should handle WebSocket enabled data loading", async () => {
const panelSchema = createMockPanelSchema();
const selectedTimeObj = createMockSelectedTimeObj();
const variablesData = createMockVariablesData();
// Mock WebSocket enabled
const { isWebSocketEnabled } = await import("@/utils/zincutils");
(isWebSocketEnabled as any).mockReturnValue(true);
mockSearchResults = { hits: [{ test: "data" }], total: 1 };
const loader = usePanelDataLoader(
panelSchema,
selectedTimeObj,
variablesData,
ref(null),
ref(true), // Set forceLoad to true to skip visibility waiting
ref("dashboards"),
ref("test-dashboard"),
ref("test-folder"),
ref(null),
);
await loader.loadData();
expect(loader.data.value).toBeDefined();
});
it("should handle streaming enabled data loading", async () => {
const panelSchema = createMockPanelSchema();
const selectedTimeObj = createMockSelectedTimeObj();
const variablesData = createMockVariablesData();
// Mock streaming enabled, WebSocket disabled
const { isWebSocketEnabled, isStreamingEnabled } = await import("@/utils/zincutils");
(isWebSocketEnabled as any).mockReturnValue(false);
(isStreamingEnabled as any).mockReturnValue(true);
mockSearchResults = { hits: [{ test: "streaming_data" }], total: 1 };
const loader = usePanelDataLoader(
panelSchema,
selectedTimeObj,
variablesData,
ref(null),
ref(true), // Set forceLoad to true to skip visibility waiting
ref("dashboards"),
ref("test-dashboard"),
ref("test-folder"),
ref(null),
);
await loader.loadData();
expect(loader.data.value).toBeDefined();
});
it("should handle PromQL query type", async () => {
const panelSchema = createMockPanelSchema({
queries: [
{
query: "up",
fields: { stream_type: "metrics" },
config: {},
},
],
});
const selectedTimeObj = createMockSelectedTimeObj();
const variablesData = createMockVariablesData();
mockMetricsResults = {
data: {
status: "success",
data: {
resultType: "vector",
result: [{ metric: { __name__: "up" }, value: [1234567890, "1"] }],
},
},
};
const loader = usePanelDataLoader(
panelSchema,
selectedTimeObj,
variablesData,
ref(null),
ref(true), // Set forceLoad to true to skip visibility waiting
ref("dashboards"),
ref("test-dashboard"),
ref("test-folder"),
ref(null),
);
await loader.loadData();
expect(loader.data.value).toBeDefined();
});
it("should handle partition-based loading", async () => {
const panelSchema = createMockPanelSchema();
const selectedTimeObj = createMockSelectedTimeObj();
const variablesData = createMockVariablesData();
mockPartitionResults = {
data: {
partitions: [
[Date.now() - 3600000, Date.now() - 1800000],
[Date.now() - 1800000, Date.now()],
],
max_query_range: 7200,
order_by: "asc",
histogram_interval: 300,
},
};
mockSearchResults = [{ timestamp: Date.now(), value: 100 }];
const loader = usePanelDataLoader(
panelSchema,
selectedTimeObj,
variablesData,
ref(null),
ref(true), // Set forceLoad to true to skip visibility waiting
ref("dashboards"),
ref("test-dashboard"),
ref("test-folder"),
ref(null),
);
await loader.loadData();
expect(loader.data.value).toBeDefined();
// Partition loading may result in empty data if partitions don't contain hits
expect(Array.isArray(loader.data.value)).toBe(true);
});
it("should handle histogram queries", async () => {
const panelSchema = createMockPanelSchema({
type: "histogram",
});
const selectedTimeObj = createMockSelectedTimeObj();
const variablesData = createMockVariablesData();
mockSearchResults = {
hits: [
{
zo_sql_key: "2023-01-01T00:00:00Z",
zo_sql_num: 10,
},
],
total: 1,
histogram_interval: 60,
};
const loader = usePanelDataLoader(
panelSchema,
selectedTimeObj,
variablesData,
ref(null),
ref(true), // Set forceLoad to true to skip visibility waiting
ref("dashboards"),
ref("test-dashboard"),
ref("test-folder"),
ref(null),
);
await loader.loadData();
expect(loader.data.value).toBeDefined();
});
it("should handle time shift queries", async () => {
const panelSchema = createMockPanelSchema({
queries: [
{
query: "SELECT * FROM test",
config: {
time_shift: [{ offSet: "1h" }, { offSet: "2h" }],
},
fields: { stream_type: "logs" },
},
],
});
const selectedTimeObj = createMockSelectedTimeObj();
const variablesData = createMockVariablesData();
mockSearchResults = { hits: [{ test: "shifted_data" }], total: 1 };
const loader = usePanelDataLoader(
panelSchema,
selectedTimeObj,
variablesData,
ref(null),
ref(true), // Set forceLoad to true to skip visibility waiting
ref("dashboards"),
ref("test-dashboard"),
ref("test-folder"),
ref(null),
);
await loader.loadData();
expect(loader.data.value).toBeDefined();
});
it("should handle search errors gracefully", async () => {
const panelSchema = createMockPanelSchema();
const selectedTimeObj = createMockSelectedTimeObj();
const variablesData = createMockVariablesData();
shouldSearchThrow = true;
const loader = usePanelDataLoader(
panelSchema,
selectedTimeObj,
variablesData,
ref(null),
ref(true), // Set forceLoad to true to skip visibility waiting
ref("dashboards"),
ref("test-dashboard"),
ref("test-folder"),
ref(null),
);
await loader.loadData();
// Check that some error state was set (either errorDetail has content or data is empty)
expect(loader.errorDetail.value.message.length > 0 || loader.data.value.length === 0).toBe(
true,
);
});
});
describe("WebSocket data loading", () => {
it("should handle WebSocket connection", async () => {
const panelSchema = createMockPanelSchema();
const selectedTimeObj = createMockSelectedTimeObj();
const variablesData = createMockVariablesData();
// Enable WebSocket
const { isWebSocketEnabled, isStreamingEnabled } = await import("@/utils/zincutils");
(isWebSocketEnabled as any).mockReturnValue(true);
(isStreamingEnabled as any).mockReturnValue(false);
const loader = usePanelDataLoader(
panelSchema,
selectedTimeObj,
variablesData,
ref(null),
ref(true), // Set forceLoad to true to skip visibility waiting
ref("dashboards"),
ref("test-dashboard"),
ref("test-folder"),
ref(null),
);
await loader.loadData();
expect(loader.data.value).toBeDefined();
});
it("should handle WebSocket errors", async () => {
const panelSchema = createMockPanelSchema();
const selectedTimeObj = createMockSelectedTimeObj();
const variablesData = createMockVariablesData();
// Enable WebSocket with error
const { isWebSocketEnabled, isStreamingEnabled } = await import("@/utils/zincutils");
(isWebSocketEnabled as any).mockReturnValue(true);
(isStreamingEnabled as any).mockReturnValue(false);
shouldWebSocketThrow = true;
const loader = usePanelDataLoader(
panelSchema,
selectedTimeObj,
variablesData,
ref(null),
ref(true), // Set forceLoad to true to skip visibility waiting
ref("dashboards"),
ref("test-dashboard"),
ref("test-folder"),
ref(null),
);
await loader.loadData();
expect(loader.errorDetail.value.message).toBeDefined();
});
});
describe("HTTP Streaming data loading", () => {
it("should handle HTTP streaming connection", async () => {
const panelSchema = createMockPanelSchema();
const selectedTimeObj = createMockSelectedTimeObj();
const variablesData = createMockVariablesData();
// Enable streaming, disable WebSocket
const { isWebSocketEnabled, isStreamingEnabled } = await import("@/utils/zincutils");
(isWebSocketEnabled as any).mockReturnValue(false);
(isStreamingEnabled as any).mockReturnValue(true);
const loader = usePanelDataLoader(
panelSchema,
selectedTimeObj,
variablesData,
ref(null),
ref(true), // Set forceLoad to true to skip visibility waiting
ref("dashboards"),
ref("test-dashboard"),
ref("test-folder"),
ref(null),
);
await loader.loadData();
expect(loader.data.value).toBeDefined();
});
it("should handle streaming errors", async () => {
const panelSchema = createMockPanelSchema();
const selectedTimeObj = createMockSelectedTimeObj();
const variablesData = createMockVariablesData();
// Enable streaming with error
const { isWebSocketEnabled, isStreamingEnabled } = await import("@/utils/zincutils");
(isWebSocketEnabled as any).mockReturnValue(false);
(isStreamingEnabled as any).mockReturnValue(true);
shouldStreamingThrow = true;
const loader = usePanelDataLoader(
panelSchema,
selectedTimeObj,
variablesData,
ref(null),
ref(true), // Set forceLoad to true to skip visibility waiting
ref("dashboards"),
ref("test-dashboard"),
ref("test-folder"),
ref(null),
);
await loader.loadData();
expect(loader.errorDetail.value.message).toBeDefined();
});
});
});
describe("superseded runs", () => {
const supersedeRun = async (edition: { isEnterprise: string; isCloud: string }) => {
const original = { isEnterprise: config.isEnterprise, isCloud: config.isCloud };
Object.assign(config, edition);
const streams: any[] = [];
streamOverride = (payload, callbacks) => {
streams.push({ payload, callbacks });
return "held-stream";
};
try {
const loader = usePanelDataLoader(
createMockPanelSchema(),
createMockSelectedTimeObj({
start_time: new Date(Date.now() - 3600000),
end_time: new Date(),
}),
createMockVariablesData(),
ref(null),
ref(true),
ref("dashboards"),
ref("test-dashboard"),
ref("test-folder"),
ref(null),
ref(null),
ref(null),
ref(null),
ref(null),
ref(false),
);
await loader.loadData();
const traceId = streams[0].payload.traceId;
expect(loader.searchRequestTraceIds.value).toContain(traceId);
vi.mocked(queryService.delete_running_queries).mockClear();
await loader.loadData();
return { streams, traceId };
} finally {
Object.assign(config, original);
}
};
it("cancels the superseded run's search on the server before starting the next", async () => {
const { streams, traceId } = await supersedeRun({ isEnterprise: "true", isCloud: "false" });
expect(queryService.delete_running_queries).toHaveBeenCalledWith("test-org", [traceId]);
expect(streams).toHaveLength(2);
});
it("skips the server-side cancel on OSS, which has no query manager route", async () => {
const { streams } = await supersedeRun({ isEnterprise: "false", isCloud: "false" });
expect(queryService.delete_running_queries).not.toHaveBeenCalled();
expect(streams).toHaveLength(2);
});
it("a stream completing while the rerun is debounced writes nothing", async () => {
const streams: any[] = [];
streamOverride = (payload, callbacks) => {
streams.push({ payload, callbacks });
return "held-stream";
};
const loader = usePanelDataLoader(
createMockPanelSchema(),
createMockSelectedTimeObj({
start_time: new Date(Date.now() - 3600000),
end_time: new Date(),
}),
createMockVariablesData(),
ref(null),
ref(true),
ref("dashboards"),
ref("test-dashboard"),
ref("test-folder"),
ref(null),
ref(null),
ref(null),
ref(null),
ref(null),
ref(false),
);
await loader.loadData();
expect(streams).toHaveLength(1);
const cacheWritesBefore = cacheOperationCount;
const rerun = loader.loadData();
const [{ payload, callbacks }] = streams;
callbacks.data(payload, {
type: "search_response_hits",
content: { results: { hits: [{ value: "stale" }] } },
});
callbacks.data(payload, { type: "end" });
await callbacks.complete(payload, { type: "end" });
expect(JSON.stringify(loader.data.value)).not.toContain("stale");
expect(loader.loading.value).toBe(true);
expect(cacheOperationCount).toBe(cacheWritesBefore);
await rerun;
});
});
describe("first load waiting on a multi-select child variable", () => {
it("sends one query when the child resolves to ALL, not one per watcher", async () => {
global.AbortController = RealAbortController;
const streams: any[] = [];
streamOverride = (payload) => {
streams.push(payload);
return `stream-${streams.length}`;
};
const ctr = {
name: "ctr",
type: "query_values",
multiSelect: true,
value: [],
isVariablePartialLoaded: false,
};
const variablesData = createMockVariablesData({ values: [ctr] });
const loader = usePanelDataLoader(
createMockPanelSchema({
queryConfig: { query: "SELECT * FROM t WHERE c IN ($ctr)" },
}),
createMockSelectedTimeObj({
start_time: new Date(Date.now() - 3600000),
end_time: new Date(),
}),
variablesData,
ref(null),
ref(true),
ref("dashboards"),
ref("test-dashboard"),
ref("test-folder"),
ref(null),
ref(null),
ref(null),
ref(null),
ref(null),
ref(false),
);
// The mount run waits for ctr, which has never loaded.
const mountRun = loader.loadData();
await flushPromises();
expect(streams).toHaveLength(0);
variablesData.value = {
isVariablesLoading: false,
values: [{ ...ctr, value: ["_o2_all_"], isVariablePartialLoaded: true }],
};
await flushPromises();
await mountRun;
await flushPromises();
expect(streams).toHaveLength(1);
});
});
// ==== CACHE FUNCTIONALITY COVERAGE ====
describe("Cache Functionality", () => {
describe("restoreFromCache", () => {
it("should restore data from cache when available", async () => {
const panelSchema = createMockPanelSchema();
const selectedTimeObj = createMockSelectedTimeObj();
const variablesData = createMockVariablesData();
// Set up mock cache data
mockCacheData = {
key: {
panelSchema: panelSchema.value,
variablesData: [],
forceLoad: false,
dashboardId: "test-dashboard",
folderId: "test-folder",
},
value: {
data: [{ test: "cached_data" }],
loading: false,
errorDetail: { message: "", code: "" },
metadata: { queries: [] },
annotations: [],
resultMetaData: [],
lastTriggeredAt: Date.now(),
isPartialData: false,
isOperationCancelled: false,
},
cacheTimeRange: {
start_time: Date.now() - 3600000,
end_time: Date.now(),
},
};
allowPanelToBecomeVisible();
const loader = usePanelDataLoader(
panelSchema,
selectedTimeObj,
variablesData,
ref({ offsetWidth: 1000 }),
ref(false),
ref("dashboards"),
ref("test-dashboard"),
ref("test-folder"),
ref(null),
ref(null), // runId
ref(null), // tabId
ref(null), // tabName
ref(null), // searchResponse
ref(false), // is_ui_histogram
);
await loader.loadData();
expect(loader.data.value).toBeDefined();
});
it("should handle cache with different time ranges", async () => {
const panelSchema = createMockPanelSchema();
const selectedTimeObj = ref({
start_time: new Date(Date.now() - 7200000), // 2 hours ago
end_time: new Date(),
});
const variablesData = createMockVariablesData();
// Set up mock cache data with different time range
mockCacheData = {
key: {
panelSchema: panelSchema.value,
variablesData: [],
forceLoad: false,
dashboardId: "test-dashboard",
folderId: "test-folder",
},
value: {
data: [{ test: "cached_data" }],
loading: false,
errorDetail: { message: "", code: "" },
metadata: { queries: [] },
annotations: [],
resultMetaData: [],
lastTriggeredAt: Date.now(),
isPartialData: false,
isOperationCancelled: false,
},
cacheTimeRange: {
start_time: Date.now() - 3600000, // Different range
end_time: Date.now(),
},
};
allowPanelToBecomeVisible();
const loader = usePanelDataLoader(
panelSchema,
selectedTimeObj,
variablesData,
ref({ offsetWidth: 1000 }),
ref(false),
ref("dashboards"),
ref("test-dashboard"),
ref("test-folder"),
ref(null),
ref(null), // runId
ref(null), // tabId
ref(null), // tabName
ref(null), // searchResponse
ref(false), // is_ui_histogram
);
await loader.loadData();
expect(loader.isCachedDataDifferWithCurrentTimeRange.value).toBeDefined();
});
it("should handle cache retrieval errors", async () => {
const panelSchema = createMockPanelSchema();
const selectedTimeObj = createMockSelectedTimeObj();
const variablesData = createMockVariablesData();
shouldCacheThrow = true;
const loader = usePanelDataLoader(
panelSchema,
selectedTimeObj,
variablesData,
ref({ offsetWidth: 1000 }),
ref(true), // Force load to skip cache restore
ref("dashboards"),
ref("test-dashboard"),
ref("test-folder"),
ref(null),
);
await loader.loadData();
expect(loader.data.value).toBeDefined();
});
it("should skip cache when forceLoad=true and runCount=0", async () => {
const panelSchema = createMockPanelSchema();
const selectedTimeObj = createMockSelectedTimeObj();
const variablesData = createMockVariablesData();
mockSearchResults = { hits: [{ test: "data" }], total: 1 };
const loader = usePanelDataLoader(
panelSchema,
selectedTimeObj,
variablesData,
ref({ offsetWidth: 1000 }),
ref(true),
ref("dashboards"),
ref("test-dashboard"),
ref("test-folder"),
ref(null),
ref(null), // runId
ref(null), // tabId
ref(null), // tabName
ref(null), // searchResponse
ref(false), // is_ui_histogram
);
await loader.loadData();
// With forceLoad=true and runCount=0, cache restore is skipped (line 800)
// Cache operations should be 0 since we skip the cache block entirely
expect(cacheOperationCount).toBe(0);
// Data should still be loaded (not from cache)
expect(loader.data.value).toBeDefined();
});
it("should attempt cache restore when forceLoad=false and runCount=0", async () => {
const panelSchema = createMockPanelSchema();
const selectedTimeObj = createMockSelectedTimeObj();
const variablesData = createMockVariablesData();
mockSearchResults = { hits: [{ test: "data" }], total: 1 };
// Set mock cache data (note: actual restore may fail due to cache key mismatch,
// but we're testing that getPanelCache is called)
mockCacheData = {
key: {}, // Simplified key - in real code this would be more complex
value: {
data: [{ test: "cached data" }],
metadata: { queries: [] },
errorDetail: { message: "", code: "" },
resultMetaData: [],
isPartialData: false,
isOperationCancelled: false,
loading: false,
annotations: [],
lastTriggeredAt: Date.now(),
},
cacheTimeRange: {
start_time: Date.now() - 3600000,
end_time: Date.now(),
},
};
allowPanelToBecomeVisible();
const loader = usePanelDataLoader(
panelSchema,
selectedTimeObj,
variablesData,
ref({ offsetWidth: 1000 }),
ref(false), // forceLoad = false
ref("dashboards"),
ref("test-dashboard"),
ref("test-folder"),
ref(null),
ref(null), // runId
ref(null), // tabId
ref(null), // tabName
ref(null), // searchResponse
ref(false), // is_ui_histogram
);
// Start loadData without waiting (the cache read happens once the panel
// is reported visible)
loader.loadData();
// Give it a moment to attempt cache restore. setTimeout is stubbed to a
// microtask here, so drain a few rather than waiting on the clock.
for (let i = 0; i < 20; i++) await Promise.resolve();
// With forceLoad=false and runCount=0, cache restore is attempted
// Cache operation count should be > 0 because getPanelCache was called
expect(cacheOperationCount).toBeGreaterThan(0);
// Note: The actual cache data restoration may not occur due to cache key mismatch,
// but the important thing is that the code path entered the cache restoration logic
// (i.e., getPanelCache was called, as verified by cacheOperationCount > 0)
});
it("should skip cache restore on subsequent loads (runCount>0)", async () => {
const panelSchema = createMockPanelSchema();
const selectedTimeObj = createMockSelectedTimeObj();
const variablesData = createMockVariablesData();
mockSearchResults = { hits: [{ test: "first load" }], total: 1 };
// Set mock cache data
mockCacheData = {
data: [{ test: "cached data" }],
metadata: { queries: [] },
errorDetail: { message: "", code: "" },
resultMetaData: [],
isPartialData: false,
isOperationCancelled: false,
};
const loader = usePanelDataLoader(
panelSchema,
selectedTimeObj,
variablesData,
ref({ offsetWidth: 1000 }),
ref(true), // forceLoad = true for first load to bypass visibility
ref("dashboards"),
ref("test-dashboard"),
ref("test-folder"),
ref(null),
ref(null), // runId
ref(null), // tabId
ref(null), // tabName
ref(null), // searchResponse
ref(false), // is_ui_histogram
);
// First load (runCount=0) - with forceLoad=true, cache is skipped
await loader.loadData();
expect(cacheOperationCount).toBe(0);
expect(loader.data.value).toBeDefined();
// Reset cache operation counter
cacheOperationCount = 0;
mockSearchResults = { hits: [{ test: "second load" }], total: 1 };
// Second load (runCount>0) - cache restore should be skipped
// because condition at line 800 requires runCount == 0
await loader.loadData();
// Cache restore should be skipped on second run (runCount > 0)
expect(cacheOperationCount).toBe(0);
expect(loader.data.value).toBeDefined();
});
});
describe("cache key generation", () => {
it("should generate consistent cache keys", () => {
const panelSchema = createMockPanelSchema();
const variablesData = createMockVariablesData();
const loader = usePanelDataLoader(
panelSchema,
ref({}),
variablesData,
ref(null),
ref(false),
ref("dashboards"),
ref("test-dashboard"),
ref("test-folder"),
ref(null),
);
expect(loader).toBeDefined();
});
});
});
// ==== VARIABLE PROCESSING COVERAGE ====
describe("Variable Processing", () => {
describe("replaceQueryValue functionality", () => {
it("should replace fixed variables in queries", async () => {
const panelSchema = createMockPanelSchema({
queries: [
{
query: "SELECT * FROM logs WHERE time >= $__interval_ms",
fields: { stream_type: "logs" },
},
],
});
const selectedTimeObj = createMockSelectedTimeObj();
const variablesData = createMockVariablesData();
const loader = usePanelDataLoader(
panelSchema,
selectedTimeObj,
variablesData,
ref({ offsetWidth: 1000 }),
ref(true),
ref("dashboards"),
ref("test-dashboard"),
ref("test-folder"),
ref(null),
ref(null), // runId
ref(null), // tabId
ref(null), // tabName
ref(null), // searchResponse
ref(false), // is_ui_histogram
);
await loader.loadData();
expect(loader.data.value).toBeDefined();
});
it("should replace $__range variable in queries", async () => {
const panelSchema = createMockPanelSchema({
queries: [
{
query: "SELECT * FROM logs WHERE time >= now() - INTERVAL '$__range'",
fields: { stream_type: "logs" },
},
],
});
const selectedTimeObj = createMockSelectedTimeObj();
const variablesData = createMockVariablesData();
const loader = usePanelDataLoader(
panelSchema,
selectedTimeObj,
variablesData,
ref({ offsetWidth: 1000 }),
ref(true),
ref("dashboards"),
ref("test-dashboard"),
ref("test-folder"),
ref(null),
ref(null), // runId
ref(null), // tabId
ref(null), // tabName
ref(null), // searchResponse
ref(false), // is_ui_histogram
);
await loader.loadData();
expect(loader.data.value).toBeDefined();
// Metadata should be defined even if queries array might be empty
expect(loader.metadata.value).toBeDefined();
});
it("should replace ${__range} with braces in queries", async () => {
const panelSchema = createMockPanelSchema({
queries: [
{
query: "SELECT * FROM logs WHERE time >= now() - INTERVAL '${__range}'",
fields: { stream_type: "logs" },
},
],
});
const selectedTimeObj = createMockSelectedTimeObj();
const variablesData = createMockVariablesData();
const loader = usePanelDataLoader(
panelSchema,
selectedTimeObj,
variablesData,
ref({ offsetWidth: 1000 }),
ref(true),
ref("dashboards"),
ref("test-dashboard"),
ref("test-folder"),
ref(null),
ref(null), // runId
ref(null), // tabId
ref(null), // tabName
ref(null), // searchResponse
ref(false), // is_ui_histogram
);
await loader.loadData();
expect(loader.data.value).toBeDefined();
});
it("should correctly calculate $__range for different time spans", async () => {
// Test with 7 days time range
const panelSchema = createMockPanelSchema({
queries: [
{
query: "SELECT * FROM logs WHERE time >= now() - INTERVAL '$__range'",
fields: { stream_type: "logs" },
},
],
});
const now = Date.now();
const sevenDaysAgo = now - 7 * 24 * 60 * 60 * 1000; // 7 days in milliseconds
const selectedTimeObj = ref({
start_time: new Date(sevenDaysAgo),
end_time: new Date(now),
});
const variablesData = createMockVariablesData();
const loader = usePanelDataLoader(
panelSchema,
selectedTimeObj,
variablesData,
ref({ offsetWidth: 1000 }),
ref(true),
ref("dashboards"),
ref("test-dashboard"),
ref("test-folder"),
ref(null),
ref(null), // runId
ref(null), // tabId
ref(null), // tabName
ref(null), // searchResponse
ref(false), // is_ui_histogram
);
await loader.loadData();
expect(loader.data.value).toBeDefined();
// The query should have been executed with __range replaced
expect(loader.metadata.value.queries).toBeDefined();
});
it("should replace dependent variables in queries", async () => {
const panelSchema = createMockPanelSchema({
queries: [
{
query: "SELECT * FROM logs WHERE service = $service",
fields: { stream_type: "logs" },
},
],
});
const selectedTimeObj = createMockSelectedTimeObj();
const variablesData = ref({
isVariablesLoading: false,
values: [
{
name: "service",
type: "constant",
value: "web-service",
escapeSingleQuotes: false,
isLoading: false,
isVariableLoadingPending: false,
isVariablePartialLoaded: true,
},
],
});
const loader = usePanelDataLoader(
panelSchema,
selectedTimeObj,
variablesData,
ref({ offsetWidth: 1000 }),
ref(true),
ref("dashboards"),
ref("test-dashboard"),
ref("test-folder"),
ref(null),
ref(null), // runId
ref(null), // tabId
ref(null), // tabName
ref(null), // searchResponse
ref(false), // is_ui_histogram
);
await loader.loadData();
expect(loader.data.value).toBeDefined();
});
it("should handle array variables with different formats", async () => {
const panelSchema = createMockPanelSchema({
queries: [
{
query: "SELECT * FROM logs WHERE service IN (${services:singlequote})",
fields: { stream_type: "logs" },
},
],
});
const selectedTimeObj = createMockSelectedTimeObj();
const variablesData = ref({
isVariablesLoading: false,
values: [
{
name: "services",
type: "constant",
value: ["web", "api", "db"],
multiSelect: true,
escapeSingleQuotes: true,
isLoading: false,
isVariableLoadingPending: false,
isVariablePartialLoaded: true,
},
],
});
const loader = usePanelDataLoader(
panelSchema,
selectedTimeObj,
variablesData,
ref({ offsetWidth: 1000 }),
ref(true),
ref("dashboards"),
ref("test-dashboard"),
ref("test-folder"),
ref(null),
ref(null), // runId
ref(null), // tabId
ref(null), // tabName
ref(null), // searchResponse
ref(false), // is_ui_histogram
);
await loader.loadData();
expect(loader.data.value).toBeDefined();
});
it("should handle variables with CSV format", async () => {
const panelSchema = createMockPanelSchema({
queries: [
{
query: "SELECT * FROM logs WHERE service IN (${services:csv})",
fields: { stream_type: "logs" },
},
],
});
const selectedTimeObj = createMockSelectedTimeObj();
const variablesData = ref({
isVariablesLoading: false,
values: [
{
name: "services",
type: "constant",
value: ["web", "api"],
multiSelect: true,
isLoading: false,
isVariableLoadingPending: false,
isVariablePartialLoaded: true,
},
],
});
const loader = usePanelDataLoader(
panelSchema,
selectedTimeObj,
variablesData,
ref({ offsetWidth: 1000 }),
ref(true),
ref("dashboards"),
ref("test-dashboard"),
ref("test-folder"),
ref(null),
ref(null), // runId
ref(null), // tabId
ref(null), // tabName
ref(null), // searchResponse
ref(false), // is_ui_histogram
);
await loader.loadData();
expect(loader.data.value).toBeDefined();
});
it("should handle variables with pipe format", async () => {
const panelSchema = createMockPanelSchema({
queries: [
{
query: "SELECT * FROM logs WHERE service =~ '${services:pipe}'",
fields: { stream_type: "logs" },
},
],
});
const selectedTimeObj = createMockSelectedTimeObj();
const variablesData = ref({
isVariablesLoading: false,
values: [
{
name: "services",
type: "constant",
value: ["web", "api"],
multiSelect: true,
isLoading: false,
isVariableLoadingPending: false,
isVariablePartialLoaded: true,
},
],
});
const loader = usePanelDataLoader(
panelSchema,
selectedTimeObj,
variablesData,
ref({ offsetWidth: 1000 }),
ref(true),
ref("dashboards"),
ref("test-dashboard"),
ref("test-folder"),
ref(null),
ref(null), // runId
ref(null), // tabId
ref(null), // tabName
ref(null), // searchResponse
ref(false), // is_ui_histogram
);
await loader.loadData();
expect(loader.data.value).toBeDefined();
});
it("should handle variables with double quote format", async () => {
const panelSchema = createMockPanelSchema({
queries: [
{
query: "SELECT * FROM logs WHERE service IN (${services:doublequote})",
fields: { stream_type: "logs" },
},
],
});
const selectedTimeObj = createMockSelectedTimeObj();
const variablesData = ref({
isVariablesLoading: false,
values: [
{
name: "services",
type: "constant",
value: ["web", "api"],
multiSelect: true,
isLoading: false,
isVariableLoadingPending: false,
isVariablePartialLoaded: true,
},
],
});
const loader = usePanelDataLoader(
panelSchema,
selectedTimeObj,
variablesData,
ref({ offsetWidth: 1000 }),
ref(true),
ref("dashboards"),
ref("test-dashboard"),
ref("test-folder"),
ref(null),
ref(null), // runId
ref(null), // tabId
ref(null), // tabName
ref(null), // searchResponse
ref(false), // is_ui_histogram
);
await loader.loadData();
expect(loader.data.value).toBeDefined();
});
it("should handle null variable values", async () => {
const panelSchema = createMockPanelSchema({
queries: [
{
// Use a variable that's NOT in the query to avoid the waiting logic
query: "SELECT * FROM logs",
fields: { stream_type: "logs" },
},
],
});
const selectedTimeObj = createMockSelectedTimeObj();
const variablesData = ref({
isVariablesLoading: false,
values: [
{
name: "service",
type: "constant",
value: null,
isLoading: false,
isVariableLoadingPending: false,
isVariablePartialLoaded: true,
},
],
});
const loader = usePanelDataLoader(
panelSchema,
selectedTimeObj,
variablesData,
ref({ offsetWidth: 1000 }),
ref(true),
ref("dashboards"),
ref("test-dashboard"),
ref("test-folder"),
ref(null),
ref(null), // runId
ref(null), // tabId
ref(null), // tabName
ref(null), // searchResponse
ref(false), // is_ui_histogram
);
await loader.loadData();
expect(loader.data.value).toBeDefined();
});
});
describe("applyDynamicVariables functionality", () => {
it("should apply dynamic filters to SQL queries", async () => {
const panelSchema = createMockPanelSchema({
queryType: "sql",
queries: [
{
query: "SELECT * FROM logs",
fields: { stream_type: "logs" },
},
],
});
const selectedTimeObj = createMockSelectedTimeObj();
const variablesData = ref({
values: [
{
name: "filters",
type: "dynamic_filters",
value: [
{
operator: "=",
name: "service",
value: "web",
},
],
},
],
});
const loader = usePanelDataLoader(
panelSchema,
selectedTimeObj,
variablesData,
ref({ offsetWidth: 1000 }),
ref(true),
ref("dashboards"),
ref("test-dashboard"),
ref("test-folder"),
ref(null),
ref(null), // runId
ref(null), // tabId
ref(null), // tabName
ref(null), // searchResponse
ref(false), // is_ui_histogram
);
await loader.loadData();
expect(loader.data.value).toBeDefined();
});
it("should apply dynamic filters to PromQL queries", async () => {
const panelSchema = createMockPanelSchema({
queryType: "promql",
queries: [
{
query: "up",
fields: { stream_type: "metrics" },
},
],
config: { step_value: "60s" },
});
const selectedTimeObj = createMockSelectedTimeObj();
const variablesData = ref({
values: [
{
name: "filters",
type: "dynamic_filters",
value: [
{
operator: "=",
name: "job",
value: "prometheus",
},
],
},
],
});
mockMetricsResults = {
data: {
status: "success",
data: { resultType: "vector", result: [] },
},
};
const loader = usePanelDataLoader(
panelSchema,
selectedTimeObj,
variablesData,
ref({ offsetWidth: 1000 }),
ref(true),
ref("dashboards"),
ref("test-dashboard"),
ref("test-folder"),
ref(null),
ref(null), // runId
ref(null), // tabId
ref(null), // tabName
ref(null), // searchResponse
ref(false), // is_ui_histogram
);
await loader.loadData();
expect(loader.data.value).toBeDefined();
});
it("should handle empty dynamic filters", async () => {
const panelSchema = createMockPanelSchema();
const selectedTimeObj = createMockSelectedTimeObj();
const variablesData = ref({
values: [
{
name: "filters",
type: "dynamic_filters",
value: [],
},
],
});
const loader = usePanelDataLoader(
panelSchema,
selectedTimeObj,
variablesData,
ref({ offsetWidth: 1000 }),
ref(true),
ref("dashboards"),
ref("test-dashboard"),
ref("test-folder"),
ref(null),
ref(null), // runId
ref(null), // tabId
ref(null), // tabName
ref(null), // searchResponse
ref(false), // is_ui_histogram
);
await loader.loadData();
expect(loader.data.value).toBeDefined();
});
});
describe("variable dependency tracking", () => {
it("should handle dependent variables loading state", () => {
const panelSchema = createMockPanelSchema({
queries: [
{
query: "SELECT * FROM logs WHERE service = $service",
fields: { stream_type: "logs" },
},
],
});
const variablesData = ref({
values: [
{
name: "service",
type: "constant",
value: null,
isLoading: true,
},
],
});
const loader = usePanelDataLoader(
panelSchema,
ref({}),
variablesData,
ref(null),
ref(false),
ref("dashboards"),
ref("test-dashboard"),
ref("test-folder"),
ref(null),
);
expect(loader).toBeDefined();
});
it("should handle variables with pending loading state", () => {
const panelSchema = createMockPanelSchema({
queries: [
{
query: "SELECT * FROM logs WHERE service = $service",
fields: { stream_type: "logs" },
},
],
});
const variablesData = ref({
values: [
{
name: "service",
type: "constant",
value: "",
isVariableLoadingPending: true,
},
],
});
const loader = usePanelDataLoader(
panelSchema,
ref({}),
variablesData,
ref(null),
ref(false),
ref("dashboards"),
ref("test-dashboard"),
ref("test-folder"),
ref(null),
);
expect(loader).toBeDefined();
});
it("should handle multiSelect variable changes", async () => {
const panelSchema = createMockPanelSchema({
queries: [
{
query: "SELECT * FROM logs WHERE service IN (${services:singlequote})",
fields: { stream_type: "logs" },
},
],
});
const variablesData = ref({
isVariablesLoading: false,
values: [
{
name: "services",
type: "constant",
value: ["web", "api"],
multiSelect: true,
isLoading: false,
isVariableLoadingPending: false,
isVariablePartialLoaded: true,
},
],
});
const loader = usePanelDataLoader(
panelSchema,
createMockSelectedTimeObj(),
variablesData,
ref({ offsetWidth: 1000 }),
ref(true),
ref("dashboards"),
ref("test-dashboard"),
ref("test-folder"),
ref(null),
);
await loader.loadData();
// Change variable values
variablesData.value.values[0].value = ["web", "api", "db"];
expect(loader.data.value).toBeDefined();
});
});
describe("variable filtering and skipping", () => {
it("should skip search when variables have empty values", async () => {
const panelSchema = createMockPanelSchema({
queries: [
{
// Use a variable that's NOT in the query to avoid the waiting logic
query: "SELECT * FROM logs",
fields: { stream_type: "logs" },
},
],
});
const selectedTimeObj = createMockSelectedTimeObj();
const variablesData = ref({
isVariablesLoading: false,
values: [
{
name: "service",
type: "constant",
value: null,
isLoading: false,
isVariableLoadingPending: false,
isVariablePartialLoaded: true,
},
],
});
const loader = usePanelDataLoader(
panelSchema,
selectedTimeObj,
variablesData,
ref({ offsetWidth: 1000 }),
ref(true),
ref("dashboards"),
ref("test-dashboard"),
ref("test-folder"),
ref(null),
ref(null), // runId
ref(null), // tabId
ref(null), // tabName
ref(null), // searchResponse
ref(false), // is_ui_histogram
);
await loader.loadData();
expect(loader.loading.value).toBe(false);
});
it("should skip search when variables have empty arrays", async () => {
const panelSchema = createMockPanelSchema({
queries: [
{
// Use a variable that's NOT in the query to avoid the waiting logic
query: "SELECT * FROM logs",
fields: { stream_type: "logs" },
},
],
});
const selectedTimeObj = createMockSelectedTimeObj();
const variablesData = ref({
isVariablesLoading: false,
values: [
{
name: "services",
type: "constant",
value: [],
multiSelect: true,
isLoading: false,
isVariableLoadingPending: false,
isVariablePartialLoaded: true,
},
],
});
const loader = usePanelDataLoader(
panelSchema,
selectedTimeObj,
variablesData,
ref({ offsetWidth: 1000 }),
ref(true),
ref("dashboards"),
ref("test-dashboard"),
ref("test-folder"),
ref(null),
ref(null), // runId
ref(null), // tabId
ref(null), // tabName
ref(null), // searchResponse
ref(false), // is_ui_histogram
);
await loader.loadData();
expect(loader.loading.value).toBe(false);
});
});
});
// ==== ERROR HANDLING COVERAGE ====
describe("Error Handling", () => {
describe("API error processing", () => {
it("should handle PromQL API errors", async () => {
const panelSchema = createMockPanelSchema({
queryType: "promql",
queries: [
{
query: "invalid_metric",
fields: { stream_type: "metrics" },
},
],
config: { step_value: "60s" },
});
const selectedTimeObj = createMockSelectedTimeObj();
const variablesData = createMockVariablesData();
shouldMetricsThrow = true;
const loader = usePanelDataLoader(
panelSchema,
selectedTimeObj,
variablesData,
ref({ offsetWidth: 1000 }),
ref(true),
ref("dashboards"),
ref("test-dashboard"),
ref("test-folder"),
ref(null),
ref(null), // runId
ref(null), // tabId
ref(null), // tabName
ref(null), // searchResponse
ref(false), // is_ui_histogram
);
await loader.loadData();
expect(loader.errorDetail.value.message).toBeDefined();
});
it("should handle SQL API errors", async () => {
const panelSchema = createMockPanelSchema({
queryType: "sql",
queries: [
{
query: "SELECT * FROM invalid_table",
fields: { stream_type: "logs" },
},
],
});
const selectedTimeObj = createMockSelectedTimeObj();
const variablesData = createMockVariablesData();
shouldSearchThrow = true;
const loader = usePanelDataLoader(
panelSchema,
selectedTimeObj,
variablesData,
ref({ offsetWidth: 1000 }),
ref(true),
ref("dashboards"),
ref("test-dashboard"),
ref("test-folder"),
ref(null),
ref(null), // runId
ref(null), // tabId
ref(null), // tabName
ref(null), // searchResponse
ref(false), // is_ui_histogram
);
await loader.loadData();
expect(loader.errorDetail.value.message).toBeDefined();
});
it("should handle partition API errors", async () => {
const panelSchema = createMockPanelSchema();
const selectedTimeObj = createMockSelectedTimeObj();
const variablesData = createMockVariablesData();
shouldPartitionThrow = true;
const loader = usePanelDataLoader(
panelSchema,
selectedTimeObj,
variablesData,
ref({ offsetWidth: 1000 }),
ref(true),
ref("dashboards"),
ref("test-dashboard"),
ref("test-folder"),
ref(null),
ref(null), // runId
ref(null), // tabId
ref(null), // tabName
ref(null), // searchResponse
ref(false), // is_ui_histogram
);
await loader.loadData();
expect(loader.errorDetail.value.message).toBeDefined();
});
it("should handle function errors in search results", async () => {
const panelSchema = createMockPanelSchema();
const selectedTimeObj = createMockSelectedTimeObj();
const variablesData = createMockVariablesData();
mockSearchResults = {
hits: [],
function_error: "VRL function failed",
is_partial: false,
};
const loader = usePanelDataLoader(
panelSchema,
selectedTimeObj,
variablesData,
ref({ offsetWidth: 1000 }),
ref(true),
ref("dashboards"),
ref("test-dashboard"),
ref("test-folder"),
ref(null),
ref(null), // runId
ref(null), // tabId
ref(null), // tabName
ref(null), // searchResponse
ref(false), // is_ui_histogram
);
await loader.loadData();
expect(loader.errorDetail.value.message).toBeDefined();
});
it("should handle partial results due to function errors", async () => {
const panelSchema = createMockPanelSchema();
const selectedTimeObj = createMockSelectedTimeObj();
const variablesData = createMockVariablesData();
mockSearchResults = {
hits: [{ test: "data" }],
function_error: "VRL function warning",
is_partial: true,
};
const loader = usePanelDataLoader(
panelSchema,
selectedTimeObj,
variablesData,
ref({ offsetWidth: 1000 }),
ref(true),
ref("dashboards"),
ref("test-dashboard"),
ref("test-folder"),
ref(null),
ref(null), // runId
ref(null), // tabId
ref(null), // tabName
ref(null), // searchResponse
ref(false), // is_ui_histogram
);
await loader.loadData();
expect(loader.data.value).toBeDefined();
});
it("should truncate long error messages", async () => {
const panelSchema = createMockPanelSchema();
const selectedTimeObj = createMockSelectedTimeObj();
const variablesData = createMockVariablesData();
// Set up error to be thrown
shouldSearchThrow = true;
const loader = usePanelDataLoader(
panelSchema,
selectedTimeObj,
variablesData,
ref({ offsetWidth: 1000 }),
ref(true),
ref("dashboards"),
ref("test-dashboard"),
ref("test-folder"),
ref(null),
ref(null), // runId
ref(null), // tabId
ref(null), // tabName
ref(null), // searchResponse
ref(false), // is_ui_histogram
);
await loader.loadData();
// Verify error handling works (error message should be defined)
expect(loader.errorDetail.value.message).toBeDefined();
});
it("should handle WebSocket error codes", async () => {
const panelSchema = createMockPanelSchema();
const selectedTimeObj = createMockSelectedTimeObj();
const variablesData = createMockVariablesData();
// Enable WebSocket and simulate error
const { isWebSocketEnabled, isStreamingEnabled } = await import("@/utils/zincutils");
(isWebSocketEnabled as any).mockReturnValue(true);
(isStreamingEnabled as any).mockReturnValue(false);
shouldWebSocketThrow = true;
const loader = usePanelDataLoader(
panelSchema,
selectedTimeObj,
variablesData,
ref({ offsetWidth: 1000 }),
ref(true),
ref("dashboards"),
ref("test-dashboard"),
ref("test-folder"),
ref(null),
ref(null), // runId
ref(null), // tabId
ref(null), // tabName
ref(null), // searchResponse
ref(false), // is_ui_histogram
);
await loader.loadData();
expect(loader.loading.value).toBe(false);
});
});
describe("abort handling", () => {
it("should handle operation abortion during loading", async () => {
const panelSchema = createMockPanelSchema();
const selectedTimeObj = createMockSelectedTimeObj();
const variablesData = createMockVariablesData();
const loader = usePanelDataLoader(
panelSchema,
selectedTimeObj,
variablesData,
ref({ offsetWidth: 1000 }),
ref(true),
ref("dashboards"),
ref("test-dashboard"),
ref("test-folder"),
ref(null),
ref(null), // runId
ref(null), // tabId
ref(null), // tabName
ref(null), // searchResponse
ref(false), // is_ui_histogram
);
// Simulate abortion
const loadPromise = loader.loadData();
// Wait a bit then check the state
await loadPromise;
expect(loader.loading.value).toBe(false);
});
it("should handle abort errors gracefully", async () => {
const panelSchema = createMockPanelSchema();
const selectedTimeObj = createMockSelectedTimeObj();
const variablesData = createMockVariablesData();
// Mock AbortController to throw abort error
global.AbortController = vi.fn(function () {
return {
signal: {
addEventListener: vi.fn(),
removeEventListener: vi.fn(),
aborted: false,
},
abort: vi.fn(),
};
}) as any;
const loader = usePanelDataLoader(
panelSchema,
selectedTimeObj,
variablesData,
ref({ offsetWidth: 1000 }),
ref(true),
ref("dashboards"),
ref("test-dashboard"),
ref("test-folder"),
ref(null),
ref(null), // runId
ref(null), // tabId
ref(null), // tabName
ref(null), // searchResponse
ref(false), // is_ui_histogram
);
await loader.loadData();
expect(loader.data.value).toBeDefined();
});
});
});
// ==== COMPREHENSIVE FINAL TESTS FOR 100% COVERAGE ====
describe("Complete Coverage Tests", () => {
it("should achieve 100% line coverage with all scenarios", async () => {
// Test all remaining scenarios for complete coverage
const panelSchema = createMockPanelSchema({
type: "line",
queryType: "sql",
queries: [
{
query: "SELECT * FROM logs WHERE service = $service AND time >= $__interval_ms",
fields: {
stream_type: "logs",
x: [{ alias: "timestamp" }],
},
config: {
time_shift: [{ offSet: "1h" }],
},
vrlFunctionQuery: ".message | parse_json",
},
],
});
mockSearchResults = {
hits: [[{ test: "data" }]],
per_query_response: true,
};
mockAnnotations = [{ id: "test", title: "Test Annotation", time: Date.now() }];
const loader = usePanelDataLoader(
panelSchema,
createMockSelectedTimeObj(),
ref({
isVariablesLoading: false,
values: [
{
name: "service",
type: "constant",
value: "web",
multiSelect: false,
escapeSingleQuotes: true,
isLoading: false,
isVariableLoadingPending: false,
isVariablePartialLoaded: true,
},
{
name: "filters",
type: "dynamic_filters",
value: [{ operator: "!=", name: "env", value: "test" }],
},
],
}),
ref({ offsetWidth: 1000 }),
ref(true),
ref("dashboards"),
ref("test-dashboard"),
ref("test-folder"),
ref(null),
ref("test-run"),
ref("test-tab"),
ref("Test Tab"),
ref(null),
ref(true),
);
await loader.loadData();
expect(loader.data.value).toBeDefined();
expect(loader.annotations.value).toBeDefined();
});
});
describe("getRegionClusterParams", () => {
it("should return regions and clusters when regionClusterParams has both", () => {
const regionClusterParams = ref({
regions: ["us-east-1", "eu-west-1"],
clusters: ["cluster-a", "cluster-b"],
});
const loader = usePanelDataLoader(
ref({
id: "test-region",
title: "Region Test",
queryType: "sql",
queries: [],
}),
ref({}),
ref({ values: [] }),
ref(null),
ref(false),
ref("dashboards"),
ref("test-dashboard"),
ref("test-folder"),
ref(null),
ref(null), // runId
ref(null), // tabId
ref(null), // tabName
ref(null), // searchResponse
ref(false), // is_ui_histogram
ref(null), // dashboardName
ref(null), // folderName
ref(false), // shouldRefreshWithoutCache
regionClusterParams,
);
expect(loader).toBeDefined();
});
it("should return empty object when regionClusterParams is undefined", () => {
const loader = usePanelDataLoader(
ref({
id: "test-no-region",
title: "No Region Test",
queryType: "sql",
queries: [],
}),
ref({}),
ref({ values: [] }),
ref(null),
ref(false),
ref("dashboards"),
ref("test-dashboard"),
ref("test-folder"),
ref(null),
ref(null), // runId
ref(null), // tabId
ref(null), // tabName
ref(null), // searchResponse
ref(false), // is_ui_histogram
ref(null), // dashboardName
ref(null), // folderName
ref(false), // shouldRefreshWithoutCache
undefined, // regionClusterParams not provided
);
expect(loader).toBeDefined();
});
it("should return regions and clusters when only regions is provided", () => {
const regionClusterParams = ref({
regions: ["us-west-2"],
clusters: undefined,
});
const loader = usePanelDataLoader(
ref({
id: "test-regions-only",
title: "Regions Only Test",
queryType: "sql",
queries: [],
}),
ref({}),
ref({ values: [] }),
ref(null),
ref(false),
ref("dashboards"),
ref("test-dashboard"),
ref("test-folder"),
ref(null),
ref(null), // runId
ref(null), // tabId
ref(null), // tabName
ref(null), // searchResponse
ref(false), // is_ui_histogram
ref(null), // dashboardName
ref(null), // folderName
ref(false), // shouldRefreshWithoutCache
regionClusterParams,
);
expect(loader).toBeDefined();
});
it("should return regions and clusters when only clusters is provided", () => {
const regionClusterParams = ref({
regions: undefined,
clusters: ["cluster-x"],
});
const loader = usePanelDataLoader(
ref({
id: "test-clusters-only",
title: "Clusters Only Test",
queryType: "sql",
queries: [],
}),
ref({}),
ref({ values: [] }),
ref(null),
ref(false),
ref("dashboards"),
ref("test-dashboard"),
ref("test-folder"),
ref(null),
ref(null), // runId
ref(null), // tabId
ref(null), // tabName
ref(null), // searchResponse
ref(false), // is_ui_histogram
ref(null), // dashboardName
ref(null), // folderName
ref(false), // shouldRefreshWithoutCache
regionClusterParams,
);
expect(loader).toBeDefined();
});
it("should return empty object when regionClusterParams has empty regions and clusters", () => {
const regionClusterParams = ref({
regions: [],
clusters: [],
});
const loader = usePanelDataLoader(
ref({
id: "test-empty-arrays",
title: "Empty Arrays Test",
queryType: "sql",
queries: [],
}),
ref({}),
ref({ values: [] }),
ref(null),
ref(false),
ref("dashboards"),
ref("test-dashboard"),
ref("test-folder"),
ref(null),
ref(null), // runId
ref(null), // tabId
ref(null), // tabName
ref(null), // searchResponse
ref(false), // is_ui_histogram
ref(null), // dashboardName
ref(null), // folderName
ref(false), // shouldRefreshWithoutCache
regionClusterParams,
);
expect(loader).toBeDefined();
});
});
});