diff --git a/frontend/src/container/NewDashboard/DashboardVariablesSelection/__test__/dashboardVariables.test.tsx b/frontend/src/container/NewDashboard/DashboardVariablesSelection/__test__/dashboardVariables.test.tsx new file mode 100644 index 0000000000..9a7982b3ce --- /dev/null +++ b/frontend/src/container/NewDashboard/DashboardVariablesSelection/__test__/dashboardVariables.test.tsx @@ -0,0 +1,241 @@ +import { + buildDependencies, + buildDependencyGraph, + buildParentDependencyGraph, + checkAPIInvocation, + onUpdateVariableNode, + VariableGraph, +} from '../util'; +import { + buildDependenciesMock, + buildGraphMock, + checkAPIInvocationMock, + onUpdateVariableNodeMock, +} from './mock'; + +describe('dashboardVariables - utilities and processors', () => { + describe('onUpdateVariableNode', () => { + const { graph, topologicalOrder } = onUpdateVariableNodeMock; + const testCases = [ + { + scenario: 'root element', + nodeToUpdate: 'deployment_environment', + expected: [ + 'deployment_environment', + 'service_name', + 'endpoint', + 'http_status_code', + ], + }, + { + scenario: 'middle child', + nodeToUpdate: 'k8s_node_name', + expected: ['k8s_node_name', 'k8s_namespace_name'], + }, + { + scenario: 'leaf element', + nodeToUpdate: 'http_status_code', + expected: ['http_status_code'], + }, + { + scenario: 'node not in graph', + nodeToUpdate: 'unknown', + expected: [], + }, + { + scenario: 'node not in topological order', + nodeToUpdate: 'unknown', + expected: [], + }, + ]; + + test.each(testCases)( + 'should update variable node when $scenario', + ({ nodeToUpdate, expected }) => { + const updatedVariables: string[] = []; + const callback = (node: string): void => { + updatedVariables.push(node); + }; + + onUpdateVariableNode(nodeToUpdate, graph, topologicalOrder, callback); + + expect(updatedVariables).toEqual(expected); + }, + ); + + it('should return empty array when topological order is empty', () => { + const updatedVariables: string[] = []; + onUpdateVariableNode('http_status_code', graph, [], (node) => + updatedVariables.push(node), + ); + expect(updatedVariables).toEqual([]); + }); + }); + + describe('checkAPIInvocation', () => { + const { + variablesToGetUpdated, + variableData, + parentDependencyGraph, + } = checkAPIInvocationMock; + + const mockRootElement = { + name: 'deployment_environment', + key: '036a47cd-9ffc-47de-9f27-0329198964a8', + id: '036a47cd-9ffc-47de-9f27-0329198964a8', + modificationUUID: '5f71b591-f583-497c-839d-6a1590c3f60f', + selectedValue: 'production', + type: 'QUERY', + // ... other properties omitted for brevity + } as any; + + describe('edge cases', () => { + it('should return false when variableData is empty', () => { + expect( + checkAPIInvocation( + variablesToGetUpdated, + variableData, + parentDependencyGraph, + ), + ).toBeFalsy(); + }); + + it('should return true when parentDependencyGraph is empty', () => { + expect( + checkAPIInvocation(variablesToGetUpdated, variableData, {}), + ).toBeTruthy(); + }); + }); + + describe('variable sequences', () => { + it('should return true for valid sequence', () => { + expect( + checkAPIInvocation( + ['k8s_node_name', 'k8s_namespace_name'], + variableData, + parentDependencyGraph, + ), + ).toBeTruthy(); + }); + + it('should return false for invalid sequence', () => { + expect( + checkAPIInvocation( + ['k8s_cluster_name', 'k8s_node_name', 'k8s_namespace_name'], + variableData, + parentDependencyGraph, + ), + ).toBeFalsy(); + }); + + it('should return false when variableData is not in sequence', () => { + expect( + checkAPIInvocation( + ['deployment_environment', 'service_name', 'endpoint'], + variableData, + parentDependencyGraph, + ), + ).toBeFalsy(); + }); + }); + + describe('root element behavior', () => { + it('should return true for valid root element sequence', () => { + expect( + checkAPIInvocation( + [ + 'deployment_environment', + 'service_name', + 'endpoint', + 'http_status_code', + ], + mockRootElement, + parentDependencyGraph, + ), + ).toBeTruthy(); + }); + + it('should return true for empty variablesToGetUpdated array', () => { + expect( + checkAPIInvocation([], mockRootElement, parentDependencyGraph), + ).toBeTruthy(); + }); + }); + }); + + describe('Graph Building Utilities', () => { + const { graph } = buildGraphMock; + const { variables } = buildDependenciesMock; + + describe('buildParentDependencyGraph', () => { + it('should build parent dependency graph with correct relationships', () => { + const expected = { + deployment_environment: [], + service_name: ['deployment_environment'], + endpoint: ['deployment_environment', 'service_name'], + http_status_code: ['endpoint'], + k8s_cluster_name: [], + k8s_node_name: ['k8s_cluster_name'], + k8s_namespace_name: ['k8s_cluster_name', 'k8s_node_name'], + environment: [], + }; + + expect(buildParentDependencyGraph(graph)).toEqual(expected); + }); + + it('should handle empty graph', () => { + expect(buildParentDependencyGraph({})).toEqual({}); + }); + }); + + describe('buildDependencyGraph', () => { + it('should build complete dependency graph with correct structure and order', () => { + const expected = { + graph: { + deployment_environment: ['service_name', 'endpoint'], + service_name: ['endpoint'], + endpoint: ['http_status_code'], + http_status_code: [], + k8s_cluster_name: ['k8s_node_name', 'k8s_namespace_name'], + k8s_node_name: ['k8s_namespace_name'], + k8s_namespace_name: [], + environment: [], + }, + order: [ + 'deployment_environment', + 'k8s_cluster_name', + 'environment', + 'service_name', + 'k8s_node_name', + 'endpoint', + 'k8s_namespace_name', + 'http_status_code', + ], + }; + + expect(buildDependencyGraph(graph)).toEqual(expected); + }); + }); + + describe('buildDependencies', () => { + it('should build dependency map from variables array', () => { + const expected: VariableGraph = { + deployment_environment: ['service_name', 'endpoint'], + service_name: ['endpoint'], + endpoint: ['http_status_code'], + http_status_code: [], + k8s_cluster_name: ['k8s_node_name', 'k8s_namespace_name'], + k8s_node_name: ['k8s_namespace_name'], + k8s_namespace_name: [], + environment: [], + }; + + expect(buildDependencies(variables)).toEqual(expected); + }); + + it('should handle empty variables array', () => { + expect(buildDependencies([])).toEqual({}); + }); + }); + }); +}); diff --git a/frontend/src/container/NewDashboard/DashboardVariablesSelection/__test__/mock.ts b/frontend/src/container/NewDashboard/DashboardVariablesSelection/__test__/mock.ts new file mode 100644 index 0000000000..c39841fcf4 --- /dev/null +++ b/frontend/src/container/NewDashboard/DashboardVariablesSelection/__test__/mock.ts @@ -0,0 +1,251 @@ +/* eslint-disable sonarjs/no-duplicate-string */ +export const checkAPIInvocationMock = { + variablesToGetUpdated: [], + variableData: { + name: 'k8s_node_name', + key: '4d71d385-beaf-4434-8dbf-c62be68049fc', + allSelected: false, + customValue: '', + description: '', + id: '4d71d385-beaf-4434-8dbf-c62be68049fc', + modificationUUID: '77233d3c-96d7-4ccb-aa9d-11b04d563068', + multiSelect: false, + order: 6, + queryValue: + "SELECT JSONExtractString(labels, 'k8s_node_name') AS k8s_node_name\nFROM signoz_metrics.distributed_time_series_v4_1day\nWHERE metric_name = 'k8s_node_cpu_time' AND JSONExtractString(labels, 'k8s_cluster_name') = {{.k8s_cluster_name}}\nGROUP BY k8s_node_name", + selectedValue: 'gke-signoz-saas-si-consumer-bsc-e2sd4-a6d430fa-gvm2', + showALLOption: false, + sort: 'DISABLED', + textboxValue: '', + type: 'QUERY', + }, + parentDependencyGraph: { + deployment_environment: [], + service_name: ['deployment_environment'], + endpoint: ['deployment_environment', 'service_name'], + http_status_code: ['endpoint'], + k8s_cluster_name: [], + environment: [], + k8s_node_name: ['k8s_cluster_name'], + k8s_namespace_name: ['k8s_cluster_name', 'k8s_node_name'], + }, +} as any; + +export const onUpdateVariableNodeMock = { + nodeToUpdate: 'deployment_environment', + graph: { + deployment_environment: ['service_name', 'endpoint'], + service_name: ['endpoint'], + endpoint: ['http_status_code'], + http_status_code: [], + k8s_cluster_name: ['k8s_node_name', 'k8s_namespace_name'], + environment: [], + k8s_node_name: ['k8s_namespace_name'], + k8s_namespace_name: [], + }, + topologicalOrder: [ + 'deployment_environment', + 'k8s_cluster_name', + 'environment', + 'service_name', + 'k8s_node_name', + 'endpoint', + 'k8s_namespace_name', + 'http_status_code', + ], + callback: jest.fn(), +}; + +export const buildGraphMock = { + graph: { + deployment_environment: ['service_name', 'endpoint'], + service_name: ['endpoint'], + endpoint: ['http_status_code'], + http_status_code: [], + k8s_cluster_name: ['k8s_node_name', 'k8s_namespace_name'], + environment: [], + k8s_node_name: ['k8s_namespace_name'], + k8s_namespace_name: [], + }, +}; + +export const buildDependenciesMock = { + variables: [ + { + key: '036a47cd-9ffc-47de-9f27-0329198964a8', + name: 'deployment_environment', + allSelected: false, + customValue: '', + description: '', + id: '036a47cd-9ffc-47de-9f27-0329198964a8', + modificationUUID: '5f71b591-f583-497c-839d-6a1590c3f60f', + multiSelect: false, + order: 0, + queryValue: + "SELECT DISTINCT JSONExtractString(labels, 'deployment_environment') AS deployment_environment\nFROM signoz_metrics.distributed_time_series_v4_1day\nWHERE metric_name = 'signoz_calls_total'", + selectedValue: 'production', + showALLOption: false, + sort: 'DISABLED', + textboxValue: '', + type: 'QUERY', + }, + { + key: 'eed5c917-1860-4c7e-bf6d-a05b97bafbc9', + name: 'service_name', + allSelected: true, + customValue: '', + description: '', + id: 'eed5c917-1860-4c7e-bf6d-a05b97bafbc9', + modificationUUID: '85db928b-ac9b-4e9f-b274-791112102fdf', + multiSelect: true, + order: 1, + queryValue: + "SELECT DISTINCT JSONExtractString(labels, 'service_name') FROM signoz_metrics.distributed_time_series_v4_1day\n WHERE metric_name = 'signoz_calls_total' and JSONExtractString(labels, 'deployment_environment') = {{.deployment_environment}}", + selectedValue: ['otelgateway'], + showALLOption: true, + sort: 'ASC', + textboxValue: '', + type: 'QUERY', + }, + { + key: '4022d3c1-e845-4952-8984-78f25f575c7a', + name: 'endpoint', + allSelected: true, + customValue: '', + description: '', + id: '4022d3c1-e845-4952-8984-78f25f575c7a', + modificationUUID: 'c0107fa1-ebb7-4dd3-aa9d-6ba08ecc594d', + multiSelect: true, + order: 2, + queryValue: + "SELECT DISTINCT JSONExtractString(labels, 'operation') FROM signoz_metrics.distributed_time_series_v4_1day\n WHERE metric_name = 'signoz_calls_total' AND JSONExtractString(labels, 'service_name') IN {{.service_name}} and JSONExtractString(labels, 'deployment_environment') = {{.deployment_environment}}", + selectedValue: [ + '//v1/traces', + '/logs/heroku', + '/logs/json', + '/logs/vector', + '/v1/logs', + '/v1/metrics', + '/v1/traces', + 'SELECT', + 'exporter/signozkafka/logs', + 'exporter/signozkafka/metrics', + 'exporter/signozkafka/traces', + 'extension/signozkeyauth/Authenticate', + 'get', + 'hmget', + 'opentelemetry.proto.collector.logs.v1.LogsService/Export', + 'opentelemetry.proto.collector.metrics.v1.MetricsService/Export', + 'opentelemetry.proto.collector.trace.v1.TraceService/Export', + 'processor/signozlimiter/LogsProcessed', + 'processor/signozlimiter/MetricsProcessed', + 'processor/signozlimiter/TracesProcessed', + 'receiver/otlp/LogsReceived', + 'receiver/otlp/MetricsReceived', + 'receiver/otlp/TraceDataReceived', + 'receiver/signozhttplog/heroku/LogsReceived', + 'receiver/signozhttplog/json/LogsReceived', + 'receiver/signozhttplog/vector/LogsReceived', + 'redis.dial', + 'redis.pipeline eval', + 'sadd', + 'set', + 'sismember', + ], + showALLOption: true, + sort: 'ASC', + textboxValue: '', + type: 'QUERY', + }, + { + key: '5e8a3cd9-3cd9-42df-a76c-79471a0f75bd', + name: 'http_status_code', + customValue: '', + description: '', + id: '5e8a3cd9-3cd9-42df-a76c-79471a0f75bd', + modificationUUID: '9a4021cc-a80a-4f15-8899-78892b763ca7', + multiSelect: true, + order: 3, + queryValue: + "SELECT DISTINCT JSONExtractString(labels, 'http_status_code') FROM signoz_metrics.distributed_time_series_v4_1day\n WHERE metric_name = 'signoz_calls_total' AND JSONExtractString(labels, 'operation') IN {{.endpoint}}", + showALLOption: true, + sort: 'ASC', + textboxValue: '', + type: 'QUERY', + selectedValue: ['', '200', '301', '400', '401', '405', '415', '429'], + allSelected: true, + }, + { + key: '48e9aa64-05ca-41c2-a1bd-6c8aeca659f1', + name: 'k8s_cluster_name', + allSelected: false, + customValue: 'test-1,\ntest-2,\ntest-3', + description: '', + id: '48e9aa64-05ca-41c2-a1bd-6c8aeca659f1', + modificationUUID: '44722322-368c-4613-bb7f-d0b12867d57a', + multiSelect: false, + order: 4, + queryValue: + "SELECT JSONExtractString(labels, 'k8s_cluster_name') AS k8s_cluster_name\nFROM signoz_metrics.distributed_time_series_v4_1day\nWHERE metric_name = 'k8s_node_cpu_time'\nGROUP BY k8s_cluster_name", + selectedValue: 'saasmonitor-cluster', + showALLOption: false, + sort: 'DISABLED', + textboxValue: '', + type: 'QUERY', + }, + { + key: '3ea18ba2-30cf-4220-b03b-720b5eaf35f8', + name: 'environment', + allSelected: false, + customValue: '', + description: '', + id: '3ea18ba2-30cf-4220-b03b-720b5eaf35f8', + modificationUUID: '9f76cb06-1b9f-460f-a174-0b210bb3cf93', + multiSelect: false, + order: 5, + queryValue: + "SELECT DISTINCT JSONExtractString(labels, 'deployment_environment') AS environment\nFROM signoz_metrics.distributed_time_series_v4_1day\nWHERE metric_name = 'signoz_calls_total'", + selectedValue: 'production', + showALLOption: false, + sort: 'DISABLED', + textboxValue: '', + type: 'QUERY', + }, + { + key: '4d71d385-beaf-4434-8dbf-c62be68049fc', + name: 'k8s_node_name', + allSelected: false, + customValue: '', + description: '', + id: '4d71d385-beaf-4434-8dbf-c62be68049fc', + modificationUUID: '77233d3c-96d7-4ccb-aa9d-11b04d563068', + multiSelect: false, + order: 6, + queryValue: + "SELECT JSONExtractString(labels, 'k8s_node_name') AS k8s_node_name\nFROM signoz_metrics.distributed_time_series_v4_1day\nWHERE metric_name = 'k8s_node_cpu_time' AND JSONExtractString(labels, 'k8s_cluster_name') = {{.k8s_cluster_name}}\nGROUP BY k8s_node_name", + selectedValue: 'gke-signoz-saas-si-consumer-bsc-e2sd4-a6d430fa-gvm2', + showALLOption: false, + sort: 'DISABLED', + textboxValue: '', + type: 'QUERY', + }, + { + key: '937ecbae-b24b-4d6d-8cc4-5d5b8d53569b', + name: 'k8s_namespace_name', + customValue: '', + description: '', + id: '937ecbae-b24b-4d6d-8cc4-5d5b8d53569b', + modificationUUID: '8ad2442d-8b4d-4c64-848e-af847d1d0eec', + multiSelect: false, + order: 7, + queryValue: + "SELECT JSONExtractString(labels, 'k8s_namespace_name') AS k8s_namespace_name\nFROM signoz_metrics.distributed_time_series_v4_1day\nWHERE metric_name = 'k8s_pod_cpu_time' AND JSONExtractString(labels, 'k8s_cluster_name') = {{.k8s_cluster_name}} AND JSONExtractString(labels, 'k8s_node_name') IN {{.k8s_node_name}}\nGROUP BY k8s_namespace_name", + showALLOption: false, + sort: 'DISABLED', + textboxValue: '', + type: 'QUERY', + selectedValue: 'saasmonitor', + allSelected: false, + }, + ] as any, +}; diff --git a/frontend/src/container/NewDashboard/DashboardVariablesSelection/util.ts b/frontend/src/container/NewDashboard/DashboardVariablesSelection/util.ts index 9a2c62f395..af44d339d6 100644 --- a/frontend/src/container/NewDashboard/DashboardVariablesSelection/util.ts +++ b/frontend/src/container/NewDashboard/DashboardVariablesSelection/util.ts @@ -46,6 +46,7 @@ export type VariableGraph = Record; export const buildDependencies = ( variables: IDashboardVariable[], ): VariableGraph => { + console.log('buildDependencies', variables); const graph: VariableGraph = {}; // Initialize empty arrays for all variables first