Skip to content
Snippets Groups Projects
webhookSyncer.spec.js 5.37 KiB
jest.mock("../../src/syncers/watcher", () => {
  function Watcher(k8sClient, gitLabClient) {
    this.k8sClient = k8sClient;
    this.gitLabClient = gitLabClient;
  }

  Watcher.prototype.start = jest.fn();
  Watcher.prototype.stop = jest.fn();
  return Watcher;
});

const WebhookSyncer = require("../../src/syncers/webhookSyncer");

const WEBHOOK_BASE_URL = "https://example.com";
const TOKEN = "a-token";

let webhookSyncer, k8sClient, gitLabClient, receiverEvent;

beforeEach(() => {
  k8sClient = {
    getSecretData: jest.fn(),
    getCustomResource: jest.fn(),
    addAnnotationsToCustomObject: jest.fn(),
  };

  k8sClient.getSecretData.mockResolvedValue({
    token: Buffer.from(TOKEN, "utf8").toString("base64"),
  });

  k8sClient.getCustomResource.mockResolvedValue(require("./events/gitrepository.json"));

  gitLabClient = {
    addWebhook: jest.fn(),
    removeWebhook: jest.fn(),
  };

  webhookSyncer = new WebhookSyncer(k8sClient, WEBHOOK_BASE_URL, gitLabClient);

  receiverEvent = JSON.parse(JSON.stringify(require("./events/receiver.json")));
});

it("constructs correctly", () => {
  expect(webhookSyncer.k8sClient).toBe(k8sClient);
  expect(webhookSyncer.gitLabClient).toBe(gitLabClient);
  expect(webhookSyncer.webhookBaseUrl).toBe(WEBHOOK_BASE_URL);
  expect(webhookSyncer.isListening).toBe(false);
  expect(webhookSyncer.name).toBe("WebhookSyncer");
});

it("starts and stops correctly", () => {
  webhookSyncer.start();
  expect(webhookSyncer.isListening).toBe(true);
  webhookSyncer.stop();
  expect(webhookSyncer.isListening).toBe(false);

  expect(webhookSyncer.watcher.start).toHaveBeenCalled();
  expect(webhookSyncer.watcher.stop).toHaveBeenCalled();
});

it("indicates it isn't listening when onClose handler invoked", () => {
  webhookSyncer.start();
  expect(webhookSyncer.isListening).toBe(true);

  webhookSyncer.onClose();
  expect(webhookSyncer.isListening).toBe(false);
})

describe("event handling", () => {
  it("ignores non-delete events for annotated repos", async () => {
    receiverEvent.metadata.annotations["platform.it.vt.edu/webhook-synced"] = "completed";

    await webhookSyncer.onEvent("POLL", receiverEvent);

    expect(gitLabClient.addWebhook).not.toHaveBeenCalled();
  });

  it("skips events for repos that don't a status (so definitely no URL)", async () => {
    delete receiverEvent.status;

    await webhookSyncer.onEvent("POLL", receiverEvent);

    expect(gitLabClient.addWebhook).not.toHaveBeenCalled();
  });

  it("skips events for repos that don't have the URL configured on the status", async () => {
    delete receiverEvent.status.webhookPath;

    await webhookSyncer.onEvent("POLL", receiverEvent);

    expect(gitLabClient.addWebhook).not.toHaveBeenCalled();
  });

  it("fails when multiple resources are found on the receiver", async () => {
    receiverEvent.spec.resources.push({});

    await webhookSyncer.onEvent("POLL", receiverEvent);

    expect(gitLabClient.addWebhook).not.toHaveBeenCalled();
  });

  it("fails when the source is not a GitRepository", async () => {
    receiverEvent.spec.resources[0].kind = "ChartRepository";

    await webhookSyncer.onEvent("DELETED", receiverEvent);

    expect(gitLabClient.addWebhook).not.toHaveBeenCalled();
  });

  it("adds the webhook when everything is set correctly", async () => {
    await webhookSyncer.onEvent("ADDED", receiverEvent);

    expect(k8sClient.getSecretData).toHaveBeenCalled();
    expect(k8sClient.getCustomResource).toHaveBeenCalledWith(
      "source.toolkit.fluxcd.io",
      "v1",
      "gitrepositories",
      "test"
    );

    expect(gitLabClient.addWebhook).toHaveBeenCalledWith(
      "test/flux-sync-test",
      "https://example.com/hook/8609574ed008cb75713738bbc61bbf105653de9e2bf50be26e02201e77cc0ad5",
      TOKEN
    );

    expect(k8sClient.addAnnotationsToCustomObject).toHaveBeenCalledWith(
      "notification.toolkit.fluxcd.io",
      "v1",
      "receivers",
      "test",
      "325926",
      {
        "platform.it.vt.edu/webhook-synced": "completed",
        "platform.it.vt.edu/webhook-sync-project": "test/flux-sync-test",
      }
    );
  });

  it("doesn't include the token when the receiver has no secretRef", async () => {
    delete receiverEvent.spec.secretRef;

    await webhookSyncer.onEvent("POLL", receiverEvent);

    expect(gitLabClient.addWebhook).toHaveBeenCalledWith(
      "test/flux-sync-test",
      "https://example.com/hook/8609574ed008cb75713738bbc61bbf105653de9e2bf50be26e02201e77cc0ad5",
      undefined
    );

    expect(k8sClient.addAnnotationsToCustomObject).toHaveBeenCalledWith(
      "notification.toolkit.fluxcd.io",
      "v1",
      "receivers",
      "test",
      "325926",
      {
        "platform.it.vt.edu/webhook-synced": "completed",
        "platform.it.vt.edu/webhook-sync-project": "test/flux-sync-test",
      }
    );
  });

  it("deletes the webhook when event type is DELETED", async () => {
    // We aren't getting the resource info because it might not exist anymore
    receiverEvent.metadata.annotations["platform.it.vt.edu/webhook-sync-project"] = "test/flux-sync-test";

    await webhookSyncer.onEvent("DELETED", receiverEvent);

    expect(k8sClient.getSecretData).not.toHaveBeenCalled();
    expect(k8sClient.getCustomResource).not.toHaveBeenCalled();
    expect(gitLabClient.removeWebhook).toHaveBeenCalledWith(
      "test/flux-sync-test",
      "https://example.com/hook/8609574ed008cb75713738bbc61bbf105653de9e2bf50be26e02201e77cc0ad5"
    );
  });
});