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,1125 @@
1
+ import { describe, it, expect, beforeAll } from "vitest";
2
+ import fs from "fs";
3
+ import path from "path";
4
+
5
+ const SALESFORCE_ENTERPRISE_PATH = path.join(process.cwd(), "test-projects/salesforce-enterprise");
6
+
7
+ describe("Salesforce SFDX Metadata and Integration Tests", () => {
8
+
9
+ // =========================================================================
10
+ // INTEGRATION TESTS - SFDX Project Detection
11
+ // =========================================================================
12
+
13
+ describe("Integration: SFDX Project Detection", () => {
14
+
15
+ it("should detect sfdx-project.json in project root", () => {
16
+ const sfdxProjectPath = path.join(SALESFORCE_ENTERPRISE_PATH, "sfdx-project.json");
17
+ expect(fs.existsSync(sfdxProjectPath)).toBe(true);
18
+ });
19
+
20
+ it("should detect package.xml in force-app/main/default", () => {
21
+ const packageXmlPath = path.join(SALESFORCE_ENTERPRISE_PATH, "force-app/main/default/package.xml");
22
+ expect(fs.existsSync(packageXmlPath)).toBe(true);
23
+ });
24
+
25
+ it("should detect Admin profile", () => {
26
+ const profilePath = path.join(SALESFORCE_ENTERPRISE_PATH, "force-app/main/default/profiles/Admin.profile-meta.xml");
27
+ expect(fs.existsSync(profilePath)).toBe(true);
28
+ });
29
+
30
+ it("should detect SalesRepresentative permission set", () => {
31
+ const permSetPath = path.join(SALESFORCE_ENTERPRISE_PATH, "force-app/main/default/permissionsets/SalesRepresentative.permissionset-meta.xml");
32
+ expect(fs.existsSync(permSetPath)).toBe(true);
33
+ });
34
+
35
+ it("should have complete SFDX project structure", () => {
36
+ // Verify key directories exist
37
+ expect(fs.existsSync(path.join(SALESFORCE_ENTERPRISE_PATH, "force-app"))).toBe(true);
38
+ expect(fs.existsSync(path.join(SALESFORCE_ENTERPRISE_PATH, "force-app/main"))).toBe(true);
39
+ expect(fs.existsSync(path.join(SALESFORCE_ENTERPRISE_PATH, "force-app/main/default"))).toBe(true);
40
+ expect(fs.existsSync(path.join(SALESFORCE_ENTERPRISE_PATH, "force-app/main/default/classes"))).toBe(true);
41
+ expect(fs.existsSync(path.join(SALESFORCE_ENTERPRISE_PATH, "force-app/main/default/triggers"))).toBe(true);
42
+ expect(fs.existsSync(path.join(SALESFORCE_ENTERPRISE_PATH, "force-app/main/default/lwc"))).toBe(true);
43
+ expect(fs.existsSync(path.join(SALESFORCE_ENTERPRISE_PATH, "force-app/main/default/flows"))).toBe(true);
44
+ expect(fs.existsSync(path.join(SALESFORCE_ENTERPRISE_PATH, "force-app/main/default/objects"))).toBe(true);
45
+ expect(fs.existsSync(path.join(SALESFORCE_ENTERPRISE_PATH, "force-app/main/default/profiles"))).toBe(true);
46
+ expect(fs.existsSync(path.join(SALESFORCE_ENTERPRISE_PATH, "force-app/main/default/permissionsets"))).toBe(true);
47
+ });
48
+ });
49
+
50
+ // =========================================================================
51
+ // SFDX-PROJECT.JSON DETECTION AND PARSING
52
+ // =========================================================================
53
+
54
+ describe("sfdx-project.json Detection and Parsing", () => {
55
+ let sfdxProjectContent: string;
56
+ let sfdxProjectJson: Record<string, unknown>;
57
+
58
+ beforeAll(() => {
59
+ const sfdxProjectPath = path.join(SALESFORCE_ENTERPRISE_PATH, "sfdx-project.json");
60
+ sfdxProjectContent = fs.readFileSync(sfdxProjectPath, "utf-8");
61
+ sfdxProjectJson = JSON.parse(sfdxProjectContent);
62
+ });
63
+
64
+ describe("File Detection", () => {
65
+ it("should be valid JSON", () => {
66
+ expect(() => JSON.parse(sfdxProjectContent)).not.toThrow();
67
+ });
68
+
69
+ it("should have correct XML declaration", () => {
70
+ // sfdx-project.json doesn't have XML declaration, it's pure JSON
71
+ expect(sfdxProjectContent.trim().startsWith("{")).toBe(true);
72
+ });
73
+ });
74
+
75
+ describe("packageDirectories Verification", () => {
76
+ it("should have packageDirectories array", () => {
77
+ expect(sfdxProjectJson).toHaveProperty("packageDirectories");
78
+ expect(Array.isArray(sfdxProjectJson.packageDirectories)).toBe(true);
79
+ });
80
+
81
+ it("should have force-app as package path", () => {
82
+ const packageDirs = sfdxProjectJson.packageDirectories as Array<Record<string, unknown>>;
83
+ expect(packageDirs[0]).toHaveProperty("path", "force-app");
84
+ });
85
+
86
+ it("should have default=true for force-app package", () => {
87
+ const packageDirs = sfdxProjectJson.packageDirectories as Array<Record<string, unknown>>;
88
+ expect(packageDirs[0]).toHaveProperty("default", true);
89
+ });
90
+
91
+ it("should have package name SalesforceEnterprise", () => {
92
+ const packageDirs = sfdxProjectJson.packageDirectories as Array<Record<string, unknown>>;
93
+ expect(packageDirs[0]).toHaveProperty("package", "SalesforceEnterprise");
94
+ });
95
+
96
+ it("should have versionName", () => {
97
+ const packageDirs = sfdxProjectJson.packageDirectories as Array<Record<string, unknown>>;
98
+ expect(packageDirs[0]).toHaveProperty("versionName", "ver 1.0");
99
+ });
100
+
101
+ it("should have versionNumber", () => {
102
+ const packageDirs = sfdxProjectJson.packageDirectories as Array<Record<string, unknown>>;
103
+ expect(packageDirs[0]).toHaveProperty("versionNumber", "1.0.0.NEXT");
104
+ });
105
+ });
106
+
107
+ describe("Project Properties Verification", () => {
108
+ it("should have name as SalesforceEnterprise", () => {
109
+ expect(sfdxProjectJson).toHaveProperty("name", "SalesforceEnterprise");
110
+ });
111
+
112
+ it("should have namespace as empty string", () => {
113
+ expect(sfdxProjectJson).toHaveProperty("namespace", "");
114
+ });
115
+
116
+ it("should have sfdcLoginUrl as https://login.salesforce.com", () => {
117
+ expect(sfdxProjectJson).toHaveProperty("sfdcLoginUrl", "https://login.salesforce.com");
118
+ });
119
+
120
+ it("should have singlePackage=false", () => {
121
+ expect(sfdxProjectJson).toHaveProperty("singlePackage", false);
122
+ });
123
+
124
+ it("should have sourceApiVersion as 59.0", () => {
125
+ expect(sfdxProjectJson).toHaveProperty("sourceApiVersion", "59.0");
126
+ });
127
+ });
128
+
129
+ describe("Complete JSON Structure", () => {
130
+ it("should have all required properties", () => {
131
+ expect(sfdxProjectJson).toHaveProperty("packageDirectories");
132
+ expect(sfdxProjectJson).toHaveProperty("name");
133
+ expect(sfdxProjectJson).toHaveProperty("namespace");
134
+ expect(sfdxProjectJson).toHaveProperty("sfdcLoginUrl");
135
+ expect(sfdxProjectJson).toHaveProperty("singlePackage");
136
+ expect(sfdxProjectJson).toHaveProperty("sourceApiVersion");
137
+ });
138
+
139
+ it("should match expected structure", () => {
140
+ expect(sfdxProjectContent).toContain('"packageDirectories"');
141
+ expect(sfdxProjectContent).toContain('"path": "force-app"');
142
+ expect(sfdxProjectContent).toContain('"default": true');
143
+ expect(sfdxProjectContent).toContain('"package": "SalesforceEnterprise"');
144
+ expect(sfdxProjectContent).toContain('"versionName": "ver 1.0"');
145
+ expect(sfdxProjectContent).toContain('"versionNumber": "1.0.0.NEXT"');
146
+ expect(sfdxProjectContent).toContain('"name": "SalesforceEnterprise"');
147
+ expect(sfdxProjectContent).toContain('"namespace": ""');
148
+ expect(sfdxProjectContent).toContain('"sfdcLoginUrl": "https://login.salesforce.com"');
149
+ expect(sfdxProjectContent).toContain('"singlePackage": false');
150
+ expect(sfdxProjectContent).toContain('"sourceApiVersion": "59.0"');
151
+ });
152
+ });
153
+ });
154
+
155
+ // =========================================================================
156
+ // PACKAGE.XML DETECTION AND COMPONENT LISTING
157
+ // =========================================================================
158
+
159
+ describe("package.xml Detection and Component Listing", () => {
160
+ let packageXmlContent: string;
161
+
162
+ beforeAll(() => {
163
+ const packageXmlPath = path.join(SALESFORCE_ENTERPRISE_PATH, "force-app/main/default/package.xml");
164
+ packageXmlContent = fs.readFileSync(packageXmlPath, "utf-8");
165
+ });
166
+
167
+ describe("File Detection", () => {
168
+ it("should have correct XML declaration", () => {
169
+ expect(packageXmlContent).toContain('<?xml version="1.0" encoding="UTF-8" standalone="yes"?>');
170
+ });
171
+
172
+ it("should have Package as root element", () => {
173
+ expect(packageXmlContent).toContain("<Package xmlns=");
174
+ expect(packageXmlContent).toContain("http://soap.sforce.com/2006/04/metadata");
175
+ });
176
+ });
177
+
178
+ describe("Component Types Detection", () => {
179
+ it("should have ApexClass type with 9 members", () => {
180
+ expect(packageXmlContent).toContain("<name>ApexClass</name>");
181
+
182
+ // Verify all 9 ApexClass members
183
+ const apexClasses = [
184
+ "AccountController",
185
+ "AccountControllerTest",
186
+ "OpportunityBatch",
187
+ "OpportunityBatchTest",
188
+ "OpportunityTriggerHandler",
189
+ "OpportunityTriggerHelper",
190
+ "Logger",
191
+ "TriggerManagement",
192
+ "SessionManagement"
193
+ ];
194
+
195
+ for (const apexClass of apexClasses) {
196
+ expect(packageXmlContent).toContain(`<members>${apexClass}</members>`);
197
+ }
198
+ });
199
+
200
+ it("should have ApexTrigger type with 1 member", () => {
201
+ expect(packageXmlContent).toContain("<name>ApexTrigger</name>");
202
+ expect(packageXmlContent).toContain("<members>OpportunityTrigger</members>");
203
+ });
204
+
205
+ it("should have LightningComponentBundle type with 2 members", () => {
206
+ expect(packageXmlContent).toContain("<name>LightningComponentBundle</name>");
207
+ expect(packageXmlContent).toContain("<members>accountList</members>");
208
+ expect(packageXmlContent).toContain("<members>opportunityCard</members>");
209
+ });
210
+
211
+ it("should have Flow type with 2 members", () => {
212
+ expect(packageXmlContent).toContain("<name>Flow</name>");
213
+ expect(packageXmlContent).toContain("<members>Account_Create</members>");
214
+ expect(packageXmlContent).toContain("<members>Opportunity_Update</members>");
215
+ });
216
+
217
+ it("should have CustomObject type with 1 member", () => {
218
+ expect(packageXmlContent).toContain("<name>CustomObject</name>");
219
+ expect(packageXmlContent).toContain("<members>CustomObject__c</members>");
220
+ });
221
+
222
+ it("should have Profile type with 1 member", () => {
223
+ expect(packageXmlContent).toContain("<name>Profile</name>");
224
+ expect(packageXmlContent).toContain("<members>Admin</members>");
225
+ });
226
+
227
+ it("should have PermissionSet type with 1 member", () => {
228
+ expect(packageXmlContent).toContain("<name>PermissionSet</name>");
229
+ expect(packageXmlContent).toContain("<members>SalesRepresentative</members>");
230
+ });
231
+ });
232
+
233
+ describe("Component Count Verification", () => {
234
+ it("should have 7 type definitions", () => {
235
+ const typeMatches = packageXmlContent.match(/<name>\w+<\/name>/g);
236
+ expect(typeMatches).toHaveLength(7);
237
+ });
238
+
239
+ it("should have correct component counts", () => {
240
+ // ApexClass: 9 members
241
+ expect(packageXmlContent.match(/<name>ApexClass<\/name>/g)).toHaveLength(1);
242
+
243
+ // ApexTrigger: 1 member
244
+ expect(packageXmlContent.match(/<name>ApexTrigger<\/name>/g)).toHaveLength(1);
245
+
246
+ // LightningComponentBundle: 2 members
247
+ expect(packageXmlContent.match(/<name>LightningComponentBundle<\/name>/g)).toHaveLength(1);
248
+
249
+ // Flow: 2 members
250
+ expect(packageXmlContent.match(/<name>Flow<\/name>/g)).toHaveLength(1);
251
+
252
+ // CustomObject: 1 member
253
+ expect(packageXmlContent.match(/<name>CustomObject<\/name>/g)).toHaveLength(1);
254
+
255
+ // Profile: 1 member
256
+ expect(packageXmlContent.match(/<name>Profile<\/name>/g)).toHaveLength(1);
257
+
258
+ // PermissionSet: 1 member
259
+ expect(packageXmlContent.match(/<name>PermissionSet<\/name>/g)).toHaveLength(1);
260
+ });
261
+
262
+ it("should have version 59.0", () => {
263
+ expect(packageXmlContent).toContain("<version>59.0</version>");
264
+ });
265
+ });
266
+
267
+ describe("Package XML Structure", () => {
268
+ it("should contain all types sections", () => {
269
+ expect(packageXmlContent).toContain("<types>");
270
+ expect(packageXmlContent).toContain("</types>");
271
+ });
272
+
273
+ it("should have types in correct order", () => {
274
+ const apexClassIdx = packageXmlContent.indexOf("<name>ApexClass</name>");
275
+ const apexTriggerIdx = packageXmlContent.indexOf("<name>ApexTrigger</name>");
276
+ const lwcIdx = packageXmlContent.indexOf("<name>LightningComponentBundle</name>");
277
+ const flowIdx = packageXmlContent.indexOf("<name>Flow</name>");
278
+ const customObjIdx = packageXmlContent.indexOf("<name>CustomObject</name>");
279
+ const profileIdx = packageXmlContent.indexOf("<name>Profile</name>");
280
+ const permSetIdx = packageXmlContent.indexOf("<name>PermissionSet</name>");
281
+
282
+ expect(apexClassIdx).toBeLessThan(apexTriggerIdx);
283
+ expect(apexTriggerIdx).toBeLessThan(lwcIdx);
284
+ expect(lwcIdx).toBeLessThan(flowIdx);
285
+ expect(flowIdx).toBeLessThan(customObjIdx);
286
+ expect(customObjIdx).toBeLessThan(profileIdx);
287
+ expect(profileIdx).toBeLessThan(permSetIdx);
288
+ });
289
+ });
290
+ });
291
+
292
+ // =========================================================================
293
+ // PROFILE METADATA (Admin.profile-meta.xml)
294
+ // =========================================================================
295
+
296
+ describe("Profile Metadata (Admin.profile-meta.xml)", () => {
297
+ let profileContent: string;
298
+
299
+ beforeAll(() => {
300
+ const profilePath = path.join(SALESFORCE_ENTERPRISE_PATH, "force-app/main/default/profiles/Admin.profile-meta.xml");
301
+ profileContent = fs.readFileSync(profilePath, "utf-8");
302
+ });
303
+
304
+ describe("File Detection", () => {
305
+ it("should have correct XML declaration", () => {
306
+ expect(profileContent).toContain('<?xml version="1.0" encoding="UTF-8"?>');
307
+ });
308
+
309
+ it("should have Profile as root element", () => {
310
+ expect(profileContent).toContain("<Profile xmlns=");
311
+ expect(profileContent).toContain("http://soap.sforce.com/2006/04/metadata");
312
+ });
313
+
314
+ it("should have custom=false", () => {
315
+ expect(profileContent).toContain("<custom>false</custom>");
316
+ });
317
+ });
318
+
319
+ describe("User License", () => {
320
+ it("should have userLicense as Salesforce", () => {
321
+ expect(profileContent).toContain("<userLicense>Salesforce</userLicense>");
322
+ });
323
+ });
324
+
325
+ describe("Field Permissions", () => {
326
+ it("should have 12 fieldPermissions entries", () => {
327
+ const fieldPermissionMatches = profileContent.match(/<fieldPermissions>/g);
328
+ expect(fieldPermissionMatches).toHaveLength(12);
329
+ });
330
+
331
+ it("should have field permissions for Account.Active__c", () => {
332
+ expect(profileContent).toMatch(/<fieldPermissions>[\s\S]*?<field>Account\.Active__c<\/field>[\s\S]*?<\/fieldPermissions>/);
333
+ expect(profileContent).toMatch(/<fieldPermissions>[\s\S]*?<field>Account\.Active__c<\/field>[\s\S]*?<readable>true<\/readable>[\s\S]*?<\/fieldPermissions>/);
334
+ expect(profileContent).toMatch(/<fieldPermissions>[\s\S]*?<field>Account\.Active__c<\/field>[\s\S]*?<editable>true<\/editable>[\s\S]*?<\/fieldPermissions>/);
335
+ });
336
+
337
+ it("should have field permissions for Account.CustomerPriority__c", () => {
338
+ expect(profileContent).toMatch(/<fieldPermissions>[\s\S]*?<field>Account\.CustomerPriority__c<\/field>[\s\S]*?<\/fieldPermissions>/);
339
+ });
340
+
341
+ it("should have field permissions for Account.SLA__c", () => {
342
+ expect(profileContent).toMatch(/<fieldPermissions>[\s\S]*?<field>Account\.SLA__c<\/field>[\s\S]*?<\/fieldPermissions>/);
343
+ });
344
+
345
+ it("should have field permissions for Account.Total_Opportunity_Amount__c (readable only)", () => {
346
+ expect(profileContent).toMatch(/<fieldPermissions>[\s\S]*?<field>Account\.Total_Opportunity_Amount__c<\/field>[\s\S]*?<readable>true<\/readable>[\s\S]*?<editable>false<\/editable>[\s\S]*?<\/fieldPermissions>/);
347
+ });
348
+
349
+ it("should have field permissions for Opportunity standard fields", () => {
350
+ expect(profileContent).toMatch(/<fieldPermissions>[\s\S]*?<field>Opportunity\.Amount<\/field>[\s\S]*?<\/fieldPermissions>/);
351
+ expect(profileContent).toMatch(/<fieldPermissions>[\s\S]*?<field>Opportunity\.LeadSource<\/field>[\s\S]*?<\/fieldPermissions>/);
352
+ expect(profileContent).toMatch(/<fieldPermissions>[\s\S]*?<field>Opportunity\.Probability<\/field>[\s\S]*?<\/fieldPermissions>/);
353
+ });
354
+
355
+ it("should have field permissions for CustomObject__c fields", () => {
356
+ expect(profileContent).toMatch(/<fieldPermissions>[\s\S]*?<field>CustomObject__c\.Amount__c<\/field>[\s\S]*?<\/fieldPermissions>/);
357
+ expect(profileContent).toMatch(/<fieldPermissions>[\s\S]*?<field>CustomObject__c\.Category__c<\/field>[\s\S]*?<\/fieldPermissions>/);
358
+ expect(profileContent).toMatch(/<fieldPermissions>[\s\S]*?<field>CustomObject__c\.CustomAccount__c<\/field>[\s\S]*?<\/fieldPermissions>/);
359
+ expect(profileContent).toMatch(/<fieldPermissions>[\s\S]*?<field>CustomObject__c\.IsActive__c<\/field>[\s\S]*?<\/fieldPermissions>/);
360
+ expect(profileContent).toMatch(/<fieldPermissions>[\s\S]*?<field>CustomObject__c\.Stage__c<\/field>[\s\S]*?<\/fieldPermissions>/);
361
+ });
362
+ });
363
+
364
+ describe("Object Permissions", () => {
365
+ it("should have 3 objectPermissions entries", () => {
366
+ const objectPermissionMatches = profileContent.match(/<objectPermissions>/g);
367
+ expect(objectPermissionMatches).toHaveLength(3);
368
+ });
369
+
370
+ it("should have full access to Account", () => {
371
+ expect(profileContent).toMatch(/<objectPermissions>[\s\S]*?<object>Account<\/object>[\s\S]*?<allowCreate>true<\/allowCreate>[\s\S]*?<allowDelete>true<\/allowDelete>[\s\S]*?<allowEdit>true<\/allowEdit>[\s\S]*?<allowRead>true<\/allowRead>[\s\S]*?<modifyAllRecords>true<\/modifyAllRecords>[\s\S]*?<\/objectPermissions>/);
372
+ });
373
+
374
+ it("should have full access to Opportunity", () => {
375
+ expect(profileContent).toMatch(/<objectPermissions>[\s\S]*?<object>Opportunity<\/object>[\s\S]*?<allowCreate>true<\/allowCreate>[\s\S]*?<allowDelete>true<\/allowDelete>[\s\S]*?<allowEdit>true<\/allowEdit>[\s\S]*?<allowRead>true<\/allowRead>[\s\S]*?<modifyAllRecords>true<\/modifyAllRecords>[\s\S]*?<\/objectPermissions>/);
376
+ });
377
+
378
+ it("should have full access to CustomObject__c", () => {
379
+ const customObjPermMatch = profileContent.match(/<objectPermissions>[\s\S]*?<object>CustomObject__c<\/object>[\s\S]*?<\/objectPermissions>/);
380
+ expect(customObjPermMatch).toBeTruthy();
381
+ const customObjSection = customObjPermMatch?.[0] || "";
382
+ expect(customObjSection).toContain("<allowCreate>true</allowCreate>");
383
+ expect(customObjSection).toContain("<allowDelete>true</allowDelete>");
384
+ expect(customObjSection).toContain("<allowEdit>true</allowEdit>");
385
+ expect(customObjSection).toContain("<allowRead>true</allowRead>");
386
+ expect(customObjSection).toContain("<modifyAllRecords>true</modifyAllRecords>");
387
+ });
388
+ });
389
+
390
+ describe("Class Accesses", () => {
391
+ it("should have 5 classAccesses entries", () => {
392
+ const classAccessMatches = profileContent.match(/<classAccesses>/g);
393
+ expect(classAccessMatches).toHaveLength(5);
394
+ });
395
+
396
+ it("should enable AccountController", () => {
397
+ expect(profileContent).toMatch(/<classAccesses>[\s\S]*?<apexClass>AccountController<\/apexClass>[\s\S]*?<enabled>true<\/enabled>[\s\S]*?<\/classAccesses>/);
398
+ });
399
+
400
+ it("should enable OpportunityBatch", () => {
401
+ expect(profileContent).toMatch(/<classAccesses>[\s\S]*?<apexClass>OpportunityBatch<\/apexClass>[\s\S]*?<enabled>true<\/enabled>[\s\S]*?<\/classAccesses>/);
402
+ });
403
+
404
+ it("should enable OpportunityTriggerHandler", () => {
405
+ expect(profileContent).toMatch(/<classAccesses>[\s\S]*?<apexClass>OpportunityTriggerHandler<\/apexClass>[\s\S]*?<enabled>true<\/enabled>[\s\S]*?<\/classAccesses>/);
406
+ });
407
+
408
+ it("should enable Logger", () => {
409
+ expect(profileContent).toMatch(/<classAccesses>[\s\S]*?<apexClass>Logger<\/apexClass>[\s\S]*?<enabled>true<\/enabled>[\s\S]*?<\/classAccesses>/);
410
+ });
411
+
412
+ it("should enable TriggerManagement", () => {
413
+ expect(profileContent).toMatch(/<classAccesses>[\s\S]*?<apexClass>TriggerManagement<\/apexClass>[\s\S]*?<enabled>true<\/enabled>[\s\S]*?<\/classAccesses>/);
414
+ });
415
+ });
416
+
417
+ describe("Flow Accesses", () => {
418
+ it("should have 2 flowAccesses entries", () => {
419
+ const flowAccessMatches = profileContent.match(/<flowAccesses>/g);
420
+ expect(flowAccessMatches).toHaveLength(2);
421
+ });
422
+
423
+ it("should enable Account_Create flow", () => {
424
+ expect(profileContent).toMatch(/<flowAccesses>[\s\S]*?<flow>Account_Create<\/flow>[\s\S]*?<enabled>true<\/enabled>[\s\S]*?<\/flowAccesses>/);
425
+ });
426
+
427
+ it("should enable Opportunity_Update flow", () => {
428
+ expect(profileContent).toMatch(/<flowAccesses>[\s\S]*?<flow>Opportunity_Update<\/flow>[\s\S]*?<enabled>true<\/enabled>[\s\S]*?<\/flowAccesses>/);
429
+ });
430
+ });
431
+
432
+ describe("Layout Assignments", () => {
433
+ it("should have 3 layoutAssignments", () => {
434
+ const layoutMatches = profileContent.match(/<layoutAssignments>/g);
435
+ expect(layoutMatches).toHaveLength(3);
436
+ });
437
+
438
+ it("should have Account layout assignment", () => {
439
+ expect(profileContent).toContain("<layout>Account-Account Layout</layout>");
440
+ });
441
+
442
+ it("should have Opportunity layout assignment", () => {
443
+ expect(profileContent).toContain("<layout>Opportunity-Opportunity Layout</layout>");
444
+ });
445
+
446
+ it("should have CustomObject__c layout assignment", () => {
447
+ expect(profileContent).toContain("<layout>CustomObject__c-Custom Object Layout</layout>");
448
+ });
449
+ });
450
+
451
+ describe("Tab Visibilities", () => {
452
+ it("should have 3 tabVisibilities entries", () => {
453
+ const tabMatches = profileContent.match(/<tabVisibilities>/g);
454
+ expect(tabMatches).toHaveLength(3);
455
+ });
456
+
457
+ it("should have Account tab visibility", () => {
458
+ expect(profileContent).toMatch(/<tabVisibilities>[\s\S]*?<tab>standard-Account<\/tab>[\s\S]*?<visibility>Available<\/visibility>[\s\S]*?<\/tabVisibilities>/);
459
+ });
460
+
461
+ it("should have Opportunity tab visibility", () => {
462
+ expect(profileContent).toMatch(/<tabVisibilities>[\s\S]*?<tab>standard-Opportunity<\/tab>[\s\S]*?<visibility>Available<\/visibility>[\s\S]*?<\/tabVisibilities>/);
463
+ });
464
+
465
+ it("should have CustomObject__c tab visibility", () => {
466
+ expect(profileContent).toMatch(/<tabVisibilities>[\s\S]*?<tab>CustomObject__c<\/tab>[\s\S]*?<visibility>Available<\/visibility>[\s\S]*?<\/tabVisibilities>/);
467
+ });
468
+ });
469
+
470
+ describe("Record Type Visibilities", () => {
471
+ it("should have 2 recordTypeVisibilities entries", () => {
472
+ const recordTypeMatches = profileContent.match(/<recordTypeVisibilities>/g);
473
+ expect(recordTypeMatches).toHaveLength(2);
474
+ });
475
+
476
+ it("should have Customer_Account record type visibility", () => {
477
+ expect(profileContent).toMatch(/<recordTypeVisibilities>[\s\S]*?<recordType>Account\.Customer_Account<\/recordType>[\s\S]*?<default>true<\/default>[\s\S]*?<visible>true<\/visible>[\s\S]*?<\/recordTypeVisibilities>/);
478
+ });
479
+
480
+ it("should have PersonAccount record type visibility", () => {
481
+ expect(profileContent).toMatch(/<recordTypeVisibilities>[\s\S]*?<recordType>Account\.PersonAccount<\/recordType>[\s\S]*?<default>false<\/default>[\s\S]*?<visible>false<\/visible>[\s\S]*?<\/recordTypeVisibilities>/);
482
+ });
483
+ });
484
+ });
485
+
486
+ // =========================================================================
487
+ // PERMISSION SET METADATA (SalesRepresentative.permissionset-meta.xml)
488
+ // =========================================================================
489
+
490
+ describe("PermissionSet Metadata (SalesRepresentative.permissionset-meta.xml)", () => {
491
+ let permSetContent: string;
492
+
493
+ beforeAll(() => {
494
+ const permSetPath = path.join(SALESFORCE_ENTERPRISE_PATH, "force-app/main/default/permissionsets/SalesRepresentative.permissionset-meta.xml");
495
+ permSetContent = fs.readFileSync(permSetPath, "utf-8");
496
+ });
497
+
498
+ describe("File Detection", () => {
499
+ it("should have correct XML declaration", () => {
500
+ expect(permSetContent).toContain('<?xml version="1.0" encoding="UTF-8"?>');
501
+ });
502
+
503
+ it("should have PermissionSet as root element", () => {
504
+ expect(permSetContent).toContain("<PermissionSet xmlns=");
505
+ expect(permSetContent).toContain("http://salesforce.com/2006/04/metadata");
506
+ });
507
+
508
+ it("should have custom=false", () => {
509
+ expect(permSetContent).toContain("<custom>false</custom>");
510
+ });
511
+ });
512
+
513
+ describe("Label and Description", () => {
514
+ it("should have label Sales Representative", () => {
515
+ expect(permSetContent).toContain("<label>Sales Representative</label>");
516
+ });
517
+
518
+ it("should have description", () => {
519
+ expect(permSetContent).toContain("<description>Permission set for Sales Representatives</description>");
520
+ });
521
+ });
522
+
523
+ describe("Field Permissions", () => {
524
+ it("should have 15 fieldPermissions entries", () => {
525
+ const fieldPermissionMatches = permSetContent.match(/<fieldPermissions>/g);
526
+ expect(fieldPermissionMatches).toHaveLength(15);
527
+ });
528
+
529
+ it("should have field permissions for Account standard fields", () => {
530
+ expect(permSetContent).toMatch(/<fieldPermissions>[\s\S]*?<field>Account\.Active__c<\/field>[\s\S]*?<\/fieldPermissions>/);
531
+ expect(permSetContent).toMatch(/<fieldPermissions>[\s\S]*?<field>Account\.AnnualRevenue<\/field>[\s\S]*?<\/fieldPermissions>/);
532
+ expect(permSetContent).toMatch(/<fieldPermissions>[\s\S]*?<field>Account\.BillingCity<\/field>[\s\S]*?<\/fieldPermissions>/);
533
+ expect(permSetContent).toMatch(/<fieldPermissions>[\s\S]*?<field>Account\.BillingState<\/field>[\s\S]*?<\/fieldPermissions>/);
534
+ expect(permSetContent).toMatch(/<fieldPermissions>[\s\S]*?<field>Account\.Industry<\/field>[\s\S]*?<\/fieldPermissions>/);
535
+ expect(permSetContent).toMatch(/<fieldPermissions>[\s\S]*?<field>Account\.Name<\/field>[\s\S]*?<\/fieldPermissions>/);
536
+ });
537
+
538
+ it("should have field permissions for Opportunity standard fields", () => {
539
+ expect(permSetContent).toMatch(/<fieldPermissions>[\s\S]*?<field>Opportunity\.Amount<\/field>[\s\S]*?<\/fieldPermissions>/);
540
+ expect(permSetContent).toMatch(/<fieldPermissions>[\s\S]*?<field>Opportunity\.CloseDate<\/field>[\s\S]*?<\/fieldPermissions>/);
541
+ expect(permSetContent).toMatch(/<fieldPermissions>[\s\S]*?<field>Opportunity\.Description<\/field>[\s\S]*?<\/fieldPermissions>/);
542
+ expect(permSetContent).toMatch(/<fieldPermissions>[\s\S]*?<field>Opportunity\.StageName<\/field>[\s\S]*?<\/fieldPermissions>/);
543
+ });
544
+
545
+ it("should have field permissions for CustomObject__c fields", () => {
546
+ expect(permSetContent).toMatch(/<fieldPermissions>[\s\S]*?<field>CustomObject__c\.Amount__c<\/field>[\s\S]*?<\/fieldPermissions>/);
547
+ expect(permSetContent).toMatch(/<fieldPermissions>[\s\S]*?<field>CustomObject__c\.Category__c<\/field>[\s\S]*?<\/fieldPermissions>/);
548
+ expect(permSetContent).toMatch(/<fieldPermissions>[\s\S]*?<field>CustomObject__c\.IsActive__c<\/field>[\s\S]*?<\/fieldPermissions>/);
549
+ expect(permSetContent).toMatch(/<fieldPermissions>[\s\S]*?<field>CustomObject__c\.Name__c<\/field>[\s\S]*?<\/fieldPermissions>/);
550
+ expect(permSetContent).toMatch(/<fieldPermissions>[\s\S]*?<field>CustomObject__c\.Stage__c<\/field>[\s\S]*?<\/fieldPermissions>/);
551
+ });
552
+ });
553
+
554
+ describe("Object Permissions", () => {
555
+ it("should have 3 objectPermissions entries", () => {
556
+ const objectPermissionMatches = permSetContent.match(/<objectPermissions>/g);
557
+ expect(objectPermissionMatches).toHaveLength(3);
558
+ });
559
+
560
+ it("should have correct permissions for Account (limited)", () => {
561
+ expect(permSetContent).toMatch(/<objectPermissions>[\s\S]*?<object>Account<\/object>[\s\S]*?<allowCreate>true<\/allowCreate>[\s\S]*?<allowDelete>false<\/allowDelete>[\s\S]*?<allowEdit>true<\/allowEdit>[\s\S]*?<allowRead>true<\/allowRead>[\s\S]*?<modifyAllRecords>false<\/modifyAllRecords>[\s\S]*?<viewAllRecords>false<\/viewAllRecords>[\s\S]*?<\/objectPermissions>/);
562
+ });
563
+
564
+ it("should have correct permissions for Opportunity (limited)", () => {
565
+ expect(permSetContent).toMatch(/<objectPermissions>[\s\S]*?<object>Opportunity<\/object>[\s\S]*?<allowCreate>true<\/allowCreate>[\s\S]*?<allowDelete>false<\/allowDelete>[\s\S]*?<allowEdit>true<\/allowEdit>[\s\S]*?<allowRead>true<\/allowRead>[\s\S]*?<modifyAllRecords>false<\/modifyAllRecords>[\s\S]*?<viewAllRecords>false<\/viewAllRecords>[\s\S]*?<\/objectPermissions>/);
566
+ });
567
+
568
+ it("should have correct permissions for CustomObject__c (limited)", () => {
569
+ const customObjPermMatch = permSetContent.match(/<objectPermissions>[\s\S]*?<object>CustomObject__c<\/object>[\s\S]*?<\/objectPermissions>/);
570
+ expect(customObjPermMatch).toBeTruthy();
571
+ const customObjSection = customObjPermMatch?.[0] || "";
572
+ expect(customObjSection).toContain("<allowCreate>true</allowCreate>");
573
+ expect(customObjSection).toContain("<allowDelete>false</allowDelete>");
574
+ expect(customObjSection).toContain("<allowEdit>true</allowEdit>");
575
+ expect(customObjSection).toContain("<allowRead>true</allowRead>");
576
+ expect(customObjSection).toContain("<modifyAllRecords>false</modifyAllRecords>");
577
+ expect(customObjSection).toContain("<viewAllRecords>false</viewAllRecords>");
578
+ });
579
+ });
580
+
581
+ describe("Class Accesses", () => {
582
+ it("should have 1 classAccesses entry", () => {
583
+ const classAccessMatches = permSetContent.match(/<classAccesses>/g);
584
+ expect(classAccessMatches).toHaveLength(1);
585
+ });
586
+
587
+ it("should enable AccountController", () => {
588
+ expect(permSetContent).toMatch(/<classAccesses>[\s\S]*?<apexClass>AccountController<\/apexClass>[\s\S]*?<enabled>true<\/enabled>[\s\S]*?<\/classAccesses>/);
589
+ });
590
+ });
591
+
592
+ describe("Flow Accesses", () => {
593
+ it("should have 1 flowAccesses entry", () => {
594
+ const flowAccessMatches = permSetContent.match(/<flowAccesses>/g);
595
+ expect(flowAccessMatches).toHaveLength(1);
596
+ });
597
+
598
+ it("should enable Account_Create flow", () => {
599
+ expect(permSetContent).toMatch(/<flowAccesses>[\s\S]*?<flow>Account_Create<\/flow>[\s\S]*?<enabled>true<\/enabled>[\s\S]*?<\/flowAccesses>/);
600
+ });
601
+ });
602
+
603
+ describe("User Permissions", () => {
604
+ it("should have 2 userPermissions entries", () => {
605
+ const userPermMatches = permSetContent.match(/<userPermissions>/g);
606
+ expect(userPermMatches).toHaveLength(2);
607
+ });
608
+
609
+ it("should have ViewReadonlyFields permission enabled", () => {
610
+ expect(permSetContent).toMatch(/<userPermissions>[\s\S]*?<name>ViewReadonlyFields<\/name>[\s\S]*?<enabled>true<\/enabled>[\s\S]*?<\/userPermissions>/);
611
+ });
612
+
613
+ it("should have AllowViewKnowledge permission enabled", () => {
614
+ expect(permSetContent).toMatch(/<userPermissions>[\s\S]*?<name>AllowViewKnowledge<\/name>[\s\S]*?<enabled>true<\/enabled>[\s\S]*?<\/userPermissions>/);
615
+ });
616
+ });
617
+
618
+ describe("hasActivationRequired", () => {
619
+ it("should have hasActivationRequired as false", () => {
620
+ expect(permSetContent).toContain("<hasActivationRequired>false</hasActivationRequired>");
621
+ });
622
+ });
623
+ });
624
+
625
+ // =========================================================================
626
+ // FULL SFDX PROJECT INTEGRATION (END-TO-END)
627
+ // =========================================================================
628
+
629
+ describe("Full SFDX Project Integration (End-to-End)", () => {
630
+
631
+ describe("SFDX File Structure Completeness", () => {
632
+ it("should have all required SFDX files", () => {
633
+ const requiredFiles = [
634
+ "sfdx-project.json",
635
+ "force-app/main/default/package.xml",
636
+ "force-app/main/default/profiles/Admin.profile-meta.xml",
637
+ "force-app/main/default/permissionsets/SalesRepresentative.permissionset-meta.xml"
638
+ ];
639
+
640
+ for (const file of requiredFiles) {
641
+ const filePath = path.join(SALESFORCE_ENTERPRISE_PATH, file);
642
+ expect(fs.existsSync(filePath)).toBe(true);
643
+ }
644
+ });
645
+
646
+ it("should have matching sourceApiVersion across SFDX files", () => {
647
+ const sfdxProjectPath = path.join(SALESFORCE_ENTERPRISE_PATH, "sfdx-project.json");
648
+ const packageXmlPath = path.join(SALESFORCE_ENTERPRISE_PATH, "force-app/main/default/package.xml");
649
+
650
+ const sfdxProjectContent = fs.readFileSync(sfdxProjectPath, "utf-8");
651
+ const packageXmlContent = fs.readFileSync(packageXmlPath, "utf-8");
652
+
653
+ const sfdxProjectJson = JSON.parse(sfdxProjectContent);
654
+ expect(sfdxProjectJson.sourceApiVersion).toBe("59.0");
655
+ expect(packageXmlContent).toContain("<version>59.0</version>");
656
+ });
657
+ });
658
+
659
+ describe("Component Cross-Reference Validation", () => {
660
+ let sfdxProjectContent: string;
661
+ let packageXmlContent: string;
662
+ let profileContent: string;
663
+ let permSetContent: string;
664
+
665
+ beforeAll(() => {
666
+ sfdxProjectContent = fs.readFileSync(path.join(SALESFORCE_ENTERPRISE_PATH, "sfdx-project.json"), "utf-8");
667
+ packageXmlContent = fs.readFileSync(path.join(SALESFORCE_ENTERPRISE_PATH, "force-app/main/default/package.xml"), "utf-8");
668
+ profileContent = fs.readFileSync(path.join(SALESFORCE_ENTERPRISE_PATH, "force-app/main/default/profiles/Admin.profile-meta.xml"), "utf-8");
669
+ permSetContent = fs.readFileSync(path.join(SALESFORCE_ENTERPRISE_PATH, "force-app/main/default/permissionsets/SalesRepresentative.permissionset-meta.xml"), "utf-8");
670
+ });
671
+
672
+ it("should reference Apex classes consistently", () => {
673
+ // package.xml lists ApexClass members
674
+ expect(packageXmlContent).toContain("<members>AccountController</members>");
675
+
676
+ // Profile enables AccountController
677
+ expect(profileContent).toContain("<apexClass>AccountController</apexClass>");
678
+
679
+ // PermissionSet enables AccountController
680
+ expect(permSetContent).toContain("<apexClass>AccountController</apexClass>");
681
+ });
682
+
683
+ it("should reference flows consistently", () => {
684
+ // package.xml lists Flow members
685
+ expect(packageXmlContent).toContain("<members>Account_Create</members>");
686
+ expect(packageXmlContent).toContain("<members>Opportunity_Update</members>");
687
+
688
+ // Profile enables both flows
689
+ expect(profileContent).toContain("<flow>Account_Create</flow>");
690
+ expect(profileContent).toContain("<flow>Opportunity_Update</flow>");
691
+
692
+ // PermissionSet enables Account_Create
693
+ expect(permSetContent).toContain("<flow>Account_Create</flow>");
694
+ });
695
+
696
+ it("should reference CustomObject__c consistently", () => {
697
+ // package.xml has CustomObject with CustomObject__c
698
+ expect(packageXmlContent).toContain("<members>CustomObject__c</members>");
699
+
700
+ // Profile has object permissions for CustomObject__c
701
+ expect(profileContent).toMatch(/<objectPermissions>[\s\S]*?<object>CustomObject__c<\/object>[\s\S]*?<\/objectPermissions>/);
702
+
703
+ // PermissionSet has object permissions for CustomObject__c
704
+ expect(permSetContent).toMatch(/<objectPermissions>[\s\S]*?<object>CustomObject__c<\/object>[\s\S]*?<\/objectPermissions>/);
705
+
706
+ // Profile has field permissions for CustomObject__c fields
707
+ expect(profileContent).toContain("<field>CustomObject__c.Amount__c</field>");
708
+ expect(profileContent).toContain("<field>CustomObject__c.Category__c</field>");
709
+
710
+ // PermissionSet has field permissions for CustomObject__c fields
711
+ expect(permSetContent).toContain("<field>CustomObject__c.Amount__c</field>");
712
+ expect(permSetContent).toContain("<field>CustomObject__c.Category__c</field>");
713
+ });
714
+
715
+ it("should have Profile listed in package.xml", () => {
716
+ expect(packageXmlContent).toMatch(/<types>[\s\S]*?<members>Admin<\/members>[\s\S]*?<name>Profile<\/name>[\s\S]*?<\/types>/);
717
+ });
718
+
719
+ it("should have PermissionSet listed in package.xml", () => {
720
+ expect(packageXmlContent).toMatch(/<types>[\s\S]*?<members>SalesRepresentative<\/members>[\s\S]*?<name>PermissionSet<\/name>[\s\S]*?<\/types>/);
721
+ });
722
+ });
723
+
724
+ describe("Metadata Component File Existence", () => {
725
+ it("should have all Apex class files", () => {
726
+ const apexClasses = [
727
+ "AccountController.cls",
728
+ "AccountControllerTest.cls",
729
+ "OpportunityBatch.cls",
730
+ "OpportunityBatchTest.cls",
731
+ "OpportunityTriggerHandler.cls",
732
+ "OpportunityTriggerHelper.cls",
733
+ "Logger.cls",
734
+ "TriggerManagement.cls"
735
+ ];
736
+
737
+ for (const apexClass of apexClasses) {
738
+ const filePath = path.join(SALESFORCE_ENTERPRISE_PATH, "force-app/main/default/classes", apexClass);
739
+ expect(fs.existsSync(filePath)).toBe(true);
740
+ }
741
+ });
742
+
743
+ it("should have Apex trigger file", () => {
744
+ const triggerPath = path.join(SALESFORCE_ENTERPRISE_PATH, "force-app/main/default/triggers/OpportunityTrigger.trigger");
745
+ expect(fs.existsSync(triggerPath)).toBe(true);
746
+ });
747
+
748
+ it("should have LWC component files", () => {
749
+ const lwcComponents = ["accountList", "opportunityCard"];
750
+
751
+ for (const component of lwcComponents) {
752
+ const componentDir = path.join(SALESFORCE_ENTERPRISE_PATH, "force-app/main/default/lwc", component);
753
+ expect(fs.existsSync(path.join(componentDir, `${component}.js`))).toBe(true);
754
+ expect(fs.existsSync(path.join(componentDir, `${component}.html`))).toBe(true);
755
+ expect(fs.existsSync(path.join(componentDir, `${component}.css`))).toBe(true);
756
+ expect(fs.existsSync(path.join(componentDir, `${component}.js-meta.xml`))).toBe(true);
757
+ }
758
+ });
759
+
760
+ it("should have Flow metadata files", () => {
761
+ const flows = ["Account_Create.flow-meta.xml", "Opportunity_Update.flow-meta.xml"];
762
+
763
+ for (const flow of flows) {
764
+ const flowPath = path.join(SALESFORCE_ENTERPRISE_PATH, "force-app/main/default/flows", flow);
765
+ expect(fs.existsSync(flowPath)).toBe(true);
766
+ }
767
+ });
768
+
769
+ it("should have CustomObject metadata file", () => {
770
+ const objectPath = path.join(SALESFORCE_ENTERPRISE_PATH, "force-app/main/default/objects/CustomObject__c/CustomObject__c.object-meta.xml");
771
+ expect(fs.existsSync(objectPath)).toBe(true);
772
+ });
773
+ });
774
+
775
+ describe("SFDX API Version Consistency", () => {
776
+ it("should have consistent API version across project", () => {
777
+ const sfdxProjectPath = path.join(SALESFORCE_ENTERPRISE_PATH, "sfdx-project.json");
778
+ const packageXmlPath = path.join(SALESFORCE_ENTERPRISE_PATH, "force-app/main/default/package.xml");
779
+
780
+ const sfdxProjectContent = fs.readFileSync(sfdxProjectPath, "utf-8");
781
+ const packageXmlContent = fs.readFileSync(packageXmlPath, "utf-8");
782
+
783
+ const sfdxProjectJson = JSON.parse(sfdxProjectContent);
784
+ const sourceApiVersion = sfdxProjectJson.sourceApiVersion;
785
+
786
+ // package.xml version
787
+ expect(packageXmlContent).toContain(`<version>${sourceApiVersion}</version>`);
788
+
789
+ // Flow API versions
790
+ const accountCreateFlowPath = path.join(SALESFORCE_ENTERPRISE_PATH, "force-app/main/default/flows/Account_Create.flow-meta.xml");
791
+ const opportunityUpdateFlowPath = path.join(SALESFORCE_ENTERPRISE_PATH, "force-app/main/default/flows/Opportunity_Update.flow-meta.xml");
792
+ const accountCreateFlowContent = fs.readFileSync(accountCreateFlowPath, "utf-8");
793
+ const opportunityUpdateFlowContent = fs.readFileSync(opportunityUpdateFlowPath, "utf-8");
794
+
795
+ expect(accountCreateFlowContent).toContain(`<apiVersion>${sourceApiVersion}</apiVersion>`);
796
+ expect(opportunityUpdateFlowContent).toContain(`<apiVersion>${sourceApiVersion}</apiVersion>`);
797
+ });
798
+ });
799
+ });
800
+
801
+ // =========================================================================
802
+ // UNIT TESTS - SFDX Metadata XML parsing patterns
803
+ // =========================================================================
804
+
805
+ describe("Unit: SFDX Metadata XML parsing patterns", () => {
806
+
807
+ describe("sfdx-project.json parsing", () => {
808
+ it("should parse valid sfdx-project.json structure", () => {
809
+ const sfdxJson = {
810
+ packageDirectories: [{
811
+ path: "force-app",
812
+ default: true,
813
+ package: "TestPackage",
814
+ versionName: "ver 1.0",
815
+ versionNumber: "1.0.0.NEXT"
816
+ }],
817
+ name: "TestProject",
818
+ namespace: "",
819
+ sfdcLoginUrl: "https://login.salesforce.com",
820
+ singlePackage: false,
821
+ sourceApiVersion: "59.0"
822
+ };
823
+
824
+ expect(sfdxJson.packageDirectories[0].path).toBe("force-app");
825
+ expect(sfdxJson.packageDirectories[0].default).toBe(true);
826
+ expect(sfdxJson.name).toBe("TestProject");
827
+ expect(sfdxJson.sourceApiVersion).toBe("59.0");
828
+ });
829
+
830
+ it("should handle namespace correctly", () => {
831
+ const sfdxJson = {
832
+ namespace: "my_namespace"
833
+ };
834
+ expect(sfdxJson.namespace).toBe("my_namespace");
835
+ });
836
+
837
+ it("should handle empty namespace", () => {
838
+ const sfdxJson = {
839
+ namespace: ""
840
+ };
841
+ expect(sfdxJson.namespace).toBe("");
842
+ });
843
+ });
844
+
845
+ describe("package.xml parsing", () => {
846
+ it("should parse package.xml type structure", () => {
847
+ const packageXml = `<?xml version="1.0" encoding="UTF-8"?>
848
+ <Package xmlns="http://soap.sforce.com/2006/04/metadata">
849
+ <types>
850
+ <members>TestClass</members>
851
+ <name>ApexClass</name>
852
+ </types>
853
+ <version>59.0</version>
854
+ </Package>`;
855
+
856
+ expect(packageXml).toContain("<name>ApexClass</name>");
857
+ expect(packageXml).toContain("<members>TestClass</members>");
858
+ expect(packageXml).toContain("<version>59.0</version>");
859
+ });
860
+
861
+ it("should parse multiple type members", () => {
862
+ const packageXml = `<?xml version="1.0" encoding="UTF-8"?>
863
+ <Package xmlns="http://soap.sforce.com/2006/04/metadata">
864
+ <types>
865
+ <members>Class1</members>
866
+ <members>Class2</members>
867
+ <members>Class3</members>
868
+ <name>ApexClass</name>
869
+ </types>
870
+ </Package>`;
871
+
872
+ const memberMatches = packageXml.match(/<members>\w+<\/members>/g);
873
+ expect(memberMatches).toHaveLength(3);
874
+ });
875
+ });
876
+
877
+ describe("Profile XML parsing", () => {
878
+ it("should parse fieldPermissions structure", () => {
879
+ const profileXml = `<?xml version="1.0" encoding="UTF-8"?>
880
+ <Profile xmlns="http://soap.sforce.com/2006/04/metadata">
881
+ <fieldPermissions>
882
+ <field>Account.Name</field>
883
+ <readable>true</readable>
884
+ <editable>true</editable>
885
+ </fieldPermissions>
886
+ </Profile>`;
887
+
888
+ expect(profileXml).toMatch(/<fieldPermissions>[\s\S]*?<field>Account\.Name<\/field>[\s\S]*?<\/fieldPermissions>/);
889
+ expect(profileXml).toContain("<readable>true</readable>");
890
+ expect(profileXml).toContain("<editable>true</editable>");
891
+ });
892
+
893
+ it("should parse objectPermissions structure", () => {
894
+ const profileXml = `<?xml version="1.0" encoding="UTF-8"?>
895
+ <Profile xmlns="http://soap.sforce.com/2006/04/metadata">
896
+ <objectPermissions>
897
+ <allowCreate>true</allowCreate>
898
+ <allowDelete>true</allowDelete>
899
+ <allowEdit>true</allowEdit>
900
+ <allowRead>true</allowRead>
901
+ <modifyAllRecords>true</modifyAllRecords>
902
+ <object>Account</object>
903
+ </objectPermissions>
904
+ </Profile>`;
905
+
906
+ expect(profileXml).toMatch(/<objectPermissions>[\s\S]*?<object>Account<\/object>[\s\S]*?<\/objectPermissions>/);
907
+ expect(profileXml).toContain("<allowCreate>true</allowCreate>");
908
+ expect(profileXml).toContain("<modifyAllRecords>true</modifyAllRecords>");
909
+ });
910
+
911
+ it("should parse classAccesses structure", () => {
912
+ const profileXml = `<?xml version="1.0" encoding="UTF-8"?>
913
+ <Profile xmlns="http://soap.sforce.com/2006/04/metadata">
914
+ <classAccesses>
915
+ <apexClass>TestClass</apexClass>
916
+ <enabled>true</enabled>
917
+ </classAccesses>
918
+ </Profile>`;
919
+
920
+ expect(profileXml).toMatch(/<classAccesses>[\s\S]*?<apexClass>TestClass<\/apexClass>[\s\S]*?<\/classAccesses>/);
921
+ expect(profileXml).toContain("<enabled>true</enabled>");
922
+ });
923
+ });
924
+
925
+ describe("PermissionSet XML parsing", () => {
926
+ it("should parse PermissionSet basic structure", () => {
927
+ const permSetXml = `<?xml version="1.0" encoding="UTF-8"?>
928
+ <PermissionSet xmlns="http://salesforce.com/2006/04/metadata">
929
+ <custom>false</custom>
930
+ <label>Test Permission Set</label>
931
+ <description>Test description</description>
932
+ </PermissionSet>`;
933
+
934
+ expect(permSetXml).toContain("<label>Test Permission Set</label>");
935
+ expect(permSetXml).toContain("<description>Test description</description>");
936
+ expect(permSetXml).toContain("<custom>false</custom>");
937
+ });
938
+
939
+ it("should parse fieldPermissions with readable and editable", () => {
940
+ const permSetXml = `<?xml version="1.0" encoding="UTF-8"?>
941
+ <PermissionSet xmlns="http://salesforce.com/2006/04/metadata">
942
+ <fieldPermissions>
943
+ <field>Account.Name</field>
944
+ <readable>true</readable>
945
+ <editable>false</editable>
946
+ </fieldPermissions>
947
+ </PermissionSet>`;
948
+
949
+ expect(permSetXml).toMatch(/<fieldPermissions>[\s\S]*?<field>Account\.Name<\/field>[\s\S]*?<\/fieldPermissions>/);
950
+ expect(permSetXml).toContain("<readable>true</readable>");
951
+ expect(permSetXml).toContain("<editable>false</editable>");
952
+ });
953
+
954
+ it("should parse objectPermissions with viewAllRecords and modifyAllRecords", () => {
955
+ const permSetXml = `<?xml version="1.0" encoding="UTF-8"?>
956
+ <PermissionSet xmlns="http://salesforce.com/2006/04/metadata">
957
+ <objectPermissions>
958
+ <allowCreate>true</allowCreate>
959
+ <allowEdit>true</allowEdit>
960
+ <allowRead>true</allowRead>
961
+ <viewAllRecords>false</viewAllRecords>
962
+ <modifyAllRecords>false</modifyAllRecords>
963
+ <object>Account</object>
964
+ </objectPermissions>
965
+ </PermissionSet>`;
966
+
967
+ expect(permSetXml).toContain("<viewAllRecords>false</viewAllRecords>");
968
+ expect(permSetXml).toContain("<modifyAllRecords>false</modifyAllRecords>");
969
+ });
970
+
971
+ it("should parse userPermissions", () => {
972
+ const permSetXml = `<?xml version="1.0" encoding="UTF-8"?>
973
+ <PermissionSet xmlns="http://salesforce.com/2006/04/metadata">
974
+ <userPermissions>
975
+ <name>ViewReadonlyFields</name>
976
+ <enabled>true</enabled>
977
+ </userPermissions>
978
+ </PermissionSet>`;
979
+
980
+ expect(permSetXml).toMatch(/<userPermissions>[\s\S]*?<name>ViewReadonlyFields<\/name>[\s\S]*?<\/userPermissions>/);
981
+ expect(permSetXml).toContain("<enabled>true</enabled>");
982
+ });
983
+ });
984
+ });
985
+
986
+ // =========================================================================
987
+ // EDGE CASES
988
+ // =========================================================================
989
+
990
+ describe("Edge Cases", () => {
991
+
992
+ describe("SFDX Project Edge Cases", () => {
993
+ it("should handle empty namespace", () => {
994
+ const sfdxProjectPath = path.join(SALESFORCE_ENTERPRISE_PATH, "sfdx-project.json");
995
+ const content = fs.readFileSync(sfdxProjectPath, "utf-8");
996
+ const json = JSON.parse(content);
997
+ expect(json.namespace).toBe("");
998
+ });
999
+
1000
+ it("should handle singlePackage=false", () => {
1001
+ const sfdxProjectPath = path.join(SALESFORCE_ENTERPRISE_PATH, "sfdx-project.json");
1002
+ const content = fs.readFileSync(sfdxProjectPath, "utf-8");
1003
+ const json = JSON.parse(content);
1004
+ expect(json.singlePackage).toBe(false);
1005
+ });
1006
+
1007
+ it("should handle versionNumber with NEXT", () => {
1008
+ const sfdxProjectPath = path.join(SALESFORCE_ENTERPRISE_PATH, "sfdx-project.json");
1009
+ const content = fs.readFileSync(sfdxProjectPath, "utf-8");
1010
+ const json = JSON.parse(content);
1011
+ expect(json.packageDirectories[0].versionNumber).toBe("1.0.0.NEXT");
1012
+ });
1013
+ });
1014
+
1015
+ describe("Profile Edge Cases", () => {
1016
+ let profileContent: string;
1017
+
1018
+ beforeAll(() => {
1019
+ const profilePath = path.join(SALESFORCE_ENTERPRISE_PATH, "force-app/main/default/profiles/Admin.profile-meta.xml");
1020
+ profileContent = fs.readFileSync(profilePath, "utf-8");
1021
+ });
1022
+
1023
+ it("should handle read-only field permissions", () => {
1024
+ // Account.Total_Opportunity_Amount__c has readable=true, editable=false
1025
+ expect(profileContent).toMatch(/<fieldPermissions>[\s\S]*?<field>Account\.Total_Opportunity_Amount__c<\/field>[\s\S]*?<readable>true<\/readable>[\s\S]*?<editable>false<\/editable>[\s\S]*?<\/fieldPermissions>/);
1026
+ });
1027
+
1028
+ it("should handle multiple layoutAssignments", () => {
1029
+ const layoutMatches = profileContent.match(/<layoutAssignments>[\s\S]*?<\/layoutAssignments>/g);
1030
+ expect(layoutMatches).toHaveLength(3);
1031
+ });
1032
+
1033
+ it("should handle recordTypeVisibilities with different default/visible settings", () => {
1034
+ // Customer_Account: default=true, visible=true
1035
+ // PersonAccount: default=false, visible=false
1036
+ expect(profileContent).toMatch(/<recordTypeVisibilities>[\s\S]*?<recordType>Account\.Customer_Account<\/recordType>[\s\S]*?<default>true<\/default>[\s\S]*?<visible>true<\/visible>[\s\S]*?<\/recordTypeVisibilities>/);
1037
+ expect(profileContent).toMatch(/<recordTypeVisibilities>[\s\S]*?<recordType>Account\.PersonAccount<\/recordType>[\s\S]*?<default>false<\/default>[\s\S]*?<visible>false<\/visible>[\s\S]*?<\/recordTypeVisibilities>/);
1038
+ });
1039
+ });
1040
+
1041
+ describe("PermissionSet Edge Cases", () => {
1042
+ let permSetContent: string;
1043
+
1044
+ beforeAll(() => {
1045
+ const permSetPath = path.join(SALESFORCE_ENTERPRISE_PATH, "force-app/main/default/permissionsets/SalesRepresentative.permissionset-meta.xml");
1046
+ permSetContent = fs.readFileSync(permSetPath, "utf-8");
1047
+ });
1048
+
1049
+ it("should handle hasActivationRequired=false", () => {
1050
+ expect(permSetContent).toContain("<hasActivationRequired>false</hasActivationRequired>");
1051
+ });
1052
+
1053
+ it("should handle limited object permissions", () => {
1054
+ // PermissionSet has allowCreate=true, allowDelete=false, allowEdit=true, allowRead=true
1055
+ // modifyAllRecords=false, viewAllRecords=false
1056
+ expect(permSetContent).toMatch(/<objectPermissions>[\s\S]*?<allowDelete>false<\/allowDelete>[\s\S]*?<\/objectPermissions>/);
1057
+ expect(permSetContent).toMatch(/<objectPermissions>[\s\S]*?<modifyAllRecords>false<\/modifyAllRecords>[\s\S]*?<\/objectPermissions>/);
1058
+ expect(permSetContent).toMatch(/<objectPermissions>[\s\S]*?<viewAllRecords>false<\/viewAllRecords>[\s\S]*?<\/objectPermissions>/);
1059
+ });
1060
+
1061
+ it("should handle multiple field permissions with different editable settings", () => {
1062
+ // Account.AnnualRevenue: readable=true, editable=false
1063
+ // Account.Name: readable=true, editable=true
1064
+ expect(permSetContent).toMatch(/<fieldPermissions>[\s\S]*?<field>Account\.AnnualRevenue<\/field>[\s\S]*?<readable>true<\/readable>[\s\S]*?<editable>false<\/editable>[\s\S]*?<\/fieldPermissions>/);
1065
+ expect(permSetContent).toMatch(/<fieldPermissions>[\s\S]*?<field>Account\.Name<\/field>[\s\S]*?<readable>true<\/readable>[\s\S]*?<editable>true<\/editable>[\s\S]*?<\/fieldPermissions>/);
1066
+ });
1067
+ });
1068
+
1069
+ describe("Package.xml Edge Cases", () => {
1070
+ it("should handle mixed component types", () => {
1071
+ const packageXmlPath = path.join(SALESFORCE_ENTERPRISE_PATH, "force-app/main/default/package.xml");
1072
+ const content = fs.readFileSync(packageXmlPath, "utf-8");
1073
+
1074
+ // 7 different type elements
1075
+ const typeNames = content.match(/<name>\w+<\/name>/g);
1076
+ expect(typeNames).toHaveLength(7);
1077
+
1078
+ // All types should be unique
1079
+ const uniqueTypes = [...new Set(typeNames)];
1080
+ expect(uniqueTypes).toHaveLength(7);
1081
+ });
1082
+
1083
+ it("should handle types with single member", () => {
1084
+ const packageXmlPath = path.join(SALESFORCE_ENTERPRISE_PATH, "force-app/main/default/package.xml");
1085
+ const content = fs.readFileSync(packageXmlPath, "utf-8");
1086
+
1087
+ // ApexTrigger has 1 member
1088
+ expect(content).toMatch(/<members>OpportunityTrigger<\/members>[\s\S]*?<name>ApexTrigger<\/name>[\s\S]*?<\/types>/);
1089
+
1090
+ // CustomObject has 1 member
1091
+ expect(content).toMatch(/<members>CustomObject__c<\/members>[\s\S]*?<name>CustomObject<\/name>[\s\S]*?<\/types>/);
1092
+
1093
+ // Profile has 1 member
1094
+ expect(content).toMatch(/<members>Admin<\/members>[\s\S]*?<name>Profile<\/name>[\s\S]*?<\/types>/);
1095
+
1096
+ // PermissionSet has 1 member
1097
+ expect(content).toMatch(/<members>SalesRepresentative<\/members>[\s\S]*?<name>PermissionSet<\/name>[\s\S]*?<\/types>/);
1098
+ });
1099
+
1100
+ it("should handle types with multiple members", () => {
1101
+ const packageXmlPath = path.join(SALESFORCE_ENTERPRISE_PATH, "force-app/main/default/package.xml");
1102
+ const content = fs.readFileSync(packageXmlPath, "utf-8");
1103
+
1104
+ // ApexClass has 9 members - extract by finding the section between types tags
1105
+ const apexClassSection = content.substring(content.indexOf("<members>AccountController</members>"), content.indexOf("</types>"));
1106
+ const apexMembers = apexClassSection.match(/<members>\w+<\/members>/g);
1107
+ expect(apexMembers).toHaveLength(9);
1108
+
1109
+ // LightningComponentBundle has 2 members
1110
+ const lwcStart = content.indexOf("<members>accountList</members>");
1111
+ const lwcEnd = content.indexOf("</types>", lwcStart);
1112
+ const lwcSection = content.substring(lwcStart - 100, lwcEnd);
1113
+ const lwcMembers = lwcSection.match(/<members>\w+<\/members>/g);
1114
+ expect(lwcMembers).toHaveLength(2);
1115
+
1116
+ // Flow has 2 members
1117
+ const flowStart = content.indexOf("<members>Account_Create</members>");
1118
+ const flowEnd = content.indexOf("</types>", flowStart);
1119
+ const flowSection = content.substring(flowStart - 100, flowEnd);
1120
+ const flowMembers = flowSection.match(/<members>\w+<\/members>/g);
1121
+ expect(flowMembers).toHaveLength(2);
1122
+ });
1123
+ });
1124
+ });
1125
+ });