Ctrl K
ComunidadGuías…

lib/billing-gift.test.ts

Rama main · réplica de lectura del repositorio original.

Archivo original ↗Preguntar a KiBot
import { describe, expect, it } from "vitest";

import {
  DEFAULT_GIFT_DURATION,
  GIFT_DURATIONS,
  coerceGiftDuration,
  giftExpiresAt,
  isGiftExpired,
} from "./billing-gift";

/**
 * Give a plan for a given time.
 *
 * What these tests protect: A gift STOPS. The right does not fall because
 * a cron has passed, but because the read refuses an expired override —
 * it is `isGiftExpired` * which carries this promise, and the shift of one second
 * in one direction or the other is the difference between "one month offered" and
 * "offered for always”.
 */

const NOW = new Date("2026-08-04T09:30:00.000Z");

describe("giftExpiresAt", () => {
  it("compte les mois en calendrier, pas en tranches de 30 jours", () => {
    expect(giftExpiresAt("1m", NOW)).toBe("2026-09-04T09:30:00.000Z");
    expect(giftExpiresAt("3m", NOW)).toBe("2026-11-04T09:30:00.000Z");
    expect(giftExpiresAt("1y", NOW)).toBe("2027-08-04T09:30:00.000Z");
  });

  it("clampe le jour au dernier jour du mois cible", () => {
    // A free month on January 31 ends at the end of February, never at the beginning of March: otherwise
    // the gift would last one day longer than announced.
    expect(giftExpiresAt("1m", new Date("2026-01-31T12:00:00.000Z"))).toBe(
      "2026-02-28T12:00:00.000Z"
    );
    expect(giftExpiresAt("1m", new Date("2028-01-31T12:00:00.000Z"))).toBe(
      "2028-02-29T12:00:00.000Z"
    );
  });

  it("compte les jours pour la durée courte", () => {
    expect(giftExpiresAt("7d", NOW)).toBe("2026-08-11T09:30:00.000Z");
  });

  it("ne pose AUCUNE échéance pour un cadeau sans limite", () => {
    expect(giftExpiresAt("unlimited", NOW)).toBeNull();
  });

  it("donne une date à chaque durée proposée sauf « sans limite »", () => {
    // A duration added to the list without its calculation would fall here, not in prod.
    for (const duration of GIFT_DURATIONS) {
      const at = giftExpiresAt(duration, NOW);
      if (duration === "unlimited") {
        expect(at).toBeNull();
        continue;
      }
      expect(new Date(at as string).getTime()).toBeGreaterThan(NOW.getTime());
    }
  });
});

describe("coerceGiftDuration", () => {
  it("accepte les durées connues et rejette tout le reste", () => {
    expect(coerceGiftDuration(DEFAULT_GIFT_DURATION)).toBe("1m");
    expect(coerceGiftDuration("2m")).toBeNull();
    expect(coerceGiftDuration(30)).toBeNull();
    expect(coerceGiftDuration(null)).toBeNull();
  });
});

describe("isGiftExpired", () => {
  const now = NOW.getTime();

  it("laisse vivre un cadeau sans échéance", () => {
    expect(isGiftExpired(null, now)).toBe(false);
    expect(isGiftExpired(undefined, now)).toBe(false);
  });

  it("laisse vivre un cadeau dont l'échéance est devant", () => {
    expect(isGiftExpired("2026-08-04T09:30:01.000Z", now)).toBe(false);
  });

  it("coupe à la seconde où l'échéance est atteinte", () => {
    expect(isGiftExpired("2026-08-04T09:30:00.000Z", now)).toBe(true);
    expect(isGiftExpired("2026-08-04T09:29:59.000Z", now)).toBe(true);
  });

  it("ne retire pas un droit sur une date illisible", () => {
    expect(isGiftExpired("pas une date", now)).toBe(false);
  });
});