@api-client/core 0.18.11 → 0.18.13

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 (264) hide show
  1. package/build/src/browser.d.ts +0 -3
  2. package/build/src/browser.d.ts.map +1 -1
  3. package/build/src/browser.js +0 -3
  4. package/build/src/browser.js.map +1 -1
  5. package/build/src/index.d.ts +2 -5
  6. package/build/src/index.d.ts.map +1 -1
  7. package/build/src/index.js +2 -5
  8. package/build/src/index.js.map +1 -1
  9. package/build/src/lib/logging/DefaultLogger.d.ts +14 -0
  10. package/build/src/lib/logging/DefaultLogger.d.ts.map +1 -1
  11. package/build/src/lib/logging/DefaultLogger.js +27 -0
  12. package/build/src/lib/logging/DefaultLogger.js.map +1 -1
  13. package/build/src/lib/logging/index.d.ts +4 -0
  14. package/build/src/lib/logging/index.d.ts.map +1 -0
  15. package/build/src/lib/logging/index.js +10 -0
  16. package/build/src/lib/logging/index.js.map +1 -0
  17. package/build/src/modeling/DomainModel.d.ts.map +1 -1
  18. package/build/src/modeling/DomainModel.js +11 -4
  19. package/build/src/modeling/DomainModel.js.map +1 -1
  20. package/build/src/models/ClientCertificate.d.ts +1 -1
  21. package/build/src/models/ClientCertificate.js.map +1 -1
  22. package/build/src/models/RequestConfig.d.ts +1 -1
  23. package/build/src/models/RequestConfig.js.map +1 -1
  24. package/build/src/models/SerializableError.d.ts +1 -1
  25. package/build/src/models/SerializableError.d.ts.map +1 -1
  26. package/build/src/models/SerializableError.js.map +1 -1
  27. package/build/src/proxy/RequestProxy.d.ts.map +1 -1
  28. package/build/src/proxy/RequestProxy.js +2 -2
  29. package/build/src/proxy/RequestProxy.js.map +1 -1
  30. package/build/src/runtime/http-engine/CoreEngine.d.ts +218 -139
  31. package/build/src/runtime/http-engine/CoreEngine.d.ts.map +1 -1
  32. package/build/src/runtime/http-engine/CoreEngine.js +716 -870
  33. package/build/src/runtime/http-engine/CoreEngine.js.map +1 -1
  34. package/build/src/runtime/http-engine/PayloadSupport.d.ts.map +1 -1
  35. package/build/src/runtime/http-engine/PayloadSupport.js +2 -1
  36. package/build/src/runtime/http-engine/PayloadSupport.js.map +1 -1
  37. package/build/src/runtime/http-engine/auth/AuthManager.d.ts +73 -0
  38. package/build/src/runtime/http-engine/auth/AuthManager.d.ts.map +1 -0
  39. package/build/src/runtime/http-engine/auth/AuthManager.js +186 -0
  40. package/build/src/runtime/http-engine/auth/AuthManager.js.map +1 -0
  41. package/build/src/runtime/http-engine/auth/index.d.ts +2 -0
  42. package/build/src/runtime/http-engine/auth/index.d.ts.map +1 -0
  43. package/build/src/runtime/http-engine/auth/index.js +2 -0
  44. package/build/src/runtime/http-engine/auth/index.js.map +1 -0
  45. package/build/src/runtime/http-engine/certificates/CertificateManager.d.ts +11 -0
  46. package/build/src/runtime/http-engine/certificates/CertificateManager.d.ts.map +1 -0
  47. package/build/src/runtime/http-engine/certificates/CertificateManager.js +76 -0
  48. package/build/src/runtime/http-engine/certificates/CertificateManager.js.map +1 -0
  49. package/build/src/runtime/http-engine/certificates/index.d.ts +2 -0
  50. package/build/src/runtime/http-engine/certificates/index.d.ts.map +1 -0
  51. package/build/src/runtime/http-engine/certificates/index.js +2 -0
  52. package/build/src/runtime/http-engine/certificates/index.js.map +1 -0
  53. package/build/src/runtime/http-engine/compression/CompressionManager.d.ts +25 -0
  54. package/build/src/runtime/http-engine/compression/CompressionManager.d.ts.map +1 -0
  55. package/build/src/runtime/http-engine/compression/CompressionManager.js +89 -0
  56. package/build/src/runtime/http-engine/compression/CompressionManager.js.map +1 -0
  57. package/build/src/runtime/http-engine/compression/index.d.ts +2 -0
  58. package/build/src/runtime/http-engine/compression/index.d.ts.map +1 -0
  59. package/build/src/runtime/http-engine/compression/index.js +2 -0
  60. package/build/src/runtime/http-engine/compression/index.js.map +1 -0
  61. package/build/src/runtime/http-engine/connections/ConnectionManager.d.ts +57 -0
  62. package/build/src/runtime/http-engine/connections/ConnectionManager.d.ts.map +1 -0
  63. package/build/src/runtime/http-engine/connections/ConnectionManager.js +78 -0
  64. package/build/src/runtime/http-engine/connections/ConnectionManager.js.map +1 -0
  65. package/build/src/runtime/http-engine/connections/DigestAuthHandler.d.ts +70 -0
  66. package/build/src/runtime/http-engine/connections/DigestAuthHandler.d.ts.map +1 -0
  67. package/build/src/runtime/http-engine/connections/DigestAuthHandler.js +184 -0
  68. package/build/src/runtime/http-engine/connections/DigestAuthHandler.js.map +1 -0
  69. package/build/src/runtime/http-engine/connections/DirectConnection.d.ts +22 -0
  70. package/build/src/runtime/http-engine/connections/DirectConnection.d.ts.map +1 -0
  71. package/build/src/runtime/http-engine/connections/DirectConnection.js +105 -0
  72. package/build/src/runtime/http-engine/connections/DirectConnection.js.map +1 -0
  73. package/build/src/runtime/http-engine/connections/ProxyAuthHandler.d.ts +60 -0
  74. package/build/src/runtime/http-engine/connections/ProxyAuthHandler.d.ts.map +1 -0
  75. package/build/src/runtime/http-engine/connections/ProxyAuthHandler.js +138 -0
  76. package/build/src/runtime/http-engine/connections/ProxyAuthHandler.js.map +1 -0
  77. package/build/src/runtime/http-engine/connections/ProxyConnection.d.ts +14 -0
  78. package/build/src/runtime/http-engine/connections/ProxyConnection.d.ts.map +1 -0
  79. package/build/src/runtime/http-engine/connections/ProxyConnection.js +47 -0
  80. package/build/src/runtime/http-engine/connections/ProxyConnection.js.map +1 -0
  81. package/build/src/runtime/http-engine/connections/TunnelConnection.d.ts +13 -0
  82. package/build/src/runtime/http-engine/connections/TunnelConnection.d.ts.map +1 -0
  83. package/build/src/runtime/http-engine/connections/TunnelConnection.js +175 -0
  84. package/build/src/runtime/http-engine/connections/TunnelConnection.js.map +1 -0
  85. package/build/src/runtime/http-engine/connections/index.d.ts +7 -0
  86. package/build/src/runtime/http-engine/connections/index.d.ts.map +1 -0
  87. package/build/src/runtime/http-engine/connections/index.js +7 -0
  88. package/build/src/runtime/http-engine/connections/index.js.map +1 -0
  89. package/build/src/runtime/http-engine/constants.d.ts +69 -0
  90. package/build/src/runtime/http-engine/constants.d.ts.map +1 -0
  91. package/build/src/runtime/http-engine/constants.js +90 -0
  92. package/build/src/runtime/http-engine/constants.js.map +1 -0
  93. package/build/src/runtime/http-engine/cookies/CookieProcessor.d.ts +5 -0
  94. package/build/src/runtime/http-engine/cookies/CookieProcessor.d.ts.map +1 -0
  95. package/build/src/runtime/http-engine/cookies/CookieProcessor.js +20 -0
  96. package/build/src/runtime/http-engine/cookies/CookieProcessor.js.map +1 -0
  97. package/build/src/runtime/http-engine/cookies/index.d.ts +2 -0
  98. package/build/src/runtime/http-engine/cookies/index.d.ts.map +1 -0
  99. package/build/src/runtime/http-engine/cookies/index.js +2 -0
  100. package/build/src/runtime/http-engine/cookies/index.js.map +1 -0
  101. package/build/src/runtime/http-engine/errors/HttpEngineErrors.d.ts +156 -0
  102. package/build/src/runtime/http-engine/errors/HttpEngineErrors.d.ts.map +1 -0
  103. package/build/src/runtime/http-engine/errors/HttpEngineErrors.js +227 -0
  104. package/build/src/runtime/http-engine/errors/HttpEngineErrors.js.map +1 -0
  105. package/build/src/runtime/http-engine/errors/index.d.ts +2 -0
  106. package/build/src/runtime/http-engine/errors/index.d.ts.map +1 -0
  107. package/build/src/runtime/http-engine/errors/index.js +2 -0
  108. package/build/src/runtime/http-engine/errors/index.js.map +1 -0
  109. package/build/src/runtime/http-engine/message/MessageBuilder.d.ts +66 -0
  110. package/build/src/runtime/http-engine/message/MessageBuilder.d.ts.map +1 -0
  111. package/build/src/runtime/http-engine/message/MessageBuilder.js +161 -0
  112. package/build/src/runtime/http-engine/message/MessageBuilder.js.map +1 -0
  113. package/build/src/runtime/http-engine/message/MessageProcessor.d.ts +27 -0
  114. package/build/src/runtime/http-engine/message/MessageProcessor.d.ts.map +1 -0
  115. package/build/src/runtime/http-engine/message/MessageProcessor.js +51 -0
  116. package/build/src/runtime/http-engine/message/MessageProcessor.js.map +1 -0
  117. package/build/src/runtime/http-engine/message/index.d.ts +3 -0
  118. package/build/src/runtime/http-engine/message/index.d.ts.map +1 -0
  119. package/build/src/runtime/http-engine/message/index.js +3 -0
  120. package/build/src/runtime/http-engine/message/index.js.map +1 -0
  121. package/build/src/runtime/http-engine/ntlm/NtlmAuth.d.ts +2 -8
  122. package/build/src/runtime/http-engine/ntlm/NtlmAuth.d.ts.map +1 -1
  123. package/build/src/runtime/http-engine/ntlm/NtlmAuth.js +11 -5
  124. package/build/src/runtime/http-engine/ntlm/NtlmAuth.js.map +1 -1
  125. package/build/src/runtime/http-engine/ntlm/NtlmMessage.js +6 -6
  126. package/build/src/runtime/http-engine/ntlm/NtlmMessage.js.map +1 -1
  127. package/build/src/runtime/http-engine/parsers/BodyParser.d.ts +39 -0
  128. package/build/src/runtime/http-engine/parsers/BodyParser.d.ts.map +1 -0
  129. package/build/src/runtime/http-engine/parsers/BodyParser.js +145 -0
  130. package/build/src/runtime/http-engine/parsers/BodyParser.js.map +1 -0
  131. package/build/src/runtime/http-engine/parsers/HeadersParser.d.ts +29 -0
  132. package/build/src/runtime/http-engine/parsers/HeadersParser.d.ts.map +1 -0
  133. package/build/src/runtime/http-engine/parsers/HeadersParser.js +88 -0
  134. package/build/src/runtime/http-engine/parsers/HeadersParser.js.map +1 -0
  135. package/build/src/runtime/http-engine/parsers/HttpResponseParser.d.ts +91 -0
  136. package/build/src/runtime/http-engine/parsers/HttpResponseParser.d.ts.map +1 -0
  137. package/build/src/runtime/http-engine/parsers/HttpResponseParser.js +236 -0
  138. package/build/src/runtime/http-engine/parsers/HttpResponseParser.js.map +1 -0
  139. package/build/src/runtime/http-engine/parsers/StatusParser.d.ts +20 -0
  140. package/build/src/runtime/http-engine/parsers/StatusParser.d.ts.map +1 -0
  141. package/build/src/runtime/http-engine/parsers/StatusParser.js +51 -0
  142. package/build/src/runtime/http-engine/parsers/StatusParser.js.map +1 -0
  143. package/build/src/runtime/http-engine/parsers/index.d.ts +5 -0
  144. package/build/src/runtime/http-engine/parsers/index.d.ts.map +1 -0
  145. package/build/src/runtime/http-engine/parsers/index.js +5 -0
  146. package/build/src/runtime/http-engine/parsers/index.js.map +1 -0
  147. package/build/src/runtime/http-engine/response/ResponseProcessor.d.ts +22 -0
  148. package/build/src/runtime/http-engine/response/ResponseProcessor.d.ts.map +1 -0
  149. package/build/src/runtime/http-engine/response/ResponseProcessor.js +25 -0
  150. package/build/src/runtime/http-engine/response/ResponseProcessor.js.map +1 -0
  151. package/build/src/runtime/http-engine/response/index.d.ts +2 -0
  152. package/build/src/runtime/http-engine/response/index.d.ts.map +1 -0
  153. package/build/src/runtime/http-engine/response/index.js +2 -0
  154. package/build/src/runtime/http-engine/response/index.js.map +1 -0
  155. package/build/src/runtime/http-engine/statistics/StatisticsProcessor.d.ts +7 -0
  156. package/build/src/runtime/http-engine/statistics/StatisticsProcessor.d.ts.map +1 -0
  157. package/build/src/runtime/http-engine/statistics/StatisticsProcessor.js +40 -0
  158. package/build/src/runtime/http-engine/statistics/StatisticsProcessor.js.map +1 -0
  159. package/build/src/runtime/http-engine/statistics/index.d.ts +2 -0
  160. package/build/src/runtime/http-engine/statistics/index.d.ts.map +1 -0
  161. package/build/src/runtime/http-engine/statistics/index.js +2 -0
  162. package/build/src/runtime/http-engine/statistics/index.js.map +1 -0
  163. package/build/src/runtime/http-engine/url/UrlProcessor.d.ts +24 -0
  164. package/build/src/runtime/http-engine/url/UrlProcessor.d.ts.map +1 -0
  165. package/build/src/runtime/http-engine/url/UrlProcessor.js +50 -0
  166. package/build/src/runtime/http-engine/url/UrlProcessor.js.map +1 -0
  167. package/build/src/runtime/http-engine/url/index.d.ts +2 -0
  168. package/build/src/runtime/http-engine/url/index.d.ts.map +1 -0
  169. package/build/src/runtime/http-engine/url/index.js +2 -0
  170. package/build/src/runtime/http-engine/url/index.js.map +1 -0
  171. package/build/src/runtime/http-runner/HttpRequestRunner.d.ts +3 -3
  172. package/build/src/runtime/http-runner/HttpRequestRunner.d.ts.map +1 -1
  173. package/build/src/runtime/http-runner/HttpRequestRunner.js.map +1 -1
  174. package/build/src/runtime/node/InteropInterfaces.d.ts +3 -3
  175. package/build/src/runtime/node/InteropInterfaces.d.ts.map +1 -1
  176. package/build/src/runtime/node/InteropInterfaces.js.map +1 -1
  177. package/build/src/runtime/node/ProjectRequestRunner.d.ts +2 -2
  178. package/build/src/runtime/node/ProjectRequestRunner.d.ts.map +1 -1
  179. package/build/src/runtime/node/ProjectRequestRunner.js.map +1 -1
  180. package/build/src/runtime/node/ProjectRunner.d.ts.map +1 -1
  181. package/build/src/runtime/node/ProjectRunner.js +2 -2
  182. package/build/src/runtime/node/ProjectRunner.js.map +1 -1
  183. package/build/tsconfig.tsbuildinfo +1 -1
  184. package/data/models/example-generator-api.json +24 -24
  185. package/package.json +2 -2
  186. package/src/lib/logging/DefaultLogger.ts +32 -0
  187. package/src/modeling/DomainModel.ts +11 -4
  188. package/src/models/ClientCertificate.ts +1 -1
  189. package/src/models/RequestConfig.ts +1 -1
  190. package/src/models/SerializableError.ts +1 -1
  191. package/src/proxy/RequestProxy.ts +2 -2
  192. package/src/runtime/http-engine/CoreEngine.ts +858 -893
  193. package/src/runtime/http-engine/PayloadSupport.ts +2 -1
  194. package/src/runtime/http-engine/auth/AuthManager.ts +242 -0
  195. package/src/runtime/http-engine/certificates/CertificateManager.ts +74 -0
  196. package/src/runtime/http-engine/compression/CompressionManager.ts +99 -0
  197. package/src/runtime/http-engine/connections/ConnectionManager.ts +123 -0
  198. package/src/runtime/http-engine/connections/DigestAuthHandler.ts +238 -0
  199. package/src/runtime/http-engine/connections/DirectConnection.ts +134 -0
  200. package/src/runtime/http-engine/connections/ProxyAuthHandler.ts +179 -0
  201. package/src/runtime/http-engine/connections/ProxyConnection.ts +55 -0
  202. package/src/runtime/http-engine/connections/TunnelConnection.ts +192 -0
  203. package/src/runtime/http-engine/constants.ts +103 -0
  204. package/src/runtime/http-engine/cookies/CookieProcessor.ts +25 -0
  205. package/src/runtime/http-engine/errors/HttpEngineErrors.ts +319 -0
  206. package/src/runtime/http-engine/message/MessageBuilder.ts +201 -0
  207. package/src/runtime/http-engine/message/MessageProcessor.ts +73 -0
  208. package/src/runtime/http-engine/ntlm/NtlmAuth.ts +16 -13
  209. package/src/runtime/http-engine/ntlm/NtlmMessage.ts +6 -6
  210. package/src/runtime/http-engine/parsers/BodyParser.ts +171 -0
  211. package/src/runtime/http-engine/parsers/HeadersParser.ts +103 -0
  212. package/src/runtime/http-engine/parsers/HttpResponseParser.ts +280 -0
  213. package/src/runtime/http-engine/parsers/StatusParser.ts +69 -0
  214. package/src/runtime/http-engine/response/ResponseProcessor.ts +46 -0
  215. package/src/runtime/http-engine/statistics/StatisticsProcessor.ts +52 -0
  216. package/src/runtime/http-engine/url/UrlProcessor.ts +55 -0
  217. package/src/runtime/http-runner/HttpRequestRunner.ts +3 -3
  218. package/src/runtime/node/InteropInterfaces.ts +3 -3
  219. package/src/runtime/node/ProjectRequestRunner.ts +2 -2
  220. package/src/runtime/node/ProjectRunner.ts +2 -2
  221. package/tests/servers/ProxyServer.ts +32 -19
  222. package/tests/servers/express-routes/ApiEndpoint.ts +24 -0
  223. package/tests/servers/express-routes/BasicAuthRoute.ts +36 -0
  224. package/tests/servers/express-routes/BearerAuthRoute.ts +35 -0
  225. package/tests/servers/express-routes/NTLMRoute.ts +2 -3
  226. package/tests/servers/express-routes/PostApi.ts +15 -2
  227. package/tests/servers/express-routes/RedirectsApi.ts +12 -1
  228. package/tests/servers/express-routes/ResponsesApi.ts +1 -1
  229. package/tests/servers/express-routes/StreamApi.ts +19 -0
  230. package/tests/servers/oauth2mock/ServerMock.js +1 -1
  231. package/tests/unit/modeling/domain_model_entities.spec.ts +306 -1
  232. package/tests/unit/runtime/http-engine/HttpResponseParser.spec.ts +337 -0
  233. package/tests/unit/runtime/http-engine/abort.spec.ts +4 -5
  234. package/tests/unit/runtime/http-engine/auth.spec.ts +7 -58
  235. package/tests/unit/runtime/http-engine/certificates/CertificateManager.spec.ts +482 -0
  236. package/tests/unit/runtime/http-engine/certificates.spec.ts +2 -2
  237. package/tests/unit/runtime/http-engine/compression/CompressionManager.spec.ts +498 -0
  238. package/tests/unit/runtime/http-engine/compression.spec.ts +3 -72
  239. package/tests/unit/runtime/http-engine/connections/ConnectionManager.spec.ts +379 -0
  240. package/tests/unit/runtime/http-engine/connections/DigestAuthHandler.spec.ts +164 -0
  241. package/tests/unit/runtime/http-engine/core_engine.spec.ts +561 -0
  242. package/tests/unit/runtime/http-engine/engine_statuses.spec.ts +2 -2
  243. package/tests/unit/runtime/http-engine/events.spec.ts +2 -2
  244. package/tests/unit/runtime/http-engine/headers.spec.ts +2 -88
  245. package/tests/unit/runtime/http-engine/hosts.spec.ts +2 -2
  246. package/tests/unit/runtime/http-engine/http-get.spec.ts +2 -2
  247. package/tests/unit/runtime/http-engine/http-post.spec.ts +2 -2
  248. package/tests/unit/runtime/http-engine/logger.spec.ts +0 -8
  249. package/tests/unit/runtime/http-engine/message.spec.ts +2 -194
  250. package/tests/unit/runtime/http-engine/params.spec.ts +4 -4
  251. package/tests/unit/runtime/http-engine/proxy.spec.ts +15 -14
  252. package/tests/unit/runtime/http-engine/redirects.spec.ts +2 -2
  253. package/tests/unit/runtime/http-engine/responses.spec.ts +170 -277
  254. package/tests/unit/runtime/http-engine/timeout.spec.ts +3 -3
  255. package/tests/unit/runtime/http-engine/timings.spec.ts +2 -2
  256. package/tests/unit/runtime/proxy/HttpProjectProxy.spec.ts +25 -28
  257. package/tests/unit/runtime/runners/project_runner.spec.ts +2 -2
  258. package/tests/unit/runtime/runners/request_runner.spec.ts +2 -2
  259. package/build/src/runtime/http-engine/HttpEngine.d.ts +0 -311
  260. package/build/src/runtime/http-engine/HttpEngine.d.ts.map +0 -1
  261. package/build/src/runtime/http-engine/HttpEngine.js +0 -802
  262. package/build/src/runtime/http-engine/HttpEngine.js.map +0 -1
  263. package/src/runtime/http-engine/HttpEngine.ts +0 -952
  264. package/tests/unit/runtime/http-engine/connecting.spec.ts +0 -140
@@ -1,5 +1,5 @@
1
1
  import { test } from '@japa/runner'
2
- import { DataDomain, DomainEntity, DomainEntityKind } from '../../../src/index.js'
2
+ import { DataDomain, DomainEntity, DomainEntityKind, DomainModel } from '../../../src/index.js'
3
3
 
4
4
  test.group('DomainModel.addEntity()', () => {
5
5
  test('adds an entity to the model', ({ assert }) => {
@@ -406,3 +406,308 @@ test.group('DomainModel.hasEntities()', () => {
406
406
  assert.isFalse(model.hasEntities())
407
407
  })
408
408
  })
409
+
410
+ test.group('DomainModel.attachEntity() - Race Condition Tests', () => {
411
+ test('simulate multiple entities moved to same target simultaneously', ({ assert }) => {
412
+ const dataDomain = new DataDomain()
413
+ const sourceModel1 = dataDomain.addModel({ key: 'source1' })
414
+ const sourceModel2 = dataDomain.addModel({ key: 'source2' })
415
+ const targetModel = dataDomain.addModel({ key: 'target' })
416
+
417
+ const entity1 = sourceModel1.addEntity({ key: 'entity1' })
418
+ const entity2 = sourceModel2.addEntity({ key: 'entity2' })
419
+
420
+ // Move multiple entities to the same target
421
+ targetModel.attachEntity(entity1.key)
422
+ targetModel.attachEntity(entity2.key)
423
+
424
+ // Verify no duplication in fields array
425
+ const entityFields = targetModel.fields.filter((f) => f.type === 'entity')
426
+ const entityKeys = entityFields.map((f) => f.key)
427
+ const uniqueKeys = [...new Set(entityKeys)]
428
+ assert.equal(entityFields.length, uniqueKeys.length, 'No duplicate entities in fields array')
429
+ assert.lengthOf(entityFields, 2, 'Should have exactly 2 entities')
430
+ assert.include(entityKeys, entity1.key, 'Should contain entity1')
431
+ assert.include(entityKeys, entity2.key, 'Should contain entity2')
432
+
433
+ // Verify graph consistency
434
+ assert.equal(dataDomain.graph.parent(entity1.key), targetModel.key, 'Entity1 parent should be target model')
435
+ assert.equal(dataDomain.graph.parent(entity2.key), targetModel.key, 'Entity2 parent should be target model')
436
+
437
+ // Verify entities removed from source models
438
+ assert.lengthOf(sourceModel1.fields, 0, 'Source1 should have no entities')
439
+ assert.lengthOf(sourceModel2.fields, 0, 'Source2 should have no entities')
440
+ })
441
+
442
+ test('simulate entity moved between models multiple times rapidly', ({ assert }) => {
443
+ const dataDomain = new DataDomain()
444
+ const model1 = dataDomain.addModel({ key: 'model1' })
445
+ const model2 = dataDomain.addModel({ key: 'model2' })
446
+ const model3 = dataDomain.addModel({ key: 'model3' })
447
+
448
+ const entity = model1.addEntity({ key: 'entity' })
449
+
450
+ // Simulate rapid moves
451
+ model2.attachEntity(entity.key)
452
+ model3.attachEntity(entity.key)
453
+ model1.attachEntity(entity.key)
454
+ model2.attachEntity(entity.key)
455
+
456
+ // Final state verification
457
+ assert.lengthOf(model2.fields, 1, 'Model2 should have 1 entity')
458
+ assert.lengthOf(model1.fields, 0, 'Model1 should have no entities')
459
+ assert.lengthOf(model3.fields, 0, 'Model3 should have no entities')
460
+ assert.equal(dataDomain.graph.parent(entity.key), model2.key, 'Entity parent should be model2')
461
+ })
462
+
463
+ test('simulate concurrent attachment of same entity to different targets', ({ assert }) => {
464
+ const dataDomain = new DataDomain()
465
+ const sourceModel = dataDomain.addModel({ key: 'source' })
466
+ const targetModel1 = dataDomain.addModel({ key: 'target1' })
467
+ const targetModel2 = dataDomain.addModel({ key: 'target2' })
468
+
469
+ const entity = sourceModel.addEntity({ key: 'entity' })
470
+
471
+ // First move should succeed
472
+ targetModel1.attachEntity(entity.key)
473
+ assert.equal(dataDomain.graph.parent(entity.key), targetModel1.key, 'Entity should be in target1')
474
+ assert.lengthOf(targetModel1.fields, 1, 'Target1 should have the entity')
475
+ assert.lengthOf(sourceModel.fields, 0, 'Source should be empty')
476
+
477
+ // Second move should move from target1 to target2
478
+ targetModel2.attachEntity(entity.key)
479
+ assert.equal(dataDomain.graph.parent(entity.key), targetModel2.key, 'Entity should be in target2')
480
+ assert.lengthOf(targetModel2.fields, 1, 'Target2 should have the entity')
481
+ assert.lengthOf(targetModel1.fields, 0, 'Target1 should be empty')
482
+ assert.lengthOf(sourceModel.fields, 0, 'Source should still be empty')
483
+ })
484
+
485
+ test('verify entity is not duplicated when moved from model with multiple entities', ({ assert }) => {
486
+ const dataDomain = new DataDomain()
487
+ const sourceModel = dataDomain.addModel({ key: 'source' })
488
+ const targetModel = dataDomain.addModel({ key: 'target' })
489
+
490
+ const entity1 = sourceModel.addEntity({ key: 'entity1' })
491
+ const entity2 = sourceModel.addEntity({ key: 'entity2' })
492
+ const entity3 = sourceModel.addEntity({ key: 'entity3' })
493
+
494
+ // Move middle entity
495
+ targetModel.attachEntity(entity2.key)
496
+
497
+ // Verify source model has 2 entities remaining
498
+ assert.lengthOf(sourceModel.fields, 2, 'Source should have 2 entities remaining')
499
+ const sourceKeys = sourceModel.fields.map((f) => f.key)
500
+ assert.include(sourceKeys, entity1.key, 'Should contain entity1')
501
+ assert.include(sourceKeys, entity3.key, 'Should contain entity3')
502
+ assert.notInclude(sourceKeys, entity2.key, 'Should not contain moved entity2')
503
+
504
+ // Verify target model has 1 entity
505
+ assert.lengthOf(targetModel.fields, 1, 'Target should have 1 entity')
506
+ assert.equal(targetModel.fields[0].key, entity2.key, 'Target should have entity2')
507
+
508
+ // Verify graph consistency
509
+ assert.equal(dataDomain.graph.parent(entity1.key), sourceModel.key, 'Entity1 parent should be source')
510
+ assert.equal(dataDomain.graph.parent(entity2.key), targetModel.key, 'Entity2 parent should be target')
511
+ assert.equal(dataDomain.graph.parent(entity3.key), sourceModel.key, 'Entity3 parent should be source')
512
+ })
513
+
514
+ test('verify field consistency when moving entities with properties', ({ assert }) => {
515
+ const dataDomain = new DataDomain()
516
+ const sourceModel = dataDomain.addModel({ key: 'source' })
517
+ const targetModel = dataDomain.addModel({ key: 'target' })
518
+
519
+ const entity = sourceModel.addEntity({ key: 'entity' })
520
+ entity.addProperty({ key: 'prop1', type: 'string' })
521
+ entity.addProperty({ key: 'prop2', type: 'number' })
522
+
523
+ // Verify initial state
524
+ assert.lengthOf(entity.fields, 2, 'Entity should have 2 properties')
525
+
526
+ // Move entity
527
+ targetModel.attachEntity(entity.key)
528
+
529
+ // Verify entity fields are preserved
530
+ assert.lengthOf(entity.fields, 2, 'Entity should still have 2 properties after move')
531
+ assert.equal(dataDomain.graph.parent(entity.key), targetModel.key, 'Entity parent should be target')
532
+
533
+ // Verify model fields
534
+ assert.lengthOf(sourceModel.fields, 0, 'Source model should be empty')
535
+ assert.lengthOf(targetModel.fields, 1, 'Target model should have 1 entity')
536
+ })
537
+
538
+ test('stress test - multiple entities moved in sequence to verify no field array corruption', ({ assert }) => {
539
+ const dataDomain = new DataDomain()
540
+ const sourceModels = []
541
+ const targetModel = dataDomain.addModel({ key: 'target' })
542
+ const entities = []
543
+
544
+ // Create 10 source models with 1 entity each
545
+ for (let i = 0; i < 10; i++) {
546
+ const sourceModel = dataDomain.addModel({ key: `source${i}` })
547
+ sourceModels.push(sourceModel)
548
+ const entity = sourceModel.addEntity({ key: `entity${i}` })
549
+ entities.push(entity)
550
+ }
551
+
552
+ // Move all entities to target model
553
+ for (const entity of entities) {
554
+ targetModel.attachEntity(entity.key)
555
+ }
556
+
557
+ // Verify target model state
558
+ assert.lengthOf(targetModel.fields, 10, 'Target should have 10 entities')
559
+ const targetKeys = targetModel.fields.map((f) => f.key).sort()
560
+ const expectedKeys = entities.map((e) => e.key).sort()
561
+ assert.deepEqual(targetKeys, expectedKeys, 'Target should have all expected entities')
562
+
563
+ // Verify no duplicates
564
+ const uniqueKeys = [...new Set(targetKeys)]
565
+ assert.equal(targetKeys.length, uniqueKeys.length, 'No duplicate keys in target')
566
+
567
+ // Verify source models are empty
568
+ for (const sourceModel of sourceModels) {
569
+ assert.lengthOf(sourceModel.fields, 0, `Source model ${sourceModel.key} should be empty`)
570
+ }
571
+
572
+ // Verify graph consistency
573
+ for (const entity of entities) {
574
+ assert.equal(dataDomain.graph.parent(entity.key), targetModel.key, `Entity ${entity.key} parent should be target`)
575
+ }
576
+ })
577
+
578
+ test('verify detach-attach sequence atomicity', ({ assert }) => {
579
+ const dataDomain = new DataDomain()
580
+ const model1 = dataDomain.addModel({ key: 'model1' })
581
+ const model2 = dataDomain.addModel({ key: 'model2' })
582
+
583
+ const entity = model1.addEntity({ key: 'entity' })
584
+
585
+ // Spy on the detachEntity method to verify it's called
586
+ let detachCalled = false
587
+ const originalDetach = model1.detachEntity
588
+ model1.detachEntity = function (key: string) {
589
+ detachCalled = true
590
+ return originalDetach.call(this, key)
591
+ }
592
+
593
+ model2.attachEntity(entity.key)
594
+
595
+ // Verify detach was called during attach
596
+ assert.isTrue(detachCalled, 'detachEntity should have been called on the source model')
597
+
598
+ // Verify final state
599
+ assert.lengthOf(model1.fields, 0, 'Model1 should be empty')
600
+ assert.lengthOf(model2.fields, 1, 'Model2 should have the entity')
601
+ assert.equal(dataDomain.graph.parent(entity.key), model2.key, 'Graph parent should be model2')
602
+ })
603
+
604
+ test('verify graph parent is updated before fields array check', ({ assert }) => {
605
+ const dataDomain = new DataDomain()
606
+ const model1 = dataDomain.addModel({ key: 'model1' })
607
+ const model2 = dataDomain.addModel({ key: 'model2' })
608
+
609
+ const entity = model1.addEntity({ key: 'entity' })
610
+
611
+ // Override setParent to track when it's called
612
+ const parentSetCalls: { nodeKey: string; parentKey?: string; timestamp: number }[] = []
613
+ const originalSetParent = dataDomain.graph.setParent
614
+ dataDomain.graph.setParent = function (nodeKey: string, parentKey?: string) {
615
+ parentSetCalls.push({ nodeKey, parentKey, timestamp: Date.now() })
616
+ return originalSetParent.call(this, nodeKey, parentKey)
617
+ }
618
+
619
+ // Override fields.push to track when it's called
620
+ const fieldsPushCalls: { items: unknown[]; timestamp: number }[] = []
621
+ const originalPush = model2.fields.push
622
+ model2.fields.push = function (...items) {
623
+ fieldsPushCalls.push({ items: [...items], timestamp: Date.now() })
624
+ return originalPush.call(this, ...items)
625
+ }
626
+
627
+ model2.attachEntity(entity.key)
628
+
629
+ // Verify the sequence - setParent should be called twice (detach + attach)
630
+ assert.lengthOf(parentSetCalls, 2, 'setParent should be called twice (detach + attach)')
631
+ assert.lengthOf(fieldsPushCalls, 1, 'fields.push should be called once')
632
+
633
+ // First call should clear the parent (detach)
634
+ assert.equal(parentSetCalls[0].nodeKey, entity.key, 'First call should be for entity key')
635
+ assert.isUndefined(parentSetCalls[0].parentKey, 'First call should clear parent')
636
+
637
+ // Second call should set the new parent (attach)
638
+ assert.equal(parentSetCalls[1].parentKey, model2.key, 'Second call should set parent to model2')
639
+ assert.equal(parentSetCalls[1].nodeKey, entity.key, 'Second call should be for entity key')
640
+ })
641
+
642
+ test('detect inconsistent graph parent - entity in fields but wrong graph parent', ({ assert }) => {
643
+ const dataDomain = new DataDomain()
644
+ const sourceModel = dataDomain.addModel({ key: 'source' })
645
+ const targetModel = dataDomain.addModel({ key: 'target' })
646
+
647
+ const entity = sourceModel.addEntity({ key: 'entity' })
648
+
649
+ // Simulate race condition where graph parent is not updated during attach
650
+ const originalSetParent = dataDomain.graph.setParent
651
+ let setParentCallCount = 0
652
+ dataDomain.graph.setParent = function (nodeKey: string, parentKey?: string) {
653
+ setParentCallCount++
654
+ // Skip the second setParent call (the attach operation)
655
+ if (setParentCallCount === 2) {
656
+ return this // Don't actually set the parent
657
+ }
658
+ return originalSetParent.call(this, nodeKey, parentKey)
659
+ }
660
+
661
+ assert.throws(() => {
662
+ targetModel.attachEntity(entity.key)
663
+ })
664
+
665
+ // Restore original method
666
+ dataDomain.graph.setParent = originalSetParent
667
+ })
668
+
669
+ test('verify consistency during rapid sequential moves', ({ assert }) => {
670
+ const dataDomain = new DataDomain()
671
+ const models: DomainModel[] = []
672
+
673
+ // Create multiple models
674
+ for (let i = 0; i < 5; i++) {
675
+ models.push(dataDomain.addModel({ key: `model${i}` }))
676
+ }
677
+
678
+ const entity = models[0].addEntity({ key: 'entity' })
679
+
680
+ // Perform rapid moves between models
681
+ for (let iteration = 0; iteration < 20; iteration++) {
682
+ const targetIndex = (iteration + 1) % models.length
683
+ models[targetIndex].attachEntity(entity.key)
684
+
685
+ // Verify consistency after each move
686
+ let entityCount = 0
687
+ let modelWithEntity = null
688
+
689
+ for (const model of models) {
690
+ const hasEntity = model.fields.some((f) => f.type === 'entity' && f.key === entity.key)
691
+ if (hasEntity) {
692
+ entityCount++
693
+ modelWithEntity = model
694
+ }
695
+ }
696
+
697
+ // Entity should exist in exactly one model
698
+ assert.equal(
699
+ entityCount,
700
+ 1,
701
+ `Iteration ${iteration}: Entity should exist in exactly one model, found in ${entityCount} models`
702
+ )
703
+
704
+ // Graph parent should match the model that has the entity
705
+ const graphParent = dataDomain.graph.parent(entity.key)
706
+ assert.equal(
707
+ graphParent,
708
+ modelWithEntity?.key,
709
+ `Iteration ${iteration}: Graph parent should match model with entity`
710
+ )
711
+ }
712
+ })
713
+ })
@@ -0,0 +1,337 @@
1
+ import { test } from '@japa/runner'
2
+ import { createLogger } from '../../../../src/index.js'
3
+ import {
4
+ HttpResponseParser,
5
+ RequestState,
6
+ type ParserCallbacks,
7
+ } from '../../../../src/runtime/http-engine/parsers/HttpResponseParser.js'
8
+
9
+ const logger = createLogger()
10
+
11
+ test.group('HttpResponseParser', (group) => {
12
+ let parser: HttpResponseParser
13
+ let callbacks: ParserCallbacks
14
+ let statusParsed = false
15
+ let headersParsed = false
16
+ let bodyComplete = false
17
+ let errorOccurred = false
18
+ let aborted = false
19
+ let emittedEvents: { event: string; detail?: unknown }[] = []
20
+
21
+ group.each.setup(() => {
22
+ statusParsed = false
23
+ headersParsed = false
24
+ bodyComplete = false
25
+ errorOccurred = false
26
+ aborted = false
27
+ emittedEvents = []
28
+
29
+ callbacks = {
30
+ onStatusParsed: () => {
31
+ statusParsed = true
32
+ },
33
+ onHeadersParsed: () => {
34
+ headersParsed = true
35
+ },
36
+ onBodyComplete: () => {
37
+ bodyComplete = true
38
+ },
39
+ onError: () => {
40
+ errorOccurred = true
41
+ },
42
+ onAbort: () => {
43
+ aborted = true
44
+ },
45
+ emit: (event: string, detail?: unknown) => {
46
+ emittedEvents.push({ event, detail })
47
+ },
48
+ }
49
+
50
+ parser = new HttpResponseParser(logger, callbacks)
51
+ })
52
+
53
+ test('parses status line correctly', ({ assert }) => {
54
+ const statusData = Buffer.from('HTTP/1.1 200 OK\r\n')
55
+ parser.processData(statusData)
56
+
57
+ assert.isTrue(statusParsed)
58
+ assert.equal(parser.getState(), RequestState.Headers)
59
+ }).tags(['@http-engine', '@response', '@parser'])
60
+
61
+ test('handles incomplete status line', ({ assert }) => {
62
+ const statusData = Buffer.from('HTTP/1.1 200')
63
+ parser.processData(statusData)
64
+
65
+ assert.isFalse(statusParsed)
66
+ assert.equal(parser.getState(), RequestState.Status)
67
+ }).tags(['@http-engine', '@response', '@parser'])
68
+
69
+ test('handles malformed status line', ({ assert }) => {
70
+ const statusData = Buffer.from('Invalid Status Line\r\n')
71
+ parser.processData(statusData)
72
+
73
+ // StatusParser doesn't throw errors, it just returns invalid status codes
74
+ assert.isTrue(statusParsed)
75
+ assert.equal(parser.getState(), RequestState.Headers)
76
+ }).tags(['@http-engine', '@response', '@parser'])
77
+
78
+ test('parses headers correctly', ({ assert }) => {
79
+ const statusData = Buffer.from('HTTP/1.1 200 OK\r\n')
80
+ const headersData = Buffer.from('Content-Type: application/json\r\nContent-Length: 114\r\n\r\n')
81
+
82
+ parser.processData(statusData)
83
+ parser.processData(headersData)
84
+
85
+ assert.isTrue(statusParsed)
86
+ assert.isTrue(headersParsed)
87
+ assert.equal(parser.getState(), RequestState.Body)
88
+ }).tags(['@http-engine', '@response', '@parser'])
89
+
90
+ test('handles partial headers', ({ assert }) => {
91
+ const statusData = Buffer.from('HTTP/1.1 200 OK\r\n')
92
+ const partialHeaders = Buffer.from('Content-Type: application/json\r\n')
93
+
94
+ parser.processData(statusData)
95
+ parser.processData(partialHeaders)
96
+
97
+ assert.isTrue(statusParsed)
98
+ assert.isFalse(headersParsed)
99
+ assert.equal(parser.getState(), RequestState.Headers)
100
+ }).tags(['@http-engine', '@response', '@parser'])
101
+
102
+ test('extracts content length from headers', ({ assert }) => {
103
+ const statusData = Buffer.from('HTTP/1.1 200 OK\r\n')
104
+ const headersData = Buffer.from('Content-Length: 114\r\n\r\n')
105
+
106
+ parser.processData(statusData)
107
+ parser.processData(headersData)
108
+
109
+ const responseInfo = parser.getResponseInfo()
110
+ assert.equal(responseInfo.contentLength, 114)
111
+ }).tags(['@http-engine', '@response', '@parser'])
112
+
113
+ test('detects chunked transfer encoding', ({ assert }) => {
114
+ const statusData = Buffer.from('HTTP/1.1 200 OK\r\n')
115
+ const headersData = Buffer.from('Transfer-Encoding: chunked\r\n\r\n')
116
+
117
+ parser.processData(statusData)
118
+ parser.processData(headersData)
119
+
120
+ const responseInfo = parser.getResponseInfo()
121
+ assert.isTrue(responseInfo.chunked)
122
+ }).tags(['@http-engine', '@response', '@parser'])
123
+
124
+ test('emits headersreceived event', ({ assert }) => {
125
+ const statusData = Buffer.from('HTTP/1.1 200 OK\r\n')
126
+ const headersData = Buffer.from('Content-Type: application/json\r\n\r\n')
127
+
128
+ parser.processData(statusData)
129
+ parser.processData(headersData)
130
+
131
+ const headersEvent = emittedEvents.find((e) => e.event === 'headersreceived')
132
+ assert.isDefined(headersEvent)
133
+ assert.isDefined(headersEvent?.detail)
134
+ }).tags(['@http-engine', '@response', '@parser'])
135
+
136
+ test('handles content-length body', ({ assert }) => {
137
+ const statusData = Buffer.from('HTTP/1.1 200 OK\r\n')
138
+ const headersData = Buffer.from('Content-Length: 4\r\n\r\n')
139
+ const bodyData = Buffer.from('test')
140
+
141
+ parser.processData(statusData)
142
+ parser.processData(headersData)
143
+ parser.processData(bodyData)
144
+
145
+ assert.isTrue(bodyComplete)
146
+ }).tags(['@http-engine', '@response', '@parser'])
147
+
148
+ test('handles chunked body', ({ assert }) => {
149
+ const statusData = Buffer.from('HTTP/1.1 200 OK\r\n')
150
+ const headersData = Buffer.from('Transfer-Encoding: chunked\r\n\r\n')
151
+ const chunkedBody = Buffer.from('4\r\ntest\r\n0\r\n\r\n')
152
+
153
+ parser.processData(statusData)
154
+ parser.processData(headersData)
155
+ parser.processData(chunkedBody)
156
+
157
+ assert.isTrue(bodyComplete)
158
+ }).tags(['@http-engine', '@response', '@parser'])
159
+
160
+ test('handles multiple chunks', ({ assert }) => {
161
+ const statusData = Buffer.from('HTTP/1.1 200 OK\r\n')
162
+ const headersData = Buffer.from('Transfer-Encoding: chunked\r\n\r\n')
163
+ const chunkedBody = Buffer.from('6\r\ntest\r\n\r\n8\r\ntest1234\r\n0\r\n\r\n')
164
+
165
+ parser.processData(statusData)
166
+ parser.processData(headersData)
167
+ parser.processData(chunkedBody)
168
+
169
+ assert.isTrue(bodyComplete)
170
+ }).tags(['@http-engine', '@response', '@parser'])
171
+
172
+ test('handles partial chunks', ({ assert }) => {
173
+ const statusData = Buffer.from('HTTP/1.1 200 OK\r\n')
174
+ const headersData = Buffer.from('Transfer-Encoding: chunked\r\n\r\n')
175
+
176
+ parser.processData(statusData)
177
+ parser.processData(headersData)
178
+
179
+ // Send partial chunk
180
+ parser.processData(Buffer.from('6\r\nte'))
181
+ parser.processData(Buffer.from('st\r\n\r\n'))
182
+ parser.processData(Buffer.from('8\r\ntest'))
183
+ parser.processData(Buffer.from('1234\r\n0\r\n\r\n'))
184
+
185
+ assert.isTrue(bodyComplete)
186
+ }).tags(['@http-engine', '@response', '@parser'])
187
+
188
+ test('handles 204 No Content without body', ({ assert }) => {
189
+ const statusData = Buffer.from('HTTP/1.1 204 No Content\r\n')
190
+ const headersData = Buffer.from('\r\n')
191
+
192
+ parser.processData(statusData)
193
+ parser.processData(headersData)
194
+
195
+ assert.isTrue(bodyComplete)
196
+ assert.equal(parser.getState(), RequestState.Done)
197
+ }).tags(['@http-engine', '@response', '@parser'])
198
+
199
+ test('handles 304 Not Modified without body', ({ assert }) => {
200
+ const statusData = Buffer.from('HTTP/1.1 304 Not Modified\r\n')
201
+ const headersData = Buffer.from('\r\n')
202
+
203
+ parser.processData(statusData)
204
+ parser.processData(headersData)
205
+
206
+ assert.isTrue(bodyComplete)
207
+ assert.equal(parser.getState(), RequestState.Done)
208
+ }).tags(['@http-engine', '@response', '@parser'])
209
+
210
+ test('handles informational responses without body', ({ assert }) => {
211
+ const statusData = Buffer.from('HTTP/1.1 100 Continue\r\n')
212
+ const headersData = Buffer.from('\r\n')
213
+
214
+ parser.processData(statusData)
215
+ parser.processData(headersData)
216
+
217
+ assert.isTrue(bodyComplete)
218
+ assert.equal(parser.getState(), RequestState.Done)
219
+ }).tags(['@http-engine', '@response', '@parser'])
220
+
221
+ test('aborts parser correctly', ({ assert }) => {
222
+ parser.abort()
223
+
224
+ assert.isTrue(aborted)
225
+ assert.equal(parser.getState(), RequestState.Done)
226
+ }).tags(['@http-engine', '@response', '@parser'])
227
+
228
+ test('ignores data after abort', ({ assert }) => {
229
+ parser.abort()
230
+
231
+ const statusData = Buffer.from('HTTP/1.1 200 OK\r\n')
232
+ parser.processData(statusData)
233
+
234
+ assert.isFalse(statusParsed)
235
+ }).tags(['@http-engine', '@response', '@parser'])
236
+
237
+ test('resets parser state correctly', ({ assert }) => {
238
+ const statusData = Buffer.from('HTTP/1.1 200 OK\r\n')
239
+ parser.processData(statusData)
240
+
241
+ parser.reset()
242
+
243
+ assert.equal(parser.getState(), RequestState.Status)
244
+ assert.isFalse(parser.getResponseInfo().chunked)
245
+ }).tags(['@http-engine', '@response', '@parser'])
246
+
247
+ test('can process new response after reset', ({ assert }) => {
248
+ const statusData = Buffer.from('HTTP/1.1 200 OK\r\n')
249
+ parser.processData(statusData)
250
+
251
+ parser.reset()
252
+
253
+ parser.processData(statusData)
254
+ assert.isTrue(statusParsed)
255
+ }).tags(['@http-engine', '@response', '@parser'])
256
+
257
+ const parts = [
258
+ Buffer.from([
259
+ 72, 84, 84, 80, 47, 49, 46, 48, 32, 50, 48, 48, 32, 79, 75, 13, 10, 67, 111, 110, 116, 101, 110, 116, 45, 84, 121,
260
+ 112, 101, 58, 32, 97, 112, 112, 108, 105, 99, 97, 116, 105, 111, 110, 47, 106, 115, 111, 110, 13, 10, 67, 111,
261
+ 110, 116, 101, 110, 116, 45, 76, 101, 110, 103, 116, 104, 58, 32, 49, 49, 52, 13, 10,
262
+ ]),
263
+ Buffer.from([
264
+ 83, 101, 114, 118, 101, 114, 58, 32, 87, 101, 114, 107, 122, 101, 117, 103, 47, 48, 46, 49, 52, 46, 49, 32, 80,
265
+ 121, 116, 104, 111, 110, 47, 50, 46, 55, 46, 49, 52, 13, 10, 68, 97, 116, 101, 58, 32, 84, 104, 117, 44, 32, 50,
266
+ 49, 32, 74, 117, 110, 32, 50, 48, 49, 56, 32, 49, 56, 58, 51, 48, 58, 53, 49, 32, 71, 77, 84, 13, 10, 13, 10, 123,
267
+ 34, 105, 110, 115, 116, 114, 117, 109, 101, 110, 116, 95, 105, 100, 34, 58, 32, 50, 52, 49, 56, 54, 44, 32, 34,
268
+ 117, 115, 101, 114, 95, 105, 100, 34, 58, 32, 53, 57, 44, 32, 34, 112, 114, 111, 100, 117, 99, 116, 95, 105, 100,
269
+ 34, 58, 32, 51, 50, 54, 57, 44, 32, 34, 112, 114, 105, 99, 101, 34, 58, 32, 50, 46, 48, 44, 32, 34, 115, 105, 100,
270
+ 101, 34, 58, 32, 34, 83, 101, 108, 108, 34, 44, 32, 34, 105, 100, 34, 58, 32, 50, 44, 32, 34, 113, 117, 97, 110,
271
+ 116, 105, 116, 121, 34, 58, 32, 48, 125, 10,
272
+ ]),
273
+ ]
274
+
275
+ test('processes complete response in multiple parts', ({ assert }) => {
276
+ parser.processData(parts[0])
277
+ parser.processData(parts[1])
278
+
279
+ assert.isTrue(statusParsed)
280
+ assert.isTrue(headersParsed)
281
+ assert.isTrue(bodyComplete)
282
+ assert.equal(parser.getState(), RequestState.Done)
283
+ }).tags(['@http-engine', '@response', '@parser'])
284
+
285
+ test('extracts correct response information', ({ assert }) => {
286
+ parser.processData(parts[0])
287
+ parser.processData(parts[1])
288
+
289
+ const responseInfo = parser.getResponseInfo()
290
+ assert.equal(responseInfo.status, 200)
291
+ assert.equal(responseInfo.contentLength, 114)
292
+ assert.isFalse(responseInfo.chunked)
293
+ }).tags(['@http-engine', '@response', '@parser'])
294
+
295
+ test('handles parsing errors gracefully', ({ assert }) => {
296
+ const invalidData = Buffer.from('Invalid HTTP Response Data')
297
+ parser.processData(invalidData)
298
+
299
+ // StatusParser doesn't throw errors for invalid data
300
+ assert.isFalse(errorOccurred)
301
+ }).tags(['@http-engine', '@response', '@parser'])
302
+
303
+ test('continues processing after error', ({ assert }) => {
304
+ const invalidData = Buffer.from('Invalid Data')
305
+ parser.processData(invalidData)
306
+
307
+ const validData = Buffer.from('HTTP/1.1 200 OK\r\n\r\n')
308
+ parser.processData(validData)
309
+
310
+ // StatusParser doesn't throw errors for invalid data
311
+ assert.isFalse(errorOccurred)
312
+ assert.isTrue(statusParsed)
313
+ }).tags(['@http-engine', '@response', '@parser'])
314
+
315
+ test('tracks state correctly through parsing stages', ({ assert }) => {
316
+ assert.equal(parser.getState(), RequestState.Status)
317
+
318
+ const statusData = Buffer.from('HTTP/1.1 200 OK\r\n')
319
+ parser.processData(statusData)
320
+ assert.equal(parser.getState(), RequestState.Headers)
321
+
322
+ const headersData = Buffer.from('Content-Length: 4\r\n\r\n')
323
+ parser.processData(headersData)
324
+ assert.equal(parser.getState(), RequestState.Body)
325
+
326
+ const bodyData = Buffer.from('test')
327
+ parser.processData(bodyData)
328
+ assert.equal(parser.getState(), RequestState.Done)
329
+ }).tags(['@http-engine', '@response', '@parser'])
330
+
331
+ test('returns correct response info', ({ assert }) => {
332
+ const responseInfo = parser.getResponseInfo()
333
+ assert.isFalse(responseInfo.chunked)
334
+ assert.isUndefined(responseInfo.status)
335
+ assert.isUndefined(responseInfo.contentLength)
336
+ }).tags(['@http-engine', '@response', '@parser'])
337
+ })
@@ -1,7 +1,7 @@
1
1
  import { test } from '@japa/runner'
2
2
  import net from 'net'
3
3
  import {
4
- DummyLogger,
4
+ createLogger,
5
5
  CoreEngine,
6
6
  IHttpRequest,
7
7
  HttpEngineOptions,
@@ -9,7 +9,7 @@ import {
9
9
  ISerializedError,
10
10
  } from '../../../../src/index.js'
11
11
 
12
- const logger = new DummyLogger()
12
+ const logger = createLogger()
13
13
 
14
14
  test.group('Aborting the request', (group) => {
15
15
  let request: IHttpRequest
@@ -70,11 +70,10 @@ test.group('Aborting the request', (group) => {
70
70
  assert.isUndefined(result)
71
71
  }).tags(['@http-engine', '@abort'])
72
72
 
73
- test('_createResponse() results to undefined', async ({ assert }) => {
73
+ test('_createResponse() throws an error', async ({ assert }) => {
74
74
  const base = new CoreEngine(request, opts)
75
75
  base.abort()
76
- const result = await base._createResponse()
77
- assert.isUndefined(result)
76
+ await assert.rejects(() => base._createResponse(), 'Request aborted.')
78
77
  }).tags(['@http-engine', '@abort'])
79
78
 
80
79
  test('aborts the request with a signal', async ({ assert }) => {