Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
75 changes: 75 additions & 0 deletions web/cypress/e2e/integration/logs-page.cy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -163,6 +163,62 @@ describe('Logs Page', () => {
});
});

it('displays a Loki error payload returned with HTTP 200', () => {
cy.intercept(QUERY_RANGE_STREAMS_URL_MATCH, {
statusCode: 200,
body: {
status: 'error',
errorType: 'bad_data',
error: 'parse error at line 1, col 1: unexpected IDENTIFIER',
},
}).as('queryRangeStreams');

cy.visit(LOGS_PAGE_URL);

cy.wait('@queryRangeStreams');

cy.byTestID(TestIds.LogsTable)
.should('exist')
.within(() => {
cy.contains(/bad_data/i);
cy.contains('parse error at line 1, col 1: unexpected IDENTIFIER');
});
});

it('keeps the latest query results when an earlier request completes late', () => {
let requestCount = 0;

cy.intercept(QUERY_RANGE_STREAMS_URL_MATCH, (req) => {
requestCount += 1;
const body = queryRangeStreamsValidResponse({
message:
requestCount === 1
? 'initial result'
: requestCount === 2
? 'stale result'
: 'latest result',
});

req.reply(requestCount === 2 ? { body, delay: 3_000 } : body);
}).as('queryRangeStreams');

cy.visit(LOGS_PAGE_URL);
cy.wait('@queryRangeStreams');

cy.wait(1_100);
cy.byTestID(TestIds.SyncButton).click();
cy.byTestID(TestIds.TimeRangeDropdown).click();
cy.contains('Last 6 hours').click();

cy.contains('latest result').should('exist');
cy.byTestID(TestIds.LoadMoreLogs).should('exist');

cy.wait(3_100);
cy.contains('latest result').should('exist');
cy.contains('stale result').should('not.exist');
cy.byTestID(TestIds.LoadMoreLogs).should('exist');
});

it('executes a query when "run query" is pressed', () => {
cy.intercept(
QUERY_RANGE_STREAMS_URL_MATCH,
Expand Down Expand Up @@ -318,6 +374,25 @@ describe('Logs Page', () => {
cy.byTestID(TestIds.TimeRangeDropdown).find('button').should('contain', 'Last 6 hours');
});

it('does not refresh while a log request is pending', () => {
cy.intercept(QUERY_RANGE_STREAMS_URL_MATCH, (req) => {
req.reply({
body: queryRangeStreamsValidResponse({ message: TEST_MESSAGE }),
delay: 30_000,
});
}).as('queryRangeStreams');

cy.visit(LOGS_PAGE_URL);
cy.get('@queryRangeStreams.all').should('have.length', 1);
cy.clock();

cy.byTestID(TestIds.RefreshIntervalDropdown).click();
cy.contains('15 seconds').click();
cy.tick(15_000);

cy.get('@queryRangeStreams.all').should('have.length', 1);
});

it('disables query executors when the query is empty', () => {
cy.intercept(
QUERY_RANGE_STREAMS_URL_MATCH,
Expand Down
6 changes: 6 additions & 0 deletions web/eslint.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,12 @@ const compat = new FlatCompat({

export default defineConfig([
{
linterOptions: {
// eslint --fix will get in a loop where there is no error so it deletes the directive,
// which in turn causes the error to then be shown.
reportUnusedDisableDirectives: 'off',
},

extends: fixupConfigRules(
compat.extends(
'eslint:recommended',
Expand Down
18 changes: 17 additions & 1 deletion web/src/__tests__/loki-client.spec.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { SchemaConfig } from '../logs.types';
import { getFetchConfig } from '../loki-client';
import { assertQueryRangeResponse, getFetchConfig, throwResponseError } from '../loki-client';

jest.mock('@openshift-console/dynamic-plugin-sdk', () => ({
consoleFetchJSON: jest.fn(),
Expand Down Expand Up @@ -64,4 +64,20 @@ describe('Loki Client', () => {
expect(getFetchConfig(config)).toEqual(expectedFetchConfig);
});
});

it('rejects Loki error responses', () => {
expect(() =>
throwResponseError({
status: 'error',
errorType: 'bad_data',
error: 'parse error at line 1, col 1',
}),
).toThrow('bad_data: parse error at line 1, col 1');
});

it('rejects malformed successful responses', () => {
expect(() => assertQueryRangeResponse({ status: 'success', data: {} })).toThrow(
'Invalid Loki query response: missing data.result',
);
});
});
15 changes: 13 additions & 2 deletions web/src/components/refresh-interval-dropdown.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -29,11 +29,13 @@ const refreshIntervalOptions = [
interface RefreshIntervalDropdownProps {
onRefresh?: () => void;
isDisabled?: boolean;
refreshEnabled?: boolean;
}

export const RefreshIntervalDropdown: FC<RefreshIntervalDropdownProps> = ({
onRefresh,
isDisabled = false,
refreshEnabled = true,
}) => {
const { t } = useTranslation('plugin__logging-view-plugin');

Expand All @@ -51,6 +53,9 @@ export const RefreshIntervalDropdown: FC<RefreshIntervalDropdownProps> = ({
const onRefreshRef = useRef(onRefresh);
// eslint-disable-next-line react-hooks/refs
onRefreshRef.current = onRefresh;
const refreshEnabledRef = useRef(refreshEnabled);
// eslint-disable-next-line react-hooks/refs
refreshEnabledRef.current = refreshEnabled;

const clearTimer = () => {
if (timer.current) {
Expand All @@ -70,8 +75,14 @@ export const RefreshIntervalDropdown: FC<RefreshIntervalDropdownProps> = ({
clearTimer();

if (delay !== 0) {
onRefreshRef.current?.();
timer.current = setInterval(() => onRefreshRef.current?.(), delay);
if (refreshEnabledRef.current) {
onRefreshRef.current?.();
}
timer.current = setInterval(() => {
if (refreshEnabledRef.current) {
onRefreshRef.current?.();
}
}, delay);
}

return () => clearTimer();
Expand Down
2 changes: 2 additions & 0 deletions web/src/components/virtualized-logs-table.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import {
import { useTranslation } from 'react-i18next';
import { LogTableData, Schema } from '../logs.types';
import { getSeverityColor, Severity } from '../severity';
import { TestIds } from '../test-ids';
import { CenteredContainer } from './centered-container';
import { ErrorMessage } from './error-message';

Expand Down Expand Up @@ -420,6 +421,7 @@ export const VirtualizedLogsTable = ({
<Tbody>
<Tr
className="lv-plugin__table__row-info lv-plugin__table__row-more-data"
data-test={TestIds.LoadMoreLogs}
onClick={() => {
setScrollToIndex(data.length - 1);
onLoadMore?.();
Expand Down
Loading