ai-first-cli 1.3.1 → 1.3.5

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 (532) hide show
  1. package/.ai-dev/index.db +0 -0
  2. package/BETA_EVALUATION_REPORT.md +151 -0
  3. package/CHANGELOG.md +178 -0
  4. package/PHASE1_USER_SIMULATION.md +56 -0
  5. package/PHASE2_USER_SIMULATION.md +81 -0
  6. package/PHASE3_USER_SIMULATION.md +176 -0
  7. package/README.es.md +18 -0
  8. package/README.md +80 -1
  9. package/ai/graph/knowledge-graph.json +10 -0
  10. package/ai-context/ai_context.md +130 -0
  11. package/{test-projects/react-app/.ai-dev → ai-context}/ai_rules.md +10 -5
  12. package/ai-context/architecture.md +136 -0
  13. package/ai-context/context/features/src.json +69 -0
  14. package/ai-context/context/features/test-projects.json +69 -0
  15. package/ai-context/context/flows/App.json +17 -0
  16. package/ai-context/context/flows/DashboardPage.json +14 -0
  17. package/ai-context/context/flows/LoginPage.json +14 -0
  18. package/ai-context/context/flows/admin.json +10 -0
  19. package/ai-context/context/flows/ai-first.json +9 -0
  20. package/ai-context/context/flows/androidresources.json +11 -0
  21. package/ai-context/context/flows/auth.json +13 -0
  22. package/ai-context/context/flows/authController.json +14 -0
  23. package/ai-context/context/flows/doctor.json +9 -0
  24. package/ai-context/context/flows/entrypoints.json +9 -0
  25. package/ai-context/context/flows/explore.json +9 -0
  26. package/ai-context/context/flows/fastapiAdapter.json +14 -0
  27. package/ai-context/context/flows/fastapiadapter.json +11 -0
  28. package/ai-context/context/flows/index.json +19 -0
  29. package/ai-context/context/flows/indexer.json +9 -0
  30. package/ai-context/context/flows/indexstate.json +9 -0
  31. package/ai-context/context/flows/init.json +22 -0
  32. package/ai-context/context/flows/main.json +18 -0
  33. package/ai-context/context/flows/mainactivity.json +9 -0
  34. package/ai-context/context/flows/models.json +15 -0
  35. package/ai-context/context/flows/posts.json +15 -0
  36. package/ai-context/context/flows/repoMapper.json +20 -0
  37. package/ai-context/context/flows/repomapper.json +11 -0
  38. package/ai-context/context/flows/routes.json +15 -0
  39. package/ai-context/context/flows/serializers.json +10 -0
  40. package/ai-context/context/flows/user.json +23 -0
  41. package/ai-context/context/flows/views.json +12 -0
  42. package/{test-projects/react-app/.ai-dev → ai-context}/conventions.md +3 -2
  43. package/ai-context/dependencies.json +3360 -0
  44. package/ai-context/entrypoints.md +45 -0
  45. package/ai-context/index-state.json +196 -0
  46. package/ai-context/modules.json +901 -0
  47. package/ai-context/project.json +33 -0
  48. package/ai-context/repo_map.json +8857 -0
  49. package/ai-context/repo_map.md +2002 -0
  50. package/{test-projects/flask-app/.ai-dev → ai-context}/schema.json +1 -1
  51. package/ai-context/summary.md +46 -0
  52. package/ai-context/symbols.json +82467 -0
  53. package/{test-projects/react-app/.ai-dev → ai-context}/tech_stack.md +15 -7
  54. package/ai-context-evaluation-report-1774223059505.md +206 -0
  55. package/dist/analyzers/architecture.d.ts.map +1 -1
  56. package/dist/analyzers/architecture.js +6 -0
  57. package/dist/analyzers/architecture.js.map +1 -1
  58. package/dist/analyzers/entrypoints.d.ts.map +1 -1
  59. package/dist/analyzers/entrypoints.js +105 -0
  60. package/dist/analyzers/entrypoints.js.map +1 -1
  61. package/dist/analyzers/symbols.d.ts.map +1 -1
  62. package/dist/analyzers/symbols.js +72 -1
  63. package/dist/analyzers/symbols.js.map +1 -1
  64. package/dist/analyzers/techStack.d.ts +8 -0
  65. package/dist/analyzers/techStack.d.ts.map +1 -1
  66. package/dist/analyzers/techStack.js +75 -0
  67. package/dist/analyzers/techStack.js.map +1 -1
  68. package/dist/scripts/ai-context-evaluator.js +367 -0
  69. package/package.json +1 -1
  70. package/quick-evaluation-report-1774396002305.md +64 -0
  71. package/quick-evaluator.ts +200 -0
  72. package/scripts/ai-context-evaluator.ts +440 -0
  73. package/src/analyzers/architecture.ts +8 -0
  74. package/src/analyzers/entrypoints.ts +115 -0
  75. package/src/analyzers/symbols.ts +77 -1
  76. package/src/analyzers/techStack.ts +93 -0
  77. package/tests/apex-parser.test.ts +193 -0
  78. package/tests/cli-commands-batch1.test.ts +808 -0
  79. package/tests/cli-commands-batch2.test.ts +1113 -0
  80. package/tests/cli-commands-batch3.test.ts +1128 -0
  81. package/tests/cli-index.test.ts +1007 -0
  82. package/tests/cli-init.test.ts +761 -0
  83. package/tests/salesforce-apex-classes.test.ts +713 -0
  84. package/tests/salesforce-apex-triggers.test.ts +871 -0
  85. package/tests/salesforce-custom-objects.test.ts +918 -0
  86. package/tests/salesforce-flows.test.ts +710 -0
  87. package/tests/salesforce-lwc.test.ts +963 -0
  88. package/tests/salesforce-sfdx-integration.test.ts +1125 -0
  89. package/ANALISIS_COMPLETO.md +0 -424
  90. package/ANALISIS_MEJORAS.md +0 -327
  91. package/CONTRIBUTING.md +0 -89
  92. package/FLOW.md +0 -129
  93. package/TEST_RESULTS.md +0 -198
  94. package/TEST_RESULTS_COMPARATIVE.md +0 -159
  95. package/TEST_RESULTS_COMPLETE.md +0 -127
  96. package/TEST_RESULTS_COMPREHENSIVE.md +0 -208
  97. package/install.sh +0 -188
  98. package/run-all-tests.sh +0 -184
  99. package/test-ai-context-understanding.sh +0 -21
  100. package/test-projects/django-app/.ai-dev/ai_context.md +0 -92
  101. package/test-projects/django-app/.ai-dev/ai_rules.md +0 -47
  102. package/test-projects/django-app/.ai-dev/architecture.md +0 -57
  103. package/test-projects/django-app/.ai-dev/cache.json +0 -169
  104. package/test-projects/django-app/.ai-dev/context/flows/views.json +0 -10
  105. package/test-projects/django-app/.ai-dev/conventions.md +0 -51
  106. package/test-projects/django-app/.ai-dev/dependencies.json +0 -312
  107. package/test-projects/django-app/.ai-dev/entrypoints.md +0 -4
  108. package/test-projects/django-app/.ai-dev/files.json +0 -209
  109. package/test-projects/django-app/.ai-dev/graph/knowledge-graph.json +0 -36
  110. package/test-projects/django-app/.ai-dev/graph/module-graph.json +0 -145
  111. package/test-projects/django-app/.ai-dev/graph/symbol-graph.json +0 -1488
  112. package/test-projects/django-app/.ai-dev/graph/symbol-references.json +0 -1
  113. package/test-projects/django-app/.ai-dev/index-state.json +0 -294
  114. package/test-projects/django-app/.ai-dev/modules.json +0 -35
  115. package/test-projects/django-app/.ai-dev/project.json +0 -11
  116. package/test-projects/django-app/.ai-dev/repo_map.json +0 -412
  117. package/test-projects/django-app/.ai-dev/repo_map.md +0 -105
  118. package/test-projects/django-app/.ai-dev/schema.json +0 -5
  119. package/test-projects/django-app/.ai-dev/summary.md +0 -15
  120. package/test-projects/django-app/.ai-dev/symbols.json +0 -1
  121. package/test-projects/django-app/.ai-dev/tech_stack.md +0 -32
  122. package/test-projects/django-app/README.md +0 -91
  123. package/test-projects/django-app/blog/__init__.py +0 -0
  124. package/test-projects/django-app/blog/admin.py +0 -31
  125. package/test-projects/django-app/blog/models.py +0 -55
  126. package/test-projects/django-app/blog/serializers.py +0 -69
  127. package/test-projects/django-app/blog/urls.py +0 -14
  128. package/test-projects/django-app/blog/views.py +0 -96
  129. package/test-projects/django-app/django_app/__init__.py +0 -0
  130. package/test-projects/django-app/django_app/settings.py +0 -90
  131. package/test-projects/django-app/django_app/urls.py +0 -11
  132. package/test-projects/django-app/django_app/wsgi.py +0 -9
  133. package/test-projects/django-app/manage.py +0 -23
  134. package/test-projects/django-app/requirements.txt +0 -3
  135. package/test-projects/django-app/users/__init__.py +0 -0
  136. package/test-projects/django-app/users/admin.py +0 -42
  137. package/test-projects/django-app/users/models.py +0 -54
  138. package/test-projects/django-app/users/serializers.py +0 -113
  139. package/test-projects/django-app/users/urls.py +0 -13
  140. package/test-projects/django-app/users/views.py +0 -135
  141. package/test-projects/express-api/.ai-dev/ai_context.md +0 -112
  142. package/test-projects/express-api/.ai-dev/ai_rules.md +0 -50
  143. package/test-projects/express-api/.ai-dev/architecture.md +0 -62
  144. package/test-projects/express-api/.ai-dev/context/features/controllers.json +0 -13
  145. package/test-projects/express-api/.ai-dev/context/features/services.json +0 -13
  146. package/test-projects/express-api/.ai-dev/context/flows/auth.json +0 -12
  147. package/test-projects/express-api/.ai-dev/context/flows/user.json +0 -13
  148. package/test-projects/express-api/.ai-dev/conventions.md +0 -51
  149. package/test-projects/express-api/.ai-dev/dependencies.json +0 -54
  150. package/test-projects/express-api/.ai-dev/entrypoints.md +0 -17
  151. package/test-projects/express-api/.ai-dev/modules.json +0 -30
  152. package/test-projects/express-api/.ai-dev/project.json +0 -15
  153. package/test-projects/express-api/.ai-dev/repo_map.json +0 -100
  154. package/test-projects/express-api/.ai-dev/repo_map.md +0 -36
  155. package/test-projects/express-api/.ai-dev/schema.json +0 -5
  156. package/test-projects/express-api/.ai-dev/summary.md +0 -14
  157. package/test-projects/express-api/.ai-dev/symbols.json +0 -7
  158. package/test-projects/express-api/.ai-dev/tech_stack.md +0 -38
  159. package/test-projects/express-api/.ai-dev/tools.json +0 -10
  160. package/test-projects/express-api/controllers/authController.js +0 -32
  161. package/test-projects/express-api/controllers/userController.js +0 -51
  162. package/test-projects/express-api/index.js +0 -30
  163. package/test-projects/express-api/middleware/authMiddleware.js +0 -30
  164. package/test-projects/express-api/models/userRepository.js +0 -25
  165. package/test-projects/express-api/package.json +0 -18
  166. package/test-projects/express-api/services/authService.js +0 -17
  167. package/test-projects/express-api/services/userService.js +0 -28
  168. package/test-projects/fastapi-app/.ai-dev/ai_context.md +0 -89
  169. package/test-projects/fastapi-app/.ai-dev/ai_rules.md +0 -47
  170. package/test-projects/fastapi-app/.ai-dev/architecture.md +0 -39
  171. package/test-projects/fastapi-app/.ai-dev/cache.json +0 -125
  172. package/test-projects/fastapi-app/.ai-dev/conventions.md +0 -51
  173. package/test-projects/fastapi-app/.ai-dev/dependencies.json +0 -244
  174. package/test-projects/fastapi-app/.ai-dev/entrypoints.md +0 -4
  175. package/test-projects/fastapi-app/.ai-dev/files.json +0 -154
  176. package/test-projects/fastapi-app/.ai-dev/graph/knowledge-graph.json +0 -15
  177. package/test-projects/fastapi-app/.ai-dev/graph/module-graph.json +0 -78
  178. package/test-projects/fastapi-app/.ai-dev/graph/symbol-graph.json +0 -1724
  179. package/test-projects/fastapi-app/.ai-dev/graph/symbol-references.json +0 -51
  180. package/test-projects/fastapi-app/.ai-dev/index-state.json +0 -217
  181. package/test-projects/fastapi-app/.ai-dev/modules.json +0 -16
  182. package/test-projects/fastapi-app/.ai-dev/project.json +0 -9
  183. package/test-projects/fastapi-app/.ai-dev/repo_map.json +0 -298
  184. package/test-projects/fastapi-app/.ai-dev/repo_map.md +0 -74
  185. package/test-projects/fastapi-app/.ai-dev/schema.json +0 -5
  186. package/test-projects/fastapi-app/.ai-dev/summary.md +0 -12
  187. package/test-projects/fastapi-app/.ai-dev/symbols.json +0 -1
  188. package/test-projects/fastapi-app/.ai-dev/tech_stack.md +0 -32
  189. package/test-projects/fastapi-app/.ai-dev/tools.json +0 -10
  190. package/test-projects/fastapi-app/README.md +0 -118
  191. package/test-projects/fastapi-app/app/database.py +0 -21
  192. package/test-projects/fastapi-app/app/dependencies.py +0 -107
  193. package/test-projects/fastapi-app/app/main.py +0 -47
  194. package/test-projects/fastapi-app/app/models.py +0 -149
  195. package/test-projects/fastapi-app/app/routers/auth.py +0 -117
  196. package/test-projects/fastapi-app/app/routers/posts.py +0 -272
  197. package/test-projects/fastapi-app/app/schemas.py +0 -191
  198. package/test-projects/fastapi-app/requirements.txt +0 -10
  199. package/test-projects/flask-app/.ai-dev/ai_context.md +0 -94
  200. package/test-projects/flask-app/.ai-dev/ai_rules.md +0 -47
  201. package/test-projects/flask-app/.ai-dev/architecture.md +0 -49
  202. package/test-projects/flask-app/.ai-dev/cache.json +0 -157
  203. package/test-projects/flask-app/.ai-dev/context/features/app.json +0 -25
  204. package/test-projects/flask-app/.ai-dev/context/flows/routes.json +0 -14
  205. package/test-projects/flask-app/.ai-dev/conventions.md +0 -51
  206. package/test-projects/flask-app/.ai-dev/dependencies.json +0 -298
  207. package/test-projects/flask-app/.ai-dev/entrypoints.md +0 -4
  208. package/test-projects/flask-app/.ai-dev/files.json +0 -194
  209. package/test-projects/flask-app/.ai-dev/graph/knowledge-graph.json +0 -60
  210. package/test-projects/flask-app/.ai-dev/graph/module-graph.json +0 -95
  211. package/test-projects/flask-app/.ai-dev/graph/symbol-graph.json +0 -1448
  212. package/test-projects/flask-app/.ai-dev/graph/symbol-references.json +0 -45
  213. package/test-projects/flask-app/.ai-dev/index-state.json +0 -273
  214. package/test-projects/flask-app/.ai-dev/modules.json +0 -21
  215. package/test-projects/flask-app/.ai-dev/project.json +0 -13
  216. package/test-projects/flask-app/.ai-dev/repo_map.json +0 -400
  217. package/test-projects/flask-app/.ai-dev/repo_map.md +0 -98
  218. package/test-projects/flask-app/.ai-dev/summary.md +0 -13
  219. package/test-projects/flask-app/.ai-dev/symbols.json +0 -1
  220. package/test-projects/flask-app/.ai-dev/tech_stack.md +0 -32
  221. package/test-projects/flask-app/.ai-dev/tools.json +0 -10
  222. package/test-projects/flask-app/README.md +0 -129
  223. package/test-projects/flask-app/app/__init__.py +0 -46
  224. package/test-projects/flask-app/app/api/__init__.py +0 -7
  225. package/test-projects/flask-app/app/api/routes.py +0 -122
  226. package/test-projects/flask-app/app/auth/__init__.py +0 -7
  227. package/test-projects/flask-app/app/auth/forms.py +0 -52
  228. package/test-projects/flask-app/app/auth/routes.py +0 -68
  229. package/test-projects/flask-app/app/blog/__init__.py +0 -7
  230. package/test-projects/flask-app/app/blog/forms.py +0 -35
  231. package/test-projects/flask-app/app/blog/routes.py +0 -140
  232. package/test-projects/flask-app/app/main/__init__.py +0 -7
  233. package/test-projects/flask-app/app/main/routes.py +0 -88
  234. package/test-projects/flask-app/app/models.py +0 -177
  235. package/test-projects/flask-app/config.py +0 -64
  236. package/test-projects/flask-app/requirements.txt +0 -10
  237. package/test-projects/laravel-app/.ai-dev/ai_context.md +0 -97
  238. package/test-projects/laravel-app/.ai-dev/ai_rules.md +0 -47
  239. package/test-projects/laravel-app/.ai-dev/architecture.md +0 -60
  240. package/test-projects/laravel-app/.ai-dev/cache.json +0 -161
  241. package/test-projects/laravel-app/.ai-dev/context/features/app.json +0 -21
  242. package/test-projects/laravel-app/.ai-dev/context/flows/.json +0 -9
  243. package/test-projects/laravel-app/.ai-dev/context/flows/category.json +0 -12
  244. package/test-projects/laravel-app/.ai-dev/context/flows/comment.json +0 -12
  245. package/test-projects/laravel-app/.ai-dev/context/flows/post.json +0 -12
  246. package/test-projects/laravel-app/.ai-dev/context/flows/unnamed.json +0 -9
  247. package/test-projects/laravel-app/.ai-dev/conventions.md +0 -51
  248. package/test-projects/laravel-app/.ai-dev/dependencies.json +0 -6
  249. package/test-projects/laravel-app/.ai-dev/entrypoints.md +0 -4
  250. package/test-projects/laravel-app/.ai-dev/files.json +0 -199
  251. package/test-projects/laravel-app/.ai-dev/graph/knowledge-graph.json +0 -98
  252. package/test-projects/laravel-app/.ai-dev/graph/module-graph.json +0 -30
  253. package/test-projects/laravel-app/.ai-dev/graph/symbol-graph.json +0 -5
  254. package/test-projects/laravel-app/.ai-dev/graph/symbol-references.json +0 -1
  255. package/test-projects/laravel-app/.ai-dev/index-state.json +0 -280
  256. package/test-projects/laravel-app/.ai-dev/modules.json +0 -29
  257. package/test-projects/laravel-app/.ai-dev/project.json +0 -17
  258. package/test-projects/laravel-app/.ai-dev/repo_map.json +0 -419
  259. package/test-projects/laravel-app/.ai-dev/repo_map.md +0 -106
  260. package/test-projects/laravel-app/.ai-dev/schema.json +0 -5
  261. package/test-projects/laravel-app/.ai-dev/summary.md +0 -15
  262. package/test-projects/laravel-app/.ai-dev/symbols.json +0 -1
  263. package/test-projects/laravel-app/.ai-dev/tech_stack.md +0 -34
  264. package/test-projects/laravel-app/.ai-dev/tools.json +0 -10
  265. package/test-projects/laravel-app/README.md +0 -107
  266. package/test-projects/laravel-app/app/Http/Controllers/Api/CategoryController.php +0 -88
  267. package/test-projects/laravel-app/app/Http/Controllers/Api/CommentController.php +0 -56
  268. package/test-projects/laravel-app/app/Http/Controllers/Api/PostController.php +0 -174
  269. package/test-projects/laravel-app/app/Http/Controllers/Controller.php +0 -12
  270. package/test-projects/laravel-app/app/Models/Category.php +0 -34
  271. package/test-projects/laravel-app/app/Models/Comment.php +0 -51
  272. package/test-projects/laravel-app/app/Models/Post.php +0 -108
  273. package/test-projects/laravel-app/app/Models/User.php +0 -85
  274. package/test-projects/laravel-app/bootstrap/app.php +0 -25
  275. package/test-projects/laravel-app/composer.json +0 -35
  276. package/test-projects/laravel-app/routes/api.php +0 -40
  277. package/test-projects/nestjs-backend/.ai-dev/ai_context.md +0 -111
  278. package/test-projects/nestjs-backend/.ai-dev/ai_rules.md +0 -52
  279. package/test-projects/nestjs-backend/.ai-dev/architecture.md +0 -49
  280. package/test-projects/nestjs-backend/.ai-dev/cache.json +0 -169
  281. package/test-projects/nestjs-backend/.ai-dev/context/features/src.json +0 -23
  282. package/test-projects/nestjs-backend/.ai-dev/context/flows/auth.controller.json +0 -14
  283. package/test-projects/nestjs-backend/.ai-dev/context/flows/auth.json +0 -10
  284. package/test-projects/nestjs-backend/.ai-dev/context/flows/users..json +0 -10
  285. package/test-projects/nestjs-backend/.ai-dev/context/flows/users.controller.json +0 -14
  286. package/test-projects/nestjs-backend/.ai-dev/context/flows/users.json +0 -10
  287. package/test-projects/nestjs-backend/.ai-dev/conventions.md +0 -52
  288. package/test-projects/nestjs-backend/.ai-dev/dependencies.json +0 -152
  289. package/test-projects/nestjs-backend/.ai-dev/entrypoints.md +0 -18
  290. package/test-projects/nestjs-backend/.ai-dev/files.json +0 -209
  291. package/test-projects/nestjs-backend/.ai-dev/graph/knowledge-graph.json +0 -132
  292. package/test-projects/nestjs-backend/.ai-dev/graph/module-graph.json +0 -29
  293. package/test-projects/nestjs-backend/.ai-dev/graph/symbol-graph.json +0 -304
  294. package/test-projects/nestjs-backend/.ai-dev/graph/symbol-references.json +0 -5
  295. package/test-projects/nestjs-backend/.ai-dev/index-state.json +0 -294
  296. package/test-projects/nestjs-backend/.ai-dev/modules.json +0 -19
  297. package/test-projects/nestjs-backend/.ai-dev/project.json +0 -18
  298. package/test-projects/nestjs-backend/.ai-dev/repo_map.json +0 -427
  299. package/test-projects/nestjs-backend/.ai-dev/repo_map.md +0 -104
  300. package/test-projects/nestjs-backend/.ai-dev/schema.json +0 -5
  301. package/test-projects/nestjs-backend/.ai-dev/summary.md +0 -13
  302. package/test-projects/nestjs-backend/.ai-dev/symbols.json +0 -1
  303. package/test-projects/nestjs-backend/.ai-dev/tech_stack.md +0 -38
  304. package/test-projects/nestjs-backend/.ai-dev/tools.json +0 -10
  305. package/test-projects/nestjs-backend/package.json +0 -22
  306. package/test-projects/nestjs-backend/src/app.module.ts +0 -8
  307. package/test-projects/nestjs-backend/src/auth/auth.controller.ts +0 -22
  308. package/test-projects/nestjs-backend/src/auth/auth.module.ts +0 -11
  309. package/test-projects/nestjs-backend/src/auth/auth.service.ts +0 -28
  310. package/test-projects/nestjs-backend/src/auth/dto/login.dto.ts +0 -4
  311. package/test-projects/nestjs-backend/src/auth/strategies/jwt.strategy.ts +0 -18
  312. package/test-projects/nestjs-backend/src/main.ts +0 -9
  313. package/test-projects/nestjs-backend/src/users/users.controller.ts +0 -32
  314. package/test-projects/nestjs-backend/src/users/users.module.ts +0 -10
  315. package/test-projects/nestjs-backend/src/users/users.service.ts +0 -42
  316. package/test-projects/nestjs-backend/tsconfig.json +0 -21
  317. package/test-projects/python-cli/.ai-dev/ai_context.md +0 -95
  318. package/test-projects/python-cli/.ai-dev/ai_rules.md +0 -47
  319. package/test-projects/python-cli/.ai-dev/architecture.md +0 -55
  320. package/test-projects/python-cli/.ai-dev/cache.json +0 -149
  321. package/test-projects/python-cli/.ai-dev/context/features/cli.json +0 -16
  322. package/test-projects/python-cli/.ai-dev/context/flows/list_.json +0 -9
  323. package/test-projects/python-cli/.ai-dev/context/flows/remove_.json +0 -9
  324. package/test-projects/python-cli/.ai-dev/conventions.md +0 -51
  325. package/test-projects/python-cli/.ai-dev/dependencies.json +0 -66
  326. package/test-projects/python-cli/.ai-dev/entrypoints.md +0 -4
  327. package/test-projects/python-cli/.ai-dev/files.json +0 -184
  328. package/test-projects/python-cli/.ai-dev/graph/knowledge-graph.json +0 -83
  329. package/test-projects/python-cli/.ai-dev/graph/module-graph.json +0 -31
  330. package/test-projects/python-cli/.ai-dev/graph/symbol-graph.json +0 -358
  331. package/test-projects/python-cli/.ai-dev/graph/symbol-references.json +0 -11
  332. package/test-projects/python-cli/.ai-dev/index-state.json +0 -259
  333. package/test-projects/python-cli/.ai-dev/modules.json +0 -21
  334. package/test-projects/python-cli/.ai-dev/project.json +0 -15
  335. package/test-projects/python-cli/.ai-dev/repo_map.json +0 -367
  336. package/test-projects/python-cli/.ai-dev/repo_map.md +0 -93
  337. package/test-projects/python-cli/.ai-dev/schema.json +0 -5
  338. package/test-projects/python-cli/.ai-dev/summary.md +0 -14
  339. package/test-projects/python-cli/.ai-dev/symbols.json +0 -1
  340. package/test-projects/python-cli/.ai-dev/tech_stack.md +0 -32
  341. package/test-projects/python-cli/.ai-dev/tools.json +0 -10
  342. package/test-projects/python-cli/__init__.py +0 -1
  343. package/test-projects/python-cli/cli/__init__.py +0 -1
  344. package/test-projects/python-cli/cli/add_command.py +0 -6
  345. package/test-projects/python-cli/cli/list_command.py +0 -7
  346. package/test-projects/python-cli/cli/remove_command.py +0 -6
  347. package/test-projects/python-cli/main.py +0 -34
  348. package/test-projects/python-cli/models/__init__.py +0 -2
  349. package/test-projects/python-cli/models/task.py +0 -19
  350. package/test-projects/python-cli/models/task_repository.py +0 -44
  351. package/test-projects/rails-app/.ai-dev/ai_context.md +0 -94
  352. package/test-projects/rails-app/.ai-dev/ai_rules.md +0 -47
  353. package/test-projects/rails-app/.ai-dev/architecture.md +0 -49
  354. package/test-projects/rails-app/.ai-dev/cache.json +0 -193
  355. package/test-projects/rails-app/.ai-dev/context/features/app.json +0 -24
  356. package/test-projects/rails-app/.ai-dev/context/features/config.json +0 -13
  357. package/test-projects/rails-app/.ai-dev/context/flows/application.json +0 -9
  358. package/test-projects/rails-app/.ai-dev/context/flows/application_.json +0 -9
  359. package/test-projects/rails-app/.ai-dev/context/flows/comments.json +0 -11
  360. package/test-projects/rails-app/.ai-dev/context/flows/comments_.json +0 -11
  361. package/test-projects/rails-app/.ai-dev/context/flows/posts.json +0 -11
  362. package/test-projects/rails-app/.ai-dev/context/flows/posts_.json +0 -11
  363. package/test-projects/rails-app/.ai-dev/context/flows/routes.json +0 -9
  364. package/test-projects/rails-app/.ai-dev/context/flows/users.json +0 -11
  365. package/test-projects/rails-app/.ai-dev/context/flows/users_.json +0 -11
  366. package/test-projects/rails-app/.ai-dev/conventions.md +0 -51
  367. package/test-projects/rails-app/.ai-dev/dependencies.json +0 -6
  368. package/test-projects/rails-app/.ai-dev/entrypoints.md +0 -4
  369. package/test-projects/rails-app/.ai-dev/files.json +0 -239
  370. package/test-projects/rails-app/.ai-dev/graph/knowledge-graph.json +0 -130
  371. package/test-projects/rails-app/.ai-dev/graph/module-graph.json +0 -27
  372. package/test-projects/rails-app/.ai-dev/graph/symbol-graph.json +0 -5
  373. package/test-projects/rails-app/.ai-dev/graph/symbol-references.json +0 -1
  374. package/test-projects/rails-app/.ai-dev/index-state.json +0 -336
  375. package/test-projects/rails-app/.ai-dev/modules.json +0 -26
  376. package/test-projects/rails-app/.ai-dev/project.json +0 -22
  377. package/test-projects/rails-app/.ai-dev/repo_map.json +0 -486
  378. package/test-projects/rails-app/.ai-dev/repo_map.md +0 -117
  379. package/test-projects/rails-app/.ai-dev/schema.json +0 -5
  380. package/test-projects/rails-app/.ai-dev/summary.md +0 -13
  381. package/test-projects/rails-app/.ai-dev/symbols.json +0 -1
  382. package/test-projects/rails-app/.ai-dev/tech_stack.md +0 -32
  383. package/test-projects/rails-app/.ai-dev/tools.json +0 -10
  384. package/test-projects/rails-app/Gemfile +0 -38
  385. package/test-projects/rails-app/README.md +0 -140
  386. package/test-projects/rails-app/Rakefile +0 -8
  387. package/test-projects/rails-app/app/controllers/api/comments_controller.rb +0 -75
  388. package/test-projects/rails-app/app/controllers/api/posts_controller.rb +0 -68
  389. package/test-projects/rails-app/app/controllers/api/users_controller.rb +0 -54
  390. package/test-projects/rails-app/app/controllers/application_controller.rb +0 -31
  391. package/test-projects/rails-app/app/models/comment.rb +0 -34
  392. package/test-projects/rails-app/app/models/post.rb +0 -36
  393. package/test-projects/rails-app/app/models/user.rb +0 -28
  394. package/test-projects/rails-app/app/services/post_service.rb +0 -92
  395. package/test-projects/rails-app/app/services/user_service.rb +0 -76
  396. package/test-projects/rails-app/config/application.rb +0 -27
  397. package/test-projects/rails-app/config/environment.rb +0 -7
  398. package/test-projects/rails-app/config/routes.rb +0 -15
  399. package/test-projects/react-app/.ai-dev/ai_context.md +0 -96
  400. package/test-projects/react-app/.ai-dev/architecture.md +0 -39
  401. package/test-projects/react-app/.ai-dev/cache.json +0 -153
  402. package/test-projects/react-app/.ai-dev/context/features/src.json +0 -18
  403. package/test-projects/react-app/.ai-dev/context/flows/UsersPage.json +0 -14
  404. package/test-projects/react-app/.ai-dev/context/flows/dashboard.json +0 -9
  405. package/test-projects/react-app/.ai-dev/context/flows/login.json +0 -9
  406. package/test-projects/react-app/.ai-dev/context/flows/users.json +0 -9
  407. package/test-projects/react-app/.ai-dev/dependencies.json +0 -128
  408. package/test-projects/react-app/.ai-dev/entrypoints.md +0 -4
  409. package/test-projects/react-app/.ai-dev/files.json +0 -189
  410. package/test-projects/react-app/.ai-dev/graph/knowledge-graph.json +0 -112
  411. package/test-projects/react-app/.ai-dev/graph/module-graph.json +0 -31
  412. package/test-projects/react-app/.ai-dev/graph/symbol-graph.json +0 -868
  413. package/test-projects/react-app/.ai-dev/graph/symbol-references.json +0 -31
  414. package/test-projects/react-app/.ai-dev/index-state.json +0 -266
  415. package/test-projects/react-app/.ai-dev/modules.json +0 -17
  416. package/test-projects/react-app/.ai-dev/project.json +0 -16
  417. package/test-projects/react-app/.ai-dev/repo_map.json +0 -391
  418. package/test-projects/react-app/.ai-dev/repo_map.md +0 -94
  419. package/test-projects/react-app/.ai-dev/schema.json +0 -5
  420. package/test-projects/react-app/.ai-dev/summary.md +0 -13
  421. package/test-projects/react-app/.ai-dev/symbols.json +0 -1
  422. package/test-projects/react-app/.ai-dev/tools.json +0 -10
  423. package/test-projects/react-app/package.json +0 -16
  424. package/test-projects/react-app/src/App.tsx +0 -21
  425. package/test-projects/react-app/src/context/AuthContext.tsx +0 -41
  426. package/test-projects/react-app/src/hooks/useAuth.ts +0 -10
  427. package/test-projects/react-app/src/main.tsx +0 -10
  428. package/test-projects/react-app/src/pages/DashboardPage.tsx +0 -17
  429. package/test-projects/react-app/src/pages/LoginPage.tsx +0 -41
  430. package/test-projects/react-app/src/pages/UsersPage.tsx +0 -36
  431. package/test-projects/react-app/src/services/userService.ts +0 -37
  432. package/test-projects/salesforce-cli/.ai-dev/ai_context.md +0 -89
  433. package/test-projects/salesforce-cli/.ai-dev/ai_rules.md +0 -47
  434. package/test-projects/salesforce-cli/.ai-dev/architecture.md +0 -39
  435. package/test-projects/salesforce-cli/.ai-dev/cache.json +0 -125
  436. package/test-projects/salesforce-cli/.ai-dev/context/features/force-app.json +0 -14
  437. package/test-projects/salesforce-cli/.ai-dev/context/flows/account.json +0 -9
  438. package/test-projects/salesforce-cli/.ai-dev/context/flows/opportunity.json +0 -9
  439. package/test-projects/salesforce-cli/.ai-dev/conventions.md +0 -51
  440. package/test-projects/salesforce-cli/.ai-dev/dependencies.json +0 -6
  441. package/test-projects/salesforce-cli/.ai-dev/entrypoints.md +0 -4
  442. package/test-projects/salesforce-cli/.ai-dev/files.json +0 -154
  443. package/test-projects/salesforce-cli/.ai-dev/graph/knowledge-graph.json +0 -64
  444. package/test-projects/salesforce-cli/.ai-dev/graph/module-graph.json +0 -13
  445. package/test-projects/salesforce-cli/.ai-dev/graph/symbol-graph.json +0 -148
  446. package/test-projects/salesforce-cli/.ai-dev/graph/symbol-references.json +0 -1
  447. package/test-projects/salesforce-cli/.ai-dev/index-state.json +0 -217
  448. package/test-projects/salesforce-cli/.ai-dev/modules.json +0 -12
  449. package/test-projects/salesforce-cli/.ai-dev/project.json +0 -14
  450. package/test-projects/salesforce-cli/.ai-dev/repo_map.json +0 -328
  451. package/test-projects/salesforce-cli/.ai-dev/repo_map.md +0 -80
  452. package/test-projects/salesforce-cli/.ai-dev/schema.json +0 -5
  453. package/test-projects/salesforce-cli/.ai-dev/summary.md +0 -13
  454. package/test-projects/salesforce-cli/.ai-dev/symbols.json +0 -1
  455. package/test-projects/salesforce-cli/.ai-dev/tech_stack.md +0 -31
  456. package/test-projects/salesforce-cli/.ai-dev/tools.json +0 -10
  457. package/test-projects/salesforce-cli/.forceignore +0 -27
  458. package/test-projects/salesforce-cli/force-app/main/default/classes/AccountController.cls +0 -24
  459. package/test-projects/salesforce-cli/force-app/main/default/classes/OpportunityController.cls +0 -25
  460. package/test-projects/salesforce-cli/force-app/main/default/objects/Project__c.object.xml +0 -45
  461. package/test-projects/salesforce-cli/force-app/main/default/triggers/AccountTrigger.trigger +0 -33
  462. package/test-projects/salesforce-cli/sfdx-project.json +0 -11
  463. package/test-projects/spring-boot-app/.ai-dev/ai_context.md +0 -91
  464. package/test-projects/spring-boot-app/.ai-dev/ai_rules.md +0 -48
  465. package/test-projects/spring-boot-app/.ai-dev/architecture.md +0 -39
  466. package/test-projects/spring-boot-app/.ai-dev/cache.json +0 -173
  467. package/test-projects/spring-boot-app/.ai-dev/context/features/src.json +0 -26
  468. package/test-projects/spring-boot-app/.ai-dev/context/flows/PostController.json +0 -19
  469. package/test-projects/spring-boot-app/.ai-dev/context/flows/UserController.json +0 -19
  470. package/test-projects/spring-boot-app/.ai-dev/context/flows/comment.json +0 -11
  471. package/test-projects/spring-boot-app/.ai-dev/context/flows/post.json +0 -14
  472. package/test-projects/spring-boot-app/.ai-dev/context/flows/user.json +0 -14
  473. package/test-projects/spring-boot-app/.ai-dev/conventions.md +0 -52
  474. package/test-projects/spring-boot-app/.ai-dev/dependencies.json +0 -326
  475. package/test-projects/spring-boot-app/.ai-dev/entrypoints.md +0 -4
  476. package/test-projects/spring-boot-app/.ai-dev/files.json +0 -214
  477. package/test-projects/spring-boot-app/.ai-dev/graph/knowledge-graph.json +0 -231
  478. package/test-projects/spring-boot-app/.ai-dev/graph/module-graph.json +0 -22
  479. package/test-projects/spring-boot-app/.ai-dev/graph/symbol-graph.json +0 -794
  480. package/test-projects/spring-boot-app/.ai-dev/graph/symbol-references.json +0 -70
  481. package/test-projects/spring-boot-app/.ai-dev/index-state.json +0 -301
  482. package/test-projects/spring-boot-app/.ai-dev/modules.json +0 -21
  483. package/test-projects/spring-boot-app/.ai-dev/project.json +0 -17
  484. package/test-projects/spring-boot-app/.ai-dev/repo_map.json +0 -461
  485. package/test-projects/spring-boot-app/.ai-dev/repo_map.md +0 -109
  486. package/test-projects/spring-boot-app/.ai-dev/schema.json +0 -5
  487. package/test-projects/spring-boot-app/.ai-dev/summary.md +0 -12
  488. package/test-projects/spring-boot-app/.ai-dev/symbols.json +0 -1
  489. package/test-projects/spring-boot-app/.ai-dev/tech_stack.md +0 -32
  490. package/test-projects/spring-boot-app/.ai-dev/tools.json +0 -10
  491. package/test-projects/spring-boot-app/.classpath +0 -57
  492. package/test-projects/spring-boot-app/.factorypath +0 -69
  493. package/test-projects/spring-boot-app/.project +0 -34
  494. package/test-projects/spring-boot-app/.settings/org.eclipse.core.resources.prefs +0 -4
  495. package/test-projects/spring-boot-app/.settings/org.eclipse.jdt.apt.core.prefs +0 -4
  496. package/test-projects/spring-boot-app/.settings/org.eclipse.jdt.core.prefs +0 -10
  497. package/test-projects/spring-boot-app/.settings/org.eclipse.m2e.core.prefs +0 -4
  498. package/test-projects/spring-boot-app/README.md +0 -122
  499. package/test-projects/spring-boot-app/pom.xml +0 -79
  500. package/test-projects/spring-boot-app/src/main/java/com/example/demo/DemoApplication.java +0 -12
  501. package/test-projects/spring-boot-app/src/main/java/com/example/demo/controllers/CommentController.java +0 -89
  502. package/test-projects/spring-boot-app/src/main/java/com/example/demo/controllers/PostController.java +0 -92
  503. package/test-projects/spring-boot-app/src/main/java/com/example/demo/controllers/UserController.java +0 -84
  504. package/test-projects/spring-boot-app/src/main/java/com/example/demo/models/Comment.java +0 -38
  505. package/test-projects/spring-boot-app/src/main/java/com/example/demo/models/Post.java +0 -56
  506. package/test-projects/spring-boot-app/src/main/java/com/example/demo/models/User.java +0 -44
  507. package/test-projects/spring-boot-app/src/main/java/com/example/demo/repositories/CommentRepository.java +0 -21
  508. package/test-projects/spring-boot-app/src/main/java/com/example/demo/repositories/PostRepository.java +0 -18
  509. package/test-projects/spring-boot-app/src/main/java/com/example/demo/repositories/UserRepository.java +0 -15
  510. package/test-projects/spring-boot-app/src/main/java/com/example/demo/services/PostService.java +0 -83
  511. package/test-projects/spring-boot-app/src/main/java/com/example/demo/services/UserService.java +0 -62
  512. package/test-projects/spring-boot-app/src/main/resources/application.properties +0 -22
  513. package/test-projects/spring-boot-app/target/classes/com/example/demo/DemoApplication.class +0 -0
  514. package/test-projects/spring-boot-app/target/classes/com/example/demo/controllers/CommentController$CommentCreateRequest.class +0 -0
  515. package/test-projects/spring-boot-app/target/classes/com/example/demo/controllers/CommentController$CommentUpdateRequest.class +0 -0
  516. package/test-projects/spring-boot-app/target/classes/com/example/demo/controllers/CommentController.class +0 -0
  517. package/test-projects/spring-boot-app/target/classes/com/example/demo/controllers/PostController$PostCreateRequest.class +0 -0
  518. package/test-projects/spring-boot-app/target/classes/com/example/demo/controllers/PostController$PostUpdateRequest.class +0 -0
  519. package/test-projects/spring-boot-app/target/classes/com/example/demo/controllers/PostController.class +0 -0
  520. package/test-projects/spring-boot-app/target/classes/com/example/demo/controllers/UserController$UserCreateRequest.class +0 -0
  521. package/test-projects/spring-boot-app/target/classes/com/example/demo/controllers/UserController$UserUpdateRequest.class +0 -0
  522. package/test-projects/spring-boot-app/target/classes/com/example/demo/controllers/UserController.class +0 -0
  523. package/test-projects/spring-boot-app/target/classes/com/example/demo/models/Comment.class +0 -0
  524. package/test-projects/spring-boot-app/target/classes/com/example/demo/models/Post.class +0 -0
  525. package/test-projects/spring-boot-app/target/classes/com/example/demo/models/User.class +0 -0
  526. package/test-projects/spring-boot-app/target/classes/com/example/demo/repositories/CommentRepository.class +0 -0
  527. package/test-projects/spring-boot-app/target/classes/com/example/demo/repositories/PostRepository.class +0 -0
  528. package/test-projects/spring-boot-app/target/classes/com/example/demo/repositories/UserRepository.class +0 -0
  529. package/test-projects/spring-boot-app/target/classes/com/example/demo/services/PostService.class +0 -0
  530. package/test-projects/spring-boot-app/target/classes/com/example/demo/services/UserService.class +0 -0
  531. package/tests/e2e/run-e2e.sh +0 -88
  532. /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
+ });