Skip to content

Commit 136862d

Browse files
fix(frontend): use grpc base path
1 parent 6ee88a1 commit 136862d

3 files changed

Lines changed: 168 additions & 3 deletions

File tree

frontend/src/app.test.tsx

Lines changed: 133 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,133 @@
1+
/**
2+
* Copyright 2022 Redpanda Data, Inc.
3+
*
4+
* Use of this software is governed by the Business Source License
5+
* included in the file https://github.com/redpanda-data/redpanda/blob/dev/licenses/bsl.md
6+
*
7+
* As of the Change Date specified in that file, in accordance with
8+
* the Business Source License, use of this software will be governed
9+
* by the Apache License, Version 2.0
10+
*/
11+
12+
// This test imports app.tsx to assert its module-level transport wiring, so the
13+
// app's heavy runtime dependencies are mocked below.
14+
vi.mock('@connectrpc/connect-web', () => ({
15+
createConnectTransport: vi.fn(() => ({ kind: 'transport' })),
16+
}));
17+
18+
vi.mock('./config', () => ({
19+
setup: vi.fn(() => vi.fn()),
20+
getGrpcBasePath: vi.fn(() => '/redpanda-console'),
21+
addBearerTokenInterceptor: vi.fn((next) => async (request: unknown) => await next(request)),
22+
checkExpiredLicenseInterceptor: vi.fn((next) => async (request: unknown) => await next(request)),
23+
}));
24+
25+
vi.mock('@builder.io/sdk-react', () => ({
26+
Content: () => null,
27+
}));
28+
29+
vi.mock('@connectrpc/connect-query', () => ({
30+
TransportProvider: ({ children }: { children: React.ReactNode }) => children,
31+
}));
32+
33+
vi.mock('@redpanda-data/ui', () => ({
34+
ChakraProvider: ({ children }: { children: React.ReactNode }) => children,
35+
redpandaToastOptions: {},
36+
}));
37+
38+
vi.mock('@tanstack/react-query', () => ({
39+
QueryClientProvider: ({ children }: { children: React.ReactNode }) => children,
40+
}));
41+
42+
vi.mock('@tanstack/react-query-devtools', () => ({
43+
ReactQueryDevtools: () => null,
44+
}));
45+
46+
vi.mock('@tanstack/react-router', () => ({
47+
createRouter: vi.fn(() => ({})),
48+
RouterProvider: () => null,
49+
}));
50+
51+
vi.mock('components/builder-io/builder-custom-components', () => ({
52+
builderCustomComponents: [],
53+
}));
54+
55+
vi.mock('components/constants', () => ({
56+
BUILDER_API_KEY: 'test-key',
57+
}));
58+
59+
vi.mock('custom-feature-flag-provider', () => ({
60+
CustomFeatureFlagProvider: ({ children }: { children: React.ReactNode }) => children,
61+
}));
62+
63+
vi.mock('hooks/use-developer-view', () => ({
64+
default: () => false,
65+
}));
66+
67+
vi.mock('protobuf-registry', () => ({
68+
protobufRegistry: {},
69+
}));
70+
71+
vi.mock('query-client', () => ({
72+
default: {},
73+
}));
74+
75+
vi.mock('utils/env', () => ({
76+
getBasePath: () => '/redpanda-console',
77+
}));
78+
79+
vi.mock('utils/redpanda-theme', () => ({
80+
patchedRedpandaTheme: {},
81+
}));
82+
83+
vi.mock('./components/misc/not-found-page', () => ({
84+
NotFoundPage: () => null,
85+
}));
86+
87+
vi.mock('./routeTree.gen', () => ({
88+
routeTree: {},
89+
}));
90+
91+
vi.mock('./state/ui', () => ({
92+
installUISettingsSideEffects: vi.fn(() => vi.fn()),
93+
}));
94+
95+
const importAppWithMocks = async ({ grpcBasePath = '/redpanda-console' }: { grpcBasePath?: string } = {}) => {
96+
const { getGrpcBasePath } = await import('./config');
97+
vi.mocked(getGrpcBasePath).mockReturnValue(grpcBasePath);
98+
99+
await import('./app');
100+
101+
const { createConnectTransport } = await import('@connectrpc/connect-web');
102+
103+
return { createConnectTransport, getGrpcBasePath };
104+
};
105+
106+
describe('App dataplane transport', () => {
107+
beforeEach(() => {
108+
vi.resetModules();
109+
vi.resetAllMocks();
110+
});
111+
112+
test('uses the configured gRPC base path for Connect Query requests', async () => {
113+
const { createConnectTransport, getGrpcBasePath } = await importAppWithMocks();
114+
115+
expect(getGrpcBasePath).toHaveBeenCalledWith();
116+
expect(createConnectTransport).toHaveBeenCalledWith(
117+
expect.objectContaining({
118+
baseUrl: '/redpanda-console',
119+
})
120+
);
121+
});
122+
123+
test('keeps origin-root behavior for root-mounted deployments', async () => {
124+
const { createConnectTransport, getGrpcBasePath } = await importAppWithMocks({ grpcBasePath: '' });
125+
126+
expect(getGrpcBasePath).toHaveBeenCalledWith();
127+
expect(createConnectTransport).toHaveBeenCalledWith(
128+
expect.objectContaining({
129+
baseUrl: '',
130+
})
131+
);
132+
});
133+
});

frontend/src/app.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ import { installUISettingsSideEffects } from './state/ui';
5555

5656
// Create transport before router so loaders can use it
5757
const dataplaneTransport = createConnectTransport({
58-
baseUrl: getGrpcBasePath(''), // Embedded mode handles the path separately.
58+
baseUrl: getGrpcBasePath(),
5959
interceptors: [addBearerTokenInterceptor, checkExpiredLicenseInterceptor],
6060
jsonOptions: {
6161
registry: protobufRegistry,

frontend/src/federation/console-app.test.tsx

Lines changed: 34 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,10 @@
1111

1212
import { act, render, waitFor } from '@testing-library/react';
1313

14+
vi.mock('@connectrpc/connect-web', () => ({
15+
createConnectTransport: vi.fn(() => ({ kind: 'transport' })),
16+
}));
17+
1418
// Mock TanStack Router before any imports
1519
vi.mock('@tanstack/react-router', async () => {
1620
const actual = await vi.importActual('@tanstack/react-router');
@@ -39,7 +43,7 @@ vi.mock('config', () => ({
3943
jwt: undefined as string | undefined,
4044
},
4145
setup: vi.fn(),
42-
getGrpcBasePath: vi.fn(() => 'http://localhost:9090'),
46+
getGrpcBasePath: vi.fn((overrideUrl?: string) => overrideUrl ?? 'http://localhost:9090'),
4347
addBearerTokenInterceptor: vi.fn((next) => async (request: unknown) => await next(request)),
4448
checkExpiredLicenseInterceptor: vi.fn((next) => async (request: unknown) => await next(request)),
4549
}));
@@ -74,9 +78,11 @@ vi.mock('./token-manager', () => ({
7478
},
7579
}));
7680

81+
import { createConnectTransport } from '@connectrpc/connect-web';
82+
7783
import { ConsoleApp } from './console-app';
7884
import type { ConsoleAppProps } from './types';
79-
import { config, setup } from '../config';
85+
import { config, getGrpcBasePath, setup } from '../config';
8086

8187
describe('ConsoleApp', () => {
8288
const mockGetAccessToken = vi.fn();
@@ -463,6 +469,32 @@ describe('ConsoleApp', () => {
463469
});
464470
});
465471

472+
test('uses explicit gRPC URL override for the federated Connect Query transport', async () => {
473+
render(<ConsoleApp {...defaultProps} config={{ urlOverride: { grpc: 'https://grpc.example.com' } }} />);
474+
475+
await waitFor(() => {
476+
expect(getGrpcBasePath).toHaveBeenCalledWith('https://grpc.example.com');
477+
expect(createConnectTransport).toHaveBeenCalledWith(
478+
expect.objectContaining({
479+
baseUrl: 'https://grpc.example.com',
480+
})
481+
);
482+
});
483+
});
484+
485+
test('uses the default gRPC base path for federated transport when no override is provided', async () => {
486+
render(<ConsoleApp {...defaultProps} />);
487+
488+
await waitFor(() => {
489+
expect(getGrpcBasePath).toHaveBeenCalledWith(undefined);
490+
expect(createConnectTransport).toHaveBeenCalledWith(
491+
expect.objectContaining({
492+
baseUrl: 'http://localhost:9090',
493+
})
494+
);
495+
});
496+
});
497+
466498
test('initializes without throwing when config is undefined', async () => {
467499
const propsWithoutConfig = {
468500
...defaultProps,

0 commit comments

Comments
 (0)