mini-games-api.spec.js 8.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277
  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:9082';
  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('POST /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: 'POST',
  110. headers: {
  111. 'Content-Type': 'application/json',
  112. 'Authorization': 'Bearer test-token'
  113. },
  114. body: JSON.stringify({})
  115. });
  116. const data = await response.json();
  117. expect(response.status).toBe(200);
  118. expect(data.code).toBe(200);
  119. expect(Array.isArray(data.data)).toBe(true);
  120. expect(data.data.length).toBeGreaterThan(0);
  121. });
  122. test('should include game details', async () => {
  123. const response = await fetch(`${BASE_URL}/api/mini-game/list`, {
  124. method: 'POST',
  125. headers: {
  126. 'Content-Type': 'application/json',
  127. 'Authorization': 'Bearer test-token'
  128. },
  129. body: JSON.stringify({})
  130. });
  131. const data = await response.json();
  132. if (data.data.length > 0) {
  133. const game = data.data[0];
  134. expect(game).toHaveProperty('code');
  135. expect(game).toHaveProperty('name');
  136. expect(game).toHaveProperty('basePoints');
  137. }
  138. });
  139. });
  140. describe('POST /api/mini-game/:gameCode', () => {
  141. test('should return game details by code', async () => {
  142. const response = await fetch(`${BASE_URL}/api/mini-game/schulte`, {
  143. method: 'POST',
  144. headers: {
  145. 'Content-Type': 'application/json',
  146. 'Authorization': 'Bearer test-token'
  147. },
  148. body: JSON.stringify({})
  149. });
  150. const data = await response.json();
  151. expect(response.status).toBe(200);
  152. expect(data.code).toBe(200);
  153. expect(data.data).toHaveProperty('code', 'schulte');
  154. });
  155. test('should return 404 for invalid game code', async () => {
  156. const response = await fetch(`${BASE_URL}/api/mini-game/nonexistent`, {
  157. method: 'POST',
  158. headers: {
  159. 'Content-Type': 'application/json',
  160. 'Authorization': 'Bearer test-token'
  161. },
  162. body: JSON.stringify({})
  163. });
  164. expect(response.status).toBe(404);
  165. });
  166. });
  167. describe('Authentication and Authorization', () => {
  168. test('should require authentication', async () => {
  169. const response = await fetch(`${BASE_URL}/api/mini-game/complete`, {
  170. method: 'POST',
  171. headers: {
  172. 'Content-Type': 'application/json'
  173. // No Authorization header
  174. },
  175. body: JSON.stringify({
  176. childId: 'child-123',
  177. gameCode: 'schulte',
  178. completionTime: 45,
  179. score: 85
  180. })
  181. });
  182. expect(response.status).toBe(401);
  183. });
  184. test('should validate user has access to child', async () => {
  185. // Test with childId that doesn't belong to user
  186. const response = await fetch(`${BASE_URL}/api/mini-game/complete`, {
  187. method: 'POST',
  188. headers: {
  189. 'Content-Type': 'application/json',
  190. 'Authorization': 'Bearer test-token'
  191. },
  192. body: JSON.stringify({
  193. childId: 'other-user-child',
  194. gameCode: 'schulte',
  195. completionTime: 45,
  196. score: 85
  197. })
  198. });
  199. expect(response.status).toBe(403);
  200. });
  201. });
  202. describe('Error Handling', () => {
  203. test('should handle database errors gracefully', async () => {
  204. // Test with invalid data that might cause DB error
  205. const response = await fetch(`${BASE_URL}/api/mini-game/complete`, {
  206. method: 'POST',
  207. headers: {
  208. 'Content-Type': 'application/json',
  209. 'Authorization': 'Bearer test-token'
  210. },
  211. body: JSON.stringify({
  212. childId: 'child-123',
  213. gameCode: 'schulte',
  214. completionTime: -1, // Invalid time
  215. score: -10 // Invalid score
  216. })
  217. });
  218. expect(response.status).toBe(400);
  219. });
  220. test('should return meaningful error messages', async () => {
  221. const response = await fetch(`${BASE_URL}/api/mini-game/complete`, {
  222. method: 'POST',
  223. headers: {
  224. 'Content-Type': 'application/json',
  225. 'Authorization': 'Bearer test-token'
  226. },
  227. body: JSON.stringify({
  228. childId: '',
  229. gameCode: '',
  230. completionTime: 0,
  231. score: 0
  232. })
  233. });
  234. const data = await response.json();
  235. expect(data).toHaveProperty('message');
  236. });
  237. });
  238. });