@cachly-dev/mcp-server 0.10.49 → 0.10.56

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 (310) hide show
  1. package/.github/workflows/publish.yml +2 -2
  2. package/README.md +33 -5
  3. package/dist/__tests__/auth.test.js.map +1 -0
  4. package/dist/{src/__tests__ → __tests__}/brain-flow.test.js +0 -141
  5. package/dist/__tests__/brain-flow.test.js.map +1 -0
  6. package/dist/__tests__/brain-predict-failures.test.d.ts +17 -0
  7. package/dist/__tests__/brain-predict-failures.test.js +339 -0
  8. package/dist/__tests__/brain-predict-failures.test.js.map +1 -0
  9. package/dist/__tests__/confidence.test.js.map +1 -0
  10. package/dist/__tests__/e2e/api-auth.test.js.map +1 -0
  11. package/dist/__tests__/e2e/api-health.test.js.map +1 -0
  12. package/dist/__tests__/e2e/cli.test.js.map +1 -0
  13. package/dist/__tests__/e2e/config.js.map +1 -0
  14. package/dist/__tests__/e2e/funnel.test.js.map +1 -0
  15. package/dist/__tests__/e2e/mcp-tools.test.js.map +1 -0
  16. package/dist/__tests__/e2e/provisioning.test.js.map +1 -0
  17. package/dist/{src/__tests__ → __tests__}/handlers-unit.test.js +2 -0
  18. package/dist/__tests__/handlers-unit.test.js.map +1 -0
  19. package/dist/__tests__/namespace.test.js.map +1 -0
  20. package/dist/auth.js.map +1 -0
  21. package/dist/ckg.js.map +1 -0
  22. package/dist/confidence.js.map +1 -0
  23. package/dist/embeddings.js.map +1 -0
  24. package/dist/handlers/advanced.js.map +1 -0
  25. package/dist/{src/handlers → handlers}/brain.js +141 -135
  26. package/dist/handlers/brain.js.map +1 -0
  27. package/dist/handlers/cache.js.map +1 -0
  28. package/dist/{src/handlers → handlers}/context.js +4 -9
  29. package/dist/handlers/context.js.map +1 -0
  30. package/dist/{src/handlers → handlers}/fedbrain.d.ts +0 -6
  31. package/dist/{src/handlers → handlers}/fedbrain.js +7 -19
  32. package/dist/handlers/fedbrain.js.map +1 -0
  33. package/dist/handlers/instances.js.map +1 -0
  34. package/dist/{src/handlers → handlers}/roadmap.js +3 -7
  35. package/dist/handlers/roadmap.js.map +1 -0
  36. package/dist/handlers/syndicate.js.map +1 -0
  37. package/dist/{src/handlers → handlers}/team.js +3 -1
  38. package/dist/handlers/team.js.map +1 -0
  39. package/dist/{src/index.js → index.js} +428 -206
  40. package/dist/index.js.map +1 -0
  41. package/dist/namespace.js.map +1 -0
  42. package/dist/search.js.map +1 -0
  43. package/dist/search.test.js.map +1 -0
  44. package/dist/{src/tools.d.ts → tools.d.ts} +0 -196
  45. package/dist/{src/tools.js → tools.js} +0 -178
  46. package/dist/tools.js.map +1 -0
  47. package/dist/utils.js.map +1 -0
  48. package/package.json +3 -3
  49. package/smithery.yaml +16 -27
  50. package/src/__tests__/brain-flow.test.ts +0 -174
  51. package/src/__tests__/brain-predict-failures.test.ts +430 -0
  52. package/src/__tests__/handlers-unit.test.ts +2 -1
  53. package/src/handlers/brain.ts +142 -142
  54. package/src/handlers/context.ts +4 -9
  55. package/src/handlers/fedbrain.ts +7 -21
  56. package/src/handlers/roadmap.ts +3 -7
  57. package/src/handlers/team.ts +3 -1
  58. package/src/index.ts +415 -221
  59. package/src/tools.ts +0 -191
  60. package/tsconfig.json +3 -2
  61. package/.github/workflows/eas-build-android.yml +0 -58
  62. package/.github/workflows/eas-update.yml +0 -55
  63. package/STRATEGY.md +0 -209
  64. package/apps/travel-chaos-organizer/.env.example +0 -49
  65. package/apps/travel-chaos-organizer/DEPLOY.md +0 -328
  66. package/apps/travel-chaos-organizer/Makefile +0 -57
  67. package/apps/travel-chaos-organizer/README.md +0 -159
  68. package/apps/travel-chaos-organizer/backend/.env.example +0 -10
  69. package/apps/travel-chaos-organizer/backend/Dockerfile +0 -25
  70. package/apps/travel-chaos-organizer/backend/alembic/env.py +0 -56
  71. package/apps/travel-chaos-organizer/backend/alembic/script.py.mako +0 -24
  72. package/apps/travel-chaos-organizer/backend/alembic/versions/.gitkeep +0 -0
  73. package/apps/travel-chaos-organizer/backend/alembic/versions/0001_stripe_customer_id.py +0 -24
  74. package/apps/travel-chaos-organizer/backend/alembic/versions/0002_telegram_chat_id.py +0 -24
  75. package/apps/travel-chaos-organizer/backend/alembic.ini +0 -39
  76. package/apps/travel-chaos-organizer/backend/app/__init__.py +0 -0
  77. package/apps/travel-chaos-organizer/backend/app/auth/__init__.py +0 -0
  78. package/apps/travel-chaos-organizer/backend/app/auth/keycloak.py +0 -81
  79. package/apps/travel-chaos-organizer/backend/app/config.py +0 -66
  80. package/apps/travel-chaos-organizer/backend/app/db/__init__.py +0 -0
  81. package/apps/travel-chaos-organizer/backend/app/db/database.py +0 -20
  82. package/apps/travel-chaos-organizer/backend/app/db/migrations/001_init.sql +0 -71
  83. package/apps/travel-chaos-organizer/backend/app/db/migrations/002_telemetry_freemium.sql +0 -31
  84. package/apps/travel-chaos-organizer/backend/app/db/migrations/003_telegram_chat_id.sql +0 -4
  85. package/apps/travel-chaos-organizer/backend/app/db/migrations/004_stripe_and_email.sql +0 -5
  86. package/apps/travel-chaos-organizer/backend/app/limiter.py +0 -4
  87. package/apps/travel-chaos-organizer/backend/app/logging_config.py +0 -25
  88. package/apps/travel-chaos-organizer/backend/app/main.py +0 -77
  89. package/apps/travel-chaos-organizer/backend/app/models/__init__.py +0 -0
  90. package/apps/travel-chaos-organizer/backend/app/models/schemas.py +0 -142
  91. package/apps/travel-chaos-organizer/backend/app/routers/__init__.py +0 -0
  92. package/apps/travel-chaos-organizer/backend/app/routers/admin.py +0 -237
  93. package/apps/travel-chaos-organizer/backend/app/routers/cache.py +0 -13
  94. package/apps/travel-chaos-organizer/backend/app/routers/events.py +0 -29
  95. package/apps/travel-chaos-organizer/backend/app/routers/export.py +0 -122
  96. package/apps/travel-chaos-organizer/backend/app/routers/inbox.py +0 -122
  97. package/apps/travel-chaos-organizer/backend/app/routers/items.py +0 -130
  98. package/apps/travel-chaos-organizer/backend/app/routers/mail.py +0 -108
  99. package/apps/travel-chaos-organizer/backend/app/routers/parse.py +0 -303
  100. package/apps/travel-chaos-organizer/backend/app/routers/payments.py +0 -179
  101. package/apps/travel-chaos-organizer/backend/app/routers/trips.py +0 -134
  102. package/apps/travel-chaos-organizer/backend/app/routers/users.py +0 -169
  103. package/apps/travel-chaos-organizer/backend/app/routers/waitlist.py +0 -37
  104. package/apps/travel-chaos-organizer/backend/app/services/__init__.py +0 -0
  105. package/apps/travel-chaos-organizer/backend/app/services/cache.py +0 -131
  106. package/apps/travel-chaos-organizer/backend/app/services/drip.py +0 -128
  107. package/apps/travel-chaos-organizer/backend/app/services/email.py +0 -61
  108. package/apps/travel-chaos-organizer/backend/app/services/errors.py +0 -44
  109. package/apps/travel-chaos-organizer/backend/app/services/notifier.py +0 -10
  110. package/apps/travel-chaos-organizer/backend/app/services/ollama.py +0 -101
  111. package/apps/travel-chaos-organizer/backend/app/services/parser.py +0 -22
  112. package/apps/travel-chaos-organizer/backend/app/services/quota.py +0 -85
  113. package/apps/travel-chaos-organizer/backend/app/services/sentry.py +0 -11
  114. package/apps/travel-chaos-organizer/backend/app/services/telemetry.py +0 -35
  115. package/apps/travel-chaos-organizer/backend/pytest.ini +0 -4
  116. package/apps/travel-chaos-organizer/backend/requirements-dev.txt +0 -6
  117. package/apps/travel-chaos-organizer/backend/requirements.txt +0 -25
  118. package/apps/travel-chaos-organizer/backend/tests/__init__.py +0 -0
  119. package/apps/travel-chaos-organizer/backend/tests/conftest.py +0 -223
  120. package/apps/travel-chaos-organizer/backend/tests/test_admin.py +0 -99
  121. package/apps/travel-chaos-organizer/backend/tests/test_cache.py +0 -142
  122. package/apps/travel-chaos-organizer/backend/tests/test_drip.py +0 -32
  123. package/apps/travel-chaos-organizer/backend/tests/test_e2e_chain.py +0 -121
  124. package/apps/travel-chaos-organizer/backend/tests/test_export.py +0 -48
  125. package/apps/travel-chaos-organizer/backend/tests/test_inbox.py +0 -107
  126. package/apps/travel-chaos-organizer/backend/tests/test_items.py +0 -138
  127. package/apps/travel-chaos-organizer/backend/tests/test_mail.py +0 -113
  128. package/apps/travel-chaos-organizer/backend/tests/test_ollama.py +0 -145
  129. package/apps/travel-chaos-organizer/backend/tests/test_parse_url.py +0 -98
  130. package/apps/travel-chaos-organizer/backend/tests/test_parser.py +0 -72
  131. package/apps/travel-chaos-organizer/backend/tests/test_payments.py +0 -163
  132. package/apps/travel-chaos-organizer/backend/tests/test_quota.py +0 -46
  133. package/apps/travel-chaos-organizer/backend/tests/test_search.py +0 -67
  134. package/apps/travel-chaos-organizer/backend/tests/test_trips.py +0 -123
  135. package/apps/travel-chaos-organizer/backend/tests/test_users.py +0 -52
  136. package/apps/travel-chaos-organizer/backups/.gitkeep +0 -0
  137. package/apps/travel-chaos-organizer/caddy/Caddyfile +0 -27
  138. package/apps/travel-chaos-organizer/docker-compose.node1.yml +0 -31
  139. package/apps/travel-chaos-organizer/docker-compose.override.yml +0 -14
  140. package/apps/travel-chaos-organizer/docker-compose.yml +0 -128
  141. package/apps/travel-chaos-organizer/frontend/.env.example +0 -13
  142. package/apps/travel-chaos-organizer/frontend/app/(app)/_layout.tsx +0 -80
  143. package/apps/travel-chaos-organizer/frontend/app/(app)/inbox.tsx +0 -166
  144. package/apps/travel-chaos-organizer/frontend/app/(app)/search.tsx +0 -130
  145. package/apps/travel-chaos-organizer/frontend/app/(app)/settings.tsx +0 -381
  146. package/apps/travel-chaos-organizer/frontend/app/(app)/trips/[id].tsx +0 -346
  147. package/apps/travel-chaos-organizer/frontend/app/(app)/trips.tsx +0 -363
  148. package/apps/travel-chaos-organizer/frontend/app/(app)/upgrade/cancel.tsx +0 -34
  149. package/apps/travel-chaos-organizer/frontend/app/(app)/upgrade/success.tsx +0 -35
  150. package/apps/travel-chaos-organizer/frontend/app/(auth)/login.tsx +0 -91
  151. package/apps/travel-chaos-organizer/frontend/app/_layout.tsx +0 -71
  152. package/apps/travel-chaos-organizer/frontend/app/auth.tsx +0 -29
  153. package/apps/travel-chaos-organizer/frontend/app/index.tsx +0 -16
  154. package/apps/travel-chaos-organizer/frontend/app/onboarding.tsx +0 -119
  155. package/apps/travel-chaos-organizer/frontend/app.config.ts +0 -54
  156. package/apps/travel-chaos-organizer/frontend/assets/adaptive-icon.svg +0 -66
  157. package/apps/travel-chaos-organizer/frontend/assets/icon.svg +0 -79
  158. package/apps/travel-chaos-organizer/frontend/assets/splash.svg +0 -15
  159. package/apps/travel-chaos-organizer/frontend/components/BottomSheet.tsx +0 -102
  160. package/apps/travel-chaos-organizer/frontend/components/ConfettiCannon.tsx +0 -107
  161. package/apps/travel-chaos-organizer/frontend/components/ConfirmDialog.tsx +0 -109
  162. package/apps/travel-chaos-organizer/frontend/components/ErrorBoundary.tsx +0 -45
  163. package/apps/travel-chaos-organizer/frontend/components/FileUploadButton.tsx +0 -19
  164. package/apps/travel-chaos-organizer/frontend/components/ImportSheet.tsx +0 -155
  165. package/apps/travel-chaos-organizer/frontend/components/OfflineBadge.tsx +0 -55
  166. package/apps/travel-chaos-organizer/frontend/components/QRCodeViewer.tsx +0 -80
  167. package/apps/travel-chaos-organizer/frontend/components/ScoreRing.tsx +0 -39
  168. package/apps/travel-chaos-organizer/frontend/components/Screen.tsx +0 -39
  169. package/apps/travel-chaos-organizer/frontend/components/Skeleton.tsx +0 -70
  170. package/apps/travel-chaos-organizer/frontend/components/SwipeableInboxItem.tsx +0 -121
  171. package/apps/travel-chaos-organizer/frontend/components/SwipeableTimelineItem.tsx +0 -70
  172. package/apps/travel-chaos-organizer/frontend/components/TimelineItem.tsx +0 -73
  173. package/apps/travel-chaos-organizer/frontend/components/TimelineItemDetail.tsx +0 -130
  174. package/apps/travel-chaos-organizer/frontend/components/Toast.tsx +0 -60
  175. package/apps/travel-chaos-organizer/frontend/components/ToastContext.tsx +0 -42
  176. package/apps/travel-chaos-organizer/frontend/components/TripCard.tsx +0 -146
  177. package/apps/travel-chaos-organizer/frontend/components/TripQRModal.tsx +0 -70
  178. package/apps/travel-chaos-organizer/frontend/components/UpgradeBanner.tsx +0 -44
  179. package/apps/travel-chaos-organizer/frontend/eas.json +0 -22
  180. package/apps/travel-chaos-organizer/frontend/hooks/useInbox.ts +0 -24
  181. package/apps/travel-chaos-organizer/frontend/hooks/useNetworkSync.ts +0 -60
  182. package/apps/travel-chaos-organizer/frontend/hooks/useTrips.ts +0 -112
  183. package/apps/travel-chaos-organizer/frontend/lib/api.ts +0 -223
  184. package/apps/travel-chaos-organizer/frontend/lib/auth.ts +0 -93
  185. package/apps/travel-chaos-organizer/frontend/lib/db.ts +0 -86
  186. package/apps/travel-chaos-organizer/frontend/lib/fileCache.ts +0 -54
  187. package/apps/travel-chaos-organizer/frontend/lib/haptics.ts +0 -13
  188. package/apps/travel-chaos-organizer/frontend/lib/notifications.ts +0 -112
  189. package/apps/travel-chaos-organizer/frontend/lib/offlineQueue.ts +0 -145
  190. package/apps/travel-chaos-organizer/frontend/lib/quota.tsx +0 -43
  191. package/apps/travel-chaos-organizer/frontend/lib/sentry.ts +0 -28
  192. package/apps/travel-chaos-organizer/frontend/lib/shareIntent.ts +0 -44
  193. package/apps/travel-chaos-organizer/frontend/package-lock.json +0 -13037
  194. package/apps/travel-chaos-organizer/frontend/package.json +0 -44
  195. package/apps/travel-chaos-organizer/frontend/tsconfig.json +0 -23
  196. package/apps/travel-chaos-organizer/keycloak/realm-export.json +0 -59
  197. package/apps/travel-chaos-organizer/landing/index.html +0 -773
  198. package/apps/travel-chaos-organizer/scripts/backup.sh +0 -14
  199. package/apps/travel-chaos-organizer/scripts/configure-env.sh +0 -82
  200. package/apps/travel-chaos-organizer/scripts/deploy.sh +0 -54
  201. package/apps/travel-chaos-organizer/scripts/health-check.sh +0 -36
  202. package/apps/travel-chaos-organizer/scripts/migrate.sh +0 -7
  203. package/apps/travel-chaos-organizer/scripts/server-setup.sh +0 -85
  204. package/apps/travel-chaos-organizer/scripts/smoke-test.sh +0 -29
  205. package/apps/travel-chaos-organizer/scripts/verify-tracking.sh +0 -71
  206. package/apps/travel-chaos-organizer/telegram-bot/.env.example +0 -3
  207. package/apps/travel-chaos-organizer/telegram-bot/Dockerfile +0 -6
  208. package/apps/travel-chaos-organizer/telegram-bot/bot.py +0 -504
  209. package/apps/travel-chaos-organizer/telegram-bot/requirements.txt +0 -3
  210. package/dist/packages/telegram-notify/client.d.ts +0 -19
  211. package/dist/packages/telegram-notify/client.js +0 -75
  212. package/dist/packages/telegram-notify/client.js.map +0 -1
  213. package/dist/src/__tests__/auth.test.js.map +0 -1
  214. package/dist/src/__tests__/brain-flow.test.js.map +0 -1
  215. package/dist/src/__tests__/confidence.test.js.map +0 -1
  216. package/dist/src/__tests__/e2e/api-auth.test.js.map +0 -1
  217. package/dist/src/__tests__/e2e/api-health.test.js.map +0 -1
  218. package/dist/src/__tests__/e2e/cli.test.js.map +0 -1
  219. package/dist/src/__tests__/e2e/config.js.map +0 -1
  220. package/dist/src/__tests__/e2e/funnel.test.js.map +0 -1
  221. package/dist/src/__tests__/e2e/mcp-tools.test.js.map +0 -1
  222. package/dist/src/__tests__/e2e/provisioning.test.js.map +0 -1
  223. package/dist/src/__tests__/handlers-unit.test.js.map +0 -1
  224. package/dist/src/__tests__/namespace.test.js.map +0 -1
  225. package/dist/src/auth.js.map +0 -1
  226. package/dist/src/ckg.js.map +0 -1
  227. package/dist/src/confidence.js.map +0 -1
  228. package/dist/src/embeddings.js.map +0 -1
  229. package/dist/src/handlers/advanced.js.map +0 -1
  230. package/dist/src/handlers/brain.js.map +0 -1
  231. package/dist/src/handlers/cache.js.map +0 -1
  232. package/dist/src/handlers/context.js.map +0 -1
  233. package/dist/src/handlers/fedbrain.js.map +0 -1
  234. package/dist/src/handlers/instances.js.map +0 -1
  235. package/dist/src/handlers/roadmap.js.map +0 -1
  236. package/dist/src/handlers/syndicate.js.map +0 -1
  237. package/dist/src/handlers/tco.d.ts +0 -16
  238. package/dist/src/handlers/tco.js +0 -303
  239. package/dist/src/handlers/tco.js.map +0 -1
  240. package/dist/src/handlers/team.js.map +0 -1
  241. package/dist/src/index.js.map +0 -1
  242. package/dist/src/namespace.js.map +0 -1
  243. package/dist/src/notifier.d.ts +0 -5
  244. package/dist/src/notifier.js +0 -6
  245. package/dist/src/notifier.js.map +0 -1
  246. package/dist/src/search.js.map +0 -1
  247. package/dist/src/search.test.js.map +0 -1
  248. package/dist/src/tools.js.map +0 -1
  249. package/dist/src/utils.js.map +0 -1
  250. package/packages/telegram-notify/SPLIT_PLAN.md +0 -106
  251. package/packages/telegram-notify/client.ts +0 -88
  252. package/packages/telegram-notify/emojis.json +0 -22
  253. package/packages/telegram-notify/pyproject.toml +0 -17
  254. package/packages/telegram-notify/telegram_notify/__init__.py +0 -3
  255. package/packages/telegram-notify/telegram_notify/client.py +0 -87
  256. package/packages/telegram-notify/telegram_notify/emojis.json +0 -22
  257. package/scripts/generate-icons.js +0 -28
  258. package/src/handlers/tco.ts +0 -347
  259. package/src/notifier.ts +0 -5
  260. /package/dist/{src/__tests__ → __tests__}/auth.test.d.ts +0 -0
  261. /package/dist/{src/__tests__ → __tests__}/auth.test.js +0 -0
  262. /package/dist/{src/__tests__ → __tests__}/brain-flow.test.d.ts +0 -0
  263. /package/dist/{src/__tests__ → __tests__}/confidence.test.d.ts +0 -0
  264. /package/dist/{src/__tests__ → __tests__}/confidence.test.js +0 -0
  265. /package/dist/{src/__tests__ → __tests__}/e2e/api-auth.test.d.ts +0 -0
  266. /package/dist/{src/__tests__ → __tests__}/e2e/api-auth.test.js +0 -0
  267. /package/dist/{src/__tests__ → __tests__}/e2e/api-health.test.d.ts +0 -0
  268. /package/dist/{src/__tests__ → __tests__}/e2e/api-health.test.js +0 -0
  269. /package/dist/{src/__tests__ → __tests__}/e2e/cli.test.d.ts +0 -0
  270. /package/dist/{src/__tests__ → __tests__}/e2e/cli.test.js +0 -0
  271. /package/dist/{src/__tests__ → __tests__}/e2e/config.d.ts +0 -0
  272. /package/dist/{src/__tests__ → __tests__}/e2e/config.js +0 -0
  273. /package/dist/{src/__tests__ → __tests__}/e2e/funnel.test.d.ts +0 -0
  274. /package/dist/{src/__tests__ → __tests__}/e2e/funnel.test.js +0 -0
  275. /package/dist/{src/__tests__ → __tests__}/e2e/mcp-tools.test.d.ts +0 -0
  276. /package/dist/{src/__tests__ → __tests__}/e2e/mcp-tools.test.js +0 -0
  277. /package/dist/{src/__tests__ → __tests__}/e2e/provisioning.test.d.ts +0 -0
  278. /package/dist/{src/__tests__ → __tests__}/e2e/provisioning.test.js +0 -0
  279. /package/dist/{src/__tests__ → __tests__}/handlers-unit.test.d.ts +0 -0
  280. /package/dist/{src/__tests__ → __tests__}/namespace.test.d.ts +0 -0
  281. /package/dist/{src/__tests__ → __tests__}/namespace.test.js +0 -0
  282. /package/dist/{src/auth.d.ts → auth.d.ts} +0 -0
  283. /package/dist/{src/auth.js → auth.js} +0 -0
  284. /package/dist/{src/ckg.d.ts → ckg.d.ts} +0 -0
  285. /package/dist/{src/ckg.js → ckg.js} +0 -0
  286. /package/dist/{src/confidence.d.ts → confidence.d.ts} +0 -0
  287. /package/dist/{src/confidence.js → confidence.js} +0 -0
  288. /package/dist/{src/embeddings.d.ts → embeddings.d.ts} +0 -0
  289. /package/dist/{src/embeddings.js → embeddings.js} +0 -0
  290. /package/dist/{src/handlers → handlers}/advanced.d.ts +0 -0
  291. /package/dist/{src/handlers → handlers}/advanced.js +0 -0
  292. /package/dist/{src/handlers → handlers}/brain.d.ts +0 -0
  293. /package/dist/{src/handlers → handlers}/cache.d.ts +0 -0
  294. /package/dist/{src/handlers → handlers}/cache.js +0 -0
  295. /package/dist/{src/handlers → handlers}/context.d.ts +0 -0
  296. /package/dist/{src/handlers → handlers}/instances.d.ts +0 -0
  297. /package/dist/{src/handlers → handlers}/instances.js +0 -0
  298. /package/dist/{src/handlers → handlers}/roadmap.d.ts +0 -0
  299. /package/dist/{src/handlers → handlers}/syndicate.d.ts +0 -0
  300. /package/dist/{src/handlers → handlers}/syndicate.js +0 -0
  301. /package/dist/{src/handlers → handlers}/team.d.ts +0 -0
  302. /package/dist/{src/index.d.ts → index.d.ts} +0 -0
  303. /package/dist/{src/namespace.d.ts → namespace.d.ts} +0 -0
  304. /package/dist/{src/namespace.js → namespace.js} +0 -0
  305. /package/dist/{src/search.d.ts → search.d.ts} +0 -0
  306. /package/dist/{src/search.js → search.js} +0 -0
  307. /package/dist/{src/search.test.d.ts → search.test.d.ts} +0 -0
  308. /package/dist/{src/search.test.js → search.test.js} +0 -0
  309. /package/dist/{src/utils.d.ts → utils.d.ts} +0 -0
  310. /package/dist/{src/utils.js → utils.js} +0 -0
@@ -1,142 +0,0 @@
1
- """
2
- Tests for Cachly Redis cache layer (services/cache.py).
3
- Uses unittest.mock — no real Redis required.
4
- """
5
- import json
6
- import pytest
7
- from unittest.mock import AsyncMock, MagicMock, patch
8
-
9
-
10
- # ── No-Redis path ─────────────────────────────────────────────────────────────
11
-
12
- def test_cachly_configured_false_when_no_env(monkeypatch):
13
- monkeypatch.delenv("CACHLY_REDIS_URL", raising=False)
14
- from app.services import cache as c
15
- assert c.cachly_configured() is False
16
-
17
-
18
- def test_cachly_configured_true_when_env_set(monkeypatch):
19
- monkeypatch.setenv("CACHLY_REDIS_URL", "redis://:pw@host:6380/0")
20
- from app.services import cache as c
21
- assert c.cachly_configured() is True
22
-
23
-
24
- @pytest.mark.asyncio
25
- async def test_cache_get_returns_none_when_no_redis(monkeypatch):
26
- monkeypatch.delenv("CACHLY_REDIS_URL", raising=False)
27
- import importlib
28
- from app.services import cache as c
29
- # Reset module-level state
30
- c._redis_client = None
31
- c._redis_available = False
32
-
33
- result = await c.cache_get("some content")
34
- assert result is None
35
-
36
-
37
- @pytest.mark.asyncio
38
- async def test_cache_set_no_ops_when_no_redis(monkeypatch):
39
- monkeypatch.delenv("CACHLY_REDIS_URL", raising=False)
40
- from app.services import cache as c
41
- c._redis_client = None
42
- c._redis_available = False
43
-
44
- # Should not raise
45
- await c.cache_set("some content", {"type": "flight"})
46
-
47
-
48
- # ── With mocked Redis ─────────────────────────────────────────────────────────
49
-
50
- @pytest.mark.asyncio
51
- async def test_cache_get_returns_dict_on_hit():
52
- from app.services import cache as c
53
-
54
- mock_redis = AsyncMock()
55
- mock_redis.get = AsyncMock(return_value=json.dumps({"type": "flight", "title": "LH 400"}))
56
-
57
- c._redis_client = mock_redis
58
- c._redis_available = True
59
-
60
- result = await c.cache_get("flight booking text")
61
- assert result == {"type": "flight", "title": "LH 400"}
62
-
63
-
64
- @pytest.mark.asyncio
65
- async def test_cache_get_returns_none_on_miss():
66
- from app.services import cache as c
67
-
68
- mock_redis = AsyncMock()
69
- mock_redis.get = AsyncMock(return_value=None)
70
-
71
- c._redis_client = mock_redis
72
- c._redis_available = True
73
-
74
- result = await c.cache_get("unknown content")
75
- assert result is None
76
-
77
-
78
- @pytest.mark.asyncio
79
- async def test_cache_set_stores_json():
80
- from app.services import cache as c
81
-
82
- mock_redis = AsyncMock()
83
- mock_redis.set = AsyncMock()
84
-
85
- c._redis_client = mock_redis
86
- c._redis_available = True
87
-
88
- parsed = {"type": "hotel", "title": "Hilton Berlin"}
89
- await c.cache_set("hotel email text", parsed)
90
-
91
- mock_redis.set.assert_called_once()
92
- call_args = mock_redis.set.call_args
93
- assert call_args[1]["ex"] == c.CACHE_TTL
94
- stored = json.loads(call_args[0][1])
95
- assert stored["type"] == "hotel"
96
-
97
-
98
- @pytest.mark.asyncio
99
- async def test_cache_get_survives_redis_error():
100
- from app.services import cache as c
101
-
102
- mock_redis = AsyncMock()
103
- mock_redis.get = AsyncMock(side_effect=Exception("connection refused"))
104
-
105
- c._redis_client = mock_redis
106
- c._redis_available = True
107
-
108
- result = await c.cache_get("any content")
109
- assert result is None # never raises
110
-
111
-
112
- @pytest.mark.asyncio
113
- async def test_cache_set_survives_redis_error():
114
- from app.services import cache as c
115
-
116
- mock_redis = AsyncMock()
117
- mock_redis.set = AsyncMock(side_effect=Exception("timeout"))
118
-
119
- c._redis_client = mock_redis
120
- c._redis_available = True
121
-
122
- await c.cache_set("any content", {"type": "other"}) # never raises
123
-
124
-
125
- def test_cache_key_is_deterministic():
126
- from app.services import cache as c
127
-
128
- k1 = c._cache_key("same text")
129
- k2 = c._cache_key("same text")
130
- k3 = c._cache_key("different text")
131
-
132
- assert k1 == k2
133
- assert k1 != k3
134
- assert k1.startswith("tco:parse:")
135
-
136
-
137
- def test_cache_key_works_for_bytes():
138
- from app.services import cache as c
139
-
140
- kb = c._cache_key(b"\x89PNG binary")
141
- assert kb.startswith("tco:parse:")
142
- assert len(kb) == len("tco:parse:") + 16
@@ -1,32 +0,0 @@
1
- """Tests for email drip sequence logic."""
2
- import pytest
3
- from unittest.mock import patch, AsyncMock
4
- from sqlalchemy import text
5
- from sqlalchemy.ext.asyncio import AsyncSession
6
- from datetime import datetime, timedelta, timezone
7
-
8
-
9
- @pytest.mark.asyncio
10
- async def test_drip_dry_run_returns_plan(client, db_session):
11
- """Dry run returns what would be sent without sending."""
12
- r = await client.post("/admin/drip/run?dry_run=true",
13
- headers={"Authorization": "Basic YWRtaW46Y2hhbmdlbWU="}) # admin:changeme
14
- # Either 200 (admin auth works in test) or 401 (expected in test env)
15
- assert r.status_code in (200, 401)
16
-
17
-
18
- @pytest.mark.asyncio
19
- async def test_drip_run_admin_required(client):
20
- """Drip run without auth returns 401."""
21
- r = await client.post("/admin/drip/run")
22
- assert r.status_code == 401
23
-
24
-
25
- @pytest.mark.asyncio
26
- async def test_drip_no_users_no_emails(db_session: AsyncSession):
27
- """run_drip with no users sends nothing."""
28
- from app.services.drip import run_drip
29
- with patch("app.services.email._send", new_callable=AsyncMock) as mock_send:
30
- result = await run_drip(db_session, dry_run=True)
31
- mock_send.assert_not_called()
32
- assert isinstance(result, dict)
@@ -1,121 +0,0 @@
1
- """
2
- E2E chain test: parse text → inbox → assign to trip.
3
- Covers the full happy path a user would take.
4
- """
5
- import json
6
- import uuid
7
- import pytest
8
- from unittest.mock import patch, AsyncMock
9
- from httpx import AsyncClient
10
- from sqlalchemy import text
11
- from tests.conftest import FAKE_USER_ID
12
-
13
- # A fixed UUID for the "other user's trip" in cross-user tests
14
- OTHER_TRIP_ID = "c3d4e5f6-a7b8-9012-cdef-123456789012"
15
-
16
-
17
- @pytest.mark.asyncio
18
- async def test_full_parse_to_assign_chain(client: AsyncClient, db_session, mock_ollama_flight):
19
- """Parse text → lands in inbox → assign to trip → trip has item."""
20
- # 1. Create a trip
21
- r = await client.post("/api/v1/trips", json={
22
- "name": "E2E Test Trip", "description": "Chain test",
23
- "start_date": "2025-08-01", "end_date": "2025-08-10"
24
- })
25
- assert r.status_code == 201
26
- trip_id = r.json()["id"]
27
-
28
- # 2. Parse text → inbox (no trip_id)
29
- with patch("app.services.telemetry.track", new_callable=AsyncMock):
30
- r = await client.post("/api/v1/parse/text", data={"raw_text": "Lufthansa Buchungsbestaetigung LH400 FRA-JFK"})
31
- assert r.status_code == 200
32
- assert r.json()["parsed"]["type"] == "flight"
33
-
34
- # 3. Fetch inbox
35
- r = await client.get("/api/v1/inbox?status_filter=pending")
36
- assert r.status_code == 200
37
- inbox = r.json()
38
- assert len(inbox) >= 1
39
- inbox_id = inbox[0]["id"]
40
-
41
- # 4. Assign inbox item to trip
42
- r = await client.post(f"/api/v1/inbox/{inbox_id}/assign",
43
- json={"trip_id": trip_id, "type": "flight"})
44
- assert r.status_code == 200
45
- trip_item_id = r.json()["trip_item_id"]
46
- assert trip_item_id
47
-
48
- # 5. Verify item appears in trip timeline
49
- r = await client.get(f"/api/v1/trips/{trip_id}/items")
50
- assert r.status_code == 200
51
- items = r.json()
52
- assert any(i["id"] == trip_item_id for i in items)
53
-
54
- # 6. Inbox item is now "assigned"
55
- r = await client.get("/api/v1/inbox?status_filter=assigned")
56
- assert r.status_code == 200
57
- assigned = [i for i in r.json() if i["id"] == inbox_id]
58
- assert len(assigned) == 1
59
-
60
-
61
- @pytest.mark.asyncio
62
- async def test_cross_user_inbox_assign_blocked(client: AsyncClient, db_session, mock_ollama_flight):
63
- """Cannot assign inbox item to another user's trip."""
64
- # Create a trip owned by a DIFFERENT user with a valid UUID
65
- await db_session.execute(
66
- text("INSERT INTO trips (id, user_id, name) VALUES (:id, :uid, :name)"),
67
- {"id": OTHER_TRIP_ID, "uid": "other-user", "name": "Other Trip"}
68
- )
69
- await db_session.commit()
70
-
71
- # Parse something into inbox
72
- with patch("app.services.telemetry.track", new_callable=AsyncMock):
73
- r = await client.post("/api/v1/parse/text", data={"raw_text": "Booking confirmation XYZ123"})
74
- assert r.status_code == 200
75
-
76
- r = await client.get("/api/v1/inbox?status_filter=pending")
77
- inbox_id = r.json()[0]["id"]
78
-
79
- # Try to assign to other user's trip — must be 403
80
- r = await client.post(f"/api/v1/inbox/{inbox_id}/assign",
81
- json={"trip_id": OTHER_TRIP_ID, "type": "other"})
82
- assert r.status_code == 403
83
-
84
-
85
- @pytest.mark.asyncio
86
- async def test_trip_search_returns_correct_results(client: AsyncClient, db_session):
87
- """Search finds trips by name and description."""
88
- # Create two trips
89
- await client.post("/api/v1/trips", json={"name": "Barcelona Sommer", "description": "Strand"})
90
- await client.post("/api/v1/trips", json={"name": "Winter Wien", "description": "Kultur"})
91
-
92
- r = await client.get("/api/v1/trips/search?q=Barcelona")
93
- assert r.status_code == 200
94
- results = r.json()
95
- assert any("Barcelona" in t["name"] for t in results)
96
- assert all("Wien" not in t["name"] for t in results)
97
-
98
-
99
- @pytest.mark.asyncio
100
- async def test_delete_trip_cascades_cleanup(client: AsyncClient, db_session):
101
- """Deleting a trip removes it from the list."""
102
- r = await client.post("/api/v1/trips", json={"name": "Delete Me Trip"})
103
- trip_id = r.json()["id"]
104
-
105
- r = await client.delete(f"/api/v1/trips/{trip_id}")
106
- assert r.status_code == 204
107
-
108
- r = await client.get("/api/v1/trips")
109
- assert all(t["id"] != trip_id for t in r.json())
110
-
111
-
112
- @pytest.mark.asyncio
113
- async def test_waitlist_signup_deduplication(client: AsyncClient):
114
- """Same email can't sign up twice."""
115
- email = "e2e-dedup@test.com"
116
- r1 = await client.post("/api/v1/waitlist", json={"email": email})
117
- assert r1.status_code in (200, 201)
118
-
119
- r2 = await client.post("/api/v1/waitlist", json={"email": email})
120
- # Should succeed (409 or 200 with "already registered") — not 500
121
- assert r2.status_code < 500
@@ -1,48 +0,0 @@
1
- """Tests for PDF export endpoint."""
2
- import uuid
3
- import pytest
4
- from unittest.mock import patch, AsyncMock
5
- from httpx import AsyncClient
6
- from sqlalchemy import text
7
- from tests.conftest import FAKE_USER_ID
8
-
9
- EXPORT_TRIP_ID = "a1b2c3d4-e5f6-7890-abcd-ef1234567890"
10
-
11
-
12
- @pytest.mark.asyncio
13
- async def test_pdf_export_trip_not_found(client: AsyncClient):
14
- """Returns 404 for non-existent trip."""
15
- r = await client.get(f"/api/v1/trips/{uuid.uuid4()}/export/pdf")
16
- assert r.status_code == 404
17
-
18
-
19
- @pytest.mark.asyncio
20
- async def test_pdf_export_returns_pdf(client: AsyncClient, db_session):
21
- """Creates a trip and exports it as PDF — verifies content-type."""
22
- # Create a trip directly in DB using a valid UUID
23
- await db_session.execute(
24
- text("INSERT INTO trips (id, user_id, name, start_date, end_date) VALUES (:id, :uid, :name, :sd, :ed)"),
25
- {"id": EXPORT_TRIP_ID, "uid": FAKE_USER_ID, "name": "Export Test Trip",
26
- "sd": "2025-06-01", "ed": "2025-06-07"}
27
- )
28
- await db_session.commit()
29
- fake_pdf = b"%PDF-1.4 fake content for test"
30
- with patch("app.routers.export._build_pdf", return_value=fake_pdf), \
31
- patch("app.services.telemetry.track", new_callable=AsyncMock):
32
- r = await client.get(f"/api/v1/trips/{EXPORT_TRIP_ID}/export/pdf")
33
- assert r.status_code == 200
34
- assert r.headers["content-type"] == "application/pdf"
35
- assert len(r.content) > 0
36
-
37
-
38
- @pytest.mark.asyncio
39
- async def test_pdf_export_other_users_trip_returns_404(client: AsyncClient, db_session):
40
- """Cannot export another user's trip."""
41
- other_trip_id = "b2c3d4e5-f6a7-8901-bcde-f12345678901"
42
- await db_session.execute(
43
- text("INSERT INTO trips (id, user_id, name) VALUES (:id, :uid, :name)"),
44
- {"id": other_trip_id, "uid": "other-user-999", "name": "Secret Trip"}
45
- )
46
- await db_session.commit()
47
- r = await client.get(f"/api/v1/trips/{other_trip_id}/export/pdf")
48
- assert r.status_code == 404
@@ -1,107 +0,0 @@
1
- """
2
- Tests for /api/v1/inbox
3
-
4
- Covers:
5
- list_inbox – empty, pending filter, other status filters
6
- assign – moves item to trip, updates status to 'assigned', migrates attachments
7
- reject – sets status to 'rejected', idempotent on 404
8
-
9
- Run: pytest tests/test_inbox.py -v
10
- """
11
-
12
- import pytest
13
- from httpx import AsyncClient
14
- from tests.conftest import make_trip
15
-
16
- pytestmark = pytest.mark.asyncio
17
-
18
-
19
- async def _create_trip(client: AsyncClient) -> str:
20
- r = await client.post("/api/v1/trips", json=make_trip())
21
- assert r.status_code == 201
22
- return r.json()["id"]
23
-
24
-
25
- async def _seed_inbox(client: AsyncClient) -> str:
26
- """Parse some text so it lands in inbox (no trip_id → inbox)."""
27
- r = await client.post(
28
- "/api/v1/parse/text",
29
- data={"raw_text": "Flight LH 400 Frankfurt to New York on Aug 15"},
30
- )
31
- assert r.status_code == 200
32
- # fetch inbox to get the id
33
- inbox = await client.get("/api/v1/inbox")
34
- assert inbox.status_code == 200
35
- items = inbox.json()
36
- assert len(items) > 0
37
- return items[0]["id"]
38
-
39
-
40
- # ── list ──────────────────────────────────────────────────────────────────────
41
-
42
- async def test_inbox_empty(client):
43
- r = await client.get("/api/v1/inbox")
44
- assert r.status_code == 200
45
- assert r.json() == []
46
-
47
-
48
- async def test_inbox_default_status_is_pending(client, mock_ollama_flight):
49
- await _seed_inbox(client)
50
- r = await client.get("/api/v1/inbox")
51
- assert r.status_code == 200
52
- items = r.json()
53
- assert all(i["status"] == "pending" for i in items)
54
-
55
-
56
- async def test_inbox_filter_by_status(client, mock_ollama_flight):
57
- inbox_id = await _seed_inbox(client)
58
- await client.delete(f"/api/v1/inbox/{inbox_id}")
59
-
60
- r = await client.get("/api/v1/inbox?status_filter=rejected")
61
- assert r.status_code == 200
62
- assert any(i["id"] == inbox_id for i in r.json())
63
-
64
-
65
- # ── assign ────────────────────────────────────────────────────────────────────
66
-
67
- async def test_assign_creates_trip_item(client, mock_ollama_flight):
68
- trip_id = await _create_trip(client)
69
- inbox_id = await _seed_inbox(client)
70
-
71
- r = await client.post(
72
- f"/api/v1/inbox/{inbox_id}/assign",
73
- json={"trip_id": trip_id, "type": "flight"},
74
- )
75
- assert r.status_code == 200
76
- assert "trip_item_id" in r.json()
77
-
78
-
79
- async def test_assign_marks_inbox_as_assigned(client, mock_ollama_flight):
80
- trip_id = await _create_trip(client)
81
- inbox_id = await _seed_inbox(client)
82
- await client.post(f"/api/v1/inbox/{inbox_id}/assign",
83
- json={"trip_id": trip_id, "type": "flight"})
84
-
85
- r = await client.get("/api/v1/inbox?status_filter=assigned")
86
- assert any(i["id"] == inbox_id for i in r.json())
87
-
88
-
89
- async def test_assign_not_found(client):
90
- r = await client.post(
91
- "/api/v1/inbox/00000000-0000-0000-0000-000000000000/assign",
92
- json={"trip_id": "00000000-0000-0000-0000-000000000001", "type": "other"},
93
- )
94
- assert r.status_code == 404
95
-
96
-
97
- # ── reject ────────────────────────────────────────────────────────────────────
98
-
99
- async def test_reject_inbox_item(client, mock_ollama_flight):
100
- inbox_id = await _seed_inbox(client)
101
- r = await client.delete(f"/api/v1/inbox/{inbox_id}")
102
- assert r.status_code == 204
103
-
104
-
105
- async def test_reject_not_found(client):
106
- r = await client.delete("/api/v1/inbox/00000000-0000-0000-0000-000000000000")
107
- assert r.status_code == 404
@@ -1,138 +0,0 @@
1
- """
2
- Tests for /api/v1/trips/{trip_id}/items
3
-
4
- Covers:
5
- list_items – empty, sorted by event_at, trip isolation
6
- create_item – happy path, optional fields, missing title → 422
7
- update_item – partial patch, type change, 404 for wrong trip
8
- delete_item – removes item, 404 guard
9
- timeline order – items sorted chronologically regardless of insert order
10
-
11
- Run: pytest tests/test_items.py -v
12
- """
13
- import pytest
14
- from httpx import AsyncClient
15
- from tests.conftest import make_trip, make_item
16
-
17
- pytestmark = pytest.mark.asyncio
18
-
19
-
20
- async def _create_trip(client: AsyncClient) -> str:
21
- r = await client.post("/api/v1/trips", json=make_trip())
22
- assert r.status_code == 201
23
- return r.json()["id"]
24
-
25
-
26
- async def _create_item(client: AsyncClient, trip_id: str, **kwargs) -> dict:
27
- r = await client.post(f"/api/v1/trips/{trip_id}/items", json=make_item(**kwargs))
28
- assert r.status_code == 201, r.text
29
- return r.json()
30
-
31
-
32
- # ── list ──────────────────────────────────────────────────────────────────────
33
-
34
- async def test_list_items_empty(client):
35
- trip_id = await _create_trip(client)
36
- r = await client.get(f"/api/v1/trips/{trip_id}/items")
37
- assert r.status_code == 200
38
- assert r.json() == []
39
-
40
-
41
- async def test_list_items_wrong_trip_404(client):
42
- r = await client.get("/api/v1/trips/00000000-0000-0000-0000-000000000000/items")
43
- assert r.status_code == 404
44
-
45
-
46
- async def test_timeline_sorted_by_event_at(client):
47
- trip_id = await _create_trip(client)
48
- await _create_item(client, trip_id, title="Late flight", event_at="2024-08-20T18:00:00")
49
- await _create_item(client, trip_id, title="Early flight", event_at="2024-08-15T06:00:00")
50
- await _create_item(client, trip_id, title="Mid hotel", event_at="2024-08-17T14:00:00")
51
-
52
- r = await client.get(f"/api/v1/trips/{trip_id}/items")
53
- titles = [i["title"] for i in r.json()]
54
- assert titles == ["Early flight", "Mid hotel", "Late flight"]
55
-
56
-
57
- # ── create ────────────────────────────────────────────────────────────────────
58
-
59
- async def test_create_item_happy_path(client):
60
- trip_id = await _create_trip(client)
61
- r = await client.post(f"/api/v1/trips/{trip_id}/items", json=make_item(title="LH 400 FRA→JFK"))
62
- assert r.status_code == 201
63
- body = r.json()
64
- assert body["title"] == "LH 400 FRA→JFK"
65
- assert body["type"] == "flight"
66
- assert body["trip_id"] == trip_id
67
-
68
-
69
- async def test_create_item_missing_title_rejected(client):
70
- trip_id = await _create_trip(client)
71
- r = await client.post(f"/api/v1/trips/{trip_id}/items", json={"type": "flight"})
72
- assert r.status_code == 422
73
-
74
-
75
- async def test_create_item_optional_fields_nullable(client):
76
- trip_id = await _create_trip(client)
77
- r = await client.post(f"/api/v1/trips/{trip_id}/items",
78
- json={"title": "Minimal item", "type": "other"})
79
- assert r.status_code == 201
80
- body = r.json()
81
- assert body["event_at"] is None
82
- assert body["booking_ref"] is None
83
-
84
-
85
- async def test_create_item_with_parsed_data(client):
86
- trip_id = await _create_trip(client)
87
- parsed = {"origin": "FRA", "destination": "JFK", "confidence": 0.95}
88
- r = await client.post(f"/api/v1/trips/{trip_id}/items",
89
- json={**make_item(), "parsed_data": parsed})
90
- assert r.status_code == 201
91
- assert r.json()["parsed_data"]["origin"] == "FRA"
92
-
93
-
94
- # ── update ────────────────────────────────────────────────────────────────────
95
-
96
- async def test_update_item_type(client):
97
- trip_id = await _create_trip(client)
98
- item = await _create_item(client, trip_id)
99
- r = await client.patch(f"/api/v1/trips/{trip_id}/items/{item['id']}",
100
- json={"type": "hotel"})
101
- assert r.status_code == 200
102
- assert r.json()["type"] == "hotel"
103
-
104
-
105
- async def test_update_item_not_found(client):
106
- trip_id = await _create_trip(client)
107
- r = await client.patch(
108
- f"/api/v1/trips/{trip_id}/items/00000000-0000-0000-0000-000000000000",
109
- json={"title": "Ghost"},
110
- )
111
- assert r.status_code == 404
112
-
113
-
114
- async def test_update_item_empty_body_rejected(client):
115
- trip_id = await _create_trip(client)
116
- item = await _create_item(client, trip_id)
117
- r = await client.patch(f"/api/v1/trips/{trip_id}/items/{item['id']}", json={})
118
- assert r.status_code == 400
119
-
120
-
121
- # ── delete ────────────────────────────────────────────────────────────────────
122
-
123
- async def test_delete_item(client):
124
- trip_id = await _create_trip(client)
125
- item = await _create_item(client, trip_id)
126
- r = await client.delete(f"/api/v1/trips/{trip_id}/items/{item['id']}")
127
- assert r.status_code == 204
128
-
129
- items = await client.get(f"/api/v1/trips/{trip_id}/items")
130
- assert items.json() == []
131
-
132
-
133
- async def test_delete_item_not_found(client):
134
- trip_id = await _create_trip(client)
135
- r = await client.delete(
136
- f"/api/v1/trips/{trip_id}/items/00000000-0000-0000-0000-000000000000"
137
- )
138
- assert r.status_code == 404