mini-games-api.spec.js 7.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269
  1. /**
  2. * Integration tests for Mini-Games API endpoints
  3. * Tests backend API endpoints for mini-games scoring
  4. */
  5. describe('Mini-Games API Integration Tests', () => {
  6. const BASE_URL = process.env.API_BASE_URL || 'http://localhost:8080';
  7. describe('POST /api/mini-game/complete', () => {
  8. test('should successfully record game completion', async () => {
  9. const response = await fetch(`${BASE_URL}/api/mini-game/complete`, {
  10. method: 'POST',
  11. headers: {
  12. 'Content-Type': 'application/json',
  13. 'Authorization': 'Bearer test-token',
  14. 'X-User-Id': 'test-user'
  15. },
  16. body: JSON.stringify({
  17. childId: 'child-123',
  18. gameCode: 'schulte',
  19. completionTime: 45,
  20. score: 85
  21. })
  22. });
  23. const data = await response.json();
  24. expect(response.status).toBe(200);
  25. expect(data.code).toBe(200);
  26. expect(data.data).toHaveProperty('pointsEarned');
  27. expect(data.data).toHaveProperty('newBalance');
  28. });
  29. test('should handle different game types', async () => {
  30. const gameCodes = ['schulte', '1a2b', 'sudoku'];
  31. for (const gameCode of gameCodes) {
  32. const response = await fetch(`${BASE_URL}/api/mini-game/complete`, {
  33. method: 'POST',
  34. headers: {
  35. 'Content-Type': 'application/json',
  36. 'Authorization': 'Bearer test-token'
  37. },
  38. body: JSON.stringify({
  39. childId: 'child-123',
  40. gameCode: gameCode,
  41. completionTime: 60,
  42. score: 75
  43. })
  44. });
  45. expect(response.status).toBe(200);
  46. }
  47. });
  48. test('should validate required fields', async () => {
  49. const response = await fetch(`${BASE_URL}/api/mini-game/complete`, {
  50. method: 'POST',
  51. headers: {
  52. 'Content-Type': 'application/json'
  53. },
  54. body: JSON.stringify({
  55. // Missing required fields
  56. gameCode: 'schulte'
  57. })
  58. });
  59. expect(response.status).toBe(400);
  60. });
  61. test('should handle invalid game code', async () => {
  62. const response = await fetch(`${BASE_URL}/api/mini-game/complete`, {
  63. method: 'POST',
  64. headers: {
  65. 'Content-Type': 'application/json',
  66. 'Authorization': 'Bearer test-token'
  67. },
  68. body: JSON.stringify({
  69. childId: 'child-123',
  70. gameCode: 'invalid-game',
  71. completionTime: 30,
  72. score: 50
  73. })
  74. });
  75. expect(response.status).toBe(400);
  76. });
  77. test('should calculate points based on score', async () => {
  78. // Test different score ranges
  79. const testCases = [
  80. { score: 100, expectedPoints: 10 }, // High score
  81. { score: 75, expectedPoints: 7.5 }, // Medium score
  82. { score: 60, expectedPoints: 6 }, // Low score
  83. { score: 30, expectedPoints: 3 } // Minimum score
  84. ];
  85. for (const testCase of testCases) {
  86. const response = await fetch(`${BASE_URL}/api/mini-game/complete`, {
  87. method: 'POST',
  88. headers: {
  89. 'Content-Type': 'application/json',
  90. 'Authorization': 'Bearer test-token'
  91. },
  92. body: JSON.stringify({
  93. childId: 'child-123',
  94. gameCode: 'schulte',
  95. completionTime: 60,
  96. score: testCase.score
  97. })
  98. });
  99. const data = await response.json();
  100. if (response.status === 200) {
  101. expect(data.data.pointsEarned).toBeGreaterThanOrEqual(0);
  102. }
  103. }
  104. });
  105. });
  106. describe('GET /api/mini-game/list', () => {
  107. test('should return list of available games', async () => {
  108. const response = await fetch(`${BASE_URL}/api/mini-game/list`, {
  109. method: 'GET',
  110. headers: {
  111. 'Authorization': 'Bearer test-token'
  112. }
  113. });
  114. const data = await response.json();
  115. expect(response.status).toBe(200);
  116. expect(data.code).toBe(200);
  117. expect(Array.isArray(data.data)).toBe(true);
  118. expect(data.data.length).toBeGreaterThan(0);
  119. });
  120. test('should include game details', async () => {
  121. const response = await fetch(`${BASE_URL}/api/mini-game/list`, {
  122. method: 'GET',
  123. headers: {
  124. 'Authorization': 'Bearer test-token'
  125. }
  126. });
  127. const data = await response.json();
  128. if (data.data.length > 0) {
  129. const game = data.data[0];
  130. expect(game).toHaveProperty('code');
  131. expect(game).toHaveProperty('name');
  132. expect(game).toHaveProperty('basePoints');
  133. }
  134. });
  135. });
  136. describe('GET /api/mini-game/:gameCode', () => {
  137. test('should return game details by code', async () => {
  138. const response = await fetch(`${BASE_URL}/api/mini-game/schulte`, {
  139. method: 'GET',
  140. headers: {
  141. 'Authorization': 'Bearer test-token'
  142. }
  143. });
  144. const data = await response.json();
  145. expect(response.status).toBe(200);
  146. expect(data.code).toBe(200);
  147. expect(data.data).toHaveProperty('code', 'schulte');
  148. });
  149. test('should return 404 for invalid game code', async () => {
  150. const response = await fetch(`${BASE_URL}/api/mini-game/nonexistent`, {
  151. method: 'GET',
  152. headers: {
  153. 'Authorization': 'Bearer test-token'
  154. }
  155. });
  156. expect(response.status).toBe(404);
  157. });
  158. });
  159. describe('Authentication and Authorization', () => {
  160. test('should require authentication', async () => {
  161. const response = await fetch(`${BASE_URL}/api/mini-game/complete`, {
  162. method: 'POST',
  163. headers: {
  164. 'Content-Type': 'application/json'
  165. // No Authorization header
  166. },
  167. body: JSON.stringify({
  168. childId: 'child-123',
  169. gameCode: 'schulte',
  170. completionTime: 45,
  171. score: 85
  172. })
  173. });
  174. expect(response.status).toBe(401);
  175. });
  176. test('should validate user has access to child', async () => {
  177. // Test with childId that doesn't belong to user
  178. const response = await fetch(`${BASE_URL}/api/mini-game/complete`, {
  179. method: 'POST',
  180. headers: {
  181. 'Content-Type': 'application/json',
  182. 'Authorization': 'Bearer test-token'
  183. },
  184. body: JSON.stringify({
  185. childId: 'other-user-child',
  186. gameCode: 'schulte',
  187. completionTime: 45,
  188. score: 85
  189. })
  190. });
  191. expect(response.status).toBe(403);
  192. });
  193. });
  194. describe('Error Handling', () => {
  195. test('should handle database errors gracefully', async () => {
  196. // Test with invalid data that might cause DB error
  197. const response = await fetch(`${BASE_URL}/api/mini-game/complete`, {
  198. method: 'POST',
  199. headers: {
  200. 'Content-Type': 'application/json',
  201. 'Authorization': 'Bearer test-token'
  202. },
  203. body: JSON.stringify({
  204. childId: 'child-123',
  205. gameCode: 'schulte',
  206. completionTime: -1, // Invalid time
  207. score: -10 // Invalid score
  208. })
  209. });
  210. expect(response.status).toBe(400);
  211. });
  212. test('should return meaningful error messages', async () => {
  213. const response = await fetch(`${BASE_URL}/api/mini-game/complete`, {
  214. method: 'POST',
  215. headers: {
  216. 'Content-Type': 'application/json',
  217. 'Authorization': 'Bearer test-token'
  218. },
  219. body: JSON.stringify({
  220. childId: '',
  221. gameCode: '',
  222. completionTime: 0,
  223. score: 0
  224. })
  225. });
  226. const data = await response.json();
  227. expect(data).toHaveProperty('message');
  228. });
  229. });
  230. });