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
5 changes: 5 additions & 0 deletions .changeset/dry-kids-strive.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"landscape-ui": patch
---

Improve test coverage for publications and mirrors
118 changes: 69 additions & 49 deletions src/features/mirrors/components/AddMirrorForm/AddMirrorForm.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,31 +7,23 @@ import {
waitFor,
waitForElementToBeRemoved,
} from "@testing-library/react";
import { beforeEach, describe, expect, it, vi } from "vitest";
import { UBUNTU_ARCHIVE_HOST, UBUNTU_SNAPSHOTS_HOST } from "../../constants";
import type { CreateMirrorData } from "@canonical/landscape-openapi";
import { beforeEach, describe, expect, it } from "vitest";
import {
UBUNTU_ARCHIVE_HOST,
UBUNTU_PRO_HOST,
UBUNTU_SNAPSHOTS_HOST,
} from "../../constants";
import server from "@/tests/server";
import { http, HttpResponse } from "msw";
import { API_URL_DEB_ARCHIVE } from "@/constants";
import type { MirrorWritable } from "@canonical/landscape-openapi";

const PULLING_NOTE = /pulling and parsing repository data/i;

const mockCreateMirror = vi.fn();

vi.mock("../../api", async () => {
const actual = await vi.importActual("../../api");

return {
...actual,
useCreateMirror: () => ({
mutateAsync: mockCreateMirror,
}),
};
});

describe("AddMirrorForm", () => {
const user = userEvent.setup();

beforeEach(async () => {
mockCreateMirror.mockClear();

renderWithProviders(<AddMirrorForm />);

// The form renders immediately; wait for the "pulling…" note in the
Expand All @@ -52,27 +44,32 @@ describe("AddMirrorForm", () => {

await user.click(screen.getByRole("button", { name: "Add mirror" }));

expect(mockCreateMirror).toHaveBeenCalledExactlyOnceWith(
expect.objectContaining({
archiveRoot: `https://${UBUNTU_ARCHIVE_HOST}/ubuntu/`,
}),
);
expect(
await screen.findByText("You have successfully added Name."),
Comment thread
marqode marked this conversation as resolved.
).toBeInTheDocument();
});

it("submits an ubuntu archive mirror pointed at a custom CDN", async () => {
const cdnUrl = "https://eu.archive.ubuntu.com/ubuntu/";

let capturedBody: Partial<MirrorWritable> | undefined;
server.use(
http.post(`${API_URL_DEB_ARCHIVE}mirrors`, async ({ request }) => {
capturedBody = (await request.json()) as Partial<MirrorWritable>;
return HttpResponse.json({});
}),
);

const sourceUrlField = screen.getByLabelText("Source URL");
await user.clear(sourceUrlField);
await user.type(sourceUrlField, cdnUrl);

await user.click(screen.getByRole("button", { name: "Add mirror" }));

expect(mockCreateMirror).toHaveBeenCalledExactlyOnceWith(
expect.objectContaining({
archiveRoot: cdnUrl,
}),
);
expect(
await screen.findByText("You have successfully added Name."),
).toBeInTheDocument();
Comment thread
marqode marked this conversation as resolved.
expect(capturedBody).toMatchObject({ archiveRoot: cdnUrl });
});

it("rejects an http source URL with an HTTPS validation error", async () => {
Expand All @@ -94,6 +91,14 @@ describe("AddMirrorForm", () => {
it("submits an ubuntu snapshot mirror", async () => {
const date = "2026-04-15";

let capturedBody: Partial<MirrorWritable> | undefined;
server.use(
http.post(`${API_URL_DEB_ARCHIVE}mirrors`, async ({ request }) => {
capturedBody = (await request.json()) as Partial<MirrorWritable>;
return HttpResponse.json({});
}),
);

await user.selectOptions(
screen.getByLabelText("Source type"),
"Ubuntu snapshots",
Expand All @@ -105,16 +110,25 @@ describe("AddMirrorForm", () => {

await user.click(screen.getByRole("button", { name: "Add mirror" }));

expect(mockCreateMirror).toHaveBeenCalledExactlyOnceWith(
expect.objectContaining({
archiveRoot: `https://${UBUNTU_SNAPSHOTS_HOST}/ubuntu/${date}`,
}),
);
expect(
await screen.findByText("You have successfully added Name."),
).toBeInTheDocument();
expect(capturedBody).toMatchObject({
archiveRoot: `https://${UBUNTU_SNAPSHOTS_HOST}/ubuntu/${date}`,
});
});

it("submits an ubuntu pro mirror", async () => {
const token = "ABCDEFG";

let capturedBody: Partial<MirrorWritable> | undefined;
server.use(
http.post(`${API_URL_DEB_ARCHIVE}mirrors`, async ({ request }) => {
capturedBody = (await request.json()) as Partial<MirrorWritable>;
return HttpResponse.json({});
}),
);

await user.selectOptions(
screen.getByLabelText("Source type"),
"Ubuntu Pro",
Expand All @@ -123,20 +137,21 @@ describe("AddMirrorForm", () => {
await user.type(screen.getByLabelText("Token"), token);
await user.click(screen.getByRole("button", { name: "Add mirror" }));

expect(mockCreateMirror).toHaveBeenCalledExactlyOnceWith(
expect.objectContaining({}),
);
expect(
await screen.findByText("You have successfully added Name."),
).toBeInTheDocument();
expect(capturedBody).toMatchObject({
archiveRoot: expect.stringContaining(UBUNTU_PRO_HOST),
});
});

it("submits a mirror with preserve signatures enabled", async () => {
await user.click(screen.getByLabelText("Preserve upstream signing key"));
await user.click(screen.getByRole("button", { name: "Add mirror" }));

expect(mockCreateMirror).toHaveBeenCalledExactlyOnceWith(
expect.objectContaining({
preserveSignatures: true,
}),
);
expect(
await screen.findByText("You have successfully added Name."),
).toBeInTheDocument();
});

it("clears package filter and include dependencies when preserve signatures is enabled", async () => {
Expand Down Expand Up @@ -164,7 +179,15 @@ describe("AddMirrorForm", () => {
components: ["main", "universe"],
architectures: ["amd64", "arm64"],
gpgKey: { armor: "ABCDEFG" },
} satisfies Partial<CreateMirrorData["body"]>;
};

let capturedBody: Partial<MirrorWritable> | undefined;
server.use(
http.post(`${API_URL_DEB_ARCHIVE}mirrors`, async ({ request }) => {
capturedBody = (await request.json()) as Partial<MirrorWritable>;
return HttpResponse.json({});
}),
);

await user.selectOptions(
screen.getByLabelText("Source type"),
Expand All @@ -188,19 +211,16 @@ describe("AddMirrorForm", () => {
);
await user.click(screen.getByRole("button", { name: "Add mirror" }));

expect(mockCreateMirror).toHaveBeenCalledExactlyOnceWith(
expect.objectContaining(params),
);
expect(
await screen.findByText("You have successfully added Name."),
).toBeInTheDocument();
expect(capturedBody).toMatchObject(params);
});
});

describe("AddMirrorForm loading state", () => {
const user = userEvent.setup();

beforeEach(() => {
mockCreateMirror.mockClear();
});

it("renders the form immediately with a muted 'pulling' note while archive info is fetched", async () => {
renderWithProviders(<AddMirrorForm />);

Expand Down
Loading
Loading