fix(helm): helm apps do not combine in applications view if different namespace [R8S-420] (#988)

release/2.33.0-rc1
Cara Ryan 2025-08-12 10:23:27 +12:00 committed by GitHub
parent 3f030394c6
commit 208534c9d9
2 changed files with 168 additions and 3 deletions

View File

@ -0,0 +1,161 @@
import { render, screen } from '@testing-library/react';
import { vi } from 'vitest';
import { withTestQueryProvider } from '@/react/test-utils/withTestQuery';
import { withTestRouter } from '@/react/test-utils/withRouter';
import { UserViewModel } from '@/portainer/models/user';
import { withUserProvider } from '@/react/test-utils/withUserProvider';
import { PodKubernetesInstanceLabel, PodManagedByLabel } from '../../constants';
import { ApplicationsDatatable } from './ApplicationsDatatable';
const mockUseCurrentStateAndParams = vi.fn();
const mockUseEnvironmentId = vi.fn();
vi.mock('@uirouter/react', async (importOriginal: () => Promise<object>) => ({
...(await importOriginal()),
useCurrentStateAndParams: () => mockUseCurrentStateAndParams(),
}));
vi.mock('@/react/hooks/useEnvironmentId', () => ({
useEnvironmentId: () => mockUseEnvironmentId(),
}));
vi.mock('@/react/kubernetes/applications/queries/useApplications', () => ({
useApplications: () => ({
data: [
{
Id: '1',
Name: 'app1',
CreationDate: '2021-10-01T00:00:00Z',
ResourcePool: 'namespace1',
Image: 'image1',
ApplicationType: 'Pod',
Kind: 'Pod',
DeploymentType: 'Replicated',
Status: 'status1',
TotalPodsCount: 1,
RunningPodsCount: 1,
Metadata: {
labels: {
[PodKubernetesInstanceLabel]: 'helm-release-1',
[PodManagedByLabel]: 'Helm',
},
},
},
{
Id: '2',
Name: 'app2',
CreationDate: '2021-10-01T00:00:00Z',
ResourcePool: 'namespace1',
Image: 'image1',
ApplicationType: 'Pod',
Kind: 'Pod',
DeploymentType: 'Replicated',
Status: 'status1',
TotalPodsCount: 1,
RunningPodsCount: 1,
Metadata: {
labels: {
[PodKubernetesInstanceLabel]: 'helm-release-1',
[PodManagedByLabel]: 'Helm',
},
},
},
{
Id: '3',
Name: 'app3',
CreationDate: '2021-10-01T00:00:00Z',
ResourcePool: 'namespace2',
Image: 'image1',
ApplicationType: 'Pod',
Kind: 'Pod',
DeploymentType: 'Replicated',
Status: 'status1',
TotalPodsCount: 1,
RunningPodsCount: 1,
Metadata: {
labels: {
[PodKubernetesInstanceLabel]: 'helm-release-1',
[PodManagedByLabel]: 'Helm',
},
},
},
],
isLoading: false,
}),
}));
vi.mock('@@/Link', () => ({
Link: ({ children }: { children: React.ReactNode }) => (
<span data-testid="mock-link">{children}</span>
),
}));
vi.mock('@/react/kubernetes/components/CreateFromManifestButton', () => ({
CreateFromManifestButton: ({
children,
...props
}: {
children?: React.ReactNode;
'data-cy'?: string;
}) => (
<button type="button" data-testid="mock-create-manifest-button" {...props}>
{children || 'Create from manifest'}
</button>
),
}));
function renderComponent() {
const user = new UserViewModel({ Username: 'user' });
const Wrapped = withTestQueryProvider(
withUserProvider(withTestRouter(ApplicationsDatatable), user)
);
return render(
<Wrapped
tableState={{
search: '',
setSearch: () => {},
namespace: '',
setNamespace: () => {},
showSystemResources: false,
autoRefreshRate: 0,
setAutoRefreshRate: () => {},
setShowSystemResources: () => {},
sortBy: { id: 'Name', desc: false },
setSortBy: () => {},
pageSize: 10,
setPageSize: () => {},
}}
/>
);
}
describe('ApplicationsDatatable', () => {
beforeEach(() => {
mockUseEnvironmentId.mockReturnValue(3);
mockUseCurrentStateAndParams.mockReturnValue({
params: {},
});
});
it('should group helm apps by namespace and instance label', async () => {
renderComponent();
const helmReleases = await screen.findAllByText('helm-release-1');
expect(helmReleases).toHaveLength(2);
// Should show both namespaces in table cells
const namespace1Cells = await screen.findAllByRole('cell', {
name: 'namespace1',
});
const namespace2Cells = await screen.findAllByRole('cell', {
name: 'namespace2',
});
expect(namespace1Cells.length).toBeGreaterThan(0);
expect(namespace2Cells.length).toBeGreaterThan(0);
});
});

View File

@ -170,14 +170,18 @@ function separateHelmApps(applications: Application[]): ApplicationRowData[] {
const groupedHelmApps: Record<string, Application[]> = groupBy(
helmApps,
(app) => app.Metadata?.labels[PodKubernetesInstanceLabel] ?? ''
(app) =>
`${app.ResourcePool}/${
app.Metadata?.labels[PodKubernetesInstanceLabel] ?? ''
}`
);
// build the helm apps row data from the grouped helm apps
const helmAppsRowData = Object.entries(groupedHelmApps).reduce<
ApplicationRowData[]
>((helmApps, [appName, apps]) => {
const helmApp = buildHelmAppRowData(appName, apps);
>((helmApps, [groupKey, apps]) => {
const instanceLabel = groupKey.split('/')[1];
const helmApp = buildHelmAppRowData(instanceLabel, apps);
return [...helmApps, helmApp];
}, []);