abstra 3.24.0__py3-none-any.whl → 3.24.2__py3-none-any.whl

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 (331) hide show
  1. abstra/cli.py +8 -2
  2. {abstra-3.24.0.dist-info → abstra-3.24.2.dist-info}/METADATA +1 -2
  3. {abstra-3.24.0.dist-info → abstra-3.24.2.dist-info}/RECORD +205 -209
  4. abstra_internals/cloud_api/__init__.py +17 -8
  5. abstra_internals/contracts_generated.py +975 -707
  6. abstra_internals/controllers/ai.py +89 -1
  7. abstra_internals/controllers/git.py +6 -2
  8. abstra_internals/controllers/main.py +1 -1
  9. abstra_internals/interface/cli/editor.py +2 -2
  10. abstra_internals/repositories/git/__init__.py +1 -14
  11. abstra_internals/repositories/git/git_test.py +9 -1216
  12. abstra_internals/repositories/git/native.py +13 -7
  13. abstra_internals/repositories/git/types.py +7 -1
  14. abstra_internals/server/routes/git.py +11 -2
  15. abstra_statics/dist/assets/AbstraButton.vue_vue_type_script_setup_true_lang.a3ba2a31.js +2 -0
  16. abstra_statics/dist/assets/AbstraLogo.vue_vue_type_script_setup_true_lang.b1a71740.js +2 -0
  17. abstra_statics/dist/assets/ApiKeys.dafa1dc2.js +2 -0
  18. abstra_statics/dist/assets/App.45396b70.js +2 -0
  19. abstra_statics/dist/assets/App.vue_vue_type_style_index_0_lang.db5596a1.js +2 -0
  20. abstra_statics/dist/assets/BaseLayout.135a51d9.js +2 -0
  21. abstra_statics/dist/assets/Billing.622c9155.js +2 -0
  22. abstra_statics/dist/assets/{Breadcrumb.26dec5f7.js → Breadcrumb.8a82fa01.js} +3 -3
  23. abstra_statics/dist/assets/Builds.38c0f966.css +1 -0
  24. abstra_statics/dist/assets/Builds.c3fd9633.js +2 -0
  25. abstra_statics/dist/assets/{Card.6cfffb80.js → Card.38a860a4.js} +5 -5
  26. abstra_statics/dist/assets/{CircularLoading.a13d6a76.js → CircularLoading.217756fb.js} +2 -2
  27. abstra_statics/dist/assets/CloseCircleOutlined.4c4707d8.js +2 -0
  28. abstra_statics/dist/assets/ConnectorsView.121898a9.js +2 -0
  29. abstra_statics/dist/assets/ConnectorsView.b594798e.css +1 -0
  30. abstra_statics/dist/assets/ConsoleOmniChat.vue_vue_type_script_setup_true_lang.502d779a.js +2 -0
  31. abstra_statics/dist/assets/ContentLayout.b1c94c2b.js +2 -0
  32. abstra_statics/dist/assets/CrudView.75a430a4.js +2 -0
  33. abstra_statics/dist/assets/CrudView.8abe5bc2.css +1 -0
  34. abstra_statics/dist/assets/DocsButton.vue_vue_type_script_setup_true_lang.a9c8118b.js +2 -0
  35. abstra_statics/dist/assets/EditorLogin.fba78ce9.js +2 -0
  36. abstra_statics/dist/assets/EditorsView.1a9ccb13.js +2 -0
  37. abstra_statics/dist/assets/EnvVars.d77957ea.js +2 -0
  38. abstra_statics/dist/assets/Error.5bd293cc.js +2 -0
  39. abstra_statics/dist/assets/ExclamationCircleOutlined.5d5c3f30.js +2 -0
  40. abstra_statics/dist/assets/Files.39b07d88.css +1 -0
  41. abstra_statics/dist/assets/Files.c4ce443f.js +2 -0
  42. abstra_statics/dist/assets/Form.a4787001.js +2 -0
  43. abstra_statics/dist/assets/{FormRunner.46f6426d.css → FormRunner.1e6e7d2a.css} +1 -1
  44. abstra_statics/dist/assets/FormRunner.73650f9e.js +2 -0
  45. abstra_statics/dist/assets/Home.19a2303b.js +2 -0
  46. abstra_statics/dist/assets/Home.4eff6ce9.js +2 -0
  47. abstra_statics/dist/assets/Home.e8bf9440.css +1 -0
  48. abstra_statics/dist/assets/LoadingContainer.97fa8f2e.js +2 -0
  49. abstra_statics/dist/assets/LoadingOutlined.e309ab16.js +2 -0
  50. abstra_statics/dist/assets/Login.632cada3.js +2 -0
  51. abstra_statics/dist/assets/{Login.75d13f6c.css → Login.ac75228f.css} +1 -1
  52. abstra_statics/dist/assets/Login.ecec1ff2.js +2 -0
  53. abstra_statics/dist/assets/Login.vue_vue_type_script_setup_true_lang.ada1c6c9.js +2 -0
  54. abstra_statics/dist/assets/Logo.d77d5637.js +2 -0
  55. abstra_statics/dist/assets/Logs.c1f01b05.js +2 -0
  56. abstra_statics/dist/assets/LogsController.0ff97ed4.css +1 -0
  57. abstra_statics/dist/assets/LogsController.2dceb3d3.js +2 -0
  58. abstra_statics/dist/assets/Main.44b7640e.js +2 -0
  59. abstra_statics/dist/assets/{MockForm.deda9355.js → MockForm.9b7a0df3.js} +2 -2
  60. abstra_statics/dist/assets/Navbar.0951ed6d.js +2 -0
  61. abstra_statics/dist/assets/Navbar.61d3e0a6.css +1 -0
  62. abstra_statics/dist/assets/NewEditor.d65a400f.js +8 -0
  63. abstra_statics/dist/assets/NewEditor.e3cfeb2c.css +1 -0
  64. abstra_statics/dist/assets/OidcLoginCallback.66b0f38a.js +2 -0
  65. abstra_statics/dist/assets/OidcLogoutCallback.48d8429a.js +2 -0
  66. abstra_statics/dist/assets/OmniChat.60ee26c8.css +1 -0
  67. abstra_statics/dist/assets/OmniChat.c3de8733.js +6 -0
  68. abstra_statics/dist/assets/OnboardingView.6cda1bc5.js +2 -0
  69. abstra_statics/dist/assets/OnboardingView.e871e6d8.css +1 -0
  70. abstra_statics/dist/assets/Organization.c36206b7.js +2 -0
  71. abstra_statics/dist/assets/Organizations.2b1c6c65.js +2 -0
  72. abstra_statics/dist/assets/{PhArrowCounterClockwise.vue.8090d021.js → PhArrowCounterClockwise.vue.9e570570.js} +2 -2
  73. abstra_statics/dist/assets/{PhArrowSquareOut.vue.26582195.js → PhArrowSquareOut.vue.bcbdb6e7.js} +2 -2
  74. abstra_statics/dist/assets/{PhClockCounterClockwise.vue.812311ad.js → PhClockCounterClockwise.vue.4bd682d8.js} +2 -2
  75. abstra_statics/dist/assets/{PhCopy.vue.59b0f1b4.js → PhCopy.vue.29934bc2.js} +2 -2
  76. abstra_statics/dist/assets/PhCopySimple.vue.0241af8c.js +2 -0
  77. abstra_statics/dist/assets/{PhCube.vue.63ae7d32.js → PhCube.vue.0fe2c514.js} +2 -2
  78. abstra_statics/dist/assets/PhDatabase.vue.fdfb515c.js +2 -0
  79. abstra_statics/dist/assets/{PhDotsThreeVertical.vue.ab4580a5.js → PhDotsThreeVertical.vue.7a0e0638.js} +2 -2
  80. abstra_statics/dist/assets/{PhDownloadSimple.vue.c2eaaad1.js → PhDownloadSimple.vue.f1245c40.js} +2 -2
  81. abstra_statics/dist/assets/PhFileArrowUp.vue.c292afe1.js +2 -0
  82. abstra_statics/dist/assets/PhFilePlus.vue.c39ff1a9.js +2 -0
  83. abstra_statics/dist/assets/{PhFolderPlus.vue.05ba4a5c.js → PhFolderPlus.vue.bc40161e.js} +2 -2
  84. abstra_statics/dist/assets/{PhGear.vue.0e4a6135.js → PhGear.vue.0feed515.js} +2 -2
  85. abstra_statics/dist/assets/{PhKey.vue.b2c184d1.js → PhKey.vue.15a9e64e.js} +2 -2
  86. abstra_statics/dist/assets/{PhPencil.vue.2f2fe576.js → PhPencil.vue.a7219766.js} +2 -2
  87. abstra_statics/dist/assets/PhPencilSimple.vue.15a2b403.js +2 -0
  88. abstra_statics/dist/assets/PhRocket.vue.7155b91f.js +2 -0
  89. abstra_statics/dist/assets/{PhSignOut.vue.a271f14b.js → PhSignOut.vue.2af17bd7.js} +2 -2
  90. abstra_statics/dist/assets/{PhSparkle.vue.726defa2.js → PhSparkle.vue.c7f06cac.js} +2 -2
  91. abstra_statics/dist/assets/PhTranslate.vue.2ce651a6.js +2 -0
  92. abstra_statics/dist/assets/{PhUsersThree.vue.275d13ab.js → PhUsersThree.vue.2942df75.js} +2 -2
  93. abstra_statics/dist/assets/{PhWarningCircle.vue.3b1aca1b.js → PhWarningCircle.vue.05a40bc4.js} +2 -2
  94. abstra_statics/dist/assets/PhWebhooksLogo.vue.e4752384.js +2 -0
  95. abstra_statics/dist/assets/PlayerConfigProvider.00af5968.js +2 -0
  96. abstra_statics/dist/assets/PlayerNavbar.117f184b.js +2 -0
  97. abstra_statics/dist/assets/{PlayerNavbar.77209eae.css → PlayerNavbar.4dc29a45.css} +1 -1
  98. abstra_statics/dist/assets/Project.66111161.js +2 -0
  99. abstra_statics/dist/assets/Project.9c418d2e.css +1 -0
  100. abstra_statics/dist/assets/ProjectLogin.d9bb1f86.js +2 -0
  101. abstra_statics/dist/assets/ProjectSettings.f8c6f60a.js +2 -0
  102. abstra_statics/dist/assets/ProjectsView.32f6ccff.js +2 -0
  103. abstra_statics/dist/assets/ProjectsView.e26ddfd5.css +1 -0
  104. abstra_statics/dist/assets/SaveButton.363ea20f.css +1 -0
  105. abstra_statics/dist/assets/SaveButton.c3f2a4bb.js +2 -0
  106. abstra_statics/dist/assets/ScrollArea.vue_vue_type_script_setup_true_lang.cb5567cd.js +2 -0
  107. abstra_statics/dist/assets/{Sidebar.29baeab0.css → Sidebar.161522da.css} +1 -1
  108. abstra_statics/dist/assets/Sidebar.1c4e35be.js +2 -0
  109. abstra_statics/dist/assets/Sql.6d9a778c.css +1 -0
  110. abstra_statics/dist/assets/Sql.8d31ec23.js +5 -0
  111. abstra_statics/dist/assets/Steps.687763a5.js +2 -0
  112. abstra_statics/dist/assets/TableCard.8c99a870.js +2 -0
  113. abstra_statics/dist/assets/{TableEditor.5853a363.css → TableEditor.20fecc75.css} +1 -1
  114. abstra_statics/dist/assets/TableEditor.ba7a8b6a.js +2 -0
  115. abstra_statics/dist/assets/Tables.113960f2.js +39 -0
  116. abstra_statics/dist/assets/TablesDiagram.8e6d1e89.js +15 -0
  117. abstra_statics/dist/assets/TablesTabs.vue_vue_type_script_setup_true_lang.5cc96b0d.js +2 -0
  118. abstra_statics/dist/assets/Tasks.90846020.js +2 -0
  119. abstra_statics/dist/assets/{UploadOutlined.eab75eb0.js → UploadOutlined.518baf9a.js} +2 -2
  120. abstra_statics/dist/assets/View.ded6b355.js +2 -0
  121. abstra_statics/dist/assets/View.vue_vue_type_script_setup_true_lang.285b5e2c.js +2 -0
  122. abstra_statics/dist/assets/Watermark.5071a4b2.js +2 -0
  123. abstra_statics/dist/assets/WebEditor.18ece735.js +2 -0
  124. abstra_statics/dist/assets/WebEditor.b886e4d1.css +1 -0
  125. abstra_statics/dist/assets/WidgetPreview.88a4f27f.js +2 -0
  126. abstra_statics/dist/assets/WorkflowViewer.3b6aee8e.css +1 -0
  127. abstra_statics/dist/assets/WorkflowViewer.778c401d.js +2 -0
  128. abstra_statics/dist/assets/ant-design.4efc9ccd.js +2 -0
  129. abstra_statics/dist/assets/{apiKey.72f497ca.js → apiKey.bd946d8c.js} +2 -2
  130. abstra_statics/dist/assets/asyncComputed.7bc1692e.js +2 -0
  131. abstra_statics/dist/assets/{build.df2d55cc.js → build.a8637e29.js} +2 -2
  132. abstra_statics/dist/assets/colorHelpers.8ba18214.js +2 -0
  133. abstra_statics/dist/assets/{console.2bf7f04d.js → console.2a5ed51a.js} +4 -4
  134. abstra_statics/dist/assets/constants.534f67bc.js +2 -0
  135. abstra_statics/dist/assets/contracts.generated.8ad36e63.js +2 -0
  136. abstra_statics/dist/assets/{cssMode.7133c7cb.js → cssMode.6c4ccf50.js} +2 -2
  137. abstra_statics/dist/assets/{datetime.8de2ff28.js → datetime.89495471.js} +2 -2
  138. abstra_statics/dist/assets/dayjs.304f38f8.js +2 -0
  139. abstra_statics/dist/assets/editor.c1a1bd33.js +2 -0
  140. abstra_statics/dist/assets/editor.main.84e237cf.js +2 -0
  141. abstra_statics/dist/assets/fetch.452c58e5.js +2 -0
  142. abstra_statics/dist/assets/files.8999afd5.js +2 -0
  143. abstra_statics/dist/assets/{folder.d8e23009.js → folder.81ef8619.js} +2 -2
  144. abstra_statics/dist/assets/{freemarker2.6698d1ea.js → freemarker2.559f77f2.js} +2 -2
  145. abstra_statics/dist/assets/{handlebars.a6c42dc0.js → handlebars.8d101b7c.js} +2 -2
  146. abstra_statics/dist/assets/{html.493a5410.js → html.b3e7d3ab.js} +3 -3
  147. abstra_statics/dist/assets/{htmlMode.a28b2fca.js → htmlMode.2305b1bb.js} +2 -2
  148. abstra_statics/dist/assets/{index.fb49354b.js → index.71eb83f3.js} +2 -2
  149. abstra_statics/dist/assets/{index.79ce3bf1.js → index.90acf038.js} +2 -2
  150. abstra_statics/dist/assets/index.b74c262c.js +4 -0
  151. abstra_statics/dist/assets/index.bc97991a.js +2 -0
  152. abstra_statics/dist/assets/index.d809956c.js +2 -0
  153. abstra_statics/dist/assets/{index.57042181.css → index.da037bc0.css} +1 -1
  154. abstra_statics/dist/assets/index.da4f9d54.js +2 -0
  155. abstra_statics/dist/assets/{index.c34a405a.js → index.e5cb42a1.js} +2 -2
  156. abstra_statics/dist/assets/{index.5d6b1e62.js → index.f2beb20d.js} +2 -2
  157. abstra_statics/dist/assets/{index.4d20c159.js → index.f6171691.js} +3 -3
  158. abstra_statics/dist/assets/{javascript.3a36cf17.js → javascript.3000fc25.js} +3 -3
  159. abstra_statics/dist/assets/{jsonMode.bead6ac8.js → jsonMode.7bbb508d.js} +2 -2
  160. abstra_statics/dist/assets/{jwt-decode.9f7a5511.css → jwt-decode.c5760184.css} +2 -2
  161. abstra_statics/dist/assets/{jwt-decode.esm.d4517a10.js → jwt-decode.esm.c9c37cdc.js} +197 -197
  162. abstra_statics/dist/assets/{linters.2f3141cb.js → linters.7fec18d9.js} +2 -2
  163. abstra_statics/dist/assets/{liquid.0c337fae.js → liquid.b4ac9aaf.js} +3 -3
  164. abstra_statics/dist/assets/member.3c12efee.js +2 -0
  165. abstra_statics/dist/assets/metadata.e627ddda.js +2 -0
  166. abstra_statics/dist/assets/omniChatStore.508e8ece.js +9 -0
  167. abstra_statics/dist/assets/omniChatStore.b58e3bed.css +1 -0
  168. abstra_statics/dist/assets/{organization.928c9bef.js → organization.cd03f9a8.js} +2 -2
  169. abstra_statics/dist/assets/os.f08724fb.js +2 -0
  170. abstra_statics/dist/assets/player.7362caf4.js +2 -0
  171. abstra_statics/dist/assets/{plotly.min.7225d3a0.js → plotly.min.50ebb925.js} +2 -2
  172. abstra_statics/dist/assets/polling.4db5ee9a.js +2 -0
  173. abstra_statics/dist/assets/{project.619b7244.js → project.9a068e8d.js} +2 -2
  174. abstra_statics/dist/assets/{python.05764499.js → python.51a7c648.js} +3 -3
  175. abstra_statics/dist/assets/{razor.81a45581.js → razor.99323f5f.js} +3 -3
  176. abstra_statics/dist/assets/record.a33d29b1.js +2 -0
  177. abstra_statics/dist/assets/{redirect.f028a879.js → redirect.42bf4f0a.js} +2 -2
  178. abstra_statics/dist/assets/repository.5190b94f.js +2 -0
  179. abstra_statics/dist/assets/repository.94fb77c7.js +2 -0
  180. abstra_statics/dist/assets/repository.c0d70cb2.js +2 -0
  181. abstra_statics/dist/assets/router.4168cc71.js +2 -0
  182. abstra_statics/dist/assets/{router.424f7da9.js → router.a8616541.js} +5 -5
  183. abstra_statics/dist/assets/string.8fab6b53.js +2 -0
  184. abstra_statics/dist/assets/{tables.1f68ec62.js → tables.2e1c934b.js} +2 -2
  185. abstra_statics/dist/assets/tasksController.1feffcfe.js +4 -0
  186. abstra_statics/dist/assets/{toggleHighContrast.0d0e5662.js → toggleHighContrast.6544a728.js} +7 -7
  187. abstra_statics/dist/assets/{tsMode.6eadbf06.js → tsMode.922e04bb.js} +2 -2
  188. abstra_statics/dist/assets/{typescript.1670e287.js → typescript.1b4f8286.js} +3 -3
  189. abstra_statics/dist/assets/url.9e033350.js +2 -0
  190. abstra_statics/dist/assets/{useCodebaseEvents.53dec1f2.js → useCodebaseEvents.ffe057d1.js} +2 -2
  191. abstra_statics/dist/assets/useTables.5fffa3f1.js +2 -0
  192. abstra_statics/dist/assets/userStore.d962fba4.js +2 -0
  193. abstra_statics/dist/assets/uuid.8581bc03.js +2 -0
  194. abstra_statics/dist/assets/vue-flow-background.3e9183ec.js +2 -0
  195. abstra_statics/dist/assets/vue-flow-core.41c647da.js +22 -0
  196. abstra_statics/dist/assets/{vue-quill.esm-bundler.c4f04985.js → vue-quill.esm-bundler.36e79a95.js} +2 -2
  197. abstra_statics/dist/assets/{workspaceStore.6244d03d.js → workspaceStore.5a435520.js} +2 -2
  198. abstra_statics/dist/assets/{xml.f2867af8.js → xml.c1692f52.js} +3 -3
  199. abstra_statics/dist/assets/{yaml.5427bb1b.js → yaml.244444c1.js} +2 -2
  200. abstra_statics/dist/console.html +15 -15
  201. abstra_statics/dist/editor.html +15 -15
  202. abstra_statics/dist/player.html +10 -10
  203. abstra/connectors/banking/__init__.py +0 -0
  204. abstra/connectors/banking/brazil.py +0 -5
  205. abstra_internals/repositories/git/dulwich.py +0 -1353
  206. abstra_internals/services/banking/__init__.py +0 -3
  207. abstra_internals/services/banking/banking_service.py +0 -104
  208. abstra_internals/services/banking/client_factory.py +0 -70
  209. abstra_internals/services/banking/sdk/__init__.py +0 -0
  210. abstra_internals/services/banking/sdk/generate_totalbank_api.py +0 -383
  211. abstra_statics/dist/assets/AbstraButton.vue_vue_type_script_setup_true_lang.eb8ccb64.js +0 -2
  212. abstra_statics/dist/assets/AbstraLogo.vue_vue_type_script_setup_true_lang.9f98292e.js +0 -2
  213. abstra_statics/dist/assets/ApiKeys.e975c4f3.js +0 -2
  214. abstra_statics/dist/assets/App.f62faff6.js +0 -2
  215. abstra_statics/dist/assets/App.vue_vue_type_style_index_0_lang.5aa45ac1.js +0 -2
  216. abstra_statics/dist/assets/BaseLayout.1ec2c96d.js +0 -2
  217. abstra_statics/dist/assets/Billing.60adc9fa.js +0 -2
  218. abstra_statics/dist/assets/Builds.ace9e3da.css +0 -1
  219. abstra_statics/dist/assets/Builds.f86210bc.js +0 -2
  220. abstra_statics/dist/assets/CloseCircleOutlined.30bc25a8.js +0 -2
  221. abstra_statics/dist/assets/ConnectorsView.33c5380f.css +0 -1
  222. abstra_statics/dist/assets/ConnectorsView.eb4c769f.js +0 -2
  223. abstra_statics/dist/assets/ConsoleOmniChat.vue_vue_type_script_setup_true_lang.8d2e4672.js +0 -2
  224. abstra_statics/dist/assets/ContentLayout.d03fee5b.js +0 -2
  225. abstra_statics/dist/assets/CrudView.c0824225.js +0 -2
  226. abstra_statics/dist/assets/CrudView.e24590ae.css +0 -1
  227. abstra_statics/dist/assets/DocsButton.vue_vue_type_script_setup_true_lang.3668aee4.js +0 -2
  228. abstra_statics/dist/assets/EditorLogin.46db248f.js +0 -2
  229. abstra_statics/dist/assets/EditorsView.e72621fa.js +0 -2
  230. abstra_statics/dist/assets/EnvVars.f9f9d61f.js +0 -2
  231. abstra_statics/dist/assets/Error.864f05b3.js +0 -2
  232. abstra_statics/dist/assets/ExclamationCircleOutlined.a489b996.js +0 -2
  233. abstra_statics/dist/assets/Files.afe615e1.js +0 -2
  234. abstra_statics/dist/assets/Files.d0e8d2ff.css +0 -1
  235. abstra_statics/dist/assets/Form.556d0de2.js +0 -2
  236. abstra_statics/dist/assets/FormRunner.7f56a8c6.js +0 -2
  237. abstra_statics/dist/assets/Home.287d17f8.js +0 -2
  238. abstra_statics/dist/assets/Home.3794e8b4.css +0 -1
  239. abstra_statics/dist/assets/Home.5b7e9c23.js +0 -2
  240. abstra_statics/dist/assets/Live.37415b2d.css +0 -1
  241. abstra_statics/dist/assets/Live.50bacfea.js +0 -2
  242. abstra_statics/dist/assets/LoadingContainer.ebace8de.js +0 -2
  243. abstra_statics/dist/assets/LoadingOutlined.9e949112.js +0 -2
  244. abstra_statics/dist/assets/Login.536a3067.js +0 -2
  245. abstra_statics/dist/assets/Login.bec408c9.js +0 -2
  246. abstra_statics/dist/assets/Login.vue_vue_type_script_setup_true_lang.1c3f108d.js +0 -2
  247. abstra_statics/dist/assets/Logo.82d6ab70.js +0 -2
  248. abstra_statics/dist/assets/Logs.f6135084.js +0 -2
  249. abstra_statics/dist/assets/LogsController.6b666816.js +0 -2
  250. abstra_statics/dist/assets/LogsController.fb0d96c2.css +0 -1
  251. abstra_statics/dist/assets/Main.77b115f8.js +0 -2
  252. abstra_statics/dist/assets/Navbar.2dc6d02c.css +0 -1
  253. abstra_statics/dist/assets/Navbar.4a6f2b09.js +0 -2
  254. abstra_statics/dist/assets/NewEditor.5f84de86.css +0 -1
  255. abstra_statics/dist/assets/NewEditor.e558e47d.js +0 -8
  256. abstra_statics/dist/assets/OidcLoginCallback.7f514b45.js +0 -2
  257. abstra_statics/dist/assets/OidcLogoutCallback.038813a1.js +0 -2
  258. abstra_statics/dist/assets/OmniChat.05ba8d8a.css +0 -1
  259. abstra_statics/dist/assets/OmniChat.60d98deb.js +0 -6
  260. abstra_statics/dist/assets/OnboardingView.3996b08d.css +0 -1
  261. abstra_statics/dist/assets/OnboardingView.9413ee50.js +0 -2
  262. abstra_statics/dist/assets/Organization.7203cc0b.js +0 -2
  263. abstra_statics/dist/assets/Organizations.91220ca0.js +0 -2
  264. abstra_statics/dist/assets/PhBookBookmark.vue.5b7ab079.js +0 -2
  265. abstra_statics/dist/assets/PhChats.vue.b5df7174.js +0 -2
  266. abstra_statics/dist/assets/PhCopySimple.vue.d41d9160.js +0 -2
  267. abstra_statics/dist/assets/PhDatabase.vue.edfcb96b.js +0 -2
  268. abstra_statics/dist/assets/PhPencilSimple.vue.cc8620ae.js +0 -2
  269. abstra_statics/dist/assets/PhRocket.vue.e397203c.js +0 -2
  270. abstra_statics/dist/assets/PhUserList.vue.6a29f16d.js +0 -2
  271. abstra_statics/dist/assets/PhWebhooksLogo.vue.5e772aac.js +0 -2
  272. abstra_statics/dist/assets/PlayerConfigProvider.e90a2b41.js +0 -2
  273. abstra_statics/dist/assets/PlayerNavbar.11ec1844.js +0 -2
  274. abstra_statics/dist/assets/Project.9c75c141.css +0 -1
  275. abstra_statics/dist/assets/Project.c16740fb.js +0 -2
  276. abstra_statics/dist/assets/ProjectLogin.e7a6f444.js +0 -2
  277. abstra_statics/dist/assets/ProjectSettings.52c19693.js +0 -2
  278. abstra_statics/dist/assets/ProjectsView.16d8ecf6.css +0 -1
  279. abstra_statics/dist/assets/ProjectsView.22fa7a8e.js +0 -2
  280. abstra_statics/dist/assets/SaveButton.719393d2.js +0 -2
  281. abstra_statics/dist/assets/SaveButton.932ac6b8.css +0 -1
  282. abstra_statics/dist/assets/ScrollArea.vue_vue_type_script_setup_true_lang.d4028954.js +0 -2
  283. abstra_statics/dist/assets/Sidebar.5cb8e04e.js +0 -2
  284. abstra_statics/dist/assets/Sql.23d80bad.js +0 -5
  285. abstra_statics/dist/assets/Sql.90e6e2ba.css +0 -1
  286. abstra_statics/dist/assets/Steps.8e5d201a.js +0 -2
  287. abstra_statics/dist/assets/TableCard.59f95f8f.js +0 -2
  288. abstra_statics/dist/assets/TableEditor.8539f984.js +0 -2
  289. abstra_statics/dist/assets/Tables.44d953f7.js +0 -2
  290. abstra_statics/dist/assets/TablesDiagram.8e47383c.js +0 -15
  291. abstra_statics/dist/assets/TablesTabs.vue_vue_type_script_setup_true_lang.6866fb32.js +0 -2
  292. abstra_statics/dist/assets/Tasks.09551b19.js +0 -2
  293. abstra_statics/dist/assets/View.5fd7ddf0.js +0 -2
  294. abstra_statics/dist/assets/View.vue_vue_type_script_setup_true_lang.a904f400.js +0 -2
  295. abstra_statics/dist/assets/Watermark.ab3d818f.js +0 -2
  296. abstra_statics/dist/assets/WebEditor.c2e271d1.css +0 -1
  297. abstra_statics/dist/assets/WebEditor.d6ec6392.js +0 -2
  298. abstra_statics/dist/assets/WidgetPreview.86b31dec.js +0 -2
  299. abstra_statics/dist/assets/ant-design.a865486e.js +0 -2
  300. abstra_statics/dist/assets/asyncComputed.cf5282fc.js +0 -2
  301. abstra_statics/dist/assets/colorHelpers.37d9932b.js +0 -2
  302. abstra_statics/dist/assets/constants.7d38ec8b.js +0 -2
  303. abstra_statics/dist/assets/contracts.generated.590b1102.js +0 -2
  304. abstra_statics/dist/assets/dayjs.f18bbbca.js +0 -2
  305. abstra_statics/dist/assets/editor.3a4714e3.js +0 -2
  306. abstra_statics/dist/assets/editor.main.9c635b9a.js +0 -2
  307. abstra_statics/dist/assets/fetch.89fd5b7b.js +0 -2
  308. abstra_statics/dist/assets/index.2af3391c.js +0 -4
  309. abstra_statics/dist/assets/index.4176fe88.js +0 -2
  310. abstra_statics/dist/assets/index.fb182bd1.js +0 -2
  311. abstra_statics/dist/assets/member.48d6f2cd.js +0 -2
  312. abstra_statics/dist/assets/metadata.c3aed6e1.js +0 -2
  313. abstra_statics/dist/assets/omniChatStore.c53bcca2.js +0 -8
  314. abstra_statics/dist/assets/omniChatStore.ec95fb81.css +0 -1
  315. abstra_statics/dist/assets/player.d3aeafc5.js +0 -2
  316. abstra_statics/dist/assets/polling.82ee6b45.js +0 -2
  317. abstra_statics/dist/assets/record.7f43486c.js +0 -2
  318. abstra_statics/dist/assets/repository.9534db4b.js +0 -2
  319. abstra_statics/dist/assets/repository.c15239ce.js +0 -2
  320. abstra_statics/dist/assets/router.262190ec.js +0 -2
  321. abstra_statics/dist/assets/string.0acf5572.js +0 -2
  322. abstra_statics/dist/assets/tasksController.371896de.js +0 -4
  323. abstra_statics/dist/assets/url.e8732f77.js +0 -2
  324. abstra_statics/dist/assets/useTables.4f034cf8.js +0 -2
  325. abstra_statics/dist/assets/userStore.31024da3.js +0 -2
  326. abstra_statics/dist/assets/uuid.bde15ce7.js +0 -2
  327. abstra_statics/dist/assets/vue-flow-background.818c7852.js +0 -2
  328. abstra_statics/dist/assets/vue-flow-core.1180ec83.js +0 -22
  329. {abstra-3.24.0.dist-info → abstra-3.24.2.dist-info}/WHEEL +0 -0
  330. {abstra-3.24.0.dist-info → abstra-3.24.2.dist-info}/entry_points.txt +0 -0
  331. {abstra-3.24.0.dist-info → abstra-3.24.2.dist-info}/top_level.txt +0 -0
@@ -1,41 +1,32 @@
1
1
  import shutil
2
2
  import tempfile
3
3
  import unittest
4
- from abc import ABC, abstractmethod
5
4
  from pathlib import Path
6
- from typing import Tuple, Type
5
+ from typing import Optional
7
6
 
8
- from abstra_internals.repositories.git.dulwich import DulwichGitRepository
9
7
  from abstra_internals.repositories.git.native import NativeGitRepository
10
8
  from abstra_internals.repositories.git.types import (
11
- GitRepositoryInterface,
12
9
  GitStatus,
13
10
  )
14
11
 
15
12
 
16
- class _GitRepositoryTestBase(unittest.TestCase, ABC):
17
- """Base class providing common test methods for GitRepositoryInterface implementations"""
18
-
19
- __test__ = False
20
-
21
- @property
22
- @abstractmethod
23
- def repo_class(self) -> Type[GitRepositoryInterface]:
24
- """The repository class to test"""
25
- pass
13
+ class NativeGitRepositoryTest(unittest.TestCase):
14
+ """Test suite for NativeGitRepository"""
26
15
 
27
16
  def setUp(self):
28
17
  """Set up test environment"""
29
18
  super().setUp()
30
19
  self.temp_dir = Path(tempfile.mkdtemp())
31
- self.repo = self.repo_class(self.temp_dir)
20
+ self.repo = NativeGitRepository(self.temp_dir)
32
21
  self._original_commit_changes = self.repo.commit_changes
33
22
  self.repo.commit_changes = self._commit_changes_wrapper
34
23
 
35
- def _commit_changes_wrapper(self, message: str, add_all: bool = True) -> bool:
24
+ def _commit_changes_wrapper(
25
+ self, message: str, author: Optional[str] = None
26
+ ) -> bool:
36
27
  """Wrapper for commit_changes that ensures git is configured"""
37
28
  self.ensure_git_configured()
38
- return self._original_commit_changes(message, add_all)
29
+ return self._original_commit_changes(message, author)
39
30
 
40
31
  def tearDown(self):
41
32
  """Clean up test environment"""
@@ -93,7 +84,7 @@ class _GitRepositoryTestBase(unittest.TestCase, ABC):
93
84
  # Create subdirectory and test from there
94
85
  sub_dir = self.temp_dir / "subdir"
95
86
  sub_dir.mkdir()
96
- sub_repo = self.repo_class(sub_dir)
87
+ sub_repo = NativeGitRepository(sub_dir)
97
88
  root = sub_repo.find_git_root()
98
89
  if root:
99
90
  # Resolve both paths to handle macOS symlinks (e.g., /var vs /private/var)
@@ -364,1209 +355,11 @@ class _GitRepositoryTestBase(unittest.TestCase, ABC):
364
355
  self.assertEqual(file_content, "version 1")
365
356
 
366
357
 
367
- class NativeGitRepositoryTest(_GitRepositoryTestBase):
368
- """Test suite for NativeGitRepository"""
369
-
370
- # Override parent's __test__ = False to make this discoverable by pytest
371
- __test__ = True
372
-
373
- @property
374
- def repo_class(self) -> Type[GitRepositoryInterface]:
375
- return NativeGitRepository
376
-
377
-
378
- class DulwichGitRepositoryTest(_GitRepositoryTestBase):
379
- """Test suite for DulwichGitRepository"""
380
-
381
- __test__ = True
382
-
383
- @property
384
- def repo_class(self) -> Type[GitRepositoryInterface]:
385
- return DulwichGitRepository
386
-
387
-
388
- class GitImplementationComparisonTest(unittest.TestCase):
389
- """Test suite to compare both implementations and ensure they have identical behavior"""
390
-
391
- def setUp(self):
392
- """Set up test environments for both implementations"""
393
- super().setUp()
394
- self.temp_dir1 = Path(tempfile.mkdtemp())
395
- self.temp_dir2 = Path(tempfile.mkdtemp())
396
- self.native_repo = NativeGitRepository(self.temp_dir1)
397
- self.native_repo.configure_git_user("Test User", "test@example.com")
398
- self.dulwich_repo = DulwichGitRepository(self.temp_dir2)
399
- self.dulwich_repo.configure_git_user("Test User", "test@example.com")
400
-
401
- # Wrap repo methods to auto-configure git when needed
402
- self._setup_repo_wrapper(self.native_repo)
403
- self._setup_repo_wrapper(self.dulwich_repo)
404
-
405
- def _setup_repo_wrapper(self, repo):
406
- """Set up commit wrapper for a repository"""
407
- original_commit = repo.commit_changes
408
-
409
- def commit_wrapper(message: str) -> bool:
410
- self._ensure_git_configured(repo)
411
- return original_commit(message)
412
-
413
- repo.commit_changes = commit_wrapper
414
-
415
- def _ensure_git_configured(self, repo):
416
- """Ensure git is configured for commits (required in CI environments)"""
417
- # Check if user is already configured
418
- if not repo.get_git_config("user.name"):
419
- repo.set_git_config("user.name", "Test User")
420
- if not repo.get_git_config("user.email"):
421
- repo.set_git_config("user.email", "test@example.com")
422
-
423
- def tearDown(self):
424
- """Clean up test environments"""
425
- super().tearDown()
426
- for temp_dir in [self.temp_dir1, self.temp_dir2]:
427
- if temp_dir and temp_dir.exists():
428
- shutil.rmtree(temp_dir, ignore_errors=True)
429
-
430
- def _create_identical_file(self, filename: str, content: str) -> Tuple[Path, Path]:
431
- """Create identical files in both test directories"""
432
- file1 = self.temp_dir1 / filename
433
- file2 = self.temp_dir2 / filename
434
-
435
- file1.parent.mkdir(parents=True, exist_ok=True)
436
- file2.parent.mkdir(parents=True, exist_ok=True)
437
-
438
- file1.write_text(content)
439
- file2.write_text(content)
440
-
441
- return file1, file2
442
-
443
- def test_git_availability_identical(self):
444
- """Test that both implementations report git availability identically"""
445
- native_available = self.native_repo.is_git_available()
446
- dulwich_available = self.dulwich_repo.is_git_available()
447
-
448
- self.assertEqual(
449
- native_available,
450
- dulwich_available,
451
- "Git availability detection differs between implementations",
452
- )
453
- self.assertIsInstance(native_available, bool)
454
- self.assertIsInstance(dulwich_available, bool)
455
-
456
- def test_repository_initialization_identical(self):
457
- """Test that both implementations initialize repositories identically"""
458
- # Create test files before initialization to test that init adds and commits them
459
- self._create_identical_file("test_file.txt", "test content for init")
460
- self._create_identical_file("another_file.py", "print('hello from init')")
461
-
462
- # Initialize repositories
463
- native_init = self.native_repo.init_repository()
464
- dulwich_init = self.dulwich_repo.init_repository()
465
-
466
- self.assertEqual(
467
- native_init, dulwich_init, "Repository initialization behavior differs"
468
- )
469
- self.assertTrue(native_init, "Native repo initialization should succeed")
470
- self.assertTrue(dulwich_init, "Dulwich repo initialization should succeed")
471
-
472
- # Both implementations should create repos
473
- self.assertTrue(
474
- self.native_repo.is_git_repository(), "Native should create git repo"
475
- )
476
- self.assertTrue(
477
- self.dulwich_repo.is_git_repository(), "Dulwich should create git repo"
478
- )
479
-
480
- # Both should have no uncommitted changes (because init should add and commit all files)
481
- native_uncommitted = self.native_repo.has_uncommitted_changes()
482
- dulwich_uncommitted = self.dulwich_repo.has_uncommitted_changes()
483
-
484
- self.assertEqual(
485
- native_uncommitted,
486
- dulwich_uncommitted,
487
- f"Uncommitted changes after init differs: Native={native_uncommitted}, Dulwich={dulwich_uncommitted}",
488
- )
489
- self.assertFalse(
490
- native_uncommitted, "Native: should have no uncommitted changes after init"
491
- )
492
- self.assertFalse(
493
- dulwich_uncommitted,
494
- "Dulwich: should have no uncommitted changes after init",
495
- )
496
-
497
- # Both should have an initial commit
498
- native_last_commit = self.native_repo.get_last_commit()
499
- dulwich_last_commit = self.dulwich_repo.get_last_commit()
500
-
501
- self.assertIsNotNone(native_last_commit, "Native: should have initial commit")
502
- self.assertIsNotNone(dulwich_last_commit, "Dulwich: should have initial commit")
503
-
504
- assert native_last_commit is not None
505
- assert dulwich_last_commit is not None
506
-
507
- # Both should have same commit message
508
- self.assertEqual(
509
- native_last_commit.message,
510
- dulwich_last_commit.message,
511
- "Initial commit messages should be identical",
512
- )
513
- self.assertEqual(
514
- native_last_commit.message,
515
- "First commit",
516
- "Initial commit message should be 'First commit'",
517
- )
518
-
519
- def test_repository_detection_identical(self):
520
- """Test that both implementations detect repositories identically"""
521
- # Test on non-repo directories
522
- native_is_repo_before = self.native_repo.is_git_repository()
523
- dulwich_is_repo_before = self.dulwich_repo.is_git_repository()
524
-
525
- self.assertEqual(
526
- native_is_repo_before,
527
- dulwich_is_repo_before,
528
- "Repository detection differs on non-repo directories",
529
- )
530
-
531
- # Initialize repositories
532
- self.native_repo.init_repository()
533
- self.dulwich_repo.init_repository()
534
-
535
- # Test on repo directories
536
- native_is_repo_after = self.native_repo.is_git_repository()
537
- dulwich_is_repo_after = self.dulwich_repo.is_git_repository()
538
-
539
- self.assertEqual(
540
- native_is_repo_after,
541
- dulwich_is_repo_after,
542
- "Repository detection differs on repo directories",
543
- )
544
- self.assertTrue(native_is_repo_after, "Native should detect repo after init")
545
- self.assertTrue(dulwich_is_repo_after, "Dulwich should detect repo after init")
546
-
547
- def test_changed_files_detection_identical(self):
548
- """Test that both implementations detect changed files identically"""
549
- # Initialize repositories
550
- self.native_repo.init_repository()
551
- self.dulwich_repo.init_repository()
552
-
553
- # Create identical test files
554
- self._create_identical_file("test.txt", "test content")
555
- self._create_identical_file("subdir/nested.txt", "nested content")
556
-
557
- # Get changed files
558
- native_changed = set(self.native_repo.get_changed_files())
559
- dulwich_changed = set(self.dulwich_repo.get_changed_files())
560
-
561
- self.assertEqual(
562
- native_changed,
563
- dulwich_changed,
564
- f"Changed files detection differs: Native={native_changed}, Dulwich={dulwich_changed}",
565
- )
566
-
567
- # Git reports directories for new directory structures
568
- expected_files = {"test.txt", "subdir/"}
569
- self.assertEqual(native_changed, expected_files)
570
- self.assertEqual(dulwich_changed, expected_files)
571
-
572
- def test_commit_behavior_identical(self):
573
- """Test that both implementations commit changes identically"""
574
- # Initialize repositories
575
- self.native_repo.init_repository()
576
- self.dulwich_repo.init_repository()
577
-
578
- # Create identical test files
579
- self._create_identical_file("test.txt", "initial content")
580
-
581
- # Commit changes
582
- native_commit_success = self.native_repo.commit_changes("Test commit")
583
- dulwich_commit_success = self.dulwich_repo.commit_changes("Test commit")
584
-
585
- self.assertEqual(
586
- native_commit_success,
587
- dulwich_commit_success,
588
- "Commit success behavior differs",
589
- )
590
- self.assertTrue(native_commit_success, "Native commit should succeed")
591
- self.assertTrue(dulwich_commit_success, "Dulwich commit should succeed")
592
-
593
- # Check last commit
594
- native_last_commit = self.native_repo.get_last_commit()
595
- dulwich_last_commit = self.dulwich_repo.get_last_commit()
596
-
597
- self.assertIsNotNone(native_last_commit, "Native should have last commit")
598
- self.assertIsNotNone(dulwich_last_commit, "Dulwich should have last commit")
599
-
600
- assert native_last_commit is not None
601
- assert dulwich_last_commit is not None
602
-
603
- self.assertEqual(
604
- native_last_commit.message,
605
- dulwich_last_commit.message,
606
- "Commit messages should be identical",
607
- )
608
- self.assertEqual(native_last_commit.message, "Test commit")
609
-
610
- def test_repository_status_identical(self):
611
- """Test that both implementations report repository status identically"""
612
- # Get status on fresh repositories
613
- native_status = self.native_repo.get_repository_status()
614
- dulwich_status = self.dulwich_repo.get_repository_status()
615
-
616
- # Both should be available after initialization
617
- self.assertEqual(
618
- native_status.available,
619
- dulwich_status.available,
620
- "Repository status availability differs",
621
- )
622
- self.assertTrue(native_status.available, "Native repo should be available")
623
- self.assertTrue(dulwich_status.available, "Dulwich repo should be available")
624
-
625
- # Test with changes
626
- self._create_identical_file("test.txt", "content")
627
-
628
- native_status_with_changes = self.native_repo.get_repository_status()
629
- dulwich_status_with_changes = self.dulwich_repo.get_repository_status()
630
-
631
- self.assertEqual(
632
- native_status_with_changes.has_changes,
633
- dulwich_status_with_changes.has_changes,
634
- "Change detection in status differs",
635
- )
636
- self.assertTrue(native_status_with_changes.has_changes)
637
- self.assertTrue(dulwich_status_with_changes.has_changes)
638
-
639
- def test_config_operations_identical(self):
640
- """Test that both implementations handle configuration identically"""
641
- # Initialize repositories
642
- self.native_repo.init_repository()
643
- self.dulwich_repo.init_repository()
644
-
645
- # Set configuration
646
- native_set_success = self.native_repo.set_git_config("user.name", "Test User")
647
- dulwich_set_success = self.dulwich_repo.set_git_config("user.name", "Test User")
648
-
649
- self.assertEqual(
650
- native_set_success, dulwich_set_success, "Config set behavior differs"
651
- )
652
- self.assertTrue(native_set_success, "Native config set should succeed")
653
- self.assertTrue(dulwich_set_success, "Dulwich config set should succeed")
654
-
655
- # Get configuration
656
- native_config_value = self.native_repo.get_git_config("user.name")
657
- dulwich_config_value = self.dulwich_repo.get_git_config("user.name")
658
-
659
- self.assertEqual(
660
- native_config_value, dulwich_config_value, "Config get behavior differs"
661
- )
662
- self.assertEqual(native_config_value, "Test User")
663
- self.assertEqual(dulwich_config_value, "Test User")
664
-
665
- def test_remote_operations_identical(self):
666
- """Test that both implementations handle remotes identically"""
667
- # Initialize repositories
668
- self.native_repo.init_repository()
669
- self.dulwich_repo.init_repository()
670
-
671
- # Initially no remotes
672
- native_initial_remotes = self.native_repo.get_remotes()
673
- dulwich_initial_remotes = self.dulwich_repo.get_remotes()
674
-
675
- self.assertEqual(
676
- set(native_initial_remotes),
677
- set(dulwich_initial_remotes),
678
- "Initial remotes differ",
679
- )
680
- self.assertEqual(len(native_initial_remotes), 0)
681
- self.assertEqual(len(dulwich_initial_remotes), 0)
682
-
683
- # Add remote
684
- remote_url = "https://example.com/repo.git"
685
- native_add_success = self.native_repo.add_remote("origin", remote_url)
686
- dulwich_add_success = self.dulwich_repo.add_remote("origin", remote_url)
687
-
688
- self.assertEqual(
689
- native_add_success, dulwich_add_success, "Add remote behavior differs"
690
- )
691
- self.assertTrue(native_add_success, "Native add remote should succeed")
692
- self.assertTrue(dulwich_add_success, "Dulwich add remote should succeed")
693
-
694
- # Check remote exists
695
- native_has_origin = self.native_repo.has_remote("origin")
696
- dulwich_has_origin = self.dulwich_repo.has_remote("origin")
697
-
698
- self.assertEqual(
699
- native_has_origin, dulwich_has_origin, "Remote existence check differs"
700
- )
701
- self.assertTrue(native_has_origin, "Native should have origin remote")
702
- self.assertTrue(dulwich_has_origin, "Dulwich should have origin remote")
703
-
704
- # Get remotes after adding
705
- native_remotes_after = set(self.native_repo.get_remotes())
706
- dulwich_remotes_after = set(self.dulwich_repo.get_remotes())
707
-
708
- self.assertEqual(
709
- native_remotes_after,
710
- dulwich_remotes_after,
711
- "Remotes list differs after adding",
712
- )
713
- self.assertIn("origin", native_remotes_after)
714
- self.assertIn("origin", dulwich_remotes_after)
715
-
716
- def test_uncommitted_changes_detection_identical(self):
717
- """Test that both implementations detect uncommitted changes identically"""
718
- # Initialize repositories
719
- self.native_repo.init_repository()
720
- self.dulwich_repo.init_repository()
721
-
722
- # Create and commit initial files
723
- self._create_identical_file("test.txt", "initial")
724
- self.native_repo.commit_changes("Initial commit")
725
- self.dulwich_repo.commit_changes("Initial commit")
726
-
727
- # Should have no changes after commit
728
- native_no_changes = self.native_repo.has_uncommitted_changes()
729
- dulwich_no_changes = self.dulwich_repo.has_uncommitted_changes()
730
-
731
- self.assertEqual(
732
- native_no_changes,
733
- dulwich_no_changes,
734
- "Uncommitted changes detection differs after commit",
735
- )
736
- self.assertFalse(
737
- native_no_changes, "Native should have no changes after commit"
738
- )
739
- self.assertFalse(
740
- dulwich_no_changes, "Dulwich should have no changes after commit"
741
- )
742
-
743
- # Modify files - should have changes
744
- (self.temp_dir1 / "test.txt").write_text("modified")
745
- (self.temp_dir2 / "test.txt").write_text("modified")
746
-
747
- native_has_changes = self.native_repo.has_uncommitted_changes()
748
- dulwich_has_changes = self.dulwich_repo.has_uncommitted_changes()
749
-
750
- self.assertEqual(
751
- native_has_changes,
752
- dulwich_has_changes,
753
- "Uncommitted changes detection differs after modification",
754
- )
755
- self.assertTrue(native_has_changes, "Native should detect changes")
756
- self.assertTrue(dulwich_has_changes, "Dulwich should detect changes")
757
-
758
- def test_branch_operations_identical(self):
759
- """Test that both implementations handle branches identically"""
760
- # Create test files before initialization to ensure commits are made
761
- self._create_identical_file("test.txt", "test content")
762
-
763
- # Initialize repositories
764
- self.native_repo.init_repository()
765
- self.dulwich_repo.init_repository()
766
-
767
- # Get current branch
768
- native_branch = self.native_repo.get_current_branch()
769
- dulwich_branch = self.dulwich_repo.get_current_branch()
770
-
771
- # Both should return the same type (str or None)
772
- self.assertEqual(
773
- type(native_branch),
774
- type(dulwich_branch),
775
- "Current branch return types differ",
776
- )
777
-
778
- # Get all branches
779
- native_branches = self.native_repo.get_all_branches()
780
- dulwich_branches = self.dulwich_repo.get_all_branches()
781
-
782
- self.assertEqual(
783
- type(native_branches),
784
- type(dulwich_branches),
785
- "All branches should return lists",
786
- )
787
- self.assertIsInstance(native_branches, list)
788
- self.assertIsInstance(dulwich_branches, list)
789
- # With files to commit, both should have the same branches
790
- self.assertEqual(
791
- set(native_branches),
792
- set(dulwich_branches),
793
- "Branch lists should be identical",
794
- )
795
-
796
- def test_single_main_branch_only(self):
797
- """Test that both implementations create only the main branch during initialization"""
798
- # Create test files before initialization
799
- self._create_identical_file("test.txt", "test content")
800
-
801
- # Initialize repositories
802
- self.native_repo.init_repository()
803
- self.dulwich_repo.init_repository()
804
-
805
- # Get all branches for both implementations
806
- native_branches = self.native_repo.get_all_branches()
807
- dulwich_branches = self.dulwich_repo.get_all_branches()
808
-
809
- # Both should have exactly one branch
810
- self.assertEqual(
811
- len(native_branches),
812
- 1,
813
- f"Native repo should have exactly 1 branch, got {len(native_branches)}: {native_branches}",
814
- )
815
- self.assertEqual(
816
- len(dulwich_branches),
817
- 1,
818
- f"Dulwich repo should have exactly 1 branch, got {len(dulwich_branches)}: {dulwich_branches}",
819
- )
820
-
821
- # Both should have the same single branch
822
- self.assertEqual(
823
- native_branches,
824
- dulwich_branches,
825
- f"Branch lists should be identical: Native={native_branches}, Dulwich={dulwich_branches}",
826
- )
827
-
828
- # That branch should be 'main'
829
- self.assertEqual(
830
- native_branches[0],
831
- "main",
832
- f"Native repo should create 'main' branch, got '{native_branches[0]}'",
833
- )
834
- self.assertEqual(
835
- dulwich_branches[0],
836
- "main",
837
- f"Dulwich repo should create 'main' branch, got '{dulwich_branches[0]}'",
838
- )
839
-
840
- # Current branch should also be 'main'
841
- native_current = self.native_repo.get_current_branch()
842
- dulwich_current = self.dulwich_repo.get_current_branch()
843
-
844
- self.assertEqual(
845
- native_current,
846
- "main",
847
- f"Native repo current branch should be 'main', got '{native_current}'",
848
- )
849
- self.assertEqual(
850
- dulwich_current,
851
- "main",
852
- f"Dulwich repo current branch should be 'main', got '{dulwich_current}'",
853
- )
854
-
855
- # Both should report the same current branch
856
- self.assertEqual(
857
- native_current,
858
- dulwich_current,
859
- f"Current branch should be identical: Native='{native_current}', Dulwich='{dulwich_current}'",
860
- )
861
-
862
- # Verify no 'master' branch exists in either implementation
863
- self.assertNotIn(
864
- "master",
865
- native_branches,
866
- f"Native repo should not create 'master' branch, branches: {native_branches}",
867
- )
868
- self.assertNotIn(
869
- "master",
870
- dulwich_branches,
871
- f"Dulwich repo should not create 'master' branch, branches: {dulwich_branches}",
872
- )
873
-
874
- def test_comprehensive_workflow_identical(self):
875
- """Test a complete workflow to ensure identical behavior throughout"""
876
- # Initialize
877
- self.assertTrue(self.native_repo.init_repository())
878
- self.assertTrue(self.dulwich_repo.init_repository())
879
-
880
- # Create files
881
- self._create_identical_file("file1.txt", "content 1")
882
- self._create_identical_file("dir/file2.txt", "content 2")
883
-
884
- # Check changes
885
- native_changes_1 = set(self.native_repo.get_changed_files())
886
- dulwich_changes_1 = set(self.dulwich_repo.get_changed_files())
887
- self.assertEqual(native_changes_1, dulwich_changes_1)
888
-
889
- # Commit
890
- self.assertTrue(self.native_repo.commit_changes("First commit"))
891
- self.assertTrue(self.dulwich_repo.commit_changes("First commit"))
892
-
893
- # Verify no changes after commit
894
- self.assertFalse(self.native_repo.has_uncommitted_changes())
895
- self.assertFalse(self.dulwich_repo.has_uncommitted_changes())
896
-
897
- # Modify files
898
- (self.temp_dir1 / "file1.txt").write_text("modified content 1")
899
- (self.temp_dir2 / "file1.txt").write_text("modified content 1")
900
-
901
- # Create new files
902
- self._create_identical_file("new_file.txt", "new content")
903
-
904
- # Check changes again
905
- native_changes_2 = set(self.native_repo.get_changed_files())
906
- dulwich_changes_2 = set(self.dulwich_repo.get_changed_files())
907
- self.assertEqual(native_changes_2, dulwich_changes_2)
908
-
909
- # Should detect changes
910
- self.assertTrue(self.native_repo.has_uncommitted_changes())
911
- self.assertTrue(self.dulwich_repo.has_uncommitted_changes())
912
-
913
- # Get status
914
- native_status = self.native_repo.get_repository_status()
915
- dulwich_status = self.dulwich_repo.get_repository_status()
916
-
917
- self.assertEqual(native_status.available, dulwich_status.available)
918
- self.assertEqual(native_status.has_changes, dulwich_status.has_changes)
919
-
920
- # Commit again
921
- self.assertTrue(self.native_repo.commit_changes("Second commit"))
922
- self.assertTrue(self.dulwich_repo.commit_changes("Second commit"))
923
-
924
- # Get commit history
925
- native_history = self.native_repo.get_commit_history(limit=5)
926
- dulwich_history = self.dulwich_repo.get_commit_history(limit=5)
927
-
928
- self.assertEqual(len(native_history), len(dulwich_history))
929
- if native_history and dulwich_history:
930
- self.assertEqual(native_history[0].message, dulwich_history[0].message)
931
-
932
- def test_merge_conflict_detection_missing_commit_identical(self):
933
- """Test merge conflict detection with non-existent remote commit - should be identical"""
934
- # Initialize repositories
935
- self.native_repo.init_repository()
936
- self.dulwich_repo.init_repository()
937
-
938
- # Create and commit a file
939
- self._create_identical_file("test.txt", "content")
940
- self.native_repo.commit_changes("Test commit")
941
- self.dulwich_repo.commit_changes("Test commit")
942
-
943
- # Test with non-existent commit hash
944
- fake_commit = "ed29b9897832c8cb1f04687ef9f515028d3e96ed"
945
-
946
- native_conflicts = self.native_repo.check_merge_conflicts(fake_commit)
947
- dulwich_conflicts = self.dulwich_repo.check_merge_conflicts(fake_commit)
948
-
949
- self.assertEqual(
950
- native_conflicts,
951
- dulwich_conflicts,
952
- f"Merge conflict detection with missing commit differs: Native={native_conflicts}, Dulwich={dulwich_conflicts}",
953
- )
954
- # Should both return True (conflicts) for missing commits
955
- self.assertTrue(
956
- native_conflicts, "Native should report conflicts for missing commit"
957
- )
958
- self.assertTrue(
959
- dulwich_conflicts, "Dulwich should report conflicts for missing commit"
960
- )
961
-
962
- def test_merge_conflict_detection_same_commit_identical(self):
963
- """Test merge conflict detection with same commit - should be identical (no conflicts)"""
964
- # Initialize repositories
965
- self.native_repo.init_repository()
966
- self.dulwich_repo.init_repository()
967
-
968
- # Create and commit a file
969
- self._create_identical_file("test.txt", "content")
970
- self.native_repo.commit_changes("Test commit")
971
- self.dulwich_repo.commit_changes("Test commit")
972
-
973
- # Get the commit hash
974
- native_commit = self.native_repo.get_last_commit()
975
- dulwich_commit = self.dulwich_repo.get_last_commit()
976
-
977
- assert native_commit is not None
978
- assert dulwich_commit is not None
979
-
980
- # Test merge conflict with same commit
981
- native_conflicts = self.native_repo.check_merge_conflicts(native_commit.hash)
982
- dulwich_conflicts = self.dulwich_repo.check_merge_conflicts(dulwich_commit.hash)
983
-
984
- self.assertEqual(
985
- native_conflicts,
986
- dulwich_conflicts,
987
- f"Merge conflict detection with same commit differs: Native={native_conflicts}, Dulwich={dulwich_conflicts}",
988
- )
989
- # Should both return False (no conflicts) for same commit
990
- self.assertFalse(
991
- native_conflicts, "Native should report no conflicts for same commit"
992
- )
993
- self.assertFalse(
994
- dulwich_conflicts, "Dulwich should report no conflicts for same commit"
995
- )
996
-
997
- def test_merge_conflict_detection_unrelated_histories_identical(self):
998
- """Test merge conflict detection with unrelated histories"""
999
- # Create separate repos to simulate unrelated histories
1000
- temp_dir3 = Path(tempfile.mkdtemp())
1001
- temp_dir4 = Path(tempfile.mkdtemp())
1002
-
1003
- try:
1004
- repo3 = NativeGitRepository(temp_dir3)
1005
- repo4 = DulwichGitRepository(temp_dir4)
1006
-
1007
- # Configure git for both
1008
- self._setup_repo_wrapper(repo3)
1009
- self._setup_repo_wrapper(repo4)
1010
-
1011
- # Initialize first repo
1012
- self.native_repo.init_repository()
1013
- self.dulwich_repo.init_repository()
1014
-
1015
- # Create first commit
1016
- self._create_identical_file("file1.txt", "content1")
1017
- self.native_repo.commit_changes("First commit")
1018
- self.dulwich_repo.commit_changes("First commit")
1019
-
1020
- # Initialize separate repos with different history
1021
- repo3.init_repository()
1022
- repo4.init_repository()
1023
-
1024
- # Create different first commit in separate repos
1025
- (temp_dir3 / "file2.txt").write_text("different content")
1026
- (temp_dir4 / "file2.txt").write_text("different content")
1027
- repo3.commit_changes("Different first commit")
1028
- repo4.commit_changes("Different first commit")
1029
-
1030
- # Get commit from separate repo
1031
- separate_commit = repo3.get_last_commit()
1032
-
1033
- assert separate_commit is not None
1034
-
1035
- # Test conflict detection with unrelated commit
1036
- # Note: This tests the case where commit exists but has no common ancestor
1037
- native_conflicts = self.native_repo.check_merge_conflicts(
1038
- separate_commit.hash
1039
- )
1040
- dulwich_conflicts = self.dulwich_repo.check_merge_conflicts(
1041
- separate_commit.hash
1042
- )
1043
-
1044
- self.assertEqual(
1045
- native_conflicts,
1046
- dulwich_conflicts,
1047
- f"Merge conflict detection with unrelated history differs: Native={native_conflicts}, Dulwich={dulwich_conflicts}",
1048
- )
1049
- # Should both return True (conflicts) for unrelated histories
1050
- self.assertTrue(
1051
- native_conflicts, "Native should report conflicts for unrelated history"
1052
- )
1053
- self.assertTrue(
1054
- dulwich_conflicts,
1055
- "Dulwich should report conflicts for unrelated history",
1056
- )
1057
-
1058
- finally:
1059
- if temp_dir3.exists():
1060
- shutil.rmtree(temp_dir3, ignore_errors=True)
1061
- if temp_dir4.exists():
1062
- shutil.rmtree(temp_dir4, ignore_errors=True)
1063
-
1064
- def test_ahead_behind_count_identical(self):
1065
- """Test ahead/behind counting behavior is identical"""
1066
- # Initialize repositories
1067
- self.native_repo.init_repository()
1068
- self.dulwich_repo.init_repository()
1069
-
1070
- # Create identical commit history
1071
- self._create_identical_file("file1.txt", "content1")
1072
- self.native_repo.commit_changes("Commit 1")
1073
- self.dulwich_repo.commit_changes("Commit 1")
1074
- commit1_native = self.native_repo.get_last_commit()
1075
- commit1_dulwich = self.dulwich_repo.get_last_commit()
1076
-
1077
- assert commit1_dulwich is not None
1078
- assert commit1_native is not None
1079
-
1080
- self._create_identical_file("file2.txt", "content2")
1081
- self.native_repo.commit_changes("Commit 2")
1082
- self.dulwich_repo.commit_changes("Commit 2")
1083
- commit2_native = self.native_repo.get_last_commit()
1084
- commit2_dulwich = self.dulwich_repo.get_last_commit()
1085
-
1086
- assert commit2_dulwich is not None
1087
- assert commit2_native is not None
1088
-
1089
- # Test same commit (should be 0,0)
1090
- native_same = self.native_repo.get_ahead_behind_count(
1091
- commit1_native.hash, commit1_native.hash
1092
- )
1093
- dulwich_same = self.dulwich_repo.get_ahead_behind_count(
1094
- commit1_dulwich.hash, commit1_dulwich.hash
1095
- )
1096
-
1097
- self.assertEqual(
1098
- native_same, dulwich_same, "Same commit ahead/behind should be identical"
1099
- )
1100
- self.assertEqual(native_same, (0, 0), "Same commit should be (0,0)")
1101
-
1102
- # Test different commits
1103
- native_diff = self.native_repo.get_ahead_behind_count(
1104
- commit2_native.hash, commit1_native.hash
1105
- )
1106
- dulwich_diff = self.dulwich_repo.get_ahead_behind_count(
1107
- commit2_dulwich.hash, commit1_dulwich.hash
1108
- )
1109
-
1110
- self.assertEqual(
1111
- native_diff,
1112
- dulwich_diff,
1113
- "Different commit ahead/behind should be identical",
1114
- )
1115
-
1116
- def test_pull_changes_simulation_identical(self):
1117
- """Test simulated pull behavior (without actual remote) is identical"""
1118
- # Initialize repositories
1119
- self.native_repo.init_repository()
1120
- self.dulwich_repo.init_repository()
1121
-
1122
- # Create initial state
1123
- self._create_identical_file("test.txt", "initial")
1124
- self.native_repo.commit_changes("Initial commit")
1125
- self.dulwich_repo.commit_changes("Initial commit")
1126
-
1127
- # Add fake remote
1128
- fake_remote_url = "https://github.com/fake/repo.git"
1129
- native_add_remote = self.native_repo.add_remote("origin", fake_remote_url)
1130
- dulwich_add_remote = self.dulwich_repo.add_remote("origin", fake_remote_url)
1131
-
1132
- self.assertEqual(
1133
- native_add_remote,
1134
- dulwich_add_remote,
1135
- "Add remote should behave identically",
1136
- )
1137
- self.assertTrue(native_add_remote, "Should successfully add remote")
1138
-
1139
- # Test pull behavior with non-existent remote (should both fail identically)
1140
- native_pull = self.native_repo.pull_changes(conflict_resolution="theirs")
1141
- dulwich_pull = self.dulwich_repo.pull_changes(conflict_resolution="theirs")
1142
-
1143
- self.assertEqual(
1144
- native_pull,
1145
- dulwich_pull,
1146
- "Pull behavior with fake remote should be identical",
1147
- )
1148
- # Both should fail since remote doesn't exist
1149
- self.assertFalse(native_pull, "Pull from fake remote should fail")
1150
- self.assertFalse(dulwich_pull, "Pull from fake remote should fail")
1151
-
1152
- def test_repository_state_after_operations_identical(self):
1153
- """Test that repository state remains identical after various operations"""
1154
- # Initialize repositories
1155
- self.native_repo.init_repository()
1156
- self.dulwich_repo.init_repository()
1157
-
1158
- # Helper to compare full repository state
1159
- def compare_repo_states(operation_name):
1160
- native_status = self.native_repo.get_repository_status()
1161
- dulwich_status = self.dulwich_repo.get_repository_status()
1162
-
1163
- self.assertEqual(
1164
- native_status.available,
1165
- dulwich_status.available,
1166
- f"After {operation_name}: availability differs",
1167
- )
1168
- self.assertEqual(
1169
- native_status.has_changes,
1170
- dulwich_status.has_changes,
1171
- f"After {operation_name}: has_changes differs",
1172
- )
1173
- self.assertEqual(
1174
- native_status.branch,
1175
- dulwich_status.branch,
1176
- f"After {operation_name}: current branch differs",
1177
- )
1178
- self.assertEqual(
1179
- set(native_status.branches),
1180
- set(dulwich_status.branches),
1181
- f"After {operation_name}: branch list differs",
1182
- )
1183
-
1184
- # Compare changed files
1185
- native_changed = set(self.native_repo.get_changed_files())
1186
- dulwich_changed = set(self.dulwich_repo.get_changed_files())
1187
- self.assertEqual(
1188
- native_changed,
1189
- dulwich_changed,
1190
- f"After {operation_name}: changed files differ",
1191
- )
1192
-
1193
- # Test state after initialization
1194
- compare_repo_states("initialization")
1195
-
1196
- # Add files and test state
1197
- self._create_identical_file("test.txt", "content")
1198
- self._create_identical_file("dir/nested.txt", "nested")
1199
- compare_repo_states("adding files")
1200
-
1201
- # Commit and test state
1202
- self.native_repo.commit_changes("Test commit")
1203
- self.dulwich_repo.commit_changes("Test commit")
1204
- compare_repo_states("first commit")
1205
-
1206
- # Modify files and test state
1207
- (self.temp_dir1 / "test.txt").write_text("modified")
1208
- (self.temp_dir2 / "test.txt").write_text("modified")
1209
- compare_repo_states("modifying files")
1210
-
1211
- # Add new files and test state
1212
- self._create_identical_file("new.txt", "new content")
1213
- compare_repo_states("adding new files")
1214
-
1215
- # Commit again and test state
1216
- self.native_repo.commit_changes("Second commit")
1217
- self.dulwich_repo.commit_changes("Second commit")
1218
- compare_repo_states("second commit")
1219
-
1220
- def test_stash_behavior_identical(self):
1221
- """Test that stash operations behave identically"""
1222
- # Initialize repositories
1223
- self.native_repo.init_repository()
1224
- self.dulwich_repo.init_repository()
1225
-
1226
- # Create and commit initial files
1227
- self._create_identical_file("test.txt", "initial")
1228
- self.native_repo.commit_changes("Initial commit")
1229
- self.dulwich_repo.commit_changes("Initial commit")
1230
-
1231
- # Modify files to create changes to stash
1232
- (self.temp_dir1 / "test.txt").write_text("modified for stash")
1233
- (self.temp_dir2 / "test.txt").write_text("modified for stash")
1234
-
1235
- # Verify we have changes before stash
1236
- self.assertTrue(self.native_repo.has_uncommitted_changes())
1237
- self.assertTrue(self.dulwich_repo.has_uncommitted_changes())
1238
-
1239
- # Stash changes
1240
- native_stash = self.native_repo.stash_changes("Test stash")
1241
- dulwich_stash = self.dulwich_repo.stash_changes("Test stash")
1242
-
1243
- self.assertEqual(
1244
- native_stash, dulwich_stash, "Stash operation results should be identical"
1245
- )
1246
- self.assertTrue(native_stash, "Native stash should succeed")
1247
- self.assertTrue(dulwich_stash, "Dulwich stash should succeed")
1248
-
1249
- # Verify state after stash
1250
- native_changes_after = self.native_repo.has_uncommitted_changes()
1251
- dulwich_changes_after = self.dulwich_repo.has_uncommitted_changes()
1252
-
1253
- self.assertEqual(
1254
- native_changes_after,
1255
- dulwich_changes_after,
1256
- "Changes detection after stash should be identical",
1257
- )
1258
- self.assertFalse(native_changes_after, "Should have no changes after stash")
1259
- self.assertFalse(dulwich_changes_after, "Should have no changes after stash")
1260
-
1261
- # Verify file content after stash
1262
- native_content = (self.temp_dir1 / "test.txt").read_text()
1263
- dulwich_content = (self.temp_dir2 / "test.txt").read_text()
1264
-
1265
- self.assertEqual(
1266
- native_content,
1267
- dulwich_content,
1268
- "File content after stash should be identical",
1269
- )
1270
- self.assertEqual(
1271
- native_content, "initial", "File should be reverted to original content"
1272
- )
1273
-
1274
- def test_checkout_commit_behavior_identical(self):
1275
- """Test that commit checkout behaves identically"""
1276
- # Initialize repositories
1277
- self.native_repo.init_repository()
1278
- self.dulwich_repo.init_repository()
1279
-
1280
- # Create commit history
1281
- self._create_identical_file("test.txt", "version 1")
1282
- self.native_repo.commit_changes("First commit")
1283
- self.dulwich_repo.commit_changes("First commit")
1284
- first_commit_native = self.native_repo.get_last_commit()
1285
- first_commit_dulwich = self.dulwich_repo.get_last_commit()
1286
-
1287
- self._create_identical_file("test.txt", "version 2")
1288
- self.native_repo.commit_changes("Second commit")
1289
- self.dulwich_repo.commit_changes("Second commit")
1290
-
1291
- assert first_commit_native is not None
1292
- assert first_commit_dulwich is not None
1293
-
1294
- # Checkout first commit
1295
- native_checkout = self.native_repo.checkout_commit(first_commit_native.hash)
1296
- dulwich_checkout = self.dulwich_repo.checkout_commit(first_commit_dulwich.hash)
1297
-
1298
- self.assertEqual(
1299
- native_checkout, dulwich_checkout, "Checkout results should be identical"
1300
- )
1301
-
1302
- if native_checkout: # Only test if checkout succeeded
1303
- # Verify file content after checkout
1304
- native_content = (self.temp_dir1 / "test.txt").read_text()
1305
- dulwich_content = (self.temp_dir2 / "test.txt").read_text()
1306
-
1307
- self.assertEqual(
1308
- native_content,
1309
- dulwich_content,
1310
- "File content after checkout should be identical",
1311
- )
1312
- self.assertEqual(
1313
- native_content, "version 1", "Should have first version content"
1314
- )
1315
-
1316
- def test_merge_conflict_with_actual_changes_identical(self):
1317
- """Test merge conflict detection with actual conflicting changes"""
1318
- # Initialize repositories
1319
- self.native_repo.init_repository()
1320
- self.dulwich_repo.init_repository()
1321
-
1322
- # Create base commit
1323
- self._create_identical_file("conflict.txt", "base content")
1324
- self.native_repo.commit_changes("Base commit")
1325
- self.dulwich_repo.commit_changes("Base commit")
1326
- base_commit_native = self.native_repo.get_last_commit()
1327
- base_commit_dulwich = self.dulwich_repo.get_last_commit()
1328
-
1329
- assert base_commit_native is not None
1330
- assert base_commit_dulwich is not None
1331
-
1332
- # Create first branch of changes
1333
- (self.temp_dir1 / "conflict.txt").write_text("branch 1 content")
1334
- (self.temp_dir2 / "conflict.txt").write_text("branch 1 content")
1335
- self.native_repo.commit_changes("Branch 1 changes")
1336
- self.dulwich_repo.commit_changes("Branch 1 changes")
1337
- branch1_commit_native = self.native_repo.get_last_commit()
1338
- branch1_commit_dulwich = self.dulwich_repo.get_last_commit()
1339
-
1340
- assert branch1_commit_native is not None
1341
- assert branch1_commit_dulwich is not None
1342
-
1343
- # Reset to base and create conflicting changes
1344
- self.native_repo.checkout_commit(base_commit_native.hash)
1345
- self.dulwich_repo.checkout_commit(base_commit_dulwich.hash)
1346
-
1347
- (self.temp_dir1 / "conflict.txt").write_text("branch 2 content")
1348
- (self.temp_dir2 / "conflict.txt").write_text("branch 2 content")
1349
- self.native_repo.commit_changes("Branch 2 changes")
1350
- self.dulwich_repo.commit_changes("Branch 2 changes")
1351
-
1352
- # Test conflict detection between conflicting branches
1353
- native_conflicts = self.native_repo.check_merge_conflicts(
1354
- branch1_commit_native.hash
1355
- )
1356
- dulwich_conflicts = self.dulwich_repo.check_merge_conflicts(
1357
- branch1_commit_dulwich.hash
1358
- )
1359
-
1360
- self.assertEqual(
1361
- native_conflicts,
1362
- dulwich_conflicts,
1363
- "Conflict detection with actual conflicts should be identical",
1364
- )
1365
-
1366
- def test_complex_workflow_with_multiple_commits_identical(self):
1367
- """Test complex workflow with multiple commits, modifications, and state changes"""
1368
- # Initialize repositories
1369
- self.native_repo.init_repository()
1370
- self.dulwich_repo.init_repository()
1371
-
1372
- commits_native = []
1373
- commits_dulwich = []
1374
-
1375
- # Create a series of commits with various file operations
1376
- operations = [
1377
- (
1378
- "Create initial files",
1379
- lambda: [
1380
- self._create_identical_file("file1.txt", "initial content 1"),
1381
- self._create_identical_file("dir/file2.txt", "initial content 2"),
1382
- ],
1383
- ),
1384
- (
1385
- "Modify existing files",
1386
- lambda: [
1387
- (self.temp_dir1 / "file1.txt").write_text("modified content 1"),
1388
- (self.temp_dir2 / "file1.txt").write_text("modified content 1"),
1389
- ],
1390
- ),
1391
- (
1392
- "Add new files",
1393
- lambda: [
1394
- self._create_identical_file("file3.txt", "new content 3"),
1395
- self._create_identical_file(
1396
- "dir/subdir/file4.txt", "nested content 4"
1397
- ),
1398
- ],
1399
- ),
1400
- (
1401
- "Delete and recreate",
1402
- lambda: [
1403
- (self.temp_dir1 / "file1.txt").unlink(),
1404
- (self.temp_dir2 / "file1.txt").unlink(),
1405
- self._create_identical_file("file1.txt", "recreated content 1"),
1406
- ],
1407
- ),
1408
- (
1409
- "Mixed operations",
1410
- lambda: [
1411
- (self.temp_dir1 / "dir/file2.txt").write_text("updated content 2"),
1412
- (self.temp_dir2 / "dir/file2.txt").write_text("updated content 2"),
1413
- self._create_identical_file("final.txt", "final content"),
1414
- ],
1415
- ),
1416
- ]
1417
-
1418
- for i, (description, operation) in enumerate(operations):
1419
- # Perform operation
1420
- operation()
1421
-
1422
- # Check repository state before commit
1423
- native_changed = set(self.native_repo.get_changed_files())
1424
- dulwich_changed = set(self.dulwich_repo.get_changed_files())
1425
- self.assertEqual(
1426
- native_changed,
1427
- dulwich_changed,
1428
- f"Step {i} ({description}): Changed files should be identical",
1429
- )
1430
-
1431
- native_has_changes = self.native_repo.has_uncommitted_changes()
1432
- dulwich_has_changes = self.dulwich_repo.has_uncommitted_changes()
1433
- self.assertEqual(
1434
- native_has_changes,
1435
- dulwich_has_changes,
1436
- f"Step {i} ({description}): Uncommitted changes detection should be identical",
1437
- )
1438
-
1439
- # Commit changes
1440
- commit_msg = f"Commit {i + 1}: {description}"
1441
- native_commit_success = self.native_repo.commit_changes(commit_msg)
1442
- dulwich_commit_success = self.dulwich_repo.commit_changes(commit_msg)
1443
-
1444
- self.assertEqual(
1445
- native_commit_success,
1446
- dulwich_commit_success,
1447
- f"Step {i} ({description}): Commit success should be identical",
1448
- )
1449
- self.assertTrue(
1450
- native_commit_success, f"Step {i}: Native commit should succeed"
1451
- )
1452
-
1453
- # Get and verify commits
1454
- native_commit = self.native_repo.get_last_commit()
1455
- dulwich_commit = self.dulwich_repo.get_last_commit()
1456
-
1457
- self.assertIsNotNone(native_commit, f"Step {i}: Native should have commit")
1458
- self.assertIsNotNone(
1459
- dulwich_commit, f"Step {i}: Dulwich should have commit"
1460
- )
1461
-
1462
- assert native_commit is not None
1463
- assert dulwich_commit is not None
1464
-
1465
- self.assertEqual(
1466
- native_commit.message,
1467
- dulwich_commit.message,
1468
- f"Step {i}: Commit messages should be identical",
1469
- )
1470
-
1471
- commits_native.append(native_commit)
1472
- commits_dulwich.append(dulwich_commit)
1473
-
1474
- # Verify no uncommitted changes after commit
1475
- native_clean = self.native_repo.has_uncommitted_changes()
1476
- dulwich_clean = self.dulwich_repo.has_uncommitted_changes()
1477
- self.assertEqual(
1478
- native_clean,
1479
- dulwich_clean,
1480
- f"Step {i}: Post-commit clean state should be identical",
1481
- )
1482
- self.assertFalse(native_clean, f"Step {i}: Should be clean after commit")
1483
-
1484
- # Test commit history
1485
- native_history = self.native_repo.get_commit_history(limit=10)
1486
- dulwich_history = self.dulwich_repo.get_commit_history(limit=10)
1487
-
1488
- self.assertEqual(
1489
- len(native_history),
1490
- len(dulwich_history),
1491
- "Commit history length should be identical",
1492
- )
1493
-
1494
- for i, (native_h, dulwich_h) in enumerate(zip(native_history, dulwich_history)):
1495
- self.assertEqual(
1496
- native_h.message,
1497
- dulwich_h.message,
1498
- f"History item {i}: Commit messages should be identical",
1499
- )
1500
-
1501
- # Test ahead/behind counts between different commits
1502
- if len(commits_native) >= 2:
1503
- native_ahead_behind = self.native_repo.get_ahead_behind_count(
1504
- commits_native[-1].hash, commits_native[0].hash
1505
- )
1506
- dulwich_ahead_behind = self.dulwich_repo.get_ahead_behind_count(
1507
- commits_dulwich[-1].hash, commits_dulwich[0].hash
1508
- )
1509
- self.assertEqual(
1510
- native_ahead_behind,
1511
- dulwich_ahead_behind,
1512
- "Ahead/behind counts should be identical",
1513
- )
1514
-
1515
- def test_empty_commit_edge_cases_identical(self):
1516
- """Test edge cases with empty commits and repositories"""
1517
- # Test on completely empty repositories
1518
- native_status_empty = self.native_repo.get_repository_status()
1519
- dulwich_status_empty = self.dulwich_repo.get_repository_status()
1520
-
1521
- self.assertEqual(
1522
- native_status_empty.available,
1523
- dulwich_status_empty.available,
1524
- "Empty repo availability should be identical",
1525
- )
1526
-
1527
- # Initialize but don't commit anything yet
1528
- self.native_repo.init_repository()
1529
- self.dulwich_repo.init_repository()
1530
-
1531
- # Both should handle empty state identically
1532
- native_changes_empty = self.native_repo.get_changed_files()
1533
- dulwich_changes_empty = self.dulwich_repo.get_changed_files()
1534
- self.assertEqual(
1535
- set(native_changes_empty),
1536
- set(dulwich_changes_empty),
1537
- "Empty repo changed files should be identical",
1538
- )
1539
-
1540
- # Test merge conflict detection on repos without any commits
1541
- fake_hash = "1234567890abcdef1234567890abcdef12345678"
1542
- native_conflicts_empty = self.native_repo.check_merge_conflicts(fake_hash)
1543
- dulwich_conflicts_empty = self.dulwich_repo.check_merge_conflicts(fake_hash)
1544
-
1545
- self.assertEqual(
1546
- native_conflicts_empty,
1547
- dulwich_conflicts_empty,
1548
- "Empty repo conflict detection should be identical",
1549
- )
1550
-
1551
- # Test with None/empty parameters
1552
- native_conflicts_none = self.native_repo.check_merge_conflicts("")
1553
- dulwich_conflicts_none = self.dulwich_repo.check_merge_conflicts("")
1554
-
1555
- self.assertEqual(
1556
- native_conflicts_none,
1557
- dulwich_conflicts_none,
1558
- "Empty string conflict detection should be identical",
1559
- )
1560
- self.assertFalse(native_conflicts_none, "Empty string should return False")
1561
-
1562
-
1563
358
  if __name__ == "__main__":
1564
359
  loader = unittest.TestLoader()
1565
360
  suite = unittest.TestSuite()
1566
361
 
1567
362
  suite.addTests(loader.loadTestsFromTestCase(NativeGitRepositoryTest))
1568
- suite.addTests(loader.loadTestsFromTestCase(DulwichGitRepositoryTest))
1569
- suite.addTests(loader.loadTestsFromTestCase(GitImplementationComparisonTest))
1570
363
 
1571
364
  runner = unittest.TextTestRunner(verbosity=2)
1572
365
  runner.run(suite)