UNPKG

@developers-joyride/shortify

Version:

High performance URL shortener library with multi-database support (MongoDB, SQLite, PostgreSQL, MySQL)

164 lines (130 loc) 5.04 kB
import { ShortenerService } from "../src/services/shortener.service"; import { IDatabaseAdapter, IUrl } from "../src/interfaces/database.interface"; // Mock database adapter for testing collection name and uniqueness class MockDatabaseAdapter implements IDatabaseAdapter { private urls: Map<string, IUrl> = new Map(); private collectionName: string = "urls"; constructor(collectionName?: string) { if (collectionName) { this.collectionName = collectionName; } } async connect(): Promise<boolean> { return true; } async disconnect(): Promise<void> {} isConnected(): boolean { return true; } async createUrl(url: Omit<IUrl, "createdAt">): Promise<IUrl> { const newUrl: IUrl = { ...url, createdAt: new Date(), }; this.urls.set(url.urlId, newUrl); return newUrl; } async findUrlByUrlId(urlId: string): Promise<IUrl | null> { return this.urls.get(urlId) || null; } async findUrlByOriginalUrl(originalUrl: string): Promise<IUrl | null> { for (const url of this.urls.values()) { if (url.originalUrl === originalUrl) { return url; } } return null; } async updateUrlClicks(urlId: string, clicks: number): Promise<void> { const url = this.urls.get(urlId); if (url) { url.clicks = clicks; } } async deleteUrl(urlId: string): Promise<boolean> { return this.urls.delete(urlId); } on(event: string, listener: (...args: any[]) => void): this { return this; } emit(event: string, ...args: any[]): boolean { return true; } // Helper method to get collection name for testing getCollectionName(): string { return this.collectionName; } // Helper method to get all URLs for testing getAllUrls(): IUrl[] { return Array.from(this.urls.values()); } } describe("Collection Name and URL ID Uniqueness Tests", () => { it("should preserve collection name case", () => { const customCollectionName = "MyCustomCollection"; const mockDbAdapter = new MockDatabaseAdapter(customCollectionName); expect(mockDbAdapter.getCollectionName()).toBe(customCollectionName); }); it("should generate unique URL IDs for same original URL", async () => { const mockDbAdapter = new MockDatabaseAdapter(); const service = new ShortenerService("https://example.com/", mockDbAdapter); const originalUrl = "https://www.google.com"; // Create first shortened URL const result1 = await service.shorten(originalUrl); expect(result1.originalUrl).toBe(originalUrl); expect(result1.urlId).toBeDefined(); // Create second shortened URL with same original URL const result2 = await service.shorten(originalUrl); expect(result2.originalUrl).toBe(originalUrl); expect(result2.urlId).toBeDefined(); // URL IDs should be different expect(result1.urlId).not.toBe(result2.urlId); // Both should be stored in database const allUrls = mockDbAdapter.getAllUrls(); expect(allUrls).toHaveLength(2); expect(allUrls[0].urlId).not.toBe(allUrls[1].urlId); }); it("should handle custom URL ID conflicts", async () => { const mockDbAdapter = new MockDatabaseAdapter(); const service = new ShortenerService("https://example.com/", mockDbAdapter); const originalUrl1 = "https://www.google.com"; const originalUrl2 = "https://www.github.com"; const customUrlId = "custom123"; // Create first URL with custom ID const result1 = await service.shorten(originalUrl1, { customUrlId }); expect(result1.urlId).toBe(customUrlId); // Try to create second URL with same custom ID const result2 = await service.shorten(originalUrl2, { customUrlId }); // Should generate a new URL ID instead of using the same one expect(result2.urlId).not.toBe(customUrlId); expect(result2.urlId).toBeDefined(); // Both URLs should be stored const allUrls = mockDbAdapter.getAllUrls(); expect(allUrls).toHaveLength(2); expect(allUrls[0].urlId).not.toBe(allUrls[1].urlId); }); it("should handle multiple URL ID conflicts gracefully", async () => { const mockDbAdapter = new MockDatabaseAdapter(); const service = new ShortenerService("https://example.com/", mockDbAdapter); // First, create a URL with a known ID to create a conflict await service.shorten("https://www.example1.com", { customUrlId: "conflict123", }); // Mock the generateUrlId method to return predictable IDs for testing const originalGenerateId = service["generateUrlId"]; let callCount = 0; service["generateUrlId"] = () => { callCount++; // Return same ID for first few calls to simulate conflicts if (callCount <= 3) { return "conflict123"; } return "unique456"; }; const result = await service.shorten("https://www.example.com"); // Should eventually get a unique ID expect(result.urlId).toBe("unique456"); // Restore original method service["generateUrlId"] = originalGenerateId; }); });