181 lines
5.6 KiB
TypeScript
181 lines
5.6 KiB
TypeScript
#!/usr/bin/env tsx
|
|
/**
|
|
* Test Redis connection and functionality
|
|
* Tests: Connection, Rate Limiting, Caching
|
|
*/
|
|
|
|
import { Redis } from "ioredis";
|
|
import { initRedisRateLimiter, isRedisConnected, checkMusicRateLimit } from "@/lib/rate-limit";
|
|
import { getCache, setCache, deleteCache } from "@/lib/cache";
|
|
|
|
async function testRedisConnection() {
|
|
console.log("🔍 Testing Redis Connection...\n");
|
|
|
|
const redisUrl = process.env.REDIS_URL;
|
|
if (!redisUrl) {
|
|
console.error("❌ REDIS_URL not set in environment");
|
|
return false;
|
|
}
|
|
|
|
console.log(`📍 Redis URL: ${redisUrl.replace(/:[^:@]+@/, ":****@")}`);
|
|
|
|
try {
|
|
const redis = new Redis(redisUrl, {
|
|
maxRetriesPerRequest: 3,
|
|
retryStrategy: (times) => {
|
|
const delay = Math.min(times * 50, 2000);
|
|
return delay;
|
|
},
|
|
});
|
|
|
|
// Test connection
|
|
const pong = await redis.ping();
|
|
console.log(`✅ Connection test: ${pong}`);
|
|
|
|
// Test set/get
|
|
await redis.set("test:connection", "success", "EX", 10);
|
|
const value = await redis.get("test:connection");
|
|
console.log(`✅ Set/Get test: ${value === "success" ? "PASSED" : "FAILED"}`);
|
|
|
|
// Test delete
|
|
await redis.del("test:connection");
|
|
const deleted = await redis.get("test:connection");
|
|
console.log(`✅ Delete test: ${deleted === null ? "PASSED" : "FAILED"}`);
|
|
|
|
// Get Redis info
|
|
const info = await redis.info("server");
|
|
const versionMatch = info.match(/redis_version:([^\r\n]+)/);
|
|
const version = versionMatch ? versionMatch[1] : "unknown";
|
|
console.log(`📊 Redis version: ${version}`);
|
|
|
|
await redis.quit();
|
|
return true;
|
|
} catch (error) {
|
|
console.error("❌ Redis connection failed:", error instanceof Error ? error.message : String(error));
|
|
return false;
|
|
}
|
|
}
|
|
|
|
async function testRateLimiting() {
|
|
console.log("\n🔍 Testing Rate Limiting...\n");
|
|
|
|
try {
|
|
await initRedisRateLimiter(process.env.REDIS_URL);
|
|
|
|
if (!isRedisConnected()) {
|
|
console.warn("⚠️ Redis not connected, rate limiting will use in-memory fallback");
|
|
} else {
|
|
console.log("✅ Rate limiter initialized with Redis");
|
|
}
|
|
|
|
// Test music rate limit
|
|
const testIdentifier = `test:${Date.now()}`;
|
|
console.log(`📍 Testing with identifier: ${testIdentifier}`);
|
|
|
|
// Make multiple requests
|
|
const results = [];
|
|
for (let i = 0; i < 5; i++) {
|
|
const result = await checkMusicRateLimit(testIdentifier);
|
|
results.push(result);
|
|
console.log(` Request ${i + 1}: ${result.allowed ? "✅ Allowed" : "❌ Blocked"} (remaining: ${result.remaining ?? "N/A"})`);
|
|
}
|
|
|
|
const allAllowed = results.every(r => r.allowed);
|
|
console.log(`\n${allAllowed ? "✅" : "⚠️"} Rate limiting test: ${allAllowed ? "All requests allowed (within limit)" : "Some requests blocked"}`);
|
|
|
|
return true;
|
|
} catch (error) {
|
|
console.error("❌ Rate limiting test failed:", error instanceof Error ? error.message : String(error));
|
|
return false;
|
|
}
|
|
}
|
|
|
|
async function testCaching() {
|
|
console.log("\n🔍 Testing Caching...\n");
|
|
|
|
try {
|
|
const testKey = `test:cache:${Date.now()}`;
|
|
const testValue = { message: "Hello Redis!", timestamp: Date.now() };
|
|
|
|
// Test set
|
|
await setCache(testKey, testValue, 60);
|
|
console.log(`✅ Set cache: ${testKey}`);
|
|
|
|
// Test get
|
|
const retrieved = await getCache<typeof testValue>(testKey);
|
|
if (retrieved && retrieved.message === testValue.message) {
|
|
console.log(`✅ Get cache: ${retrieved.message}`);
|
|
} else {
|
|
console.error(`❌ Get cache failed: Expected "${testValue.message}", got "${retrieved?.message ?? "null"}"`);
|
|
return false;
|
|
}
|
|
|
|
// Test delete
|
|
await deleteCache(testKey);
|
|
const afterDelete = await getCache(testKey);
|
|
if (afterDelete === null) {
|
|
console.log(`✅ Delete cache: Successfully deleted`);
|
|
} else {
|
|
console.error(`❌ Delete cache failed: Value still exists`);
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
} catch (error) {
|
|
console.error("❌ Caching test failed:", error instanceof Error ? error.message : String(error));
|
|
return false;
|
|
}
|
|
}
|
|
|
|
async function main() {
|
|
console.log("🚀 Redis Test Suite\n");
|
|
console.log("=" .repeat(50) + "\n");
|
|
|
|
// Load environment variables
|
|
if (process.env.NODE_ENV !== "production") {
|
|
// Try to load .env.local
|
|
try {
|
|
const { config } = await import("dotenv");
|
|
const result = config({ path: ".env.local" });
|
|
if (result.error) {
|
|
console.warn("⚠️ Could not load .env.local, using process.env");
|
|
}
|
|
} catch (e) {
|
|
// dotenv not available or already loaded
|
|
}
|
|
}
|
|
|
|
const results = {
|
|
connection: false,
|
|
rateLimiting: false,
|
|
caching: false,
|
|
};
|
|
|
|
// Run tests
|
|
results.connection = await testRedisConnection();
|
|
results.rateLimiting = await testRateLimiting();
|
|
results.caching = await testCaching();
|
|
|
|
// Summary
|
|
console.log("\n" + "=".repeat(50));
|
|
console.log("📊 Test Summary\n");
|
|
console.log(`Connection: ${results.connection ? "✅ PASSED" : "❌ FAILED"}`);
|
|
console.log(`Rate Limiting: ${results.rateLimiting ? "✅ PASSED" : "❌ FAILED"}`);
|
|
console.log(`Caching: ${results.caching ? "✅ PASSED" : "❌ FAILED"}`);
|
|
console.log("=".repeat(50) + "\n");
|
|
|
|
const allPassed = Object.values(results).every(r => r);
|
|
if (allPassed) {
|
|
console.log("🎉 All tests passed! Redis is working correctly.\n");
|
|
process.exit(0);
|
|
} else {
|
|
console.log("⚠️ Some tests failed. Please check the errors above.\n");
|
|
process.exit(1);
|
|
}
|
|
}
|
|
|
|
main().catch((error) => {
|
|
console.error("💥 Test suite crashed:", error);
|
|
process.exit(1);
|
|
});
|