ai-first-cli 1.3.1 → 1.3.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (514) hide show
  1. package/.ai-dev/index.db +0 -0
  2. package/CHANGELOG.md +241 -0
  3. package/PHASE1_USER_SIMULATION.md +56 -0
  4. package/PHASE2_USER_SIMULATION.md +81 -0
  5. package/PHASE3_USER_SIMULATION.md +176 -0
  6. package/README.es.md +73 -0
  7. package/README.md +119 -15
  8. package/ai/graph/knowledge-graph.json +10 -0
  9. package/ai-context/ai_context.md +130 -0
  10. package/{test-projects/react-app/.ai-dev → ai-context}/ai_rules.md +10 -5
  11. package/ai-context/architecture.md +136 -0
  12. package/ai-context/context/features/src.json +69 -0
  13. package/ai-context/context/features/test-projects.json +69 -0
  14. package/ai-context/context/flows/ai-first.json +9 -0
  15. package/ai-context/context/flows/auth.json +13 -0
  16. package/ai-context/context/flows/doctor.json +9 -0
  17. package/ai-context/context/flows/explore.json +9 -0
  18. package/ai-context/context/flows/routes.json +15 -0
  19. package/ai-context/context/flows/user.json +23 -0
  20. package/ai-context/context/flows/views.json +12 -0
  21. package/{test-projects/react-app/.ai-dev → ai-context}/conventions.md +3 -2
  22. package/ai-context/dependencies.json +3360 -0
  23. package/ai-context/entrypoints.md +45 -0
  24. package/ai-context/index-state.json +196 -0
  25. package/ai-context/modules.json +901 -0
  26. package/ai-context/project.json +33 -0
  27. package/ai-context/repo_map.json +8857 -0
  28. package/ai-context/repo_map.md +2002 -0
  29. package/{test-projects/flask-app/.ai-dev → ai-context}/schema.json +1 -1
  30. package/ai-context/summary.md +46 -0
  31. package/ai-context/symbols.json +82467 -0
  32. package/{test-projects/react-app/.ai-dev → ai-context}/tech_stack.md +15 -7
  33. package/ai-context-evaluation-report-1774223059505.md +206 -0
  34. package/dist/analyzers/architecture.d.ts.map +1 -1
  35. package/dist/analyzers/architecture.js +78 -5
  36. package/dist/analyzers/architecture.js.map +1 -1
  37. package/dist/analyzers/entrypoints.d.ts.map +1 -1
  38. package/dist/analyzers/entrypoints.js +358 -0
  39. package/dist/analyzers/entrypoints.js.map +1 -1
  40. package/dist/analyzers/symbols.d.ts.map +1 -1
  41. package/dist/analyzers/symbols.js +119 -3
  42. package/dist/analyzers/symbols.js.map +1 -1
  43. package/dist/analyzers/techStack.d.ts +8 -0
  44. package/dist/analyzers/techStack.d.ts.map +1 -1
  45. package/dist/analyzers/techStack.js +118 -0
  46. package/dist/analyzers/techStack.js.map +1 -1
  47. package/dist/utils/fileUtils.d.ts.map +1 -1
  48. package/dist/utils/fileUtils.js +5 -0
  49. package/dist/utils/fileUtils.js.map +1 -1
  50. package/package.json +1 -1
  51. package/scripts/ai-context-evaluator.ts +440 -0
  52. package/src/analyzers/architecture.ts +83 -6
  53. package/src/analyzers/entrypoints.ts +400 -0
  54. package/src/analyzers/symbols.ts +129 -3
  55. package/src/analyzers/techStack.ts +137 -0
  56. package/src/utils/fileUtils.ts +5 -0
  57. package/tests/apex-parser.test.ts +193 -0
  58. package/tests/cli-commands-batch1.test.ts +808 -0
  59. package/tests/cli-commands-batch2.test.ts +1113 -0
  60. package/tests/cli-commands-batch3.test.ts +1128 -0
  61. package/tests/cli-index.test.ts +1007 -0
  62. package/tests/cli-init.test.ts +761 -0
  63. package/tests/entrypoints-languages.test.ts +373 -0
  64. package/tests/framework-detection.test.ts +296 -0
  65. package/tests/salesforce-apex-classes.test.ts +713 -0
  66. package/tests/salesforce-apex-triggers.test.ts +871 -0
  67. package/tests/salesforce-custom-objects.test.ts +918 -0
  68. package/tests/salesforce-flows.test.ts +710 -0
  69. package/tests/salesforce-lwc.test.ts +963 -0
  70. package/tests/salesforce-sfdx-integration.test.ts +1125 -0
  71. package/ANALISIS_COMPLETO.md +0 -424
  72. package/ANALISIS_MEJORAS.md +0 -327
  73. package/CONTRIBUTING.md +0 -89
  74. package/FLOW.md +0 -129
  75. package/TEST_RESULTS.md +0 -198
  76. package/TEST_RESULTS_COMPARATIVE.md +0 -159
  77. package/TEST_RESULTS_COMPLETE.md +0 -127
  78. package/TEST_RESULTS_COMPREHENSIVE.md +0 -208
  79. package/install.sh +0 -188
  80. package/run-all-tests.sh +0 -184
  81. package/test-ai-context-understanding.sh +0 -21
  82. package/test-projects/django-app/.ai-dev/ai_context.md +0 -92
  83. package/test-projects/django-app/.ai-dev/ai_rules.md +0 -47
  84. package/test-projects/django-app/.ai-dev/architecture.md +0 -57
  85. package/test-projects/django-app/.ai-dev/cache.json +0 -169
  86. package/test-projects/django-app/.ai-dev/context/flows/views.json +0 -10
  87. package/test-projects/django-app/.ai-dev/conventions.md +0 -51
  88. package/test-projects/django-app/.ai-dev/dependencies.json +0 -312
  89. package/test-projects/django-app/.ai-dev/entrypoints.md +0 -4
  90. package/test-projects/django-app/.ai-dev/files.json +0 -209
  91. package/test-projects/django-app/.ai-dev/graph/knowledge-graph.json +0 -36
  92. package/test-projects/django-app/.ai-dev/graph/module-graph.json +0 -145
  93. package/test-projects/django-app/.ai-dev/graph/symbol-graph.json +0 -1488
  94. package/test-projects/django-app/.ai-dev/graph/symbol-references.json +0 -1
  95. package/test-projects/django-app/.ai-dev/index-state.json +0 -294
  96. package/test-projects/django-app/.ai-dev/modules.json +0 -35
  97. package/test-projects/django-app/.ai-dev/project.json +0 -11
  98. package/test-projects/django-app/.ai-dev/repo_map.json +0 -412
  99. package/test-projects/django-app/.ai-dev/repo_map.md +0 -105
  100. package/test-projects/django-app/.ai-dev/schema.json +0 -5
  101. package/test-projects/django-app/.ai-dev/summary.md +0 -15
  102. package/test-projects/django-app/.ai-dev/symbols.json +0 -1
  103. package/test-projects/django-app/.ai-dev/tech_stack.md +0 -32
  104. package/test-projects/django-app/README.md +0 -91
  105. package/test-projects/django-app/blog/__init__.py +0 -0
  106. package/test-projects/django-app/blog/admin.py +0 -31
  107. package/test-projects/django-app/blog/models.py +0 -55
  108. package/test-projects/django-app/blog/serializers.py +0 -69
  109. package/test-projects/django-app/blog/urls.py +0 -14
  110. package/test-projects/django-app/blog/views.py +0 -96
  111. package/test-projects/django-app/django_app/__init__.py +0 -0
  112. package/test-projects/django-app/django_app/settings.py +0 -90
  113. package/test-projects/django-app/django_app/urls.py +0 -11
  114. package/test-projects/django-app/django_app/wsgi.py +0 -9
  115. package/test-projects/django-app/manage.py +0 -23
  116. package/test-projects/django-app/requirements.txt +0 -3
  117. package/test-projects/django-app/users/__init__.py +0 -0
  118. package/test-projects/django-app/users/admin.py +0 -42
  119. package/test-projects/django-app/users/models.py +0 -54
  120. package/test-projects/django-app/users/serializers.py +0 -113
  121. package/test-projects/django-app/users/urls.py +0 -13
  122. package/test-projects/django-app/users/views.py +0 -135
  123. package/test-projects/express-api/.ai-dev/ai_context.md +0 -112
  124. package/test-projects/express-api/.ai-dev/ai_rules.md +0 -50
  125. package/test-projects/express-api/.ai-dev/architecture.md +0 -62
  126. package/test-projects/express-api/.ai-dev/context/features/controllers.json +0 -13
  127. package/test-projects/express-api/.ai-dev/context/features/services.json +0 -13
  128. package/test-projects/express-api/.ai-dev/context/flows/auth.json +0 -12
  129. package/test-projects/express-api/.ai-dev/context/flows/user.json +0 -13
  130. package/test-projects/express-api/.ai-dev/conventions.md +0 -51
  131. package/test-projects/express-api/.ai-dev/dependencies.json +0 -54
  132. package/test-projects/express-api/.ai-dev/entrypoints.md +0 -17
  133. package/test-projects/express-api/.ai-dev/modules.json +0 -30
  134. package/test-projects/express-api/.ai-dev/project.json +0 -15
  135. package/test-projects/express-api/.ai-dev/repo_map.json +0 -100
  136. package/test-projects/express-api/.ai-dev/repo_map.md +0 -36
  137. package/test-projects/express-api/.ai-dev/schema.json +0 -5
  138. package/test-projects/express-api/.ai-dev/summary.md +0 -14
  139. package/test-projects/express-api/.ai-dev/symbols.json +0 -7
  140. package/test-projects/express-api/.ai-dev/tech_stack.md +0 -38
  141. package/test-projects/express-api/.ai-dev/tools.json +0 -10
  142. package/test-projects/express-api/controllers/authController.js +0 -32
  143. package/test-projects/express-api/controllers/userController.js +0 -51
  144. package/test-projects/express-api/index.js +0 -30
  145. package/test-projects/express-api/middleware/authMiddleware.js +0 -30
  146. package/test-projects/express-api/models/userRepository.js +0 -25
  147. package/test-projects/express-api/package.json +0 -18
  148. package/test-projects/express-api/services/authService.js +0 -17
  149. package/test-projects/express-api/services/userService.js +0 -28
  150. package/test-projects/fastapi-app/.ai-dev/ai_context.md +0 -89
  151. package/test-projects/fastapi-app/.ai-dev/ai_rules.md +0 -47
  152. package/test-projects/fastapi-app/.ai-dev/architecture.md +0 -39
  153. package/test-projects/fastapi-app/.ai-dev/cache.json +0 -125
  154. package/test-projects/fastapi-app/.ai-dev/conventions.md +0 -51
  155. package/test-projects/fastapi-app/.ai-dev/dependencies.json +0 -244
  156. package/test-projects/fastapi-app/.ai-dev/entrypoints.md +0 -4
  157. package/test-projects/fastapi-app/.ai-dev/files.json +0 -154
  158. package/test-projects/fastapi-app/.ai-dev/graph/knowledge-graph.json +0 -15
  159. package/test-projects/fastapi-app/.ai-dev/graph/module-graph.json +0 -78
  160. package/test-projects/fastapi-app/.ai-dev/graph/symbol-graph.json +0 -1724
  161. package/test-projects/fastapi-app/.ai-dev/graph/symbol-references.json +0 -51
  162. package/test-projects/fastapi-app/.ai-dev/index-state.json +0 -217
  163. package/test-projects/fastapi-app/.ai-dev/modules.json +0 -16
  164. package/test-projects/fastapi-app/.ai-dev/project.json +0 -9
  165. package/test-projects/fastapi-app/.ai-dev/repo_map.json +0 -298
  166. package/test-projects/fastapi-app/.ai-dev/repo_map.md +0 -74
  167. package/test-projects/fastapi-app/.ai-dev/schema.json +0 -5
  168. package/test-projects/fastapi-app/.ai-dev/summary.md +0 -12
  169. package/test-projects/fastapi-app/.ai-dev/symbols.json +0 -1
  170. package/test-projects/fastapi-app/.ai-dev/tech_stack.md +0 -32
  171. package/test-projects/fastapi-app/.ai-dev/tools.json +0 -10
  172. package/test-projects/fastapi-app/README.md +0 -118
  173. package/test-projects/fastapi-app/app/database.py +0 -21
  174. package/test-projects/fastapi-app/app/dependencies.py +0 -107
  175. package/test-projects/fastapi-app/app/main.py +0 -47
  176. package/test-projects/fastapi-app/app/models.py +0 -149
  177. package/test-projects/fastapi-app/app/routers/auth.py +0 -117
  178. package/test-projects/fastapi-app/app/routers/posts.py +0 -272
  179. package/test-projects/fastapi-app/app/schemas.py +0 -191
  180. package/test-projects/fastapi-app/requirements.txt +0 -10
  181. package/test-projects/flask-app/.ai-dev/ai_context.md +0 -94
  182. package/test-projects/flask-app/.ai-dev/ai_rules.md +0 -47
  183. package/test-projects/flask-app/.ai-dev/architecture.md +0 -49
  184. package/test-projects/flask-app/.ai-dev/cache.json +0 -157
  185. package/test-projects/flask-app/.ai-dev/context/features/app.json +0 -25
  186. package/test-projects/flask-app/.ai-dev/context/flows/routes.json +0 -14
  187. package/test-projects/flask-app/.ai-dev/conventions.md +0 -51
  188. package/test-projects/flask-app/.ai-dev/dependencies.json +0 -298
  189. package/test-projects/flask-app/.ai-dev/entrypoints.md +0 -4
  190. package/test-projects/flask-app/.ai-dev/files.json +0 -194
  191. package/test-projects/flask-app/.ai-dev/graph/knowledge-graph.json +0 -60
  192. package/test-projects/flask-app/.ai-dev/graph/module-graph.json +0 -95
  193. package/test-projects/flask-app/.ai-dev/graph/symbol-graph.json +0 -1448
  194. package/test-projects/flask-app/.ai-dev/graph/symbol-references.json +0 -45
  195. package/test-projects/flask-app/.ai-dev/index-state.json +0 -273
  196. package/test-projects/flask-app/.ai-dev/modules.json +0 -21
  197. package/test-projects/flask-app/.ai-dev/project.json +0 -13
  198. package/test-projects/flask-app/.ai-dev/repo_map.json +0 -400
  199. package/test-projects/flask-app/.ai-dev/repo_map.md +0 -98
  200. package/test-projects/flask-app/.ai-dev/summary.md +0 -13
  201. package/test-projects/flask-app/.ai-dev/symbols.json +0 -1
  202. package/test-projects/flask-app/.ai-dev/tech_stack.md +0 -32
  203. package/test-projects/flask-app/.ai-dev/tools.json +0 -10
  204. package/test-projects/flask-app/README.md +0 -129
  205. package/test-projects/flask-app/app/__init__.py +0 -46
  206. package/test-projects/flask-app/app/api/__init__.py +0 -7
  207. package/test-projects/flask-app/app/api/routes.py +0 -122
  208. package/test-projects/flask-app/app/auth/__init__.py +0 -7
  209. package/test-projects/flask-app/app/auth/forms.py +0 -52
  210. package/test-projects/flask-app/app/auth/routes.py +0 -68
  211. package/test-projects/flask-app/app/blog/__init__.py +0 -7
  212. package/test-projects/flask-app/app/blog/forms.py +0 -35
  213. package/test-projects/flask-app/app/blog/routes.py +0 -140
  214. package/test-projects/flask-app/app/main/__init__.py +0 -7
  215. package/test-projects/flask-app/app/main/routes.py +0 -88
  216. package/test-projects/flask-app/app/models.py +0 -177
  217. package/test-projects/flask-app/config.py +0 -64
  218. package/test-projects/flask-app/requirements.txt +0 -10
  219. package/test-projects/laravel-app/.ai-dev/ai_context.md +0 -97
  220. package/test-projects/laravel-app/.ai-dev/ai_rules.md +0 -47
  221. package/test-projects/laravel-app/.ai-dev/architecture.md +0 -60
  222. package/test-projects/laravel-app/.ai-dev/cache.json +0 -161
  223. package/test-projects/laravel-app/.ai-dev/context/features/app.json +0 -21
  224. package/test-projects/laravel-app/.ai-dev/context/flows/.json +0 -9
  225. package/test-projects/laravel-app/.ai-dev/context/flows/category.json +0 -12
  226. package/test-projects/laravel-app/.ai-dev/context/flows/comment.json +0 -12
  227. package/test-projects/laravel-app/.ai-dev/context/flows/post.json +0 -12
  228. package/test-projects/laravel-app/.ai-dev/context/flows/unnamed.json +0 -9
  229. package/test-projects/laravel-app/.ai-dev/conventions.md +0 -51
  230. package/test-projects/laravel-app/.ai-dev/dependencies.json +0 -6
  231. package/test-projects/laravel-app/.ai-dev/entrypoints.md +0 -4
  232. package/test-projects/laravel-app/.ai-dev/files.json +0 -199
  233. package/test-projects/laravel-app/.ai-dev/graph/knowledge-graph.json +0 -98
  234. package/test-projects/laravel-app/.ai-dev/graph/module-graph.json +0 -30
  235. package/test-projects/laravel-app/.ai-dev/graph/symbol-graph.json +0 -5
  236. package/test-projects/laravel-app/.ai-dev/graph/symbol-references.json +0 -1
  237. package/test-projects/laravel-app/.ai-dev/index-state.json +0 -280
  238. package/test-projects/laravel-app/.ai-dev/modules.json +0 -29
  239. package/test-projects/laravel-app/.ai-dev/project.json +0 -17
  240. package/test-projects/laravel-app/.ai-dev/repo_map.json +0 -419
  241. package/test-projects/laravel-app/.ai-dev/repo_map.md +0 -106
  242. package/test-projects/laravel-app/.ai-dev/schema.json +0 -5
  243. package/test-projects/laravel-app/.ai-dev/summary.md +0 -15
  244. package/test-projects/laravel-app/.ai-dev/symbols.json +0 -1
  245. package/test-projects/laravel-app/.ai-dev/tech_stack.md +0 -34
  246. package/test-projects/laravel-app/.ai-dev/tools.json +0 -10
  247. package/test-projects/laravel-app/README.md +0 -107
  248. package/test-projects/laravel-app/app/Http/Controllers/Api/CategoryController.php +0 -88
  249. package/test-projects/laravel-app/app/Http/Controllers/Api/CommentController.php +0 -56
  250. package/test-projects/laravel-app/app/Http/Controllers/Api/PostController.php +0 -174
  251. package/test-projects/laravel-app/app/Http/Controllers/Controller.php +0 -12
  252. package/test-projects/laravel-app/app/Models/Category.php +0 -34
  253. package/test-projects/laravel-app/app/Models/Comment.php +0 -51
  254. package/test-projects/laravel-app/app/Models/Post.php +0 -108
  255. package/test-projects/laravel-app/app/Models/User.php +0 -85
  256. package/test-projects/laravel-app/bootstrap/app.php +0 -25
  257. package/test-projects/laravel-app/composer.json +0 -35
  258. package/test-projects/laravel-app/routes/api.php +0 -40
  259. package/test-projects/nestjs-backend/.ai-dev/ai_context.md +0 -111
  260. package/test-projects/nestjs-backend/.ai-dev/ai_rules.md +0 -52
  261. package/test-projects/nestjs-backend/.ai-dev/architecture.md +0 -49
  262. package/test-projects/nestjs-backend/.ai-dev/cache.json +0 -169
  263. package/test-projects/nestjs-backend/.ai-dev/context/features/src.json +0 -23
  264. package/test-projects/nestjs-backend/.ai-dev/context/flows/auth.controller.json +0 -14
  265. package/test-projects/nestjs-backend/.ai-dev/context/flows/auth.json +0 -10
  266. package/test-projects/nestjs-backend/.ai-dev/context/flows/users..json +0 -10
  267. package/test-projects/nestjs-backend/.ai-dev/context/flows/users.controller.json +0 -14
  268. package/test-projects/nestjs-backend/.ai-dev/context/flows/users.json +0 -10
  269. package/test-projects/nestjs-backend/.ai-dev/conventions.md +0 -52
  270. package/test-projects/nestjs-backend/.ai-dev/dependencies.json +0 -152
  271. package/test-projects/nestjs-backend/.ai-dev/entrypoints.md +0 -18
  272. package/test-projects/nestjs-backend/.ai-dev/files.json +0 -209
  273. package/test-projects/nestjs-backend/.ai-dev/graph/knowledge-graph.json +0 -132
  274. package/test-projects/nestjs-backend/.ai-dev/graph/module-graph.json +0 -29
  275. package/test-projects/nestjs-backend/.ai-dev/graph/symbol-graph.json +0 -304
  276. package/test-projects/nestjs-backend/.ai-dev/graph/symbol-references.json +0 -5
  277. package/test-projects/nestjs-backend/.ai-dev/index-state.json +0 -294
  278. package/test-projects/nestjs-backend/.ai-dev/modules.json +0 -19
  279. package/test-projects/nestjs-backend/.ai-dev/project.json +0 -18
  280. package/test-projects/nestjs-backend/.ai-dev/repo_map.json +0 -427
  281. package/test-projects/nestjs-backend/.ai-dev/repo_map.md +0 -104
  282. package/test-projects/nestjs-backend/.ai-dev/schema.json +0 -5
  283. package/test-projects/nestjs-backend/.ai-dev/summary.md +0 -13
  284. package/test-projects/nestjs-backend/.ai-dev/symbols.json +0 -1
  285. package/test-projects/nestjs-backend/.ai-dev/tech_stack.md +0 -38
  286. package/test-projects/nestjs-backend/.ai-dev/tools.json +0 -10
  287. package/test-projects/nestjs-backend/package.json +0 -22
  288. package/test-projects/nestjs-backend/src/app.module.ts +0 -8
  289. package/test-projects/nestjs-backend/src/auth/auth.controller.ts +0 -22
  290. package/test-projects/nestjs-backend/src/auth/auth.module.ts +0 -11
  291. package/test-projects/nestjs-backend/src/auth/auth.service.ts +0 -28
  292. package/test-projects/nestjs-backend/src/auth/dto/login.dto.ts +0 -4
  293. package/test-projects/nestjs-backend/src/auth/strategies/jwt.strategy.ts +0 -18
  294. package/test-projects/nestjs-backend/src/main.ts +0 -9
  295. package/test-projects/nestjs-backend/src/users/users.controller.ts +0 -32
  296. package/test-projects/nestjs-backend/src/users/users.module.ts +0 -10
  297. package/test-projects/nestjs-backend/src/users/users.service.ts +0 -42
  298. package/test-projects/nestjs-backend/tsconfig.json +0 -21
  299. package/test-projects/python-cli/.ai-dev/ai_context.md +0 -95
  300. package/test-projects/python-cli/.ai-dev/ai_rules.md +0 -47
  301. package/test-projects/python-cli/.ai-dev/architecture.md +0 -55
  302. package/test-projects/python-cli/.ai-dev/cache.json +0 -149
  303. package/test-projects/python-cli/.ai-dev/context/features/cli.json +0 -16
  304. package/test-projects/python-cli/.ai-dev/context/flows/list_.json +0 -9
  305. package/test-projects/python-cli/.ai-dev/context/flows/remove_.json +0 -9
  306. package/test-projects/python-cli/.ai-dev/conventions.md +0 -51
  307. package/test-projects/python-cli/.ai-dev/dependencies.json +0 -66
  308. package/test-projects/python-cli/.ai-dev/entrypoints.md +0 -4
  309. package/test-projects/python-cli/.ai-dev/files.json +0 -184
  310. package/test-projects/python-cli/.ai-dev/graph/knowledge-graph.json +0 -83
  311. package/test-projects/python-cli/.ai-dev/graph/module-graph.json +0 -31
  312. package/test-projects/python-cli/.ai-dev/graph/symbol-graph.json +0 -358
  313. package/test-projects/python-cli/.ai-dev/graph/symbol-references.json +0 -11
  314. package/test-projects/python-cli/.ai-dev/index-state.json +0 -259
  315. package/test-projects/python-cli/.ai-dev/modules.json +0 -21
  316. package/test-projects/python-cli/.ai-dev/project.json +0 -15
  317. package/test-projects/python-cli/.ai-dev/repo_map.json +0 -367
  318. package/test-projects/python-cli/.ai-dev/repo_map.md +0 -93
  319. package/test-projects/python-cli/.ai-dev/schema.json +0 -5
  320. package/test-projects/python-cli/.ai-dev/summary.md +0 -14
  321. package/test-projects/python-cli/.ai-dev/symbols.json +0 -1
  322. package/test-projects/python-cli/.ai-dev/tech_stack.md +0 -32
  323. package/test-projects/python-cli/.ai-dev/tools.json +0 -10
  324. package/test-projects/python-cli/__init__.py +0 -1
  325. package/test-projects/python-cli/cli/__init__.py +0 -1
  326. package/test-projects/python-cli/cli/add_command.py +0 -6
  327. package/test-projects/python-cli/cli/list_command.py +0 -7
  328. package/test-projects/python-cli/cli/remove_command.py +0 -6
  329. package/test-projects/python-cli/main.py +0 -34
  330. package/test-projects/python-cli/models/__init__.py +0 -2
  331. package/test-projects/python-cli/models/task.py +0 -19
  332. package/test-projects/python-cli/models/task_repository.py +0 -44
  333. package/test-projects/rails-app/.ai-dev/ai_context.md +0 -94
  334. package/test-projects/rails-app/.ai-dev/ai_rules.md +0 -47
  335. package/test-projects/rails-app/.ai-dev/architecture.md +0 -49
  336. package/test-projects/rails-app/.ai-dev/cache.json +0 -193
  337. package/test-projects/rails-app/.ai-dev/context/features/app.json +0 -24
  338. package/test-projects/rails-app/.ai-dev/context/features/config.json +0 -13
  339. package/test-projects/rails-app/.ai-dev/context/flows/application.json +0 -9
  340. package/test-projects/rails-app/.ai-dev/context/flows/application_.json +0 -9
  341. package/test-projects/rails-app/.ai-dev/context/flows/comments.json +0 -11
  342. package/test-projects/rails-app/.ai-dev/context/flows/comments_.json +0 -11
  343. package/test-projects/rails-app/.ai-dev/context/flows/posts.json +0 -11
  344. package/test-projects/rails-app/.ai-dev/context/flows/posts_.json +0 -11
  345. package/test-projects/rails-app/.ai-dev/context/flows/routes.json +0 -9
  346. package/test-projects/rails-app/.ai-dev/context/flows/users.json +0 -11
  347. package/test-projects/rails-app/.ai-dev/context/flows/users_.json +0 -11
  348. package/test-projects/rails-app/.ai-dev/conventions.md +0 -51
  349. package/test-projects/rails-app/.ai-dev/dependencies.json +0 -6
  350. package/test-projects/rails-app/.ai-dev/entrypoints.md +0 -4
  351. package/test-projects/rails-app/.ai-dev/files.json +0 -239
  352. package/test-projects/rails-app/.ai-dev/graph/knowledge-graph.json +0 -130
  353. package/test-projects/rails-app/.ai-dev/graph/module-graph.json +0 -27
  354. package/test-projects/rails-app/.ai-dev/graph/symbol-graph.json +0 -5
  355. package/test-projects/rails-app/.ai-dev/graph/symbol-references.json +0 -1
  356. package/test-projects/rails-app/.ai-dev/index-state.json +0 -336
  357. package/test-projects/rails-app/.ai-dev/modules.json +0 -26
  358. package/test-projects/rails-app/.ai-dev/project.json +0 -22
  359. package/test-projects/rails-app/.ai-dev/repo_map.json +0 -486
  360. package/test-projects/rails-app/.ai-dev/repo_map.md +0 -117
  361. package/test-projects/rails-app/.ai-dev/schema.json +0 -5
  362. package/test-projects/rails-app/.ai-dev/summary.md +0 -13
  363. package/test-projects/rails-app/.ai-dev/symbols.json +0 -1
  364. package/test-projects/rails-app/.ai-dev/tech_stack.md +0 -32
  365. package/test-projects/rails-app/.ai-dev/tools.json +0 -10
  366. package/test-projects/rails-app/Gemfile +0 -38
  367. package/test-projects/rails-app/README.md +0 -140
  368. package/test-projects/rails-app/Rakefile +0 -8
  369. package/test-projects/rails-app/app/controllers/api/comments_controller.rb +0 -75
  370. package/test-projects/rails-app/app/controllers/api/posts_controller.rb +0 -68
  371. package/test-projects/rails-app/app/controllers/api/users_controller.rb +0 -54
  372. package/test-projects/rails-app/app/controllers/application_controller.rb +0 -31
  373. package/test-projects/rails-app/app/models/comment.rb +0 -34
  374. package/test-projects/rails-app/app/models/post.rb +0 -36
  375. package/test-projects/rails-app/app/models/user.rb +0 -28
  376. package/test-projects/rails-app/app/services/post_service.rb +0 -92
  377. package/test-projects/rails-app/app/services/user_service.rb +0 -76
  378. package/test-projects/rails-app/config/application.rb +0 -27
  379. package/test-projects/rails-app/config/environment.rb +0 -7
  380. package/test-projects/rails-app/config/routes.rb +0 -15
  381. package/test-projects/react-app/.ai-dev/ai_context.md +0 -96
  382. package/test-projects/react-app/.ai-dev/architecture.md +0 -39
  383. package/test-projects/react-app/.ai-dev/cache.json +0 -153
  384. package/test-projects/react-app/.ai-dev/context/features/src.json +0 -18
  385. package/test-projects/react-app/.ai-dev/context/flows/UsersPage.json +0 -14
  386. package/test-projects/react-app/.ai-dev/context/flows/dashboard.json +0 -9
  387. package/test-projects/react-app/.ai-dev/context/flows/login.json +0 -9
  388. package/test-projects/react-app/.ai-dev/context/flows/users.json +0 -9
  389. package/test-projects/react-app/.ai-dev/dependencies.json +0 -128
  390. package/test-projects/react-app/.ai-dev/entrypoints.md +0 -4
  391. package/test-projects/react-app/.ai-dev/files.json +0 -189
  392. package/test-projects/react-app/.ai-dev/graph/knowledge-graph.json +0 -112
  393. package/test-projects/react-app/.ai-dev/graph/module-graph.json +0 -31
  394. package/test-projects/react-app/.ai-dev/graph/symbol-graph.json +0 -868
  395. package/test-projects/react-app/.ai-dev/graph/symbol-references.json +0 -31
  396. package/test-projects/react-app/.ai-dev/index-state.json +0 -266
  397. package/test-projects/react-app/.ai-dev/modules.json +0 -17
  398. package/test-projects/react-app/.ai-dev/project.json +0 -16
  399. package/test-projects/react-app/.ai-dev/repo_map.json +0 -391
  400. package/test-projects/react-app/.ai-dev/repo_map.md +0 -94
  401. package/test-projects/react-app/.ai-dev/schema.json +0 -5
  402. package/test-projects/react-app/.ai-dev/summary.md +0 -13
  403. package/test-projects/react-app/.ai-dev/symbols.json +0 -1
  404. package/test-projects/react-app/.ai-dev/tools.json +0 -10
  405. package/test-projects/react-app/package.json +0 -16
  406. package/test-projects/react-app/src/App.tsx +0 -21
  407. package/test-projects/react-app/src/context/AuthContext.tsx +0 -41
  408. package/test-projects/react-app/src/hooks/useAuth.ts +0 -10
  409. package/test-projects/react-app/src/main.tsx +0 -10
  410. package/test-projects/react-app/src/pages/DashboardPage.tsx +0 -17
  411. package/test-projects/react-app/src/pages/LoginPage.tsx +0 -41
  412. package/test-projects/react-app/src/pages/UsersPage.tsx +0 -36
  413. package/test-projects/react-app/src/services/userService.ts +0 -37
  414. package/test-projects/salesforce-cli/.ai-dev/ai_context.md +0 -89
  415. package/test-projects/salesforce-cli/.ai-dev/ai_rules.md +0 -47
  416. package/test-projects/salesforce-cli/.ai-dev/architecture.md +0 -39
  417. package/test-projects/salesforce-cli/.ai-dev/cache.json +0 -125
  418. package/test-projects/salesforce-cli/.ai-dev/context/features/force-app.json +0 -14
  419. package/test-projects/salesforce-cli/.ai-dev/context/flows/account.json +0 -9
  420. package/test-projects/salesforce-cli/.ai-dev/context/flows/opportunity.json +0 -9
  421. package/test-projects/salesforce-cli/.ai-dev/conventions.md +0 -51
  422. package/test-projects/salesforce-cli/.ai-dev/dependencies.json +0 -6
  423. package/test-projects/salesforce-cli/.ai-dev/entrypoints.md +0 -4
  424. package/test-projects/salesforce-cli/.ai-dev/files.json +0 -154
  425. package/test-projects/salesforce-cli/.ai-dev/graph/knowledge-graph.json +0 -64
  426. package/test-projects/salesforce-cli/.ai-dev/graph/module-graph.json +0 -13
  427. package/test-projects/salesforce-cli/.ai-dev/graph/symbol-graph.json +0 -148
  428. package/test-projects/salesforce-cli/.ai-dev/graph/symbol-references.json +0 -1
  429. package/test-projects/salesforce-cli/.ai-dev/index-state.json +0 -217
  430. package/test-projects/salesforce-cli/.ai-dev/modules.json +0 -12
  431. package/test-projects/salesforce-cli/.ai-dev/project.json +0 -14
  432. package/test-projects/salesforce-cli/.ai-dev/repo_map.json +0 -328
  433. package/test-projects/salesforce-cli/.ai-dev/repo_map.md +0 -80
  434. package/test-projects/salesforce-cli/.ai-dev/schema.json +0 -5
  435. package/test-projects/salesforce-cli/.ai-dev/summary.md +0 -13
  436. package/test-projects/salesforce-cli/.ai-dev/symbols.json +0 -1
  437. package/test-projects/salesforce-cli/.ai-dev/tech_stack.md +0 -31
  438. package/test-projects/salesforce-cli/.ai-dev/tools.json +0 -10
  439. package/test-projects/salesforce-cli/.forceignore +0 -27
  440. package/test-projects/salesforce-cli/force-app/main/default/classes/AccountController.cls +0 -24
  441. package/test-projects/salesforce-cli/force-app/main/default/classes/OpportunityController.cls +0 -25
  442. package/test-projects/salesforce-cli/force-app/main/default/objects/Project__c.object.xml +0 -45
  443. package/test-projects/salesforce-cli/force-app/main/default/triggers/AccountTrigger.trigger +0 -33
  444. package/test-projects/salesforce-cli/sfdx-project.json +0 -11
  445. package/test-projects/spring-boot-app/.ai-dev/ai_context.md +0 -91
  446. package/test-projects/spring-boot-app/.ai-dev/ai_rules.md +0 -48
  447. package/test-projects/spring-boot-app/.ai-dev/architecture.md +0 -39
  448. package/test-projects/spring-boot-app/.ai-dev/cache.json +0 -173
  449. package/test-projects/spring-boot-app/.ai-dev/context/features/src.json +0 -26
  450. package/test-projects/spring-boot-app/.ai-dev/context/flows/PostController.json +0 -19
  451. package/test-projects/spring-boot-app/.ai-dev/context/flows/UserController.json +0 -19
  452. package/test-projects/spring-boot-app/.ai-dev/context/flows/comment.json +0 -11
  453. package/test-projects/spring-boot-app/.ai-dev/context/flows/post.json +0 -14
  454. package/test-projects/spring-boot-app/.ai-dev/context/flows/user.json +0 -14
  455. package/test-projects/spring-boot-app/.ai-dev/conventions.md +0 -52
  456. package/test-projects/spring-boot-app/.ai-dev/dependencies.json +0 -326
  457. package/test-projects/spring-boot-app/.ai-dev/entrypoints.md +0 -4
  458. package/test-projects/spring-boot-app/.ai-dev/files.json +0 -214
  459. package/test-projects/spring-boot-app/.ai-dev/graph/knowledge-graph.json +0 -231
  460. package/test-projects/spring-boot-app/.ai-dev/graph/module-graph.json +0 -22
  461. package/test-projects/spring-boot-app/.ai-dev/graph/symbol-graph.json +0 -794
  462. package/test-projects/spring-boot-app/.ai-dev/graph/symbol-references.json +0 -70
  463. package/test-projects/spring-boot-app/.ai-dev/index-state.json +0 -301
  464. package/test-projects/spring-boot-app/.ai-dev/modules.json +0 -21
  465. package/test-projects/spring-boot-app/.ai-dev/project.json +0 -17
  466. package/test-projects/spring-boot-app/.ai-dev/repo_map.json +0 -461
  467. package/test-projects/spring-boot-app/.ai-dev/repo_map.md +0 -109
  468. package/test-projects/spring-boot-app/.ai-dev/schema.json +0 -5
  469. package/test-projects/spring-boot-app/.ai-dev/summary.md +0 -12
  470. package/test-projects/spring-boot-app/.ai-dev/symbols.json +0 -1
  471. package/test-projects/spring-boot-app/.ai-dev/tech_stack.md +0 -32
  472. package/test-projects/spring-boot-app/.ai-dev/tools.json +0 -10
  473. package/test-projects/spring-boot-app/.classpath +0 -57
  474. package/test-projects/spring-boot-app/.factorypath +0 -69
  475. package/test-projects/spring-boot-app/.project +0 -34
  476. package/test-projects/spring-boot-app/.settings/org.eclipse.core.resources.prefs +0 -4
  477. package/test-projects/spring-boot-app/.settings/org.eclipse.jdt.apt.core.prefs +0 -4
  478. package/test-projects/spring-boot-app/.settings/org.eclipse.jdt.core.prefs +0 -10
  479. package/test-projects/spring-boot-app/.settings/org.eclipse.m2e.core.prefs +0 -4
  480. package/test-projects/spring-boot-app/README.md +0 -122
  481. package/test-projects/spring-boot-app/pom.xml +0 -79
  482. package/test-projects/spring-boot-app/src/main/java/com/example/demo/DemoApplication.java +0 -12
  483. package/test-projects/spring-boot-app/src/main/java/com/example/demo/controllers/CommentController.java +0 -89
  484. package/test-projects/spring-boot-app/src/main/java/com/example/demo/controllers/PostController.java +0 -92
  485. package/test-projects/spring-boot-app/src/main/java/com/example/demo/controllers/UserController.java +0 -84
  486. package/test-projects/spring-boot-app/src/main/java/com/example/demo/models/Comment.java +0 -38
  487. package/test-projects/spring-boot-app/src/main/java/com/example/demo/models/Post.java +0 -56
  488. package/test-projects/spring-boot-app/src/main/java/com/example/demo/models/User.java +0 -44
  489. package/test-projects/spring-boot-app/src/main/java/com/example/demo/repositories/CommentRepository.java +0 -21
  490. package/test-projects/spring-boot-app/src/main/java/com/example/demo/repositories/PostRepository.java +0 -18
  491. package/test-projects/spring-boot-app/src/main/java/com/example/demo/repositories/UserRepository.java +0 -15
  492. package/test-projects/spring-boot-app/src/main/java/com/example/demo/services/PostService.java +0 -83
  493. package/test-projects/spring-boot-app/src/main/java/com/example/demo/services/UserService.java +0 -62
  494. package/test-projects/spring-boot-app/src/main/resources/application.properties +0 -22
  495. package/test-projects/spring-boot-app/target/classes/com/example/demo/DemoApplication.class +0 -0
  496. package/test-projects/spring-boot-app/target/classes/com/example/demo/controllers/CommentController$CommentCreateRequest.class +0 -0
  497. package/test-projects/spring-boot-app/target/classes/com/example/demo/controllers/CommentController$CommentUpdateRequest.class +0 -0
  498. package/test-projects/spring-boot-app/target/classes/com/example/demo/controllers/CommentController.class +0 -0
  499. package/test-projects/spring-boot-app/target/classes/com/example/demo/controllers/PostController$PostCreateRequest.class +0 -0
  500. package/test-projects/spring-boot-app/target/classes/com/example/demo/controllers/PostController$PostUpdateRequest.class +0 -0
  501. package/test-projects/spring-boot-app/target/classes/com/example/demo/controllers/PostController.class +0 -0
  502. package/test-projects/spring-boot-app/target/classes/com/example/demo/controllers/UserController$UserCreateRequest.class +0 -0
  503. package/test-projects/spring-boot-app/target/classes/com/example/demo/controllers/UserController$UserUpdateRequest.class +0 -0
  504. package/test-projects/spring-boot-app/target/classes/com/example/demo/controllers/UserController.class +0 -0
  505. package/test-projects/spring-boot-app/target/classes/com/example/demo/models/Comment.class +0 -0
  506. package/test-projects/spring-boot-app/target/classes/com/example/demo/models/Post.class +0 -0
  507. package/test-projects/spring-boot-app/target/classes/com/example/demo/models/User.class +0 -0
  508. package/test-projects/spring-boot-app/target/classes/com/example/demo/repositories/CommentRepository.class +0 -0
  509. package/test-projects/spring-boot-app/target/classes/com/example/demo/repositories/PostRepository.class +0 -0
  510. package/test-projects/spring-boot-app/target/classes/com/example/demo/repositories/UserRepository.class +0 -0
  511. package/test-projects/spring-boot-app/target/classes/com/example/demo/services/PostService.class +0 -0
  512. package/test-projects/spring-boot-app/target/classes/com/example/demo/services/UserService.class +0 -0
  513. package/tests/e2e/run-e2e.sh +0 -88
  514. /package/{test-projects/django-app/.ai-dev → ai-context}/tools.json +0 -0
@@ -0,0 +1,871 @@
1
+ import { describe, it, expect, beforeAll, afterAll } from "vitest";
2
+ import { extractSymbols } from "../src/analyzers/symbols";
3
+ import { FileInfo } from "../src/core/repoScanner";
4
+ import fs from "fs";
5
+ import path from "path";
6
+ import os from "os";
7
+
8
+ const SALESFORCE_ENTERPRISE_PATH = path.join(process.cwd(), "test-projects/salesforce-enterprise");
9
+
10
+ describe("Salesforce Apex Triggers Detection", () => {
11
+ let tempDir: string;
12
+
13
+ beforeAll(() => {
14
+ tempDir = fs.mkdtempSync(path.join(os.tmpdir(), "apex-triggers-test-"));
15
+ });
16
+
17
+ afterAll(() => {
18
+ fs.rmSync(tempDir, { recursive: true, force: true });
19
+ });
20
+
21
+ const createFileInfo = (filePath: string, extension: string, content: string): FileInfo => {
22
+ const fullPath = path.join(tempDir, filePath);
23
+ fs.mkdirSync(path.dirname(fullPath), { recursive: true });
24
+ fs.writeFileSync(fullPath, content);
25
+
26
+ return {
27
+ path: fullPath,
28
+ relativePath: filePath,
29
+ extension,
30
+ name: filePath.split("/").pop()?.replace(`.${extension}`, "") || "",
31
+ };
32
+ };
33
+
34
+ // =========================================================================
35
+ // INTEGRATION TESTS - Using real Salesforce Enterprise test project
36
+ // =========================================================================
37
+
38
+ describe("Integration: Real Salesforce Enterprise Project", () => {
39
+ it("should detect trigger files in the enterprise project", () => {
40
+ const triggersDir = path.join(SALESFORCE_ENTERPRISE_PATH, "force-app/main/default/triggers");
41
+ const files = fs.readdirSync(triggersDir).filter(f => f.endsWith(".trigger"));
42
+
43
+ expect(files).toHaveLength(1);
44
+ expect(files).toContain("OpportunityTrigger.trigger");
45
+ });
46
+
47
+ it("should extract OpportunityTrigger symbol from real file", () => {
48
+ const triggerPath = path.join(SALESFORCE_ENTERPRISE_PATH, "force-app/main/default/triggers/OpportunityTrigger.trigger");
49
+ const content = fs.readFileSync(triggerPath, "utf-8");
50
+
51
+ const file: FileInfo = {
52
+ path: triggerPath,
53
+ relativePath: "force-app/main/default/triggers/OpportunityTrigger.trigger",
54
+ extension: "trigger",
55
+ name: "OpportunityTrigger",
56
+ };
57
+
58
+ const result = extractSymbols([file]);
59
+ const triggerSymbol = result.symbols.find(s => s.name === "OpportunityTrigger");
60
+
61
+ expect(triggerSymbol).toBeDefined();
62
+ expect(triggerSymbol?.type).toBe("function");
63
+ expect(triggerSymbol?.export).toBe(true);
64
+ });
65
+ });
66
+
67
+ // =========================================================================
68
+ // OpportunityTrigger - Main trigger under test
69
+ // =========================================================================
70
+
71
+ describe("OpportunityTrigger", () => {
72
+ it("should have trigger name 'OpportunityTrigger'", () => {
73
+ const content = fs.readFileSync(
74
+ path.join(SALESFORCE_ENTERPRISE_PATH, "force-app/main/default/triggers/OpportunityTrigger.trigger"),
75
+ "utf-8"
76
+ );
77
+
78
+ const file = createFileInfo("force-app/main/default/triggers/OpportunityTrigger.trigger", "trigger", content);
79
+ const result = extractSymbols([file]);
80
+
81
+ const triggerSymbol = result.symbols.find(s => s.name === "OpportunityTrigger");
82
+ expect(triggerSymbol).toBeDefined();
83
+ expect(triggerSymbol?.name).toBe("OpportunityTrigger");
84
+ });
85
+
86
+ it("should fire on 'Opportunity' object", () => {
87
+ const content = fs.readFileSync(
88
+ path.join(SALESFORCE_ENTERPRISE_PATH, "force-app/main/default/triggers/OpportunityTrigger.trigger"),
89
+ "utf-8"
90
+ );
91
+
92
+ expect(content).toContain("trigger OpportunityTrigger on Opportunity");
93
+ });
94
+
95
+ it("should detect all 7 trigger events", () => {
96
+ const content = fs.readFileSync(
97
+ path.join(SALESFORCE_ENTERPRISE_PATH, "force-app/main/default/triggers/OpportunityTrigger.trigger"),
98
+ "utf-8"
99
+ );
100
+
101
+ // All 7 events: before insert, before update, before delete, after insert, after update, after delete, after undelete
102
+ expect(content).toContain("before insert");
103
+ expect(content).toContain("before update");
104
+ expect(content).toContain("before delete");
105
+ expect(content).toContain("after insert");
106
+ expect(content).toContain("after update");
107
+ expect(content).toContain("after delete");
108
+ expect(content).toContain("after undelete");
109
+ });
110
+
111
+ it("should have all 7 events in single trigger definition", () => {
112
+ const content = fs.readFileSync(
113
+ path.join(SALESFORCE_ENTERPRISE_PATH, "force-app/main/default/triggers/OpportunityTrigger.trigger"),
114
+ "utf-8"
115
+ );
116
+
117
+ // The trigger definition should contain all events
118
+ const triggerDefinitionMatch = content.match(/trigger\s+\w+\s+on\s+\w+\s*\(([^)]+)\)/);
119
+ expect(triggerDefinitionMatch).toBeDefined();
120
+
121
+ const events = triggerDefinitionMatch?.[1] || "";
122
+ expect(events).toContain("before insert");
123
+ expect(events).toContain("before update");
124
+ expect(events).toContain("before delete");
125
+ expect(events).toContain("after insert");
126
+ expect(events).toContain("after update");
127
+ expect(events).toContain("after delete");
128
+ expect(events).toContain("after undelete");
129
+ });
130
+
131
+ it("should use handler pattern with OpportunityTriggerHandler", () => {
132
+ const content = fs.readFileSync(
133
+ path.join(SALESFORCE_ENTERPRISE_PATH, "force-app/main/default/triggers/OpportunityTrigger.trigger"),
134
+ "utf-8"
135
+ );
136
+
137
+ expect(content).toContain("OpportunityTriggerHandler");
138
+ expect(content).toContain("new OpportunityTriggerHandler()");
139
+ });
140
+
141
+ it("should have bypass logic with TriggerManagement.isTriggerBypassed", () => {
142
+ const content = fs.readFileSync(
143
+ path.join(SALESFORCE_ENTERPRISE_PATH, "force-app/main/default/triggers/OpportunityTrigger.trigger"),
144
+ "utf-8"
145
+ );
146
+
147
+ expect(content).toContain("TriggerManagement.isTriggerBypassed");
148
+ expect(content).toContain("isTriggerBypassed('Opportunity')");
149
+ });
150
+
151
+ it("should use Trigger.isBefore and Trigger.isAfter context variables", () => {
152
+ const content = fs.readFileSync(
153
+ path.join(SALESFORCE_ENTERPRISE_PATH, "force-app/main/default/triggers/OpportunityTrigger.trigger"),
154
+ "utf-8"
155
+ );
156
+
157
+ expect(content).toContain("Trigger.isBefore");
158
+ expect(content).toContain("Trigger.isAfter");
159
+ });
160
+
161
+ it("should use Trigger.isInsert, Trigger.isUpdate, Trigger.isDelete, Trigger.isUndelete", () => {
162
+ const content = fs.readFileSync(
163
+ path.join(SALESFORCE_ENTERPRISE_PATH, "force-app/main/default/triggers/OpportunityTrigger.trigger"),
164
+ "utf-8"
165
+ );
166
+
167
+ expect(content).toContain("Trigger.isInsert");
168
+ expect(content).toContain("Trigger.isUpdate");
169
+ expect(content).toContain("Trigger.isDelete");
170
+ expect(content).toContain("Trigger.isUndelete");
171
+ });
172
+
173
+ it("should use Trigger.new and Trigger.oldMap context variables", () => {
174
+ const content = fs.readFileSync(
175
+ path.join(SALESFORCE_ENTERPRISE_PATH, "force-app/main/default/triggers/OpportunityTrigger.trigger"),
176
+ "utf-8"
177
+ );
178
+
179
+ expect(content).toContain("Trigger.new");
180
+ expect(content).toContain("Trigger.oldMap");
181
+ });
182
+
183
+ it("should have before insert handler", () => {
184
+ const content = fs.readFileSync(
185
+ path.join(SALESFORCE_ENTERPRISE_PATH, "force-app/main/default/triggers/OpportunityTrigger.trigger"),
186
+ "utf-8"
187
+ );
188
+
189
+ expect(content).toContain("onBeforeInsert");
190
+ expect(content).toContain("Trigger.new");
191
+ });
192
+
193
+ it("should have before update handler", () => {
194
+ const content = fs.readFileSync(
195
+ path.join(SALESFORCE_ENTERPRISE_PATH, "force-app/main/default/triggers/OpportunityTrigger.trigger"),
196
+ "utf-8"
197
+ );
198
+
199
+ expect(content).toContain("onBeforeUpdate");
200
+ expect(content).toContain("Trigger.new");
201
+ expect(content).toContain("Trigger.oldMap");
202
+ });
203
+
204
+ it("should have before delete handler", () => {
205
+ const content = fs.readFileSync(
206
+ path.join(SALESFORCE_ENTERPRISE_PATH, "force-app/main/default/triggers/OpportunityTrigger.trigger"),
207
+ "utf-8"
208
+ );
209
+
210
+ expect(content).toContain("onBeforeDelete");
211
+ expect(content).toContain("Trigger.oldMap");
212
+ });
213
+
214
+ it("should have after insert handler", () => {
215
+ const content = fs.readFileSync(
216
+ path.join(SALESFORCE_ENTERPRISE_PATH, "force-app/main/default/triggers/OpportunityTrigger.trigger"),
217
+ "utf-8"
218
+ );
219
+
220
+ expect(content).toContain("onAfterInsert");
221
+ expect(content).toContain("Trigger.new");
222
+ });
223
+
224
+ it("should have after update handler", () => {
225
+ const content = fs.readFileSync(
226
+ path.join(SALESFORCE_ENTERPRISE_PATH, "force-app/main/default/triggers/OpportunityTrigger.trigger"),
227
+ "utf-8"
228
+ );
229
+
230
+ expect(content).toContain("onAfterUpdate");
231
+ expect(content).toContain("Trigger.new");
232
+ expect(content).toContain("Trigger.oldMap");
233
+ });
234
+
235
+ it("should have after delete handler", () => {
236
+ const content = fs.readFileSync(
237
+ path.join(SALESFORCE_ENTERPRISE_PATH, "force-app/main/default/triggers/OpportunityTrigger.trigger"),
238
+ "utf-8"
239
+ );
240
+
241
+ expect(content).toContain("onAfterDelete");
242
+ expect(content).toContain("Trigger.oldMap");
243
+ });
244
+
245
+ it("should have after undelete handler", () => {
246
+ const content = fs.readFileSync(
247
+ path.join(SALESFORCE_ENTERPRISE_PATH, "force-app/main/default/triggers/OpportunityTrigger.trigger"),
248
+ "utf-8"
249
+ );
250
+
251
+ expect(content).toContain("onAfterUndelete");
252
+ expect(content).toContain("Trigger.new");
253
+ });
254
+ });
255
+
256
+ // =========================================================================
257
+ // UNIT TESTS - Apex trigger parsing edge cases
258
+ // =========================================================================
259
+
260
+ describe("Unit: Apex trigger parsing edge cases", () => {
261
+ it("should parse trigger with single event", () => {
262
+ const content = `trigger AccountTrigger on Account (before insert) {
263
+ // handler logic
264
+ }`;
265
+ const file = createFileInfo("AccountTrigger.trigger", "trigger", content);
266
+ const result = extractSymbols([file]);
267
+
268
+ const triggerSymbol = result.symbols.find(s => s.name === "AccountTrigger");
269
+ expect(triggerSymbol).toBeDefined();
270
+ expect(triggerSymbol?.type).toBe("function");
271
+ expect(triggerSymbol?.export).toBe(true);
272
+ });
273
+
274
+ it("should parse trigger with multiple events on same line", () => {
275
+ const content = `trigger ContactTrigger on Contact (before insert, before update, after insert, after update) {
276
+ // handler logic
277
+ }`;
278
+ const file = createFileInfo("ContactTrigger.trigger", "trigger", content);
279
+ const result = extractSymbols([file]);
280
+
281
+ const triggerSymbol = result.symbols.find(s => s.name === "ContactTrigger");
282
+ expect(triggerSymbol).toBeDefined();
283
+ expect(triggerSymbol?.type).toBe("function");
284
+ });
285
+
286
+ it("should parse trigger with before events only", () => {
287
+ const content = `trigger CaseTrigger on Case (before insert, before update, before delete) {
288
+ // before event handlers
289
+ }`;
290
+ const file = createFileInfo("CaseTrigger.trigger", "trigger", content);
291
+ const result = extractSymbols([file]);
292
+
293
+ const triggerSymbol = result.symbols.find(s => s.name === "CaseTrigger");
294
+ expect(triggerSymbol).toBeDefined();
295
+ expect(triggerSymbol?.type).toBe("function");
296
+ });
297
+
298
+ it("should parse trigger with after events only", () => {
299
+ const content = `trigger LeadTrigger on Lead (after insert, after update, after delete, after undelete) {
300
+ // after event handlers
301
+ }`;
302
+ const file = createFileInfo("LeadTrigger.trigger", "trigger", content);
303
+ const result = extractSymbols([file]);
304
+
305
+ const triggerSymbol = result.symbols.find(s => s.name === "LeadTrigger");
306
+ expect(triggerSymbol).toBeDefined();
307
+ expect(triggerSymbol?.type).toBe("function");
308
+ });
309
+
310
+ it("should parse trigger on different SObjects (Account)", () => {
311
+ const content = `trigger AccountTrigger on Account (before insert, before update) {
312
+ for(Account a : Trigger.new) {
313
+ // process
314
+ }
315
+ }`;
316
+ const file = createFileInfo("AccountTrigger.trigger", "trigger", content);
317
+ const result = extractSymbols([file]);
318
+
319
+ const triggerSymbol = result.symbols.find(s => s.name === "AccountTrigger");
320
+ expect(triggerSymbol).toBeDefined();
321
+
322
+ const content2 = fs.readFileSync(file.path, "utf-8");
323
+ expect(content2).toContain("on Account");
324
+ });
325
+
326
+ it("should parse trigger on different SObjects (Contact)", () => {
327
+ const content = `trigger ContactTrigger on Contact (before insert, before update) {
328
+ for(Contact c : Trigger.new) {
329
+ // process
330
+ }
331
+ }`;
332
+ const file = createFileInfo("ContactTrigger.trigger", "trigger", content);
333
+ const result = extractSymbols([file]);
334
+
335
+ const triggerSymbol = result.symbols.find(s => s.name === "ContactTrigger");
336
+ expect(triggerSymbol).toBeDefined();
337
+
338
+ const content2 = fs.readFileSync(file.path, "utf-8");
339
+ expect(content2).toContain("on Contact");
340
+ });
341
+
342
+ it("should parse trigger on custom object", () => {
343
+ const content = `trigger CustomObjectTrigger on CustomObject__c (before insert) {
344
+ // custom object trigger
345
+ }`;
346
+ const file = createFileInfo("CustomObjectTrigger.trigger", "trigger", content);
347
+ const result = extractSymbols([file]);
348
+
349
+ const triggerSymbol = result.symbols.find(s => s.name === "CustomObjectTrigger");
350
+ expect(triggerSymbol).toBeDefined();
351
+ });
352
+
353
+ it("should detect trigger with handler instantiation", () => {
354
+ const content = `trigger OrderTrigger on Order (before insert) {
355
+ OrderTriggerHandler handler = new OrderTriggerHandler();
356
+ if (Trigger.isBefore) {
357
+ handler.onBeforeInsert(Trigger.new);
358
+ }
359
+ }`;
360
+ const file = createFileInfo("OrderTrigger.trigger", "trigger", content);
361
+ const result = extractSymbols([file]);
362
+
363
+ const triggerSymbol = result.symbols.find(s => s.name === "OrderTrigger");
364
+ expect(triggerSymbol).toBeDefined();
365
+
366
+ const fileContent = fs.readFileSync(file.path, "utf-8");
367
+ expect(fileContent).toContain("new OrderTriggerHandler()");
368
+ });
369
+
370
+ it("should detect trigger with bypass check at start", () => {
371
+ const content = `trigger ProductTrigger on Product2 (before insert) {
372
+ if (TriggerManagement.isTriggerBypassed('Product2')) {
373
+ return;
374
+ }
375
+ // trigger logic
376
+ }`;
377
+ const file = createFileInfo("ProductTrigger.trigger", "trigger", content);
378
+ const result = extractSymbols([file]);
379
+
380
+ const triggerSymbol = result.symbols.find(s => s.name === "ProductTrigger");
381
+ expect(triggerSymbol).toBeDefined();
382
+
383
+ const fileContent = fs.readFileSync(file.path, "utf-8");
384
+ expect(fileContent).toContain("isTriggerBypassed");
385
+ });
386
+
387
+ it("should parse trigger with Trigger.new", () => {
388
+ const content = `trigger AssetTrigger on Asset (before insert, before update) {
389
+ for(Asset a : Trigger.new) {
390
+ a.Description = 'Updated';
391
+ }
392
+ }`;
393
+ const file = createFileInfo("AssetTrigger.trigger", "trigger", content);
394
+ const result = extractSymbols([file]);
395
+
396
+ expect(result.symbols.find(s => s.name === "AssetTrigger")).toBeDefined();
397
+ });
398
+
399
+ it("should parse trigger with Trigger.oldMap", () => {
400
+ const content = `trigger CampaignTrigger on Campaign (before delete) {
401
+ Map<Id, Campaign> oldMap = Trigger.oldMap;
402
+ // process deletions
403
+ }`;
404
+ const file = createFileInfo("CampaignTrigger.trigger", "trigger", content);
405
+ const result = extractSymbols([file]);
406
+
407
+ expect(result.symbols.find(s => s.name === "CampaignTrigger")).toBeDefined();
408
+ });
409
+
410
+ it("should detect trigger as entrypoint type", () => {
411
+ const content = fs.readFileSync(
412
+ path.join(SALESFORCE_ENTERPRISE_PATH, "force-app/main/default/triggers/OpportunityTrigger.trigger"),
413
+ "utf-8"
414
+ );
415
+
416
+ // Trigger files are entrypoints in Salesforce
417
+ expect(content).toContain("trigger");
418
+ expect(content).toContain("Trigger.isBefore");
419
+ expect(content).toContain("Trigger.isAfter");
420
+ });
421
+ });
422
+
423
+ // =========================================================================
424
+ // TRIGGER EVENTS VERIFICATION
425
+ // =========================================================================
426
+
427
+ describe("Trigger Events Verification", () => {
428
+ it("should correctly identify before insert event", () => {
429
+ const content = `trigger AccountTrigger on Account (before insert) {
430
+ if (Trigger.isBefore && Trigger.isInsert) {
431
+ // before insert logic
432
+ }
433
+ }`;
434
+ const file = createFileInfo("AccountTrigger.trigger", "trigger", content);
435
+ const fileContent = fs.readFileSync(file.path, "utf-8");
436
+
437
+ expect(fileContent).toContain("before insert");
438
+ expect(fileContent).toContain("Trigger.isBefore");
439
+ expect(fileContent).toContain("Trigger.isInsert");
440
+ });
441
+
442
+ it("should correctly identify before update event", () => {
443
+ const content = `trigger AccountTrigger on Account (before update) {
444
+ if (Trigger.isBefore && Trigger.isUpdate) {
445
+ // before update logic
446
+ }
447
+ }`;
448
+ const file = createFileInfo("AccountTrigger.trigger", "trigger", content);
449
+ const fileContent = fs.readFileSync(file.path, "utf-8");
450
+
451
+ expect(fileContent).toContain("before update");
452
+ expect(fileContent).toContain("Trigger.isBefore");
453
+ expect(fileContent).toContain("Trigger.isUpdate");
454
+ });
455
+
456
+ it("should correctly identify before delete event", () => {
457
+ const content = `trigger AccountTrigger on Account (before delete) {
458
+ if (Trigger.isBefore && Trigger.isDelete) {
459
+ // before delete logic
460
+ }
461
+ }`;
462
+ const file = createFileInfo("AccountTrigger.trigger", "trigger", content);
463
+ const fileContent = fs.readFileSync(file.path, "utf-8");
464
+
465
+ expect(fileContent).toContain("before delete");
466
+ expect(fileContent).toContain("Trigger.isBefore");
467
+ expect(fileContent).toContain("Trigger.isDelete");
468
+ });
469
+
470
+ it("should correctly identify after insert event", () => {
471
+ const content = `trigger AccountTrigger on Account (after insert) {
472
+ if (Trigger.isAfter && Trigger.isInsert) {
473
+ // after insert logic
474
+ }
475
+ }`;
476
+ const file = createFileInfo("AccountTrigger.trigger", "trigger", content);
477
+ const fileContent = fs.readFileSync(file.path, "utf-8");
478
+
479
+ expect(fileContent).toContain("after insert");
480
+ expect(fileContent).toContain("Trigger.isAfter");
481
+ expect(fileContent).toContain("Trigger.isInsert");
482
+ });
483
+
484
+ it("should correctly identify after update event", () => {
485
+ const content = `trigger AccountTrigger on Account (after update) {
486
+ if (Trigger.isAfter && Trigger.isUpdate) {
487
+ // after update logic
488
+ }
489
+ }`;
490
+ const file = createFileInfo("AccountTrigger.trigger", "trigger", content);
491
+ const fileContent = fs.readFileSync(file.path, "utf-8");
492
+
493
+ expect(fileContent).toContain("after update");
494
+ expect(fileContent).toContain("Trigger.isAfter");
495
+ expect(fileContent).toContain("Trigger.isUpdate");
496
+ });
497
+
498
+ it("should correctly identify after delete event", () => {
499
+ const content = `trigger AccountTrigger on Account (after delete) {
500
+ if (Trigger.isAfter && Trigger.isDelete) {
501
+ // after delete logic
502
+ }
503
+ }`;
504
+ const file = createFileInfo("AccountTrigger.trigger", "trigger", content);
505
+ const fileContent = fs.readFileSync(file.path, "utf-8");
506
+
507
+ expect(fileContent).toContain("after delete");
508
+ expect(fileContent).toContain("Trigger.isAfter");
509
+ expect(fileContent).toContain("Trigger.isDelete");
510
+ });
511
+
512
+ it("should correctly identify after undelete event", () => {
513
+ const content = `trigger AccountTrigger on Account (after undelete) {
514
+ if (Trigger.isAfter && Trigger.isUndelete) {
515
+ // after undelete logic
516
+ }
517
+ }`;
518
+ const file = createFileInfo("AccountTrigger.trigger", "trigger", content);
519
+ const fileContent = fs.readFileSync(file.path, "utf-8");
520
+
521
+ expect(fileContent).toContain("after undelete");
522
+ expect(fileContent).toContain("Trigger.isAfter");
523
+ expect(fileContent).toContain("Trigger.isUndelete");
524
+ });
525
+ });
526
+
527
+ // =========================================================================
528
+ // TRIGGER CONTEXT VARIABLES VERIFICATION
529
+ // =========================================================================
530
+
531
+ describe("Trigger Context Variables Verification", () => {
532
+ it("should verify Trigger.new is used correctly", () => {
533
+ const content = fs.readFileSync(
534
+ path.join(SALESFORCE_ENTERPRISE_PATH, "force-app/main/default/triggers/OpportunityTrigger.trigger"),
535
+ "utf-8"
536
+ );
537
+
538
+ expect(content).toMatch(/Trigger\.new/);
539
+ });
540
+
541
+ it("should verify Trigger.old is used correctly", () => {
542
+ const content = `trigger AccountTrigger on Account (before update) {
543
+ for(Account oldAcc : Trigger.old) {
544
+ // use old values
545
+ }
546
+ }`;
547
+ const file = createFileInfo("AccountTrigger.trigger", "trigger", content);
548
+ const fileContent = fs.readFileSync(file.path, "utf-8");
549
+
550
+ expect(fileContent).toMatch(/Trigger\.old/);
551
+ });
552
+
553
+ it("should verify Trigger.oldMap is used correctly", () => {
554
+ const content = fs.readFileSync(
555
+ path.join(SALESFORCE_ENTERPRISE_PATH, "force-app/main/default/triggers/OpportunityTrigger.trigger"),
556
+ "utf-8"
557
+ );
558
+
559
+ expect(content).toMatch(/Trigger\.oldMap/);
560
+ });
561
+
562
+ it("should verify Trigger.newMap is used correctly", () => {
563
+ const content = `trigger AccountTrigger on Account (after update) {
564
+ Map<Id, Account> newMap = Trigger.newMap;
565
+ // process with new map
566
+ }`;
567
+ const file = createFileInfo("AccountTrigger.trigger", "trigger", content);
568
+ const fileContent = fs.readFileSync(file.path, "utf-8");
569
+
570
+ expect(fileContent).toMatch(/Trigger\.newMap/);
571
+ });
572
+
573
+ it("should verify Trigger.isExecuting is used", () => {
574
+ const content = `trigger AccountTrigger on Account (before insert) {
575
+ if (Trigger.isExecuting) {
576
+ // running in trigger context
577
+ }
578
+ }`;
579
+ const file = createFileInfo("AccountTrigger.trigger", "trigger", content);
580
+ const fileContent = fs.readFileSync(file.path, "utf-8");
581
+
582
+ expect(fileContent).toMatch(/Trigger\.isExecuting/);
583
+ });
584
+
585
+ it("should verify Trigger.operationType is used", () => {
586
+ const content = `trigger AccountTrigger on Account (before insert) {
587
+ System.Debug('Operation: ' + Trigger.operationType);
588
+ }`;
589
+ const file = createFileInfo("AccountTrigger.trigger", "trigger", content);
590
+ const fileContent = fs.readFileSync(file.path, "utf-8");
591
+
592
+ expect(fileContent).toMatch(/Trigger\.operationType/);
593
+ });
594
+ });
595
+
596
+ // =========================================================================
597
+ // TRIGGER HANDLER PATTERN VERIFICATION
598
+ // =========================================================================
599
+
600
+ describe("Trigger Handler Pattern Verification", () => {
601
+ it("should have trigger handler class for OpportunityTrigger", () => {
602
+ const handlerPath = path.join(SALESFORCE_ENTERPRISE_PATH, "force-app/main/default/classes/OpportunityTriggerHandler.cls");
603
+ expect(fs.existsSync(handlerPath)).toBe(true);
604
+
605
+ const content = fs.readFileSync(handlerPath, "utf-8");
606
+ expect(content).toContain("class OpportunityTriggerHandler");
607
+ });
608
+
609
+ it("should verify handler has onBeforeInsert method", () => {
610
+ const handlerPath = path.join(SALESFORCE_ENTERPRISE_PATH, "force-app/main/default/classes/OpportunityTriggerHandler.cls");
611
+ const content = fs.readFileSync(handlerPath, "utf-8");
612
+
613
+ expect(content).toContain("public void onBeforeInsert");
614
+ expect(content).toContain("onBeforeInsert");
615
+ });
616
+
617
+ it("should verify handler has onBeforeUpdate method", () => {
618
+ const handlerPath = path.join(SALESFORCE_ENTERPRISE_PATH, "force-app/main/default/classes/OpportunityTriggerHandler.cls");
619
+ const content = fs.readFileSync(handlerPath, "utf-8");
620
+
621
+ expect(content).toContain("public void onBeforeUpdate");
622
+ expect(content).toContain("onBeforeUpdate");
623
+ });
624
+
625
+ it("should verify handler has onBeforeDelete method", () => {
626
+ const handlerPath = path.join(SALESFORCE_ENTERPRISE_PATH, "force-app/main/default/classes/OpportunityTriggerHandler.cls");
627
+ const content = fs.readFileSync(handlerPath, "utf-8");
628
+
629
+ expect(content).toContain("public void onBeforeDelete");
630
+ expect(content).toContain("onBeforeDelete");
631
+ });
632
+
633
+ it("should verify handler has onAfterInsert method", () => {
634
+ const handlerPath = path.join(SALESFORCE_ENTERPRISE_PATH, "force-app/main/default/classes/OpportunityTriggerHandler.cls");
635
+ const content = fs.readFileSync(handlerPath, "utf-8");
636
+
637
+ expect(content).toContain("public void onAfterInsert");
638
+ expect(content).toContain("onAfterInsert");
639
+ });
640
+
641
+ it("should verify handler has onAfterUpdate method", () => {
642
+ const handlerPath = path.join(SALESFORCE_ENTERPRISE_PATH, "force-app/main/default/classes/OpportunityTriggerHandler.cls");
643
+ const content = fs.readFileSync(handlerPath, "utf-8");
644
+
645
+ expect(content).toContain("public void onAfterUpdate");
646
+ expect(content).toContain("onAfterUpdate");
647
+ });
648
+
649
+ it("should verify handler has onAfterDelete method", () => {
650
+ const handlerPath = path.join(SALESFORCE_ENTERPRISE_PATH, "force-app/main/default/classes/OpportunityTriggerHandler.cls");
651
+ const content = fs.readFileSync(handlerPath, "utf-8");
652
+
653
+ expect(content).toContain("public void onAfterDelete");
654
+ expect(content).toContain("onAfterDelete");
655
+ });
656
+
657
+ it("should verify handler has onAfterUndelete method", () => {
658
+ const handlerPath = path.join(SALESFORCE_ENTERPRISE_PATH, "force-app/main/default/classes/OpportunityTriggerHandler.cls");
659
+ const content = fs.readFileSync(handlerPath, "utf-8");
660
+
661
+ expect(content).toContain("public void onAfterUndelete");
662
+ expect(content).toContain("onAfterUndelete");
663
+ });
664
+
665
+ it("should use handler pattern in trigger", () => {
666
+ const content = fs.readFileSync(
667
+ path.join(SALESFORCE_ENTERPRISE_PATH, "force-app/main/default/triggers/OpportunityTrigger.trigger"),
668
+ "utf-8"
669
+ );
670
+
671
+ // Verify handler instantiation and usage pattern
672
+ expect(content).toContain("OpportunityTriggerHandler handler = new OpportunityTriggerHandler()");
673
+ });
674
+ });
675
+
676
+ // =========================================================================
677
+ // TRIGGER BYPASS MANAGEMENT VERIFICATION
678
+ // =========================================================================
679
+
680
+ describe("Trigger Bypass Management Verification", () => {
681
+ it("should have TriggerManagement class", () => {
682
+ const managementPath = path.join(SALESFORCE_ENTERPRISE_PATH, "force-app/main/default/classes/TriggerManagement.cls");
683
+ expect(fs.existsSync(managementPath)).toBe(true);
684
+
685
+ const content = fs.readFileSync(managementPath, "utf-8");
686
+ expect(content).toContain("class TriggerManagement");
687
+ });
688
+
689
+ it("should have isTriggerBypassed method", () => {
690
+ const managementPath = path.join(SALESFORCE_ENTERPRISE_PATH, "force-app/main/default/classes/TriggerManagement.cls");
691
+ const content = fs.readFileSync(managementPath, "utf-8");
692
+
693
+ expect(content).toContain("isTriggerBypassed");
694
+ });
695
+
696
+ it("should use isTriggerBypassed in trigger", () => {
697
+ const triggerPath = path.join(SALESFORCE_ENTERPRISE_PATH, "force-app/main/default/triggers/OpportunityTrigger.trigger");
698
+ const content = fs.readFileSync(triggerPath, "utf-8");
699
+
700
+ expect(content).toContain("TriggerManagement.isTriggerBypassed('Opportunity')");
701
+ });
702
+
703
+ it("should return early when trigger is bypassed", () => {
704
+ const triggerPath = path.join(SALESFORCE_ENTERPRISE_PATH, "force-app/main/default/triggers/OpportunityTrigger.trigger");
705
+ const content = fs.readFileSync(triggerPath, "utf-8");
706
+
707
+ expect(content).toContain("return;");
708
+ });
709
+ });
710
+
711
+ // =========================================================================
712
+ // SYMBOL EXTRACTION ACCURACY
713
+ // =========================================================================
714
+
715
+ describe("Symbol Extraction Accuracy", () => {
716
+ it("should extract OpportunityTrigger as function symbol", () => {
717
+ const triggerPath = path.join(SALESFORCE_ENTERPRISE_PATH, "force-app/main/default/triggers/OpportunityTrigger.trigger");
718
+ const content = fs.readFileSync(triggerPath, "utf-8");
719
+
720
+ const file: FileInfo = {
721
+ path: triggerPath,
722
+ relativePath: "force-app/main/default/triggers/OpportunityTrigger.trigger",
723
+ extension: "trigger",
724
+ name: "OpportunityTrigger",
725
+ };
726
+
727
+ const result = extractSymbols([file]);
728
+ const triggerSymbols = result.symbols.filter(s => s.type === "function");
729
+
730
+ expect(triggerSymbols.length).toBeGreaterThanOrEqual(1);
731
+ expect(triggerSymbols.some(s => s.name === "OpportunityTrigger")).toBe(true);
732
+ });
733
+
734
+ it("should have correct line number for OpportunityTrigger", () => {
735
+ const triggerPath = path.join(SALESFORCE_ENTERPRISE_PATH, "force-app/main/default/triggers/OpportunityTrigger.trigger");
736
+ const content = fs.readFileSync(triggerPath, "utf-8");
737
+
738
+ const file: FileInfo = {
739
+ path: triggerPath,
740
+ relativePath: "force-app/main/default/triggers/OpportunityTrigger.trigger",
741
+ extension: "trigger",
742
+ name: "OpportunityTrigger",
743
+ };
744
+
745
+ const result = extractSymbols([file]);
746
+ const triggerSymbol = result.symbols.find(s => s.name === "OpportunityTrigger");
747
+
748
+ expect(triggerSymbol).toBeDefined();
749
+ expect(triggerSymbol?.line).toBe(7); // trigger definition starts at line 7
750
+ });
751
+
752
+ it("should extract all trigger symbols from multiple trigger files", () => {
753
+ const trigger1 = `trigger AccountTrigger on Account (before insert) {}`;
754
+ const trigger2 = `trigger ContactTrigger on Contact (before insert) {}`;
755
+ const trigger3 = `trigger LeadTrigger on Lead (before insert) {}`;
756
+
757
+ const files = [
758
+ createFileInfo("AccountTrigger.trigger", "trigger", trigger1),
759
+ createFileInfo("ContactTrigger.trigger", "trigger", trigger2),
760
+ createFileInfo("LeadTrigger.trigger", "trigger", trigger3),
761
+ ];
762
+
763
+ const result = extractSymbols(files);
764
+ const triggerSymbols = result.symbols.filter(s => s.type === "function");
765
+
766
+ expect(triggerSymbols.length).toBe(3);
767
+ expect(triggerSymbols.some(s => s.name === "AccountTrigger")).toBe(true);
768
+ expect(triggerSymbols.some(s => s.name === "ContactTrigger")).toBe(true);
769
+ expect(triggerSymbols.some(s => s.name === "LeadTrigger")).toBe(true);
770
+ });
771
+ });
772
+
773
+ // =========================================================================
774
+ // EDGE CASES
775
+ // =========================================================================
776
+
777
+ describe("Edge Cases", () => {
778
+ it("should handle trigger without comments", () => {
779
+ const content = `trigger SimpleTrigger on Account (before insert) {
780
+ // no comments
781
+ }`;
782
+ const file = createFileInfo("SimpleTrigger.trigger", "trigger", content);
783
+ const result = extractSymbols([file]);
784
+
785
+ expect(result.symbols.find(s => s.name === "SimpleTrigger")).toBeDefined();
786
+ });
787
+
788
+ it("should handle trigger with multi-line events definition", () => {
789
+ const content = `trigger AccountTrigger on Account (
790
+ before insert,
791
+ before update,
792
+ before delete,
793
+ after insert,
794
+ after update,
795
+ after delete,
796
+ after undelete
797
+ ) {
798
+ // all events
799
+ }`;
800
+ const file = createFileInfo("AccountTrigger.trigger", "trigger", content);
801
+ const result = extractSymbols([file]);
802
+
803
+ expect(result.symbols.find(s => s.name === "AccountTrigger")).toBeDefined();
804
+ });
805
+
806
+ it("should handle trigger on object with namespace prefix", () => {
807
+ const content = `trigger CustomTrigger on MyNamespace__CustomObject__c (before insert) {
808
+ // namespaced object
809
+ }`;
810
+ const file = createFileInfo("CustomTrigger.trigger", "trigger", content);
811
+ const result = extractSymbols([file]);
812
+
813
+ expect(result.symbols.find(s => s.name === "CustomTrigger")).toBeDefined();
814
+ });
815
+
816
+ it("should handle trigger with no explicit handler (inline logic)", () => {
817
+ const content = `trigger InlineTrigger on Account (before insert) {
818
+ for(Account a : Trigger.new) {
819
+ a.Description = 'Auto-generated';
820
+ }
821
+ }`;
822
+ const file = createFileInfo("InlineTrigger.trigger", "trigger", content);
823
+ const result = extractSymbols([file]);
824
+
825
+ expect(result.symbols.find(s => s.name === "InlineTrigger")).toBeDefined();
826
+ });
827
+
828
+ it("should handle trigger with try-catch block", () => {
829
+ const content = `trigger SafeTrigger on Account (before insert) {
830
+ try {
831
+ // might fail
832
+ } catch (Exception e) {
833
+ System.debug(e.getMessage());
834
+ }
835
+ }`;
836
+ const file = createFileInfo("SafeTrigger.trigger", "trigger", content);
837
+ const result = extractSymbols([file]);
838
+
839
+ expect(result.symbols.find(s => s.name === "SafeTrigger")).toBeDefined();
840
+ });
841
+
842
+ it("should handle trigger with SOQL query", () => {
843
+ const content = `trigger AccountTrigger on Account (before insert) {
844
+ List<Account> related = [SELECT Id FROM Account WHERE ParentId = null LIMIT 1];
845
+ }`;
846
+ const file = createFileInfo("AccountTrigger.trigger", "trigger", content);
847
+ const result = extractSymbols([file]);
848
+
849
+ expect(result.symbols.find(s => s.name === "AccountTrigger")).toBeDefined();
850
+ });
851
+
852
+ it("should handle trigger with DML statement", () => {
853
+ const content = `trigger AccountTrigger on Account (before insert) {
854
+ insert new Account(Name = 'Test');
855
+ }`;
856
+ const file = createFileInfo("AccountTrigger.trigger", "trigger", content);
857
+ const result = extractSymbols([file]);
858
+
859
+ expect(result.symbols.find(s => s.name === "AccountTrigger")).toBeDefined();
860
+ });
861
+
862
+ it("should handle empty trigger body", () => {
863
+ const content = `trigger EmptyTrigger on Account (before insert) {
864
+ }`;
865
+ const file = createFileInfo("EmptyTrigger.trigger", "trigger", content);
866
+ const result = extractSymbols([file]);
867
+
868
+ expect(result.symbols.find(s => s.name === "EmptyTrigger")).toBeDefined();
869
+ });
870
+ });
871
+ });