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 => ({ 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, }); }); });