Files
prowler/ui/components/registry/registry-explorer.model.test.ts
T

305 lines
7.8 KiB
TypeScript

import { describe, expect, it } from "vitest";
import type { RegistryCatalogArtifact } from "@/types/registry";
import { buildRegistryMarketplaceModel } from "./registry-explorer.model";
const artifact = (
normalizedName: string,
overrides: Partial<RegistryCatalogArtifact> = {},
): RegistryCatalogArtifact => ({
normalizedName,
name: normalizedName,
providers: [],
isVerified: false,
isOfficial: false,
isBuiltin: false,
isMeta: false,
hasProvider: false,
hasChecks: false,
hasCompliance: false,
isInstallable: false,
versionCount: 0,
totalDownloads: 0,
owners: [],
...overrides,
});
describe("Registry marketplace model", () => {
it("reports a newer release for an installed checks artifact that defines no provider", () => {
// Given
const catalog = {
status: "complete" as const,
artifacts: [
artifact("aws-checks", {
latestVersion: "0.3.0",
hasChecks: true,
isInstallable: true,
}),
],
};
// When
const model = buildRegistryMarketplaceModel(
catalog,
[
{
normalizedName: "aws-checks",
versionSpec: "latest",
resolvedVersion: "0.2.2",
extendsProviderSlugs: ["aws"],
},
],
{},
"name",
);
// Then
expect(model).toMatchObject({
artifacts: [{ updateAvailable: true, extendsProviderSlugs: ["aws"] }],
myArtifacts: [
{
extendsProviderSlugs: ["aws"],
catalogArtifact: { extendsProviderSlugs: ["aws"] },
},
],
});
});
it("offers the catalog version for an installed artifact with a different resolved version", () => {
// Given
const catalog = {
status: "complete" as const,
artifacts: [
artifact("template", { latestVersion: " 1.1.0 ", hasProvider: true }),
],
};
// When
const model = buildRegistryMarketplaceModel(
catalog,
[
{
normalizedName: "template",
versionSpec: "latest",
resolvedVersion: " 1.0.0 ",
},
],
{},
"name",
);
// Then
expect(model).toMatchObject({
artifacts: [
{
isAdded: true,
resolvedVersion: "1.0.0",
latestVersion: "1.1.0",
updateAvailable: true,
},
],
myArtifacts: [{ catalogArtifact: { updateAvailable: true } }],
});
});
it.each([
{ resolvedVersion: "1.0.0", latestVersion: "1.0.0", expected: false },
{ resolvedVersion: "2.0.0", latestVersion: "1.0.0", expected: true },
{ resolvedVersion: undefined, latestVersion: "1.0.0", expected: false },
{ resolvedVersion: " ", latestVersion: "1.0.0", expected: false },
{ resolvedVersion: "1.0.0", latestVersion: undefined, expected: false },
])(
"compares resolved $resolvedVersion against catalog $latestVersion ($expected)",
(example) => {
// Given / When
const model = buildRegistryMarketplaceModel(
{
status: "complete",
artifacts: [artifact("template", { hasProvider: true, ...example })],
},
[
{
normalizedName: "template",
versionSpec: "latest",
resolvedVersion: example.resolvedVersion,
},
],
{},
"name",
);
// Then
expect(model).toMatchObject({
artifacts: [{ updateAvailable: example.expected }],
});
},
);
it("keeps the full catalog visible with tenant membership merged in", () => {
// Given
const catalog = {
status: "complete" as const,
artifacts: [
artifact("zeta", { providers: ["azure"], hasProvider: true }),
artifact("core", { providers: ["aws"], isOfficial: true }),
artifact("global", {
name: "Global insight",
description: "Security checks",
providers: ["aws", "gcp"],
hasChecks: true,
isOfficial: true,
}),
],
};
const mine = [
{ normalizedName: "core", versionSpec: "latest" },
{ normalizedName: "manual", versionSpec: "1.2.3" },
];
// When
const model = buildRegistryMarketplaceModel(catalog, mine, {}, "name");
// Then
expect(model).toMatchObject({
isComplete: true,
providers: ["aws", "azure", "gcp"],
});
if (!model.isComplete) throw new Error("expected complete model");
expect(
model.artifacts.map(({ normalizedName, isAdded }) => ({
normalizedName,
isAdded,
})),
).toEqual([
{ normalizedName: "core", isAdded: true },
{ normalizedName: "global", isAdded: false },
{ normalizedName: "zeta", isAdded: false },
]);
expect(model.myArtifacts).toEqual([
{
normalizedName: "core",
versionSpec: "latest",
extendsProviderSlugs: [],
catalogArtifact: expect.objectContaining({
normalizedName: "core",
isAdded: true,
}),
},
{
normalizedName: "manual",
versionSpec: "1.2.3",
extendsProviderSlugs: [],
catalogArtifact: undefined,
},
]);
});
it("applies search, provider, and capability filters together", () => {
// Given
const catalog = {
status: "complete" as const,
artifacts: [
artifact("core", { providers: ["aws"] }),
artifact("global", {
name: "Global insight",
description: "Security checks",
providers: ["aws", "gcp"],
hasChecks: true,
}),
artifact("zeta", { providers: ["azure"], hasProvider: true }),
],
};
// When
const model = buildRegistryMarketplaceModel(
catalog,
[],
{ search: "security", providers: ["aws"], capabilities: ["checks"] },
"name",
);
// Then
if (!model.isComplete) throw new Error("expected complete model");
expect(model.artifacts.map(({ normalizedName }) => normalizedName)).toEqual(
["global"],
);
});
it("unions providers and capabilities within each filter", () => {
// Given
const catalog = {
status: "complete" as const,
artifacts: [
artifact("aws-checks", { providers: ["aws"], hasChecks: true }),
artifact("gcp-provider", { providers: ["gcp"], hasProvider: true }),
artifact("azure-checks", { providers: ["azure"], hasChecks: true }),
],
};
// When
const model = buildRegistryMarketplaceModel(
catalog,
[],
{ providers: ["aws", "gcp"], capabilities: ["checks", "provider"] },
"name",
);
// Then
expect(model).toMatchObject({
artifacts: [
expect.objectContaining({ normalizedName: "aws-checks" }),
expect.objectContaining({ normalizedName: "gcp-provider" }),
],
});
});
it("sorts by downloads descending with name as the tiebreak", () => {
// Given
const catalog = {
status: "complete" as const,
artifacts: [
artifact("alpha", { totalDownloads: 5 }),
artifact("delta", { totalDownloads: 9 }),
artifact("beta", { totalDownloads: 5 }),
],
};
// When
const model = buildRegistryMarketplaceModel(catalog, [], {}, "downloads");
// Then
if (!model.isComplete) throw new Error("expected complete model");
expect(model.artifacts.map(({ normalizedName }) => normalizedName)).toEqual(
["delta", "alpha", "beta"],
);
});
it("keeps incomplete catalogs out of complete-only controls and selectors", () => {
// Given
const catalog = {
status: "incomplete" as const,
reason: "page_failed" as const,
collectedCount: 3,
};
// When
const model = buildRegistryMarketplaceModel(
catalog,
[],
{ search: "core" },
"name",
);
// Then
expect(model).toEqual({
isComplete: false,
});
});
});