server/models/Team.test.ts
Rama main · réplica de lectura del repositorio original.
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);
});
});
});