diff --git a/tests/edge-cases/api-edge-cases.test.js b/tests/edge-cases/api-edge-cases.test.js
new file mode 100644
index 0000000..4ebfa88
--- /dev/null
+++ b/tests/edge-cases/api-edge-cases.test.js
@@ -0,0 +1,477 @@
+/**
+ * TESTS EDGE CASES - API Controller
+ * Tests des cas limites, erreurs et comportements extrêmes
+ */
+
+const { describe, it, before, after } = require('node:test');
+const assert = require('node:assert');
+const http = require('node:http');
+const { ManualServer } = require('../../lib/modes/ManualServer');
+
+// Helper pour requêtes HTTP
+function makeRequest(options, postData = null) {
+ return new Promise((resolve, reject) => {
+ const req = http.request(options, (res) => {
+ let data = '';
+ res.on('data', chunk => data += chunk);
+ res.on('end', () => {
+ try {
+ const parsed = res.headers['content-type']?.includes('application/json')
+ ? JSON.parse(data)
+ : data;
+ resolve({ statusCode: res.statusCode, headers: res.headers, data: parsed });
+ } catch (e) {
+ resolve({ statusCode: res.statusCode, headers: res.headers, data });
+ }
+ });
+ });
+
+ req.on('error', reject);
+ req.setTimeout(10000, () => {
+ req.destroy();
+ reject(new Error('Request timeout'));
+ });
+
+ if (postData) {
+ req.write(typeof postData === 'object' ? JSON.stringify(postData) : postData);
+ }
+ req.end();
+ });
+}
+
+describe('API Edge Cases - Tests des Cas Limites', () => {
+ let server;
+ let baseUrl;
+ const testPort = 3098; // Port différent pour éviter conflits
+
+ before(async () => {
+ server = new ManualServer({ port: testPort, wsPort: 8098 });
+ await server.start();
+ baseUrl = `http://localhost:${testPort}`;
+ console.log(`🧪 Serveur edge cases démarré sur ${baseUrl}`);
+ });
+
+ after(async () => {
+ if (server) {
+ await server.stop();
+ console.log('🛑 Serveur edge cases arrêté');
+ }
+ });
+
+ describe('🔥 Paramètres Extrêmes', () => {
+ it('should handle very long project names', async () => {
+ const longName = 'A'.repeat(10000); // 10KB de nom
+
+ const response = await makeRequest({
+ hostname: 'localhost',
+ port: testPort,
+ path: '/api/projects',
+ method: 'POST',
+ headers: { 'Content-Type': 'application/json' }
+ }, {
+ name: longName,
+ description: 'Test nom très long'
+ });
+
+ // L'API doit gérer les noms longs gracieusement
+ assert.ok([201, 400].includes(response.statusCode));
+
+ if (response.statusCode === 201) {
+ assert.strictEqual(response.data.success, true);
+ assert.strictEqual(response.data.data.name, longName);
+ } else {
+ assert.strictEqual(response.data.success, false);
+ }
+ });
+
+ it('should handle pagination with extreme values', async () => {
+ // Test avec des valeurs de pagination extrêmes
+ const testCases = [
+ { limit: -1, offset: 0 },
+ { limit: 999999, offset: 0 },
+ { limit: 50, offset: -1 },
+ { limit: 'invalid', offset: 'invalid' },
+ { limit: 0, offset: 999999 }
+ ];
+
+ for (const testCase of testCases) {
+ const response = await makeRequest({
+ hostname: 'localhost',
+ port: testPort,
+ path: `/api/articles?limit=${testCase.limit}&offset=${testCase.offset}`,
+ method: 'GET'
+ });
+
+ // L'API doit soit réussir avec des valeurs normalisées, soit échouer gracieusement
+ assert.ok([200, 400, 500].includes(response.statusCode));
+
+ if (response.statusCode === 200) {
+ assert.ok(response.data.data.limit >= 0);
+ assert.ok(response.data.data.offset >= 0);
+ }
+ }
+ });
+
+ it('should handle special characters in all fields', async () => {
+ const specialChars = {
+ name: '🚀 Test "Special" & émojis 中文',
+ description: 'Description avec \n\t\r caractères spéciaux \\n "quotes" \'single\' & symbols €£¥',
+ config: {
+ special: '<>&"\'{}[]()=+-*/\\',
+ unicode: '🎯🔥💯✨🌟⭐',
+ mixed: 'Normal text avec 中文 и русский'
+ }
+ };
+
+ const response = await makeRequest({
+ hostname: 'localhost',
+ port: testPort,
+ path: '/api/projects',
+ method: 'POST',
+ headers: { 'Content-Type': 'application/json' }
+ }, specialChars);
+
+ assert.ok([201, 400].includes(response.statusCode));
+
+ if (response.statusCode === 201) {
+ assert.strictEqual(response.data.data.name, specialChars.name);
+ assert.strictEqual(response.data.data.description, specialChars.description);
+ }
+ });
+ });
+
+ describe('🛡️ Sécurité et Injection', () => {
+ it('should reject SQL injection attempts', async () => {
+ const sqlInjections = [
+ "'; DROP TABLE projects; --",
+ "1' OR '1'='1",
+ "admin'/*",
+ "1; SELECT * FROM users; --"
+ ];
+
+ for (const injection of sqlInjections) {
+ const response = await makeRequest({
+ hostname: 'localhost',
+ port: testPort,
+ path: '/api/projects',
+ method: 'POST',
+ headers: { 'Content-Type': 'application/json' }
+ }, {
+ name: injection,
+ description: 'Test injection SQL'
+ });
+
+ // L'API doit traiter cela comme une chaîne normale, pas d'injection
+ assert.ok([201, 400].includes(response.statusCode));
+
+ if (response.statusCode === 201) {
+ assert.strictEqual(response.data.data.name, injection); // Stocké tel quel
+ }
+ }
+ });
+
+ it('should handle XSS attempts gracefully', async () => {
+ const xssPayloads = [
+ '',
+ 'javascript:alert(1)',
+ '
',
+ '