Major Changes: - Moved legacy system to Legacy/ folder for archival - Built new modular architecture with strict separation of concerns - Created core system: Module, EventBus, ModuleLoader, Router - Added Application bootstrap with auto-start functionality - Implemented development server with ES6 modules support - Created comprehensive documentation and project context - Converted SBS-7-8 content to JSON format - Copied all legacy games and content to new structure New Architecture Features: - Sealed modules with WeakMap private data - Strict dependency injection system - Event-driven communication only - Inviolable responsibility patterns - Auto-initialization without commands - Component-based UI foundation ready Technical Stack: - Vanilla JS/HTML/CSS only - ES6 modules with proper imports/exports - HTTP development server (no file:// protocol) - Modular CSS with component scoping - Comprehensive error handling and debugging Ready for Phase 2: Converting legacy modules to new architecture 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
378 lines
12 KiB
JavaScript
378 lines
12 KiB
JavaScript
// Fixtures spécifiques pour les edge cases
|
|
|
|
export const corruptedJSONSamples = [
|
|
'{"name": "Incomplete JSON"', // JSON tronqué
|
|
'{"name": "Bad JSON",}', // Virgule finale
|
|
'{name: "No quotes"}', // Clés sans guillemets
|
|
'{"name": "Unicode\\uXXXX"}', // Unicode invalide
|
|
'{"circular": {"self":', // JSON circulaire interrompu
|
|
'{}{}', // Plusieurs objets JSON
|
|
'null', // JSON valide mais pas un objet
|
|
'[]', // Array au lieu d'objet
|
|
'"just a string"', // String au lieu d'objet
|
|
'{"huge": "' + 'x'.repeat(100000) + '"}' // Énorme chaîne
|
|
];
|
|
|
|
export const maliciousContentSamples = [
|
|
{
|
|
name: "XSS Attempt",
|
|
vocabulary: {
|
|
"<script>alert('xss')</script>": "malicious",
|
|
"javascript:alert(1)": "dangerous"
|
|
}
|
|
},
|
|
{
|
|
name: "SQL Injection Like",
|
|
vocabulary: {
|
|
"'; DROP TABLE vocabulary; --": "injection",
|
|
"1' OR '1'='1": "boolean"
|
|
}
|
|
},
|
|
{
|
|
name: "Path Traversal",
|
|
vocabulary: {
|
|
"../../../etc/passwd": "traversal",
|
|
"..\\\\..\\\\..\\\\windows\\\\system32": "windows"
|
|
}
|
|
},
|
|
{
|
|
name: "Null Bytes",
|
|
vocabulary: {
|
|
"test\\x00hidden": "null byte",
|
|
"normal\\0text": "embedded null"
|
|
}
|
|
}
|
|
];
|
|
|
|
export const extremeDataSamples = {
|
|
// Objet avec une profondeur excessive
|
|
deeplyNested: (() => {
|
|
let obj = { vocabulary: {} };
|
|
let current = obj;
|
|
for (let i = 0; i < 1000; i++) {
|
|
current.nested = { level: i };
|
|
current = current.nested;
|
|
}
|
|
return obj;
|
|
})(),
|
|
|
|
// Objet avec énormément de propriétés
|
|
manyProperties: (() => {
|
|
const obj = { name: "Many Props", vocabulary: {} };
|
|
for (let i = 0; i < 10000; i++) {
|
|
obj.vocabulary[`word${i}`] = `translation${i}`;
|
|
}
|
|
return obj;
|
|
})(),
|
|
|
|
// Chaînes très longues
|
|
longStrings: {
|
|
name: "A".repeat(100000),
|
|
description: "B".repeat(50000),
|
|
vocabulary: {
|
|
"test": "C".repeat(200000)
|
|
}
|
|
},
|
|
|
|
// Caractères spéciaux et Unicode
|
|
unicodeHeavy: {
|
|
name: "Unicode Test 🚀🎉💥🌟⭐🔥💯🎯🌈",
|
|
vocabulary: {
|
|
"你好": "Hello in Chinese",
|
|
"🏠🏡🏢": "Buildings",
|
|
"café": "Coffee with accent",
|
|
"naïve": "French word",
|
|
"Москва": "Moscow in Russian",
|
|
"العربية": "Arabic text",
|
|
"🧪⚗️🔬": "Science emojis",
|
|
"\\u{1F600}": "Unicode escape",
|
|
"\\x41\\x42\\x43": "Hex escapes"
|
|
}
|
|
},
|
|
|
|
// Données avec types mixtes (pas recommandé mais possible)
|
|
mixedTypes: {
|
|
name: "Mixed Types",
|
|
vocabulary: {
|
|
"string": "normal",
|
|
"number": 123,
|
|
"boolean": true,
|
|
"null": null,
|
|
"array": ["item1", "item2"],
|
|
"object": { nested: "value" }
|
|
}
|
|
}
|
|
};
|
|
|
|
export const networkFailureSimulations = {
|
|
// Différents types de timeouts
|
|
timeouts: [
|
|
{ delay: 100, succeed: true }, // Rapide
|
|
{ delay: 1000, succeed: true }, // Normal
|
|
{ delay: 5000, succeed: true }, // Lent
|
|
{ delay: 30000, succeed: false }, // Timeout
|
|
{ delay: 0, succeed: false } // Échec immédiat
|
|
],
|
|
|
|
// Codes d'erreur HTTP à tester
|
|
httpErrors: [
|
|
{ status: 400, message: "Bad Request" },
|
|
{ status: 401, message: "Unauthorized" },
|
|
{ status: 403, message: "Forbidden" },
|
|
{ status: 404, message: "Not Found" },
|
|
{ status: 408, message: "Request Timeout" },
|
|
{ status: 429, message: "Too Many Requests" },
|
|
{ status: 500, message: "Internal Server Error" },
|
|
{ status: 502, message: "Bad Gateway" },
|
|
{ status: 503, message: "Service Unavailable" },
|
|
{ status: 504, message: "Gateway Timeout" },
|
|
{ status: 520, message: "Unknown Error" },
|
|
{ status: 521, message: "Web Server Is Down" },
|
|
{ status: 522, message: "Connection Timed Out" },
|
|
{ status: 523, message: "Origin Is Unreachable" },
|
|
{ status: 524, message: "A Timeout Occurred" }
|
|
],
|
|
|
|
// Patterns de pannes réseau
|
|
networkPatterns: [
|
|
'intermittent', // Succès/échec alterné
|
|
'degrading', // Performance qui se dégrade
|
|
'recovering', // Récupération progressive
|
|
'cascade', // Échecs en cascade
|
|
'random' // Échecs aléatoires
|
|
]
|
|
};
|
|
|
|
export const browserCompatibilityTests = {
|
|
// APIs manquantes à simuler
|
|
missingAPIs: [
|
|
'fetch',
|
|
'crypto',
|
|
'crypto.subtle',
|
|
'URLSearchParams',
|
|
'localStorage',
|
|
'sessionStorage',
|
|
'console',
|
|
'JSON',
|
|
'Promise',
|
|
'setTimeout',
|
|
'clearTimeout'
|
|
],
|
|
|
|
// Différents User-Agents à tester
|
|
userAgents: [
|
|
// Desktop browsers
|
|
'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36',
|
|
'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:89.0) Gecko/20100101 Firefox/89.0',
|
|
'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/14.1.1 Safari/605.1.15',
|
|
'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36 Edg/91.0.864.59',
|
|
|
|
// Mobile browsers
|
|
'Mozilla/5.0 (iPhone; CPU iPhone OS 14_6 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/14.1.1 Mobile/15E148 Safari/604.1',
|
|
'Mozilla/5.0 (Linux; Android 11; SM-G991B) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.120 Mobile Safari/537.36',
|
|
'Mozilla/5.0 (iPad; CPU OS 14_6 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/14.1.1 Mobile/15E148 Safari/604.1',
|
|
|
|
// Older browsers
|
|
'Mozilla/5.0 (Windows NT 6.1; WOW64; Trident/7.0; rv:11.0) like Gecko', // IE11
|
|
'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/49.0.2623.112 Safari/537.36', // Old Chrome
|
|
|
|
// Bots and tools
|
|
'curl/7.68.0',
|
|
'node-fetch/1.0 (+https://github.com/bitinn/node-fetch)',
|
|
'PostmanRuntime/7.28.0',
|
|
'Googlebot/2.1 (+http://www.google.com/bot.html)'
|
|
],
|
|
|
|
// Différents Accept headers
|
|
acceptHeaders: [
|
|
'application/json',
|
|
'application/json, text/plain, */*',
|
|
'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8',
|
|
'*/*',
|
|
'application/json;charset=utf-8',
|
|
'text/plain',
|
|
'application/xml',
|
|
'text/html'
|
|
]
|
|
};
|
|
|
|
export const securityTestCases = {
|
|
// Tentatives d'injection de chemin
|
|
pathInjections: [
|
|
'../../../etc/passwd',
|
|
'..\\\\..\\\\..\\\\windows\\\\system32\\\\config\\\\sam',
|
|
'%2e%2e%2f%2e%2e%2f%2e%2e%2fetc%2fpasswd',
|
|
'....//....//....//etc//passwd',
|
|
'\\\\x2e\\\\x2e\\\\x2f\\\\x2e\\\\x2e\\\\x2f\\\\x2e\\\\x2e\\\\x2fetc\\\\x2fpasswd',
|
|
'/proc/self/environ',
|
|
'/dev/null',
|
|
'CON', // Windows device name
|
|
'aux.txt', // Windows reserved name
|
|
'file:///etc/passwd',
|
|
'http://evil.com/malicious.json'
|
|
],
|
|
|
|
// Headers malveillants
|
|
maliciousHeaders: {
|
|
'X-Forwarded-For': '127.0.0.1, evil.com',
|
|
'Host': 'evil.com',
|
|
'Referer': 'http://evil.com/attack.html',
|
|
'User-Agent': '<script>alert("xss")</script>',
|
|
'X-Real-IP': ''; DROP TABLE users; --',
|
|
'Content-Length': '-1',
|
|
'Transfer-Encoding': 'chunked\\r\\nContent-Length: 0\\r\\n\\r\\n',
|
|
'X-Custom': 'A'.repeat(100000) // Header très long
|
|
},
|
|
|
|
// Requêtes malformées
|
|
malformedRequests: [
|
|
{ method: 'GET\\r\\nHost: evil.com' }, // HTTP splitting
|
|
{ method: 'GET HTTP/1.1\\r\\nHost: evil.com\\r\\n\\r\\nGET' }, // Request smuggling
|
|
{ url: '/do-proxy/test.json\\r\\nHost: evil.com' },
|
|
{ url: '/do-proxy/test.json?param=value&' + 'x'.repeat(10000) } // Query très longue
|
|
]
|
|
};
|
|
|
|
export const performanceTestData = {
|
|
// Différentes tailles de payload pour tester les performances
|
|
payloadSizes: [
|
|
{ name: 'tiny', size: 100 }, // 100 bytes
|
|
{ name: 'small', size: 1024 }, // 1KB
|
|
{ name: 'medium', size: 10240 }, // 10KB
|
|
{ name: 'large', size: 102400 }, // 100KB
|
|
{ name: 'huge', size: 1048576 } // 1MB
|
|
],
|
|
|
|
// Patterns de charge à tester
|
|
loadPatterns: [
|
|
{ name: 'constant', requests: 100, interval: 100 }, // Charge constante
|
|
{ name: 'burst', requests: 50, interval: 10 }, // Pics de charge
|
|
{ name: 'gradual', requests: 100, interval: 'ramp' }, // Montée progressive
|
|
{ name: 'spike', requests: 200, interval: 'random' } // Pics aléatoires
|
|
],
|
|
|
|
// Métriques à mesurer
|
|
metrics: [
|
|
'responseTime',
|
|
'throughput',
|
|
'errorRate',
|
|
'memoryUsage',
|
|
'cpuUsage',
|
|
'connectionCount'
|
|
]
|
|
};
|
|
|
|
export const concurrencyTestScenarios = [
|
|
{
|
|
name: 'Chargement simultané de modules identiques',
|
|
scenario: 'multiple_same_module',
|
|
concurrency: 10,
|
|
resource: 'sbs-level-7-8-new.json'
|
|
},
|
|
{
|
|
name: 'Chargement simultané de modules différents',
|
|
scenario: 'multiple_different_modules',
|
|
concurrency: 10,
|
|
resources: ['sbs-level-7-8-new.json', 'english-class-demo.json']
|
|
},
|
|
{
|
|
name: 'Création simultanée de jeux',
|
|
scenario: 'multiple_game_creation',
|
|
concurrency: 5,
|
|
gameTypes: ['whack-a-mole', 'memory-match', 'quiz-game']
|
|
},
|
|
{
|
|
name: 'Navigation rapide entre pages',
|
|
scenario: 'rapid_navigation',
|
|
concurrency: 3,
|
|
pages: ['home', 'games', 'levels', 'play']
|
|
},
|
|
{
|
|
name: 'Configuration simultanée',
|
|
scenario: 'concurrent_config_changes',
|
|
concurrency: 5,
|
|
operations: ['set', 'get', 'test_connection']
|
|
}
|
|
];
|
|
|
|
export const memoryLeakTestData = {
|
|
// Scénarios susceptibles de causer des fuites mémoire
|
|
scenarios: [
|
|
'repeated_module_loading',
|
|
'game_creation_destruction',
|
|
'config_changes',
|
|
'event_listener_attachment',
|
|
'timer_creation',
|
|
'fetch_operations'
|
|
],
|
|
|
|
// Tailles d'objets pour détecter les fuites
|
|
objectSizes: [
|
|
{ name: 'small', items: 100 },
|
|
{ name: 'medium', items: 1000 },
|
|
{ name: 'large', items: 10000 }
|
|
],
|
|
|
|
// Cycles à répéter pour détecter les fuites
|
|
cycles: 100
|
|
};
|
|
|
|
// Helper pour générer des données de test aléatoires
|
|
export function generateRandomContent(size = 'medium') {
|
|
const sizes = {
|
|
small: { vocab: 10, sentences: 5 },
|
|
medium: { vocab: 100, sentences: 20 },
|
|
large: { vocab: 1000, sentences: 100 }
|
|
};
|
|
|
|
const config = sizes[size] || sizes.medium;
|
|
const vocabulary = {};
|
|
const sentences = [];
|
|
|
|
// Générer du vocabulaire aléatoire
|
|
for (let i = 0; i < config.vocab; i++) {
|
|
vocabulary[`word${i}`] = `translation${i}`;
|
|
}
|
|
|
|
// Générer des phrases aléatoires
|
|
for (let i = 0; i < config.sentences; i++) {
|
|
sentences.push({
|
|
english: `English sentence ${i}`,
|
|
chinese: `中文句子 ${i}`,
|
|
prononciation: `pronunciation ${i}`
|
|
});
|
|
}
|
|
|
|
return {
|
|
name: `Random Content ${size}`,
|
|
vocabulary,
|
|
sentences
|
|
};
|
|
}
|
|
|
|
// Helper pour créer des erreurs réseau simulées
|
|
export function createNetworkErrorSimulator(pattern = 'random') {
|
|
let callCount = 0;
|
|
|
|
return () => {
|
|
callCount++;
|
|
|
|
switch (pattern) {
|
|
case 'intermittent':
|
|
return callCount % 2 === 0;
|
|
|
|
case 'degrading':
|
|
return Math.random() > (callCount * 0.1); // De plus en plus d'échecs
|
|
|
|
case 'recovering':
|
|
return Math.random() > Math.max(0.1, 0.9 - callCount * 0.1); // De moins en moins d'échecs
|
|
|
|
case 'cascade':
|
|
return callCount < 3; // Échecs en début puis récupération
|
|
|
|
case 'random':
|
|
default:
|
|
return Math.random() > 0.3; // 70% de succès
|
|
}
|
|
};
|
|
} |