Ctrl K
ComunidadGuías…

server/models/Team.test.ts

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

Archivo original ↗Preguntar a KiBot
import { randomUUID } from "node:crypto";
import { TeamValidation } from "@shared/validations";
import { Team } from "@server/models";
import {
  buildTeam,
  buildCollection,
  buildAttachment,
  buildSubdomain,
} from "@server/test/factories";

describe("Team", () => {
  describe("findByDomain", () => {
    it("should find a team by its domain", async () => {
      const domain = `${randomUUID()}.example.com`;
      const team = await buildTeam({ domain });
      const result = await Team.findByDomain(domain);
      expect(result?.id).toEqual(team.id);
    });

    it("should normalize domain to lowercase", async () => {
      const id = randomUUID().split("-")[0];
      const team = await buildTeam({ domain: `${id}.example.com` });
      const result = await Team.findByDomain(`${id}.Example.COM`);
      expect(result?.id).toEqual(team.id);
    });

    it("should strip protocol from input", async () => {
      const domain = `${randomUUID()}.example.com`;
      const team = await buildTeam({ domain });
      const result = await Team.findByDomain(`https://${domain}`);
      expect(result?.id).toEqual(team.id);
    });

    it("should strip port from input", async () => {
      const domain = `${randomUUID()}.example.com`;
      const team = await buildTeam({ domain });
      const result = await Team.findByDomain(`${domain}:3000`);
      expect(result?.id).toEqual(team.id);
    });

    it("should strip path from input", async () => {
      const domain = `${randomUUID()}.example.com`;
      const team = await buildTeam({ domain });
      const result = await Team.findByDomain(`${domain}/some/path`);
      expect(result?.id).toEqual(team.id);
    });

    it("should return null for unregistered domain", async () => {
      const result = await Team.findByDomain("unknown.example.com");
      expect(result).toBeNull();
    });
  });

  describe("findAvailableSubdomain", () => {
    it("should slugify the requested value", async () => {
      const slug = buildSubdomain();
      const subdomain = await Team.findAvailableSubdomain(
        `${slug.toUpperCase()} Inc`
      );
      expect(subdomain).toEqual(`${slug}-inc`);
    });

    it("should strip a trailing top-level domain", async () => {
      const slug = buildSubdomain();
      expect(await Team.findAvailableSubdomain(`${slug}.com`)).toEqual(slug);
      expect(await Team.findAvailableSubdomain(`${slug} .com`)).toEqual(slug);
      expect(await Team.findAvailableSubdomain(`${slug}.io`)).toEqual(slug);
    });

    it("should fall back to a default when too short or reserved", async () => {
      expect(await Team.findAvailableSubdomain("ab")).toMatch(/^team\d*$/);
      expect(await Team.findAvailableSubdomain("www")).toMatch(/^team\d*$/);
    });

    it("should append a number when the subdomain is taken", async () => {
      const slug = buildSubdomain();
      await buildTeam({ subdomain: slug });
      expect(await Team.findAvailableSubdomain(slug)).toEqual(`${slug}1`);
    });

    it("should truncate to the maximum length", async () => {
      const max = TeamValidation.maxSubdomainSelfHostedLength;
      const slug = buildSubdomain();
      const requested = slug.padEnd(max + 20, "a");
      expect(await Team.findAvailableSubdomain(requested)).toEqual(
        requested.slice(0, max)
      );
    });

    it("should remove trailing hyphens after truncating", async () => {
      const max = TeamValidation.maxSubdomainSelfHostedLength;
      const slug = buildSubdomain().padEnd(max - 1, "a");
      expect(await Team.findAvailableSubdomain(`${slug} more`)).toEqual(slug);
    });

    it("should keep the numeric suffix within the maximum length", async () => {
      const max = TeamValidation.maxSubdomainSelfHostedLength;
      const slug = buildSubdomain().padEnd(max, "a");
      await buildTeam({ subdomain: slug });
      expect(await Team.findAvailableSubdomain(slug)).toEqual(
        `${slug.slice(0, max - 1)}1`
      );
    });
  });

  describe("collectionIds", () => {
    it("should return non-private collection ids", async () => {
      const team = await buildTeam();
      const collection = await buildCollection({
        teamId: team.id,
      });
      // build a collection in another team
      await buildCollection();
      // build a private collection
      await buildCollection({
        teamId: team.id,
        permission: null,
      });
      const response = await team.collectionIds();
      expect(response.length).toEqual(1);
      expect(response[0]).toEqual(collection.id);
    });
  });

  describe("previousSubdomains", () => {
    it("should list the previous subdomains", async () => {
      const id = randomUUID();
      const originalSubdomain = `example-${id}`;
      const subdomain = `updated-${id}`;
      const subdomain2 = `another-${id}`;
      const team = await buildTeam({
        subdomain: originalSubdomain,
      });

      await team.update({ subdomain });
      expect(team.subdomain).toEqual(subdomain);
      expect(team.previousSubdomains?.length).toEqual(1);
      expect(team.previousSubdomains?.[0]).toEqual(originalSubdomain);

      await team.update({ subdomain: subdomain2 });
      expect(team.subdomain).toEqual(subdomain2);
      expect(team.previousSubdomains?.length).toEqual(2);
      expect(team.previousSubdomains?.[0]).toEqual(originalSubdomain);
      expect(team.previousSubdomains?.[1]).toEqual(subdomain);
    });

    it("should persist every previous subdomain", async () => {
      const id = randomUUID();
      const subdomains = [1, 2, 3, 4, 5].map((n) => `sub${n}-${id}`);
      const team = await buildTeam({ subdomain: subdomains[0] });

      for (const subdomain of subdomains.slice(1)) {
        await team.update({ subdomain });
      }

      await team.reload();
      expect(team.previousSubdomains).toEqual(subdomains.slice(1, 4));
      expect((await Team.findBySubdomain(subdomains[3]))?.id).toEqual(team.id);
      expect(await Team.findBySubdomain(subdomains[0])).toBeNull();
    });
  });

  describe("publicAvatarUrl", () => {
    it("should return null when no avatarUrl is set", async () => {
      const team = await buildTeam({ avatarUrl: null });
      const result = await team.publicAvatarUrl();
      expect(result).toBeNull();
    });

    it("should return external URL unchanged", async () => {
      const url = "https://example.com/logo.png";
      const team = await buildTeam({ avatarUrl: url });
      const result = await team.publicAvatarUrl();
      expect(result).toEqual(url);
    });

    it("should return signed URL for private-bucket attachment redirect", async () => {
      vi.useFakeTimers();
      vi.setSystemTime(new Date("2026-04-16T00:00:00.000Z"));
      try {
        const team = await buildTeam();
        const attachment = await buildAttachment({
          teamId: team.id,
          acl: "private",
        });

        await team.update({
          avatarUrl: `/api/attachments.redirect?id=${attachment.id}`,
        });

        const result = await team.publicAvatarUrl();
        expect(result).toEqual(await attachment.signedUrl);
      } finally {
        vi.useRealTimers();
      }
    });

    it("should return canonical URL for public-bucket attachment redirect", async () => {
      const team = await buildTeam();
      const id = randomUUID();
      const attachment = await buildAttachment({
        id,
        teamId: team.id,
        key: `avatars/${team.id}/${id}/logo.png`,
        acl: "public-read",
      });

      await team.update({
        avatarUrl: `/api/attachments.redirect?id=${attachment.id}`,
      });

      const result = await team.publicAvatarUrl();
      expect(result).toEqual(attachment.canonicalUrl);
    });
  });
});