Skip to content

Commit d254728

Browse files
chore: Added an integration-test for execute-dql (#78)
1 parent 93a3fec commit d254728

File tree

2 files changed

+190
-4
lines changed

2 files changed

+190
-4
lines changed
Lines changed: 189 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,189 @@
1+
/**
2+
* Integration test for execute DQL functionality
3+
*
4+
* This test verifies the DQL execution functionality by making actual API calls
5+
* to the Dynatrace environment. These tests require valid authentication credentials.
6+
*/
7+
8+
import { config } from 'dotenv';
9+
import { createDtHttpClient } from '../src/authentication/dynatrace-clients';
10+
import { executeDql, verifyDqlStatement } from '../src/capabilities/execute-dql';
11+
import { getDynatraceEnv, DynatraceEnv } from '../src/getDynatraceEnv';
12+
13+
// Load environment variables
14+
config();
15+
16+
const API_RATE_LIMIT_DELAY = 100; // Delay in milliseconds to avoid hitting API rate limits
17+
18+
const scopesBase = [
19+
'app-engine:apps:run', // needed for environmentInformationClient
20+
'app-engine:functions:run', // needed for environmentInformationClient
21+
];
22+
23+
const scopesDqlExecution = [
24+
'storage:buckets:read', // Read all system data stored on Grail
25+
'storage:logs:read', // Read logs for reliability guardian validations
26+
'storage:metrics:read', // Read metrics for reliability guardian validations
27+
'storage:bizevents:read', // Read bizevents for reliability guardian validations
28+
'storage:spans:read', // Read spans from Grail
29+
'storage:entities:read', // Read Entities from Grail
30+
'storage:events:read', // Read events from Grail
31+
'storage:system:read', // Read System Data from Grail
32+
'storage:user.events:read', // Read User events from Grail
33+
'storage:user.sessions:read', // Read User sessions from Grail
34+
'storage:security.events:read', // Read Security events from Grail
35+
];
36+
37+
describe('Execute DQL Integration Tests', () => {
38+
let dynatraceEnv: DynatraceEnv;
39+
40+
// Setup that runs once before all tests
41+
beforeAll(async () => {
42+
try {
43+
dynatraceEnv = getDynatraceEnv();
44+
console.log(`Testing against environment: ${dynatraceEnv.dtEnvironment}`);
45+
} catch (err) {
46+
throw new Error(`Environment configuration error: ${(err as Error).message}`);
47+
}
48+
});
49+
50+
afterEach(async () => {
51+
// sleep after every call to avoid hitting API Rate limits
52+
await new Promise((resolve) => setTimeout(resolve, API_RATE_LIMIT_DELAY)); // Delay to avoid hitting API rate limits
53+
});
54+
55+
// Helper function to create HTTP client for DQL execution
56+
const createHttpClient = async () => {
57+
const { oauthClientId, oauthClientSecret, dtEnvironment, dtPlatformToken } = dynatraceEnv;
58+
59+
return await createDtHttpClient(
60+
dtEnvironment,
61+
scopesBase.concat(scopesDqlExecution),
62+
oauthClientId,
63+
oauthClientSecret,
64+
dtPlatformToken,
65+
);
66+
};
67+
68+
// Helper function to create HTTP client for verification only
69+
const createVerificationClient = async () => {
70+
const { oauthClientId, oauthClientSecret, dtEnvironment, dtPlatformToken } = dynatraceEnv;
71+
72+
return await createDtHttpClient(
73+
dtEnvironment,
74+
scopesBase, // verification doesn't need storage scopes
75+
oauthClientId,
76+
oauthClientSecret,
77+
dtPlatformToken,
78+
);
79+
};
80+
81+
test('should verify and execute a simple DQL query', async () => {
82+
const dtClient = await createHttpClient();
83+
84+
// Simple query to fetch limited logs
85+
const simpleDql = 'fetch logs | limit 10';
86+
87+
// First verify the DQL
88+
const verificationResponse = await verifyDqlStatement(dtClient, simpleDql);
89+
expect(verificationResponse.valid).toBe(true);
90+
91+
// Then execute it
92+
const executionResponse = await executeDql(dtClient, {
93+
query: simpleDql,
94+
maxResultRecords: 10, // Limit results to avoid large responses
95+
});
96+
97+
expect(executionResponse).toBeDefined();
98+
expect(Array.isArray(executionResponse)).toBe(true);
99+
100+
// Should return an array of records (even if empty)
101+
if (executionResponse && executionResponse.length > 0) {
102+
// Check that records have expected structure
103+
expect(typeof executionResponse[0]).toBe('object');
104+
}
105+
});
106+
107+
test('should execute metrics query', async () => {
108+
const dtClient = await createHttpClient();
109+
110+
// Valid timeseries query for host CPU usage
111+
const metricsDql = 'timeseries from:now() - 1h, to:now(), avg_cpu_usage = avg(dt.host.cpu.usage)';
112+
113+
// First verify the DQL
114+
const verificationResponse = await verifyDqlStatement(dtClient, metricsDql);
115+
console.log('Verification response:', verificationResponse);
116+
expect(verificationResponse.valid).toBe(true);
117+
118+
// Then execute it
119+
const executionResponse = await executeDql(dtClient, {
120+
query: metricsDql,
121+
maxResultRecords: 5,
122+
});
123+
124+
expect(executionResponse).toBeDefined();
125+
expect(Array.isArray(executionResponse)).toBe(true);
126+
127+
// Metrics might not always have data, so we just check structure
128+
if (executionResponse && executionResponse.length > 0) {
129+
expect(typeof executionResponse[0]).toBe('object');
130+
}
131+
});
132+
133+
test('should execute events query', async () => {
134+
const dtClient = await createHttpClient();
135+
136+
// Query to fetch events from the last hour
137+
const eventsDql = 'fetch events | limit 10';
138+
139+
// First verify the DQL
140+
const verificationResponse = await verifyDqlStatement(dtClient, eventsDql);
141+
console.log('Events verification response:', verificationResponse);
142+
expect(verificationResponse.valid).toBe(true);
143+
144+
// Then execute it
145+
const executionResponse = await executeDql(dtClient, {
146+
query: eventsDql,
147+
maxResultRecords: 10,
148+
});
149+
150+
expect(executionResponse).toBeDefined();
151+
expect(Array.isArray(executionResponse)).toBe(true);
152+
153+
// Events might not always have data, so we just check structure
154+
if (executionResponse && executionResponse.length > 0) {
155+
expect(typeof executionResponse[0]).toBe('object');
156+
// Events should have common fields like timestamp, event.type, etc.
157+
const firstEvent = executionResponse[0] as Record<string, any>;
158+
expect(firstEvent).toHaveProperty('timestamp');
159+
}
160+
});
161+
162+
test('should handle invalid DQL syntax gracefully', async () => {
163+
const dtClient = await createVerificationClient();
164+
165+
const invalidDql = 'this is not valid dql syntax';
166+
167+
// Verify should return invalid
168+
const verificationResponse = await verifyDqlStatement(dtClient, invalidDql);
169+
expect(verificationResponse.valid).toBe(false);
170+
expect(verificationResponse.notifications).toBeDefined();
171+
expect(verificationResponse.notifications?.length).toBeGreaterThan(0);
172+
173+
if (verificationResponse.notifications && verificationResponse.notifications.length > 0) {
174+
expect(verificationResponse.notifications[0].severity).toBe('ERROR');
175+
}
176+
});
177+
178+
test('should handle empty DQL statement', async () => {
179+
const dtClient = await createVerificationClient();
180+
181+
// Test with empty string
182+
const emptyDql = '';
183+
184+
const verificationResponse = await verifyDqlStatement(dtClient, emptyDql);
185+
expect(verificationResponse.valid).toBe(false);
186+
expect(verificationResponse.notifications).toBeDefined();
187+
expect(verificationResponse.notifications?.length).toBeGreaterThan(0);
188+
});
189+
});

jest.config.js

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,21 @@
1-
const { run } = require('node:test');
2-
31
module.exports = {
42
preset: 'ts-jest',
53
testEnvironment: 'node',
64
testMatch: ['**/*.test.ts'],
5+
testTimeout: 10000, // Default timeout for tests
76
// Separate test configurations
87
projects: [
98
{
109
displayName: 'unit',
1110
preset: 'ts-jest',
1211
testEnvironment: 'node',
1312
testMatch: ['<rootDir>/src/**/*.test.ts'],
14-
testTimeout: 10000, // 10 seconds for unit tests
1513
},
1614
{
1715
displayName: 'integration',
1816
preset: 'ts-jest',
1917
testEnvironment: 'node',
2018
testMatch: ['<rootDir>/integration-tests/**/*.integration.test.ts'],
21-
testTimeout: 30000, // 30 seconds for integration tests
2219
},
2320
],
2421
};

0 commit comments

Comments
 (0)