aethex-forge/tests/e2e-creator-network.test.ts
2025-11-08 03:49:51 +00:00

530 lines
15 KiB
TypeScript
Raw Permalink Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/**
* 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<><6E> 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);
});