/** * Creator Network End-to-End Test Suite * Phase 3: Testing & Validation * * Tests complete user flows: * 1. Sign up → Create creator profile * 2. Post opportunity → Receive applications * 3. Browse creators → Browse opportunities * 4. Apply for opportunity → Track application * 5. DevConnect linking */ interface TestCase { name: string; passed: boolean; message: string; duration: number; } const results: TestCase[] = []; const BASE_URL = "http://localhost:5173"; // Test utilities const sleep = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms)); const test = ( name: string, passed: boolean, message: string, duration: number = 0, ) => { results.push({ name, passed, message, duration }); const symbol = passed ? "✓" : "✗"; console.log(`${symbol} ${name}`); if (!passed) console.log(` → ${message}`); }; const assertEquals = (actual: any, expected: any, msg: string) => { const pass = actual === expected; if (!pass) { throw new Error(`${msg}: expected ${expected}, got ${actual}`); } }; const assertExists = (value: any, msg: string) => { if (!value) { throw new Error(`${msg}: value is null or undefined`); } }; const assertInRange = ( actual: number, min: number, max: number, msg: string, ) => { if (actual < min || actual > max) { throw new Error(`${msg}: value ${actual} not in range [${min}, ${max}]`); } }; // Test data const testUsers = { creator1: { id: `creator-${Date.now()}-1`, username: `creator_${Date.now()}_1`, email: `creator1-${Date.now()}@test.com`, }, creator2: { id: `creator-${Date.now()}-2`, username: `creator_${Date.now()}_2`, email: `creator2-${Date.now()}@test.com`, }, }; // E2E Test Flows async function runE2ETests() { console.log("🚀 Creator Network End-to-End Test Suite\n"); // FLOW 1: Creator Registration and Profile Setup console.log("\n📝 FLOW 1: Creator Registration & Profile Setup"); console.log("=".repeat(50)); try { // Create first creator profile const createCreator1Start = Date.now(); const createRes1 = await fetch(`${BASE_URL}/api/creators`, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ user_id: testUsers.creator1.id, username: testUsers.creator1.username, bio: "Experienced game developer", avatar_url: "https://example.com/avatar1.jpg", experience_level: "senior", primary_arm: "gameforge", arm_affiliations: ["gameforge", "labs"], skills: ["unity", "c#", "game design"], }), }); const creator1Data = await createRes1.json(); const createCreator1Duration = Date.now() - createCreator1Start; test( "Create creator profile 1", createRes1.status === 201, `Status: ${createRes1.status}`, createCreator1Duration, ); if (createRes1.ok) { assertExists(creator1Data.id, "Creator ID should exist"); assertEquals( creator1Data.username, testUsers.creator1.username, "Username mismatch", ); assertEquals( creator1Data.primary_arm, "gameforge", "Primary arm mismatch", ); } // Create second creator profile const createCreator2Start = Date.now(); const createRes2 = await fetch(`${BASE_URL}/api/creators`, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ user_id: testUsers.creator2.id, username: testUsers.creator2.username, bio: "Aspiring developer", avatar_url: "https://example.com/avatar2.jpg", experience_level: "junior", primary_arm: "labs", arm_affiliations: ["labs"], skills: ["javascript", "react", "web development"], }), }); const creator2Data = await createRes2.json(); const createCreator2Duration = Date.now() - createCreator2Start; test( "Create creator profile 2", createRes2.status === 201, `Status: ${createRes2.status}`, createCreator2Duration, ); } catch (error: any) { test("Create creator profiles", false, error.message); } // FLOW 2: Opportunity Creation console.log("\n📋 FLOW 2: Opportunity Creation & Discovery"); console.log("=".repeat(50)); let opportunityId: string | null = null; try { // Creator 1 posts opportunity const createOppStart = Date.now(); const oppRes = await fetch(`${BASE_URL}/api/opportunities`, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ user_id: testUsers.creator1.id, title: "Senior Game Dev - Unity Project", description: "Looking for experienced Unity developer for 6-month contract", job_type: "contract", salary_min: 80000, salary_max: 120000, experience_level: "senior", arm_affiliation: "gameforge", }), }); const oppData = await oppRes.json(); const createOppDuration = Date.now() - createOppStart; test( "Create opportunity", oppRes.status === 201, `Status: ${oppRes.status}`, createOppDuration, ); if (oppRes.ok) { opportunityId = oppData.id; assertExists(oppData.id, "Opportunity ID should exist"); assertEquals( oppData.title, "Senior Game Dev - Unity Project", "Title mismatch", ); assertEquals(oppData.status, "open", "Status should be open"); } // Browse opportunities with filters const browseOppStart = Date.now(); const browseRes = await fetch( `${BASE_URL}/api/opportunities?arm=gameforge&page=1&limit=10`, ); const browseData = await browseRes.json(); const browseOppDuration = Date.now() - browseOppStart; test( "Browse opportunities with filters", browseRes.ok && Array.isArray(browseData.data), `Status: ${browseRes.status}, Found: ${browseData.data?.length || 0}`, browseOppDuration, ); if (browseRes.ok) { assertExists(browseData.pagination, "Pagination data should exist"); assertInRange(browseOppDuration, 0, 1000, "Response time reasonable"); } } catch (error: any) { test("Create and browse opportunities", false, error.message); } // FLOW 3: Creator Discovery console.log("\n👥 FLOW 3: Creator Discovery & Profiles"); console.log("=".repeat(50)); try { // Browse creators const browseCreatorsStart = Date.now(); const creatorsRes = await fetch( `${BASE_URL}/api/creators?arm=gameforge&page=1&limit=20`, ); const creatorsData = await creatorsRes.json(); const browseCreatorsDuration = Date.now() - browseCreatorsStart; test( "Browse creators with arm filter", creatorsRes.ok && Array.isArray(creatorsData.data), `Status: ${creatorsRes.status}, Found: ${creatorsData.data?.length || 0}`, browseCreatorsDuration, ); // Get individual creator profile const getCreatorStart = Date.now(); const creatorRes = await fetch( `${BASE_URL}/api/creators/${testUsers.creator1.username}`, ); const creatorData = await creatorRes.json(); const getCreatorDuration = Date.now() - getCreatorStart; test( "Get creator profile by username", creatorRes.ok && creatorData.username === testUsers.creator1.username, `Status: ${creatorRes.status}, Username: ${creatorData.username}`, getCreatorDuration, ); if (creatorRes.ok) { assertExists(creatorData.bio, "Bio should exist"); assertExists(creatorData.skills, "Skills should exist"); assertEquals( Array.isArray(creatorData.arm_affiliations), true, "Arm affiliations should be array", ); } } catch (error: any) { test("Creator discovery and profiles", false, error.message); } // FLOW 4: Application Submission & Tracking console.log("\n✉️ FLOW 4: Apply for Opportunity & Track Status"); console.log("=".repeat(50)); let applicationId: string | null = null; try { if (!opportunityId) { throw new Error("Opportunity not created, cannot test applications"); } // Creator 2 applies for opportunity const applyStart = Date.now(); const applyRes = await fetch(`${BASE_URL}/api/applications`, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ user_id: testUsers.creator2.id, opportunity_id: opportunityId, cover_letter: "I'm very interested in this opportunity. I have 3 years of Unity experience.", }), }); const appData = await applyRes.json(); const applyDuration = Date.now() - applyStart; test( "Submit application", applyRes.status === 201, `Status: ${applyRes.status}`, applyDuration, ); if (applyRes.ok) { applicationId = appData.id; assertExists(appData.id, "Application ID should exist"); assertEquals(appData.status, "submitted", "Status should be submitted"); assertExists(appData.applied_at, "Applied timestamp should exist"); } // Duplicate application should fail const dupRes = await fetch(`${BASE_URL}/api/applications`, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ user_id: testUsers.creator2.id, opportunity_id: opportunityId, cover_letter: "Second attempt", }), }); test( "Prevent duplicate applications", dupRes.status === 400, `Status: ${dupRes.status} (should be 400)`, 0, ); // Get applications for creator const getAppsStart = Date.now(); const appsRes = await fetch( `${BASE_URL}/api/applications?user_id=${testUsers.creator2.id}`, ); const appsData = await appsRes.json(); const getAppsDuration = Date.now() - getAppsStart; test( "Get creator's applications", appsRes.ok && Array.isArray(appsData.data), `Status: ${appsRes.status}, Found: ${appsData.data?.length || 0}`, getAppsDuration, ); // Update application status (as opportunity creator) if (applicationId) { const updateStart = Date.now(); const updateRes = await fetch( `${BASE_URL}/api/applications/${applicationId}`, { method: "PUT", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ user_id: testUsers.creator1.id, status: "accepted", response_message: "Great! We'd love to have you on board.", }), }, ); const updateDuration = Date.now() - updateStart; test( "Update application status", updateRes.ok, `Status: ${updateRes.status}`, updateDuration, ); } } catch (error: any) { test("Application workflow", false, error.message); } // FLOW 5: DevConnect Linking console.log("\n�� FLOW 5: DevConnect Account Linking"); console.log("=".repeat(50)); try { // Link DevConnect account const linkStart = Date.now(); const linkRes = await fetch(`${BASE_URL}/api/devconnect/link`, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ user_id: testUsers.creator1.id, devconnect_username: "devconnect_user_1", devconnect_profile_url: "https://dev-link.me/devconnect_user_1", }), }); const linkData = await linkRes.json(); const linkDuration = Date.now() - linkStart; test( "Link DevConnect account", linkRes.status === 201 || linkRes.status === 200, `Status: ${linkRes.status}`, linkDuration, ); // Get DevConnect link const getLinkStart = Date.now(); const getLinkRes = await fetch( `${BASE_URL}/api/devconnect/link?user_id=${testUsers.creator1.id}`, ); const getLinkData = await getLinkRes.json(); const getLinkDuration = Date.now() - getLinkStart; test( "Get DevConnect link", getLinkRes.ok && getLinkData.data, `Status: ${getLinkRes.status}`, getLinkDuration, ); if (getLinkRes.ok && getLinkData.data) { assertEquals( getLinkData.data.devconnect_username, "devconnect_user_1", "Username mismatch", ); } // Unlink DevConnect account const unlinkRes = await fetch(`${BASE_URL}/api/devconnect/link`, { method: "DELETE", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ user_id: testUsers.creator1.id }), }); test( "Unlink DevConnect account", unlinkRes.ok, `Status: ${unlinkRes.status}`, 0, ); } catch (error: any) { test("DevConnect linking", false, error.message); } // FLOW 6: Filtering and Search console.log("\n🔍 FLOW 6: Advanced Filtering & Search"); console.log("=".repeat(50)); try { // Test creator search const searchStart = Date.now(); const searchRes = await fetch( `${BASE_URL}/api/creators?search=${testUsers.creator1.username.substring(0, 5)}`, ); const searchData = await searchRes.json(); const searchDuration = Date.now() - searchStart; test( "Search creators by name", searchRes.ok && Array.isArray(searchData.data), `Status: ${searchRes.status}, Found: ${searchData.data?.length || 0}`, searchDuration, ); // Test opportunity filtering by experience level const expFilterStart = Date.now(); const expRes = await fetch( `${BASE_URL}/api/opportunities?experienceLevel=senior`, ); const expData = await expRes.json(); const expFilterDuration = Date.now() - expFilterStart; test( "Filter opportunities by experience level", expRes.ok && Array.isArray(expData.data), `Status: ${expRes.status}, Found: ${expData.data?.length || 0}`, expFilterDuration, ); // Test pagination const page1Start = Date.now(); const page1Res = await fetch(`${BASE_URL}/api/creators?page=1&limit=5`); const page1Data = await page1Res.json(); const page1Duration = Date.now() - page1Start; test( "Pagination - page 1", page1Res.ok && page1Data.pagination?.page === 1, `Page: ${page1Data.pagination?.page}, Limit: ${page1Data.pagination?.limit}`, page1Duration, ); assertExists( page1Data.pagination?.pages, "Total pages should be calculated", ); } catch (error: any) { test("Filtering and search", false, error.message); } // Summary console.log("\n" + "=".repeat(50)); const passed = results.filter((r) => r.passed).length; const failed = results.filter((r) => r.passed === false).length; const totalDuration = results.reduce((sum, r) => sum + r.duration, 0); console.log(`\n📊 Test Summary:`); console.log(` ✓ Passed: ${passed}`); console.log(` ✗ Failed: ${failed}`); console.log(` Total: ${results.length}`); console.log( ` Duration: ${totalDuration}ms (avg ${(totalDuration / results.length).toFixed(0)}ms per test)`, ); console.log("\n" + "=".repeat(50)); if (failed > 0) { console.log("\n❌ Failed Tests:"); results .filter((r) => !r.passed) .forEach((r) => { console.log(` - ${r.name}: ${r.message}`); }); } else { console.log("\n✅ All tests passed!"); } return { passed, failed, total: results.length, totalDuration }; } // Run tests runE2ETests() .then((summary) => { process.exit(summary.failed > 0 ? 1 : 0); }) .catch((error) => { console.error("Test suite failed:", error); process.exit(1); });