refactor: streamline provider testing utility and remove deprecated test configurations

This commit is contained in:
Himanshu
2025-12-14 15:05:23 +05:30
parent 5238f7be2c
commit 28eeb51898
3 changed files with 588 additions and 300 deletions

View File

@@ -8,10 +8,8 @@
"main": "index.js",
"scripts": {
"test": "node test-providers.js",
"test:single": "node test-providers.js",
"test:provider": "node test-provider.js",
"test:provider:build": "npm run build && node test-provider.js",
"test:interactive": "npm run build && node interactive-test.js",
"test:quick": "npm run build && node quick-test.js",
"test:ui": "npm run test:interactive",
"build": "node build-simple.js",
"build:dev": "cross-env SKIP_MINIFY=true node build-simple.js",

View File

@@ -1,111 +0,0 @@
// Common test configurations
// You can customize these and run them quickly
const testConfigs = {
// UHD Tests
uhdMeta: {
provider: "uhd",
module: "meta",
function: "getMeta",
params: {
link: "https://uhdmovies.email/download-squid-game-season-2-hindi-1080p-2160p-4k/",
},
},
// LuxMovies Tests
luxPosts: {
provider: "luxMovies",
module: "posts",
function: "getPosts",
params: {
url: "https://rogmovies.sbs/page/1/?s=pan",
},
},
// PrimeMirror Tests
primeMirrorEpisodes: {
provider: "primeMirror",
module: "episodes",
function: "getEpisodes",
params: {
url: "0KMA7H0RHEPJA51SUBXKN9V6VA",
},
},
primeMirrorMeta: {
provider: "primeMirror",
module: "meta",
function: "getMeta",
params: {
link: "https://www.netflixmirror.com/title/82020512",
},
},
primeMirrorSearch: {
provider: "primeMirror",
module: "posts",
function: "getSearchPosts",
params: {
searchQuery: "breaking",
page: 1,
providerValue: "primeMirror",
},
},
// CinemaLuxe Tests
cinemaLuxeEpisodes: {
provider: "cinemaLuxe",
module: "episodes",
function: "getEpisodes",
params: {
url: "https://cinemalux.net/?88fdac61e5=cVQxdnNXeGRIRXlZTEQ0bTZSZlFsT09qclNlQzExOUNwVk5JZ05JK1ZjbzVxSWt1SHZSZjdZUm5vVnZEOEd1QXlrdXhPdnNETHRHTnpPUUNFN3k3VVdpY0J0OW5rem10c1ZlZ2xRcjI2YjFWRm9Uc3FEeEd0aWZlNFBpOHJ6bms=",
},
},
// Add more test configurations here as needed
// Template:
// yourTestName: {
// provider: 'providerName',
// module: 'moduleName',
// function: 'functionName',
// params: {
// // your parameters here
// }
// }
};
// Predefined test batches
const testBatches = {
// Test all meta functions
allMeta: [
testConfigs.uhdMeta,
testConfigs.primeMirrorMeta,
// Add more meta tests
],
// Test all posts/search functions
allPosts: [
testConfigs.luxPosts,
testConfigs.primeMirrorSearch,
// Add more posts tests
],
// Test all episode functions
allEpisodes: [
testConfigs.primeMirrorEpisodes,
testConfigs.cinemaLuxeEpisodes,
// Add more episode tests
],
// Quick smoke test - test one function from each major provider
smokeTest: [
testConfigs.uhdMeta,
testConfigs.luxPosts,
testConfigs.primeMirrorSearch,
],
};
module.exports = {
testConfigs,
testBatches,
};

View File

@@ -1,188 +1,590 @@
const axios = require("axios");
const cheerio = require("cheerio");
const fs = require("fs");
const path = require("path");
// Load extractors and utilities
let providerContext;
try {
const { getBaseUrl } = require("./dist/getBaseUrl.js");
const { hubcloudExtracter } = require("./dist/hubcloudExtractor.js");
const { gofileExtracter } = require("./dist/gofileExtracter.js");
const { superVideoExtractor } = require("./dist/superVideoExtractor.js");
const { gdFlixExtracter } = require("./dist/gdFlixExtractor.js");
providerContext = {
axios,
cheerio,
getBaseUrl,
commonHeaders: {
"User-Agent":
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36",
},
extractors: {
hubcloudExtracter,
gofileExtracter,
superVideoExtractor,
gdFlixExtracter,
},
Crypto: {},
};
} catch (error) {
console.log(
"⚠️ Could not load provider context. Run 'npm run build' first."
);
providerContext = null;
}
/**
* Provider testing utility
* Helper to pick random items from array
*/
function pickRandom(arr, count = 1) {
const shuffled = [...arr].sort(() => Math.random() - 0.5);
return count === 1 ? shuffled[0] : shuffled.slice(0, count);
}
/**
* Sleep helper
*/
function sleep(ms) {
return new Promise((resolve) => setTimeout(resolve, ms));
}
/**
* Provider testing utility - Full integration test
*/
class ProviderTester {
constructor(serverUrl = "http://localhost:3001") {
this.serverUrl = serverUrl;
this.axios = axios.create({
baseURL: serverUrl,
timeout: 10000,
});
constructor(options = {}) {
this.timeout = options.timeout || 30000;
this.postsToTest = options.postsToTest || 2;
this.linksToTest = options.linksToTest || 2;
this.signal = new AbortController().signal;
this.results = {};
}
/**
* Test server connectivity
* Load provider module
*/
async testConnection() {
loadModule(providerName, moduleName) {
try {
const response = await this.axios.get("/health");
console.log("✅ Server connection OK");
return true;
const modulePath = `./dist/${providerName}/${moduleName}.js`;
// Clear cache to get fresh module
delete require.cache[require.resolve(modulePath)];
return require(modulePath);
} catch (error) {
console.error("❌ Server connection failed:", error.message);
return false;
}
}
/**
* Test manifest endpoint
*/
async testManifest() {
try {
const response = await this.axios.get("/manifest.json");
const providers = response.data;
console.log(`✅ Manifest OK - Found ${providers.length} providers:`);
providers.forEach((p) => {
console.log(` 📦 ${p.display_name} (${p.value}) v${p.version}`);
});
return providers;
} catch (error) {
console.error("❌ Manifest test failed:", error.message);
return null;
}
}
/**
* Test individual provider modules
* Load manifest to get enabled providers
*/
loadManifest() {
try {
const manifestPath = path.join(__dirname, "manifest.json");
if (!fs.existsSync(manifestPath)) {
console.log("⚠️ manifest.json not found");
return [];
}
const manifest = JSON.parse(fs.readFileSync(manifestPath, "utf-8"));
return manifest;
} catch (error) {
console.log("⚠️ Failed to load manifest:", error.message);
return [];
}
}
/**
* Get available providers from dist folder (excluding disabled ones)
*/
getAvailableProviders() {
const distPath = path.join(__dirname, "dist");
if (!fs.existsSync(distPath)) {
console.log("❌ dist folder not found. Run 'npm run build' first.");
return [];
}
// Load manifest to check for disabled providers
const manifest = this.loadManifest();
const disabledProviders = manifest
.filter((p) => p.disabled === true)
.map((p) => p.value);
if (disabledProviders.length > 0) {
console.log(
`\n⏭️ Skipping disabled providers: ${disabledProviders.join(", ")}`
);
}
const providers = fs
.readdirSync(distPath, { withFileTypes: true })
.filter((dirent) => dirent.isDirectory())
.map((dirent) => dirent.name)
.filter((name) => {
// Skip disabled providers
if (disabledProviders.includes(name)) {
return false;
}
// Check if it has required modules
const hasRequired = [
"catalog.js",
"posts.js",
"meta.js",
"stream.js",
].every((file) => fs.existsSync(path.join(distPath, name, file)));
return hasRequired;
});
return providers;
}
/**
* Test a single provider with full flow
*/
async testProvider(providerName) {
console.log(`\n🧪 Testing provider: ${providerName}`);
console.log(`\n${"=".repeat(60)}`);
console.log(`🧪 Testing Provider: ${providerName}`);
console.log("=".repeat(60));
const modules = ["catalog", "posts", "meta", "stream", "episodes"];
const results = {};
const result = {
provider: providerName,
catalog: { success: false, data: null, error: null },
posts: { success: false, data: null, error: null },
meta: { success: false, data: null, error: null },
episodes: { success: false, data: null, error: null, skipped: false },
stream: { success: false, data: null, error: null, skipped: false },
summary: { passed: 0, failed: 0, skipped: 0 },
};
for (const module of modules) {
try {
const response = await this.axios.get(
`/dist/${providerName}/${module}.js`
try {
// Step 1: Load and test catalog
console.log("\n📂 Step 1: Loading Catalog...");
const catalogModule = this.loadModule(providerName, "catalog");
if (!catalogModule) {
throw new Error("Catalog module not found");
}
const catalog = catalogModule.catalog || [];
// const genres = catalogModule.genres || [];
const allFilters = [...catalog];
if (allFilters.length === 0) {
throw new Error("No filters found in catalog");
}
result.catalog.success = true;
result.catalog.data = {
catalogCount: catalog.length,
};
console.log(` ✅ Found ${catalog.length} catalog items`);
// Pick a random filter
const randomFilter = pickRandom(allFilters);
console.log(
` 🎲 Selected random filter: "${randomFilter.title}" (${randomFilter.filter})`
);
// Step 2: Test getPosts with random filter
console.log("\n📝 Step 2: Testing getPosts...");
const postsModule = this.loadModule(providerName, "posts");
if (!postsModule || !postsModule.getPosts) {
throw new Error("getPosts function not found");
}
const posts = await postsModule.getPosts({
filter: randomFilter.filter,
page: 1,
providerValue: providerName,
signal: this.signal,
providerContext,
});
if (!Array.isArray(posts) || posts.length === 0) {
throw new Error("getPosts returned empty or invalid result");
}
result.posts.success = true;
result.posts.data = { count: posts.length };
console.log(` ✅ Got ${posts.length} posts`);
// Pick random posts to test
const postsToTest = pickRandom(
posts,
Math.min(this.postsToTest, posts.length)
);
console.log(
` 🎲 Selected ${postsToTest.length} random posts for meta testing`
);
// Step 3: Test getMeta with random posts
console.log("\n📋 Step 3: Testing getMeta...");
const metaModule = this.loadModule(providerName, "meta");
if (!metaModule || !metaModule.getMeta) {
throw new Error("getMeta function not found");
}
const metaResults = [];
for (const post of postsToTest) {
console.log(`\n 📌 Testing: "${post.title.substring(0, 50)}..."`);
console.log(` Link: ${post.link}`);
try {
await sleep(500); // Small delay between requests
const meta = await metaModule.getMeta({
link: post.link,
providerContext,
});
if (!meta || !meta.linkList) {
console.log(
` ⚠️ Meta returned but linkList is empty/missing`
);
continue;
}
metaResults.push({ post, meta });
console.log(
` ✅ Got meta: type=${meta.type}, links=${meta.linkList.length}`
);
// Show link structure
meta.linkList.forEach((link, i) => {
const hasEpisodes = !!link.episodesLink;
const hasDirectLinks =
link.directLinks && link.directLinks.length > 0;
console.log(
` [${i + 1}] ${link.title.substring(0, 30)} - ${
hasEpisodes ? "📺 Episodes" : ""
}${hasDirectLinks ? "🎬 Direct" : ""}`
);
});
} catch (err) {
console.log(` ❌ Error: ${err.message}`);
}
}
if (metaResults.length === 0) {
throw new Error("No valid meta data retrieved");
}
result.meta.success = true;
result.meta.data = { testedCount: metaResults.length };
// Step 4: Test episodes OR stream based on meta content
console.log("\n🔗 Step 4: Testing Episodes/Stream...");
// Find links with episodes
const episodeLinks = [];
const directLinks = [];
for (const { meta } of metaResults) {
for (const link of meta.linkList) {
if (link.episodesLink) {
episodeLinks.push({ meta, link });
}
if (link.directLinks && link.directLinks.length > 0) {
directLinks.push({ meta, link });
}
}
}
// Test episodes if available
if (episodeLinks.length > 0) {
console.log(`\n 📺 Found ${episodeLinks.length} episode links`);
const episodesModule = this.loadModule(providerName, "episodes");
if (episodesModule && episodesModule.getEpisodes) {
const testEpisodeLink = pickRandom(episodeLinks);
console.log(
` 🎲 Testing episodes from: ${testEpisodeLink.link.title}`
);
console.log(` URL: ${testEpisodeLink.link.episodesLink}`);
try {
const episodes = await episodesModule.getEpisodes({
url: testEpisodeLink.link.episodesLink,
providerContext,
});
if (Array.isArray(episodes) && episodes.length > 0) {
result.episodes.success = true;
result.episodes.data = { count: episodes.length };
console.log(` ✅ Got ${episodes.length} episodes`);
// Show first few episodes
episodes.slice(0, 3).forEach((ep, i) => {
console.log(` [${i + 1}] ${ep.title}`);
});
// Test stream with random episode
console.log(`\n 🎬 Testing stream with random episode...`);
const randomEpisode = pickRandom(episodes);
console.log(` Episode: ${randomEpisode.title}`);
try {
const streamModule = this.loadModule(providerName, "stream");
if (streamModule && streamModule.getStream) {
const streams = await streamModule.getStream({
link: randomEpisode.link,
type: "series",
signal: this.signal,
providerContext,
});
if (Array.isArray(streams) && streams.length > 0) {
result.stream.success = true;
result.stream.data = {
count: streams.length,
type: "series",
};
console.log(` ✅ Got ${streams.length} stream(s)`);
streams.forEach((s, i) => {
console.log(
` [${i + 1}] ${s.server} - ${
s.quality || "unknown"
} quality`
);
});
} else {
console.log(` ⚠️ No streams returned`);
result.stream.error = "No streams returned";
}
}
} catch (err) {
console.log(` ❌ Stream error: ${err.message}`);
result.stream.error = err.message;
}
} else {
console.log(` ⚠️ No episodes returned`);
result.episodes.error = "No episodes returned";
}
} catch (err) {
console.log(` ❌ Episodes error: ${err.message}`);
result.episodes.error = err.message;
}
} else {
console.log(` ⚠️ getEpisodes function not found`);
result.episodes.skipped = true;
result.episodes.error = "Function not available";
}
} else {
result.episodes.skipped = true;
console.log(` No episode links found, skipping episodes test`);
}
// Test direct links/stream if episodes not tested or no episode links
if (directLinks.length > 0 && !result.stream.success) {
console.log(`\n 🎬 Found ${directLinks.length} direct link entries`);
const testDirectLink = pickRandom(directLinks);
const linksToTest = pickRandom(
testDirectLink.link.directLinks,
Math.min(this.linksToTest, testDirectLink.link.directLinks.length)
);
results[module] = {
success: true,
size: response.data.length,
hasExports: response.data.includes("exports."),
};
console.log(`${module}.js (${results[module].size} bytes)`);
} catch (error) {
results[module] = {
success: false,
error: error.response?.status === 404 ? "Not found" : error.message,
};
const isOptional = module === "episodes";
const icon = isOptional ? "⚠️ " : "❌";
console.log(
` ${icon} ${module}.js - ${results[module].error}${
isOptional ? " (optional)" : ""
}`
` 🎲 Testing ${linksToTest.length} random direct link(s)`
);
const streamModule = this.loadModule(providerName, "stream");
if (streamModule && streamModule.getStream) {
for (const directLink of Array.isArray(linksToTest)
? linksToTest
: [linksToTest]) {
console.log(`\n Testing: ${directLink.title}`);
console.log(` Link: ${directLink.link}`);
try {
await sleep(500);
const streams = await streamModule.getStream({
link: directLink.link,
type: directLink.type || "movie",
signal: this.signal,
providerContext,
});
if (Array.isArray(streams) && streams.length > 0) {
result.stream.success = true;
result.stream.data = {
count: streams.length,
type: directLink.type || "movie",
};
console.log(` ✅ Got ${streams.length} stream(s)`);
streams.forEach((s, i) => {
console.log(
` [${i + 1}] ${s.server} - ${
s.quality || "unknown"
} quality`
);
});
break; // One success is enough
} else {
console.log(` ⚠️ No streams returned`);
}
} catch (err) {
console.log(` ❌ Stream error: ${err.message}`);
result.stream.error = err.message;
}
}
} else {
console.log(` ❌ getStream function not found`);
result.stream.error = "Function not available";
}
} else if (
!result.stream.success &&
directLinks.length === 0 &&
episodeLinks.length === 0
) {
result.stream.skipped = true;
console.log(` No links to test stream with`);
}
} catch (error) {
console.log(`\n❌ Test failed: ${error.message}`);
// Determine which step failed
if (!result.catalog.success) {
result.catalog.error = error.message;
} else if (!result.posts.success) {
result.posts.error = error.message;
} else if (!result.meta.success) {
result.meta.error = error.message;
}
}
return results;
// Calculate summary
const steps = ["catalog", "posts", "meta", "episodes", "stream"];
for (const step of steps) {
if (result[step].success) {
result.summary.passed++;
} else if (result[step].skipped) {
result.summary.skipped++;
} else if (result[step].error) {
result.summary.failed++;
}
}
// Print summary
console.log(`\n${"─".repeat(60)}`);
console.log(`📊 Provider Summary: ${providerName}`);
console.log("─".repeat(60));
console.log(` ✅ Passed: ${result.summary.passed}`);
console.log(` ❌ Failed: ${result.summary.failed}`);
console.log(` ⏭️ Skipped: ${result.summary.skipped}`);
// List which steps passed/failed/skipped
console.log("\n Step Results:");
for (const step of steps) {
if (result[step].success) {
console.log(`${step}`);
} else if (result[step].skipped) {
console.log(` ⏭️ ${step} (skipped)`);
} else if (result[step].error) {
console.log(`${step}: ${result[step].error}`);
} else {
console.log(`${step} (not tested)`);
}
}
const statusIcon = result.summary.failed === 0 ? "✅" : "❌";
console.log(
`\n ${statusIcon} Overall: ${
result.summary.failed === 0 ? "PASSED" : "FAILED"
}`
);
return result;
}
/**
* Test all providers
*/
async testAllProviders() {
console.log("🚀 Starting comprehensive provider test...\n");
console.log("🚀 Starting comprehensive provider tests...\n");
// Test connection
const connected = await this.testConnection();
if (!connected) return;
// Test manifest
const providers = await this.testManifest();
if (!providers) return;
// Test each provider
const results = {};
for (const provider of providers) {
results[provider.value] = await this.testProvider(provider.value);
}
// Summary
console.log("\n📊 Test Summary:");
console.log("=".repeat(50));
let totalProviders = 0;
let passedProviders = 0;
for (const [providerName, modules] of Object.entries(results)) {
totalProviders++;
const requiredModules = ["catalog", "posts", "meta", "stream"];
const passedRequired = requiredModules.every(
(mod) => modules[mod]?.success
);
if (passedRequired) {
passedProviders++;
console.log(`${providerName} - All required modules OK`);
} else {
console.log(`${providerName} - Missing required modules`);
}
}
console.log(
`\n📈 Results: ${passedProviders}/${totalProviders} providers passed`
);
if (passedProviders === totalProviders) {
console.log("🎉 All providers are ready for testing!");
} else {
console.log("⚠️ Some providers need attention before testing.");
}
return results;
}
/**
* Trigger rebuild on server
*/
async rebuild() {
try {
console.log("🔨 Triggering rebuild...");
const response = await this.axios.post("/build");
console.log("✅ Rebuild completed");
return true;
} catch (error) {
console.error(
"❌ Rebuild failed:",
error.response?.data?.error || error.message
);
return false;
}
}
/**
* Get server status
*/
async getStatus() {
try {
const response = await this.axios.get("/status");
const status = response.data;
console.log("📊 Server Status:");
console.log(` 🟢 Status: ${status.status}`);
console.log(` 🔌 Port: ${status.port}`);
console.log(` 📦 Providers: ${status.providers}`);
console.log(` 🕐 Last Build: ${status.buildTime || "Never"}`);
if (status.providerList.length > 0) {
console.log(" 📋 Available Providers:");
status.providerList.forEach((p) => console.log(`${p}`));
}
return status;
} catch (error) {
console.error("❌ Failed to get status:", error.message);
if (!providerContext) {
console.log("❌ Provider context not loaded. Run 'npm run build' first.");
return null;
}
const providers = this.getAvailableProviders();
if (providers.length === 0) {
console.log("❌ No providers found.");
return null;
}
console.log(`📦 Found ${providers.length} providers to test:`);
providers.forEach((p) => console.log(`${p}`));
const results = {};
let passed = 0;
let failed = 0;
for (const provider of providers) {
try {
results[provider] = await this.testProvider(provider);
if (results[provider].summary.failed === 0) {
passed++;
} else {
failed++;
}
} catch (error) {
console.log(
`\n❌ Critical error testing ${provider}: ${error.message}`
);
failed++;
results[provider] = { error: error.message };
}
// Small delay between providers
await sleep(1000);
}
// Final summary
console.log(`\n${"═".repeat(60)}`);
console.log("📊 FINAL TEST SUMMARY");
console.log("═".repeat(60));
console.log(` Total Providers: ${providers.length}`);
console.log(` ✅ Passed: ${passed}`);
console.log(` ❌ Failed: ${failed}`);
// List failed providers with details
if (failed > 0) {
console.log(`\n${"─".repeat(60)}`);
console.log("❌ FAILED PROVIDERS:");
console.log("─".repeat(60));
for (const [name, result] of Object.entries(results)) {
if (result.error) {
// Critical error
console.log(`\n${name}`);
console.log(` Error: ${result.error}`);
} else if (result.summary?.failed > 0) {
// Step failures
console.log(`\n${name}`);
const steps = ["catalog", "posts", "meta", "episodes", "stream"];
for (const step of steps) {
if (result[step]?.error && !result[step]?.skipped) {
console.log(`${step}: ${result[step].error}`);
}
}
}
}
}
// List passed providers
if (passed > 0) {
console.log(`\n${"─".repeat(60)}`);
console.log("✅ PASSED PROVIDERS:");
console.log("─".repeat(60));
const passedProviders = Object.entries(results)
.filter(([_, result]) => result.summary?.failed === 0 && !result.error)
.map(([name]) => name);
console.log(` ${passedProviders.join(", ")}`);
}
console.log(`\n${"═".repeat(60)}`);
return results;
}
}
@@ -191,53 +593,52 @@ class ProviderTester {
*/
async function main() {
const args = process.argv.slice(2);
const command = args[0] || "test";
const providerName = args[1];
const providerName = args[0];
const tester = new ProviderTester();
// Check for options
const postsToTest =
parseInt(args.find((a) => a.startsWith("--posts="))?.split("=")[1]) || 2;
const linksToTest =
parseInt(args.find((a) => a.startsWith("--links="))?.split("=")[1]) || 2;
switch (command) {
case "test":
if (providerName) {
await tester.testProvider(providerName);
} else {
await tester.testAllProviders();
}
break;
const tester = new ProviderTester({ postsToTest, linksToTest });
case "status":
await tester.getStatus();
break;
if (args.includes("--help") || args.includes("-h")) {
console.log(`
🎯 Vega Providers Integration Tester
=====================================
case "rebuild":
await tester.rebuild();
break;
Usage: node test-providers.js [provider] [options]
case "connection":
await tester.testConnection();
break;
Arguments:
provider Name of specific provider to test (optional)
If not provided, tests all providers
case "manifest":
await tester.testManifest();
break;
Options:
--posts=N Number of random posts to test (default: 2)
--links=N Number of random direct links to test (default: 2)
--help, -h Show this help message
default:
console.log(`
Usage: node test-providers.js [command] [provider]
Commands:
test [provider] - Test all providers or specific provider
status - Show server status
rebuild - Trigger rebuild
connection - Test server connection
manifest - Test manifest endpoint
Test Flow:
1. Load catalog → pick random filter
2. Call getPosts with filter
3. Pick random posts → call getMeta
4. If episodesLink → call getEpisodes → getStream
5. If directLinks → call getStream
Examples:
node test-providers.js # Test all providers
node test-providers.js test vega # Test specific provider
node test-providers.js status # Show status
node test-providers.js rebuild # Rebuild and test
`);
node test-providers.js # Test all providers
node test-providers.js vega # Test only vega provider
node test-providers.js mod --posts=3 # Test mod with 3 random posts
node test-providers.js --posts=1 --links=1 # Quick test all providers
`);
return;
}
if (providerName && !providerName.startsWith("--")) {
await tester.testProvider(providerName);
} else {
await tester.testAllProviders();
}
}