ai-first-cli 1.3.0 → 1.3.5

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