Skip to content

Commit 7604004

Browse files
committed
fix: review fixes
1 parent 5329153 commit 7604004

File tree

8 files changed

+383
-21
lines changed

8 files changed

+383
-21
lines changed

src/store/reducers/executeTopQueries/executeTopQueries.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,9 @@ export const topQueriesApi = api.injectEndpoints({
9595
) => {
9696
try {
9797
const filterConditions = filters?.text ? `Query ILIKE '%${filters.text}%'` : '';
98-
const queryText = `SELECT UserSID, QueryStartAt, Query as QueryText, ApplicationName from \`.sys/query_sessions\` WHERE ${filterConditions || 'true'} ORDER BY SessionStartAt limit 100`;
98+
const commonQueryPart = `SELECT UserSID, QueryStartAt, Query as QueryText, ApplicationName from \`.sys/query_sessions\` WHERE ${filterConditions || 'true'}`;
99+
100+
const queryText = `${commonQueryPart} AND Query NOT LIKE '${commonQueryPart}%' ORDER BY SessionStartAt limit 100`;
99101

100102
const response = await window.api.sendQuery(
101103
{

src/utils/query.test.ts

Lines changed: 246 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,252 @@ describe('API utils', () => {
4747
expect(actual.columns).toBeUndefined();
4848
expect(actual.stats).toEqual(response.stats);
4949
});
50+
51+
const mockColumns = [
52+
{name: 'PDiskFilter', type: 'Utf8?'},
53+
{name: 'ErasureSpecies', type: 'Utf8?'},
54+
{name: 'CurrentAvailableSize', type: 'Uint64?'},
55+
{name: 'CurrentAllocatedSize', type: 'Uint64?'},
56+
{name: 'CurrentGroupsCreated', type: 'Uint32?'},
57+
{name: 'AvailableGroupsToCreate', type: 'Uint32?'},
58+
];
59+
60+
const mockRows = [
61+
['Type:SSD', 'block-4-2', '1000', '2000', 100, 50],
62+
['Type:ROT', 'block-4-2', '2000', '1000', 50, 0],
63+
];
64+
65+
it('should parse a valid ExecuteResponse correctly', () => {
66+
const input = {
67+
result: [
68+
{
69+
columns: mockColumns,
70+
rows: mockRows,
71+
truncated: false,
72+
},
73+
],
74+
stats: {DurationUs: '1000'},
75+
};
76+
77+
const expected = {
78+
resultSets: [
79+
{
80+
columns: mockColumns,
81+
result: [
82+
{
83+
PDiskFilter: 'Type:SSD',
84+
ErasureSpecies: 'block-4-2',
85+
CurrentAvailableSize: '1000',
86+
CurrentAllocatedSize: '2000',
87+
CurrentGroupsCreated: 100,
88+
AvailableGroupsToCreate: 50,
89+
},
90+
{
91+
PDiskFilter: 'Type:ROT',
92+
ErasureSpecies: 'block-4-2',
93+
CurrentAvailableSize: '2000',
94+
CurrentAllocatedSize: '1000',
95+
CurrentGroupsCreated: 50,
96+
AvailableGroupsToCreate: 0,
97+
},
98+
],
99+
truncated: false,
100+
},
101+
],
102+
stats: {DurationUs: '1000'},
103+
};
104+
105+
expect(parseQueryAPIExecuteResponse(input)).toEqual(expected);
106+
});
107+
108+
it('should handle empty result array', () => {
109+
const input = {
110+
result: [],
111+
stats: {DurationUs: '1000'},
112+
};
113+
114+
const expected = {
115+
result: [],
116+
stats: {DurationUs: '1000'},
117+
};
118+
119+
expect(parseQueryAPIExecuteResponse(input)).toEqual(expected);
120+
});
121+
122+
it('should handle result with columns but no rows', () => {
123+
const input = {
124+
result: [
125+
{
126+
columns: mockColumns,
127+
rows: [],
128+
truncated: false,
129+
},
130+
],
131+
stats: {DurationUs: '1000'},
132+
};
133+
134+
const expected = {
135+
resultSets: [
136+
{
137+
columns: mockColumns,
138+
result: [],
139+
truncated: false,
140+
},
141+
],
142+
stats: {DurationUs: '1000'},
143+
};
144+
145+
expect(parseQueryAPIExecuteResponse(input)).toEqual(expected);
146+
});
147+
148+
it('should return empty object for unsupported format', () => {
149+
const input = {
150+
result: 'unsupported',
151+
};
152+
153+
expect(parseQueryAPIExecuteResponse(input)).toEqual({});
154+
});
155+
156+
it('should handle multiple result sets', () => {
157+
const input = {
158+
result: [
159+
{
160+
columns: mockColumns,
161+
rows: mockRows,
162+
truncated: false,
163+
},
164+
{
165+
columns: [{name: 'Count', type: 'Uint32?'}],
166+
rows: [[2]],
167+
truncated: false,
168+
},
169+
],
170+
stats: {DurationUs: '1500'},
171+
};
172+
173+
const expected = {
174+
resultSets: [
175+
{
176+
columns: mockColumns,
177+
result: [
178+
{
179+
PDiskFilter: 'Type:SSD',
180+
ErasureSpecies: 'block-4-2',
181+
CurrentAvailableSize: '1000',
182+
CurrentAllocatedSize: '2000',
183+
CurrentGroupsCreated: 100,
184+
AvailableGroupsToCreate: 50,
185+
},
186+
{
187+
PDiskFilter: 'Type:ROT',
188+
ErasureSpecies: 'block-4-2',
189+
CurrentAvailableSize: '2000',
190+
CurrentAllocatedSize: '1000',
191+
CurrentGroupsCreated: 50,
192+
AvailableGroupsToCreate: 0,
193+
},
194+
],
195+
truncated: false,
196+
},
197+
{
198+
columns: [{name: 'Count', type: 'Uint32?'}],
199+
result: [{Count: 2}],
200+
truncated: false,
201+
},
202+
],
203+
stats: {DurationUs: '1500'},
204+
};
205+
206+
expect(parseQueryAPIExecuteResponse(input)).toEqual(expected);
207+
});
208+
209+
it('should handle null values in rows', () => {
210+
const input = {
211+
result: [
212+
{
213+
columns: mockColumns,
214+
rows: [
215+
['Type:SSD', null, '1000', null, 100, 50],
216+
[null, 'block-4-2', null, '1000', null, 0],
217+
],
218+
truncated: false,
219+
},
220+
],
221+
stats: {DurationUs: '1000'},
222+
};
223+
224+
const expected = {
225+
resultSets: [
226+
{
227+
columns: mockColumns,
228+
result: [
229+
{
230+
PDiskFilter: 'Type:SSD',
231+
ErasureSpecies: null,
232+
CurrentAvailableSize: '1000',
233+
CurrentAllocatedSize: null,
234+
CurrentGroupsCreated: 100,
235+
AvailableGroupsToCreate: 50,
236+
},
237+
{
238+
PDiskFilter: null,
239+
ErasureSpecies: 'block-4-2',
240+
CurrentAvailableSize: null,
241+
CurrentAllocatedSize: '1000',
242+
CurrentGroupsCreated: null,
243+
AvailableGroupsToCreate: 0,
244+
},
245+
],
246+
truncated: false,
247+
},
248+
],
249+
stats: {DurationUs: '1000'},
250+
};
251+
252+
expect(parseQueryAPIExecuteResponse(input)).toEqual(expected);
253+
});
254+
255+
it('should handle truncated results', () => {
256+
const input = {
257+
result: [
258+
{
259+
columns: mockColumns,
260+
rows: mockRows,
261+
truncated: true,
262+
},
263+
],
264+
stats: {DurationUs: '1000'},
265+
};
266+
267+
const result = parseQueryAPIExecuteResponse(input);
268+
expect(result.resultSets?.[0].truncated).toBe(true);
269+
});
270+
271+
it('should handle empty columns and rows', () => {
272+
const input = {
273+
result: [
274+
{
275+
columns: [],
276+
rows: [],
277+
truncated: false,
278+
},
279+
],
280+
stats: {DurationUs: '1000'},
281+
};
282+
283+
const expected = {
284+
resultSets: [
285+
{
286+
columns: [],
287+
result: [],
288+
truncated: false,
289+
},
290+
],
291+
stats: {DurationUs: '1000'},
292+
};
293+
294+
expect(parseQueryAPIExecuteResponse(input)).toEqual(expected);
295+
});
50296
});
51297
});
52298

File renamed without changes.

tests/suites/tenant/diagnostics/Diagnostics.ts

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,33 +1,96 @@
11
import type {Locator, Page} from '@playwright/test';
22

3+
import {retryAction} from '../../../utils/retryAction';
34
import {VISIBILITY_TIMEOUT} from '../TenantPage';
45

56
export enum DiagnosticsTab {
67
Info = 'Info',
78
Schema = 'Schema',
89
TopShards = 'Top shards',
10+
Queries = 'Queries',
911
Nodes = 'Nodes',
1012
Graph = 'Graph',
1113
Tablets = 'Tablets',
1214
HotKeys = 'Hot keys',
1315
Describe = 'Describe',
1416
}
1517

18+
export class Table {
19+
private table: Locator;
20+
21+
constructor(selector: Locator) {
22+
this.table = selector.locator('.ydb-resizeable-data-table');
23+
}
24+
25+
async isVisible() {
26+
await this.table.waitFor({state: 'visible', timeout: VISIBILITY_TIMEOUT});
27+
return true;
28+
}
29+
30+
async isHidden() {
31+
await this.table.waitFor({state: 'hidden', timeout: VISIBILITY_TIMEOUT});
32+
return true;
33+
}
34+
35+
async hasNoData() {
36+
const noDataCell = this.table.locator('td.data-table__no-data');
37+
return (
38+
(await noDataCell.isVisible()) &&
39+
(await this.getRowCount()) === 1 &&
40+
(await noDataCell.innerText()) === 'No data'
41+
);
42+
}
43+
44+
async getRowCount() {
45+
const rows = this.table.locator('tr.data-table__row');
46+
return rows.count();
47+
}
48+
49+
async getCellValue(row: number, col: number) {
50+
const cell = this.table.locator(`tr:nth-child(${row}) td:nth-child(${col})`);
51+
return cell.innerText();
52+
}
53+
54+
async waitForCellValue(row: number, col: number, value: string) {
55+
const cell = this.table.locator(`tr:nth-child(${row}) td:nth-child(${col})`);
56+
await retryAction(async () => {
57+
const cellValue = (await cell.innerText()).trim();
58+
if (cellValue === value) {
59+
return true;
60+
}
61+
62+
throw new Error(`Cell value ${cellValue} did not match expected ${value}`);
63+
});
64+
65+
return true;
66+
}
67+
}
68+
69+
export enum QueriesSwitch {
70+
Top = 'Top',
71+
Running = 'Running',
72+
}
73+
1674
export class Diagnostics {
75+
table: Table;
76+
1777
private tabs: Locator;
1878
private schemaViewer: Locator;
79+
private tableControls: Locator;
1980
private dataTable: Locator;
2081
private primaryKeys: Locator;
2182
private refreshButton: Locator;
2283
private autoRefreshSelect: Locator;
2384

2485
constructor(page: Page) {
2586
this.tabs = page.locator('.kv-tenant-diagnostics__tabs');
87+
this.tableControls = page.locator('.ydb-table-with-controls-layout__controls');
2688
this.schemaViewer = page.locator('.schema-viewer');
2789
this.dataTable = page.locator('.data-table__table');
2890
this.primaryKeys = page.locator('.schema-viewer__keys_type_primary');
2991
this.refreshButton = page.locator('button[aria-label="Refresh"]');
3092
this.autoRefreshSelect = page.locator('.g-select');
93+
this.table = new Table(page.locator('.object-general'));
3194
}
3295

3396
async isSchemaViewerVisible() {
@@ -45,6 +108,14 @@ export class Diagnostics {
45108
await tab.click();
46109
}
47110

111+
async clickRadioSwitch(radioName: QueriesSwitch): Promise<void> {
112+
const option = this.tableControls.locator(
113+
`.g-radio-button__option:has-text("${radioName}")`,
114+
);
115+
116+
await option.evaluate((el) => (el as HTMLElement).click());
117+
}
118+
48119
async getPrimaryKeys(): Promise<string[]> {
49120
const keysElement = this.primaryKeys.locator('.schema-viewer__keys-values');
50121
const keysText = (await keysElement.textContent()) || '';

0 commit comments

Comments
 (0)