Compare commits

...

1 commit

Author SHA1 Message Date
Claude
1d83347992
Add comprehensive tests for identifier-resolver module
Added complete test coverage for the identifier-resolver utility module which provides username/UUID resolution functionality. The test suite includes:

- isUUID validation tests (valid/invalid formats, case sensitivity, edge cases)
- resolveIdentifierToCreator tests (username lookups, UUID fallbacks, error handling)
- resolveIdentifierToUserId tests (optimization paths, resolution logic)
- resolveIdentifierToUsername tests (direct returns, API resolution)
- Integration scenarios (full workflows, mixed case handling)

All 26 tests passing with comprehensive mocking of fetch API calls.
2026-01-03 19:30:57 +00:00

View file

@ -0,0 +1,403 @@
import { describe, it, expect, vi, beforeEach, afterEach } from "vitest";
import {
isUUID,
resolveIdentifierToCreator,
resolveIdentifierToUserId,
resolveIdentifierToUsername,
} from "./identifier-resolver";
describe("identifier-resolver", () => {
describe("isUUID", () => {
it("should return true for valid UUIDs", () => {
expect(isUUID("550e8400-e29b-41d4-a716-446655440000")).toBe(true);
expect(isUUID("6ba7b810-9dad-11d1-80b4-00c04fd430c8")).toBe(true);
expect(isUUID("f47ac10b-58cc-4372-a567-0e02b2c3d479")).toBe(true);
expect(isUUID("123e4567-e89b-12d3-a456-426614174000")).toBe(true);
});
it("should return true for UUIDs in different cases", () => {
expect(isUUID("550E8400-E29B-41D4-A716-446655440000")).toBe(true);
expect(isUUID("550e8400-E29B-41d4-A716-446655440000")).toBe(true);
});
it("should return false for invalid UUIDs", () => {
expect(isUUID("not-a-uuid")).toBe(false);
expect(isUUID("550e8400-e29b-41d4-a716")).toBe(false); // too short
expect(isUUID("550e8400-e29b-41d4-a716-446655440000-extra")).toBe(false); // too long
expect(isUUID("550e8400e29b41d4a716446655440000")).toBe(false); // missing dashes
expect(isUUID("550e8400-e29b-41d4-a716-44665544000g")).toBe(false); // invalid character 'g'
});
it("should return false for empty and invalid inputs", () => {
expect(isUUID("")).toBe(false);
expect(isUUID(" ")).toBe(false);
});
it("should return false for non-string-like UUIDs", () => {
expect(isUUID("12345678-1234-1234-1234-12345678901")).toBe(false); // one char short
expect(isUUID("12345678-1234-1234-1234-1234567890123")).toBe(false); // one char long
});
});
describe("resolveIdentifierToCreator", () => {
let fetchSpy: any;
beforeEach(() => {
// Mock the global fetch function
fetchSpy = vi.fn();
global.fetch = fetchSpy;
});
afterEach(() => {
vi.restoreAllMocks();
});
it("should return null for empty identifier", async () => {
const result = await resolveIdentifierToCreator("");
expect(result).toBeNull();
expect(fetchSpy).not.toHaveBeenCalled();
});
it("should resolve username successfully", async () => {
const mockCreator = {
id: "550e8400-e29b-41d4-a716-446655440000",
username: "testuser",
email: "test@example.com",
};
fetchSpy.mockResolvedValueOnce({
ok: true,
json: async () => mockCreator,
});
const result = await resolveIdentifierToCreator("testuser");
expect(result).toEqual(mockCreator);
expect(fetchSpy).toHaveBeenCalledTimes(1);
expect(fetchSpy).toHaveBeenCalledWith("/api/creators/testuser");
});
it("should try UUID lookup when username fails and identifier is UUID", async () => {
const uuid = "550e8400-e29b-41d4-a716-446655440000";
const mockCreator = {
id: uuid,
username: "testuser",
email: "test@example.com",
};
// First call (username) fails
fetchSpy.mockResolvedValueOnce({
ok: false,
status: 404,
});
// Second call (UUID) succeeds
fetchSpy.mockResolvedValueOnce({
ok: true,
json: async () => mockCreator,
});
const result = await resolveIdentifierToCreator(uuid);
expect(result).toEqual(mockCreator);
expect(fetchSpy).toHaveBeenCalledTimes(2);
expect(fetchSpy).toHaveBeenNthCalledWith(1, `/api/creators/${uuid}`);
expect(fetchSpy).toHaveBeenNthCalledWith(2, `/api/creators/user/${uuid}`);
});
it("should return null when username lookup fails and identifier is not a UUID", async () => {
fetchSpy.mockResolvedValueOnce({
ok: false,
status: 404,
});
const result = await resolveIdentifierToCreator("notauser");
expect(result).toBeNull();
expect(fetchSpy).toHaveBeenCalledTimes(1);
expect(fetchSpy).toHaveBeenCalledWith("/api/creators/notauser");
});
it("should return null when both username and UUID lookups fail", async () => {
const uuid = "550e8400-e29b-41d4-a716-446655440000";
fetchSpy.mockResolvedValueOnce({
ok: false,
status: 404,
});
fetchSpy.mockResolvedValueOnce({
ok: false,
status: 404,
});
const result = await resolveIdentifierToCreator(uuid);
expect(result).toBeNull();
expect(fetchSpy).toHaveBeenCalledTimes(2);
});
it("should handle fetch errors gracefully", async () => {
const consoleErrorSpy = vi.spyOn(console, "error").mockImplementation(() => {});
fetchSpy.mockRejectedValueOnce(new Error("Network error"));
const result = await resolveIdentifierToCreator("testuser");
expect(result).toBeNull();
expect(consoleErrorSpy).toHaveBeenCalledWith(
"[Identifier Resolver] Error resolving identifier:",
expect.any(Error)
);
consoleErrorSpy.mockRestore();
});
it("should handle malformed JSON responses", async () => {
const consoleErrorSpy = vi.spyOn(console, "error").mockImplementation(() => {});
fetchSpy.mockResolvedValueOnce({
ok: true,
json: async () => {
throw new Error("Invalid JSON");
},
});
const result = await resolveIdentifierToCreator("testuser");
expect(result).toBeNull();
expect(consoleErrorSpy).toHaveBeenCalled();
consoleErrorSpy.mockRestore();
});
});
describe("resolveIdentifierToUserId", () => {
let fetchSpy: any;
beforeEach(() => {
fetchSpy = vi.fn();
global.fetch = fetchSpy;
});
afterEach(() => {
vi.restoreAllMocks();
});
it("should return null for empty identifier", async () => {
const result = await resolveIdentifierToUserId("");
expect(result).toBeNull();
expect(fetchSpy).not.toHaveBeenCalled();
});
it("should return the UUID directly if identifier is already a UUID", async () => {
const uuid = "550e8400-e29b-41d4-a716-446655440000";
const result = await resolveIdentifierToUserId(uuid);
expect(result).toBe(uuid);
expect(fetchSpy).not.toHaveBeenCalled();
});
it("should resolve username to user ID", async () => {
const mockCreator = {
id: "550e8400-e29b-41d4-a716-446655440000",
username: "testuser",
};
fetchSpy.mockResolvedValueOnce({
ok: true,
json: async () => mockCreator,
});
const result = await resolveIdentifierToUserId("testuser");
expect(result).toBe(mockCreator.id);
expect(fetchSpy).toHaveBeenCalledWith("/api/creators/testuser");
});
it("should return null when username resolution fails", async () => {
fetchSpy.mockResolvedValueOnce({
ok: false,
status: 404,
});
const result = await resolveIdentifierToUserId("nonexistent");
expect(result).toBeNull();
});
it("should return null when API returns creator without ID", async () => {
fetchSpy.mockResolvedValueOnce({
ok: true,
json: async () => ({ username: "testuser" }), // no id field
});
const result = await resolveIdentifierToUserId("testuser");
expect(result).toBeNull();
});
it("should handle errors gracefully", async () => {
fetchSpy.mockRejectedValueOnce(new Error("Network error"));
const result = await resolveIdentifierToUserId("testuser");
expect(result).toBeNull();
});
});
describe("resolveIdentifierToUsername", () => {
let fetchSpy: any;
beforeEach(() => {
fetchSpy = vi.fn();
global.fetch = fetchSpy;
});
afterEach(() => {
vi.restoreAllMocks();
});
it("should return null for empty identifier", async () => {
const result = await resolveIdentifierToUsername("");
expect(result).toBeNull();
expect(fetchSpy).not.toHaveBeenCalled();
});
it("should return the username directly if identifier is not a UUID", async () => {
const result = await resolveIdentifierToUsername("testuser");
expect(result).toBe("testuser");
expect(fetchSpy).not.toHaveBeenCalled();
});
it("should resolve UUID to username", async () => {
const uuid = "550e8400-e29b-41d4-a716-446655440000";
const mockCreator = {
id: uuid,
username: "testuser",
};
// First call (username endpoint with UUID) will fail
fetchSpy.mockResolvedValueOnce({
ok: false,
status: 404,
});
// Second call (UUID endpoint) will succeed
fetchSpy.mockResolvedValueOnce({
ok: true,
json: async () => mockCreator,
});
const result = await resolveIdentifierToUsername(uuid);
expect(result).toBe(mockCreator.username);
expect(fetchSpy).toHaveBeenCalledTimes(2);
});
it("should return null when UUID resolution fails", async () => {
const uuid = "550e8400-e29b-41d4-a716-446655440000";
fetchSpy.mockResolvedValueOnce({
ok: false,
status: 404,
});
fetchSpy.mockResolvedValueOnce({
ok: false,
status: 404,
});
const result = await resolveIdentifierToUsername(uuid);
expect(result).toBeNull();
});
it("should return null when API returns creator without username", async () => {
const uuid = "550e8400-e29b-41d4-a716-446655440000";
fetchSpy.mockResolvedValueOnce({
ok: false,
status: 404,
});
fetchSpy.mockResolvedValueOnce({
ok: true,
json: async () => ({ id: uuid }), // no username field
});
const result = await resolveIdentifierToUsername(uuid);
expect(result).toBeNull();
});
it("should handle errors gracefully", async () => {
const uuid = "550e8400-e29b-41d4-a716-446655440000";
fetchSpy.mockRejectedValueOnce(new Error("Network error"));
const result = await resolveIdentifierToUsername(uuid);
expect(result).toBeNull();
});
});
describe("integration scenarios", () => {
let fetchSpy: any;
beforeEach(() => {
fetchSpy = vi.fn();
global.fetch = fetchSpy;
});
afterEach(() => {
vi.restoreAllMocks();
});
it("should handle full workflow: username -> userId -> username", async () => {
const mockCreator = {
id: "550e8400-e29b-41d4-a716-446655440000",
username: "testuser",
};
// First call: resolve username to creator
fetchSpy.mockResolvedValueOnce({
ok: true,
json: async () => mockCreator,
});
const userId = await resolveIdentifierToUserId("testuser");
expect(userId).toBe(mockCreator.id);
// Second call: resolve userId (UUID) back to username
// This will make API calls since userId is a UUID
fetchSpy.mockResolvedValueOnce({
ok: false, // username endpoint will fail
status: 404,
});
fetchSpy.mockResolvedValueOnce({
ok: true,
json: async () => mockCreator,
});
const username = await resolveIdentifierToUsername(userId);
expect(username).toBe(mockCreator.username);
});
it("should handle mixed case UUIDs consistently", async () => {
const lowerUUID = "550e8400-e29b-41d4-a716-446655440000";
const upperUUID = "550E8400-E29B-41D4-A716-446655440000";
const mixedUUID = "550e8400-E29B-41d4-A716-446655440000";
expect(isUUID(lowerUUID)).toBe(true);
expect(isUUID(upperUUID)).toBe(true);
expect(isUUID(mixedUUID)).toBe(true);
// All should be recognized as UUIDs and returned directly
expect(await resolveIdentifierToUserId(lowerUUID)).toBe(lowerUUID);
expect(await resolveIdentifierToUserId(upperUUID)).toBe(upperUUID);
expect(await resolveIdentifierToUserId(mixedUUID)).toBe(mixedUUID);
});
});
});