@shortlink-org/portolan 0.1.0 → 0.2.0

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 (381) hide show
  1. package/README.md +69 -205
  2. package/cli/init.mjs +285 -0
  3. package/cli/init.test.mjs +158 -0
  4. package/cli/portolan.mjs +37 -73
  5. package/index.html +19 -0
  6. package/package.json +5 -10
  7. package/plugins/README.md +66 -27
  8. package/plugins/portolan-go.wasm +0 -0
  9. package/portolan.json +52 -73
  10. package/public/landing/cats/bug-hunter.webp +0 -0
  11. package/public/landing/cats/compass-nap.webp +0 -0
  12. package/public/landing/cats/diagrammer.webp +0 -0
  13. package/public/landing/cats/docs-reader.webp +0 -0
  14. package/public/landing/cats/laptop.webp +0 -0
  15. package/public/landing/cats/map-inspector.webp +0 -0
  16. package/public/landing/cats/server-break.webp +0 -0
  17. package/public/landing/cats/star-mapper.webp +0 -0
  18. package/public/landing/cats/system-builder.webp +0 -0
  19. package/public/landing/cats/thread-tangle.webp +0 -0
  20. package/public/og.png +0 -0
  21. package/public/readme-header.webp +0 -0
  22. package/schema/portolan.schema.json +180 -0
  23. package/scripts/builtin-plugins.mjs +14 -0
  24. package/scripts/delivery-presets.mjs +353 -0
  25. package/scripts/gen.mjs +37 -4
  26. package/scripts/history.mjs +126 -0
  27. package/scripts/history.test.mjs +110 -0
  28. package/scripts/host-plugins/fetch-bsr.mjs +351 -0
  29. package/{plugins/fetch-bsr/options.schema.json → scripts/host-plugins/fetch-bsr.options.json} +4 -0
  30. package/scripts/host-plugins/fetch-bsr.test.mjs +212 -0
  31. package/scripts/host-plugins/fetch-csr.mjs +386 -0
  32. package/scripts/host-plugins/fetch-csr.test.mjs +178 -0
  33. package/scripts/host-plugins/fetch-git.mjs +379 -0
  34. package/scripts/host-plugins/fetch-git.test.mjs +210 -0
  35. package/scripts/local-api.mjs +22 -4
  36. package/scripts/local-api.test.mjs +84 -2
  37. package/scripts/manifest.mjs +3 -2
  38. package/scripts/package-smoke.mjs +6 -2
  39. package/scripts/plugin-host.mjs +77 -18
  40. package/scripts/plugin-host.test.mjs +50 -1
  41. package/scripts/plugin-wasm-worker.mjs +6 -3
  42. package/scripts/run-builtin.mjs +6 -26
  43. package/scripts/schema.mjs +6 -1
  44. package/src/app/App.tsx +24 -429
  45. package/src/app/Breadcrumbs.test.ts +7 -0
  46. package/src/app/Breadcrumbs.tsx +15 -2
  47. package/src/app/CatalogApp.tsx +436 -0
  48. package/src/app/Sidebar.tsx +0 -1
  49. package/src/chat/ChatPanel.tsx +98 -41
  50. package/src/chat/Composer.tsx +4 -2
  51. package/src/chat/Conversation.tsx +79 -15
  52. package/src/chat/Header.tsx +43 -9
  53. package/src/chat/Starter.tsx +64 -3
  54. package/src/chat/Steps.tsx +1 -1
  55. package/src/chat/flags.test.ts +51 -14
  56. package/src/chat/flags.ts +8 -2
  57. package/src/chat/page-context.test.ts +51 -0
  58. package/src/chat/page-context.ts +128 -0
  59. package/src/chat/prompt.test.ts +49 -1
  60. package/src/chat/prompt.ts +42 -1
  61. package/src/chat/tools.ts +7 -2
  62. package/src/chat/transport.ts +16 -4
  63. package/src/components/ProblemRow.tsx +182 -0
  64. package/src/data.ts +20 -5
  65. package/src/er/ErCanvas.tsx +1 -1
  66. package/src/graph/DependencyGraph.tsx +10 -1
  67. package/src/graph/FocusedEventGraph.tsx +1 -1
  68. package/src/graph/GraphToolbar.tsx +2 -4
  69. package/src/index.css +220 -2
  70. package/src/landing/DraggableReveal.tsx +168 -0
  71. package/src/landing/EstateGraph.tsx +28 -0
  72. package/src/landing/FlowPlayback.tsx +282 -0
  73. package/src/landing/HeroMap.tsx +128 -0
  74. package/src/landing/LandingPage.tsx +757 -0
  75. package/src/landing/ProductFrame.tsx +68 -0
  76. package/src/landing/ProductTour.tsx +371 -0
  77. package/src/landing/catalog.ts +25 -0
  78. package/src/landing/motion.tsx +52 -0
  79. package/src/lib/all-problems.ts +28 -0
  80. package/src/lib/local-api.ts +35 -1
  81. package/src/lib/setup-info.test.ts +8 -0
  82. package/src/lib/setup-info.ts +3 -2
  83. package/src/lib/source-code.ts +2 -1
  84. package/src/likec4/C4View.tsx +5 -0
  85. package/src/likec4/InteractiveView.tsx +5 -1
  86. package/src/map/ContextMapGraph.tsx +47 -20
  87. package/src/pages/ContextMap.tsx +5 -1
  88. package/src/pages/FlowDetail.tsx +24 -7
  89. package/src/pages/Overview.tsx +68 -12
  90. package/src/pages/Problems.tsx +6 -193
  91. package/src/pages/Settings.tsx +133 -108
  92. package/src/pages/settings/DeliverySettings.tsx +229 -0
  93. package/src/pages/settings/PreferencesSettings.tsx +101 -0
  94. package/src/routes.test.ts +20 -0
  95. package/src/routes.ts +13 -1
  96. package/vite.config.ts +12 -8
  97. package/catalog/enum_test.go +0 -46
  98. package/catalog/model.go +0 -932
  99. package/catalog/roundtrip_test.go +0 -185
  100. package/catalog/via_test.go +0 -38
  101. package/go.mod +0 -14
  102. package/go.sum +0 -14
  103. package/internal/commands/cargo.go +0 -154
  104. package/internal/commands/commands.go +0 -143
  105. package/internal/commands/commands_test.go +0 -323
  106. package/internal/commands/gradle.go +0 -77
  107. package/internal/commands/justfile.go +0 -82
  108. package/internal/commands/makefile.go +0 -106
  109. package/internal/commands/maven.go +0 -93
  110. package/internal/commands/packagejson.go +0 -159
  111. package/internal/commands/pyproject.go +0 -169
  112. package/internal/commands/taskfile.go +0 -101
  113. package/internal/commands/testdata/estate/.cargo/config.toml +0 -7
  114. package/internal/commands/testdata/estate/Makefile +0 -33
  115. package/internal/commands/testdata/estate/Taskfile.yml +0 -21
  116. package/internal/commands/testdata/estate/build.gradle.kts +0 -21
  117. package/internal/commands/testdata/estate/justfile +0 -20
  118. package/internal/commands/testdata/estate/package.json +0 -13
  119. package/internal/commands/testdata/estate/pom.xml +0 -29
  120. package/internal/commands/testdata/estate/pyproject.toml +0 -27
  121. package/internal/commands/testdata/estate/xtask/src/main.rs +0 -27
  122. package/internal/commands/testdata/golden/commands.json +0 -305
  123. package/internal/gohttp/analyze.go +0 -2500
  124. package/internal/gohttp/endpoints.go +0 -1067
  125. package/internal/gohttp/roots.go +0 -320
  126. package/internal/gohttp/typed.go +0 -143
  127. package/internal/goscan/constants.go +0 -85
  128. package/internal/goscan/goscan_test.go +0 -227
  129. package/internal/goscan/names.go +0 -52
  130. package/internal/goscan/parse_test.go +0 -11
  131. package/internal/goscan/source.go +0 -36
  132. package/internal/goscan/tree.go +0 -155
  133. package/internal/goscan/types.go +0 -99
  134. package/internal/wsdl/ids.go +0 -127
  135. package/internal/wsdl/ids_test.go +0 -21
  136. package/internal/wsdl/model.go +0 -62
  137. package/internal/wsdl/parse.go +0 -920
  138. package/internal/wsdl/parse_test.go +0 -133
  139. package/plugin/describe.go +0 -107
  140. package/plugin/describe_test.go +0 -114
  141. package/plugin/protocol.go +0 -117
  142. package/plugin/schematest/schematest.go +0 -126
  143. package/plugins/extract-adr/describe.go +0 -19
  144. package/plugins/extract-adr/describe_test.go +0 -11
  145. package/plugins/extract-adr/extract.go +0 -153
  146. package/plugins/extract-adr/extract_test.go +0 -350
  147. package/plugins/extract-adr/history.go +0 -99
  148. package/plugins/extract-adr/main.go +0 -65
  149. package/plugins/extract-adr/parse.go +0 -642
  150. package/plugins/extract-adr/parse_test.go +0 -405
  151. package/plugins/extract-asyncapi/describe.go +0 -19
  152. package/plugins/extract-asyncapi/describe_test.go +0 -11
  153. package/plugins/extract-asyncapi/extract.go +0 -315
  154. package/plugins/extract-asyncapi/extract_test.go +0 -197
  155. package/plugins/extract-asyncapi/main.go +0 -48
  156. package/plugins/extract-asyncapi/spec.go +0 -151
  157. package/plugins/extract-commands/describe.go +0 -19
  158. package/plugins/extract-commands/describe_test.go +0 -11
  159. package/plugins/extract-commands/extract.go +0 -79
  160. package/plugins/extract-commands/extract_test.go +0 -92
  161. package/plugins/extract-commands/main.go +0 -55
  162. package/plugins/extract-csr/avro.go +0 -258
  163. package/plugins/extract-csr/describe.go +0 -19
  164. package/plugins/extract-csr/describe_test.go +0 -11
  165. package/plugins/extract-csr/extract.go +0 -338
  166. package/plugins/extract-csr/extract_test.go +0 -374
  167. package/plugins/extract-csr/jsonschema.go +0 -343
  168. package/plugins/extract-csr/lock.go +0 -25
  169. package/plugins/extract-csr/main.go +0 -84
  170. package/plugins/extract-csr/subject.go +0 -102
  171. package/plugins/extract-flows/describe.go +0 -19
  172. package/plugins/extract-flows/describe_test.go +0 -11
  173. package/plugins/extract-flows/extract.go +0 -91
  174. package/plugins/extract-flows/main.go +0 -45
  175. package/plugins/extract-flows/parse.go +0 -593
  176. package/plugins/extract-flows/parse_test.go +0 -204
  177. package/plugins/extract-glossary/describe.go +0 -19
  178. package/plugins/extract-glossary/describe_test.go +0 -11
  179. package/plugins/extract-glossary/extract.go +0 -115
  180. package/plugins/extract-glossary/extract_test.go +0 -220
  181. package/plugins/extract-glossary/main.go +0 -59
  182. package/plugins/extract-glossary/parse.go +0 -214
  183. package/plugins/extract-glossary/parse_test.go +0 -203
  184. package/plugins/extract-go/aggregate.go +0 -214
  185. package/plugins/extract-go/client.go +0 -409
  186. package/plugins/extract-go/client_test.go +0 -266
  187. package/plugins/extract-go/describe.go +0 -19
  188. package/plugins/extract-go/describe_test.go +0 -11
  189. package/plugins/extract-go/enum.go +0 -195
  190. package/plugins/extract-go/enum_test.go +0 -82
  191. package/plugins/extract-go/event.go +0 -99
  192. package/plugins/extract-go/extract.go +0 -191
  193. package/plugins/extract-go/extract_test.go +0 -261
  194. package/plugins/extract-go/flow.go +0 -1441
  195. package/plugins/extract-go/flow_test.go +0 -609
  196. package/plugins/extract-go/httpclient.go +0 -174
  197. package/plugins/extract-go/httpclient_test.go +0 -296
  198. package/plugins/extract-go/ids.go +0 -92
  199. package/plugins/extract-go/layout.go +0 -225
  200. package/plugins/extract-go/layout_test.go +0 -125
  201. package/plugins/extract-go/lifecycle.go +0 -324
  202. package/plugins/extract-go/lifecycle_test.go +0 -108
  203. package/plugins/extract-go/main.go +0 -79
  204. package/plugins/extract-go/operation.go +0 -157
  205. package/plugins/extract-go/source.go +0 -316
  206. package/plugins/extract-go/transport.go +0 -321
  207. package/plugins/extract-go/transport_test.go +0 -145
  208. package/plugins/extract-go/wiring.go +0 -434
  209. package/plugins/extract-go-nats/describe.go +0 -19
  210. package/plugins/extract-go-nats/describe_test.go +0 -11
  211. package/plugins/extract-go-nats/extract.go +0 -177
  212. package/plugins/extract-go-nats/extract_test.go +0 -389
  213. package/plugins/extract-go-nats/index.go +0 -379
  214. package/plugins/extract-go-nats/main.go +0 -42
  215. package/plugins/extract-go-nats/resolve.go +0 -161
  216. package/plugins/extract-go-nats/sites.go +0 -224
  217. package/plugins/extract-graphql/describe.go +0 -19
  218. package/plugins/extract-graphql/describe_test.go +0 -11
  219. package/plugins/extract-graphql/extract.go +0 -433
  220. package/plugins/extract-graphql/extract_test.go +0 -256
  221. package/plugins/extract-graphql/ids.go +0 -49
  222. package/plugins/extract-graphql/lex.go +0 -237
  223. package/plugins/extract-graphql/main.go +0 -51
  224. package/plugins/extract-graphql/parse.go +0 -621
  225. package/plugins/extract-graphql/parse_test.go +0 -122
  226. package/plugins/extract-http-clients/describe.go +0 -19
  227. package/plugins/extract-http-clients/describe_test.go +0 -11
  228. package/plugins/extract-http-clients/extract.go +0 -705
  229. package/plugins/extract-http-clients/extract_test.go +0 -1263
  230. package/plugins/extract-http-clients/main.go +0 -42
  231. package/plugins/extract-openapi/describe.go +0 -19
  232. package/plugins/extract-openapi/describe_test.go +0 -11
  233. package/plugins/extract-openapi/discover.go +0 -243
  234. package/plugins/extract-openapi/extract.go +0 -526
  235. package/plugins/extract-openapi/extract_test.go +0 -545
  236. package/plugins/extract-openapi/main.go +0 -75
  237. package/plugins/extract-openapi/spec.go +0 -350
  238. package/plugins/extract-project/describe.go +0 -19
  239. package/plugins/extract-project/describe_test.go +0 -11
  240. package/plugins/extract-project/extract.go +0 -221
  241. package/plugins/extract-project/extract_test.go +0 -109
  242. package/plugins/extract-project/main.go +0 -41
  243. package/plugins/extract-proto/ast.go +0 -125
  244. package/plugins/extract-proto/consumes.go +0 -77
  245. package/plugins/extract-proto/describe.go +0 -19
  246. package/plugins/extract-proto/extract.go +0 -293
  247. package/plugins/extract-proto/extract_test.go +0 -459
  248. package/plugins/extract-proto/ids.go +0 -89
  249. package/plugins/extract-proto/ids_test.go +0 -57
  250. package/plugins/extract-proto/lex.go +0 -285
  251. package/plugins/extract-proto/main.go +0 -100
  252. package/plugins/extract-proto/module.go +0 -120
  253. package/plugins/extract-proto/parse.go +0 -720
  254. package/plugins/extract-proto/parse_test.go +0 -307
  255. package/plugins/extract-proto/provides.go +0 -236
  256. package/plugins/extract-proto/resolve.go +0 -222
  257. package/plugins/extract-proto/resolve_test.go +0 -119
  258. package/plugins/extract-redis/describe.go +0 -19
  259. package/plugins/extract-redis/describe_test.go +0 -11
  260. package/plugins/extract-redis/extract.go +0 -183
  261. package/plugins/extract-redis/extract_test.go +0 -168
  262. package/plugins/extract-redis/keyspaces.go +0 -469
  263. package/plugins/extract-redis/main.go +0 -44
  264. package/plugins/extract-river/describe.go +0 -19
  265. package/plugins/extract-river/describe_test.go +0 -11
  266. package/plugins/extract-river/extract.go +0 -507
  267. package/plugins/extract-river/extract_test.go +0 -132
  268. package/plugins/extract-river/main.go +0 -42
  269. package/plugins/extract-sql/ddl.go +0 -893
  270. package/plugins/extract-sql/ddl_test.go +0 -401
  271. package/plugins/extract-sql/describe.go +0 -19
  272. package/plugins/extract-sql/describe_test.go +0 -11
  273. package/plugins/extract-sql/layout.go +0 -221
  274. package/plugins/extract-sql/layout_test.go +0 -55
  275. package/plugins/extract-sql/lineage.go +0 -107
  276. package/plugins/extract-sql/main.go +0 -141
  277. package/plugins/extract-sql/maps.go +0 -564
  278. package/plugins/extract-sql/maps_java.go +0 -117
  279. package/plugins/extract-sql/maps_rust.go +0 -333
  280. package/plugins/extract-sql/maps_rust_test.go +0 -70
  281. package/plugins/extract-sql/maps_test.go +0 -204
  282. package/plugins/extract-sql/maps_ts.go +0 -398
  283. package/plugins/extract-sql/maps_ts_test.go +0 -136
  284. package/plugins/extract-sql/projection.go +0 -70
  285. package/plugins/extract-sql/projection_test.go +0 -50
  286. package/plugins/extract-sql/store.go +0 -420
  287. package/plugins/extract-sql/store_test.go +0 -233
  288. package/plugins/extract-sql/view.go +0 -295
  289. package/plugins/extract-watermill/describe.go +0 -19
  290. package/plugins/extract-watermill/describe_test.go +0 -11
  291. package/plugins/extract-watermill/extract.go +0 -1228
  292. package/plugins/extract-watermill/extract_test.go +0 -234
  293. package/plugins/extract-watermill/main.go +0 -41
  294. package/plugins/extract-wsdl/describe.go +0 -19
  295. package/plugins/extract-wsdl/describe_test.go +0 -11
  296. package/plugins/extract-wsdl/extract.go +0 -148
  297. package/plugins/extract-wsdl/extract_test.go +0 -45
  298. package/plugins/extract-wsdl/main.go +0 -53
  299. package/plugins/fetch-bsr/auth.go +0 -114
  300. package/plugins/fetch-bsr/bsr.go +0 -240
  301. package/plugins/fetch-bsr/cache.go +0 -66
  302. package/plugins/fetch-bsr/describe.go +0 -19
  303. package/plugins/fetch-bsr/fetch.go +0 -192
  304. package/plugins/fetch-bsr/fetch_test.go +0 -465
  305. package/plugins/fetch-bsr/lock.go +0 -71
  306. package/plugins/fetch-bsr/main.go +0 -117
  307. package/plugins/fetch-csr/auth.go +0 -95
  308. package/plugins/fetch-csr/cache.go +0 -69
  309. package/plugins/fetch-csr/describe.go +0 -19
  310. package/plugins/fetch-csr/describe_test.go +0 -11
  311. package/plugins/fetch-csr/fetch.go +0 -254
  312. package/plugins/fetch-csr/fetch_test.go +0 -494
  313. package/plugins/fetch-csr/lock.go +0 -95
  314. package/plugins/fetch-csr/main.go +0 -122
  315. package/plugins/fetch-csr/registry.go +0 -209
  316. package/plugins/fetch-git/cache.go +0 -65
  317. package/plugins/fetch-git/describe.go +0 -19
  318. package/plugins/fetch-git/describe_test.go +0 -11
  319. package/plugins/fetch-git/fetch.go +0 -181
  320. package/plugins/fetch-git/fetch_test.go +0 -332
  321. package/plugins/fetch-git/git.go +0 -169
  322. package/plugins/fetch-git/lock.go +0 -72
  323. package/plugins/fetch-git/main.go +0 -127
  324. package/plugins/fetch-git/offline.go +0 -39
  325. package/plugins/fetch-git/pin.go +0 -90
  326. package/plugins/fetch-git/pin_test.go +0 -74
  327. package/plugins/gen-backstage/describe.go +0 -17
  328. package/plugins/gen-backstage/main.go +0 -22
  329. package/plugins/gen-backstage/plugin.go +0 -473
  330. package/plugins/gen-backstage/plugin_test.go +0 -145
  331. package/plugins/gen-backstage.wasm +0 -0
  332. package/plugins/gen-markdown/adr.go +0 -162
  333. package/plugins/gen-markdown/aggregate.go +0 -362
  334. package/plugins/gen-markdown/canonical.go +0 -93
  335. package/plugins/gen-markdown/context.go +0 -90
  336. package/plugins/gen-markdown/coverage_test.go +0 -89
  337. package/plugins/gen-markdown/describe.go +0 -19
  338. package/plugins/gen-markdown/describe_test.go +0 -11
  339. package/plugins/gen-markdown/external.go +0 -71
  340. package/plugins/gen-markdown/flow.go +0 -277
  341. package/plugins/gen-markdown/glossary.go +0 -76
  342. package/plugins/gen-markdown/glossary_test.go +0 -148
  343. package/plugins/gen-markdown/llms.go +0 -302
  344. package/plugins/gen-markdown/main.go +0 -22
  345. package/plugins/gen-markdown/markdown.go +0 -254
  346. package/plugins/gen-markdown/markdown_test.go +0 -100
  347. package/plugins/gen-markdown/module.go +0 -107
  348. package/plugins/gen-markdown/plugin.go +0 -39
  349. package/plugins/gen-markdown/quality_test.go +0 -176
  350. package/plugins/gen-markdown/redis_test.go +0 -38
  351. package/plugins/gen-markdown/render.go +0 -344
  352. package/plugins/gen-markdown/render_test.go +0 -186
  353. package/plugins/gen-markdown/service.go +0 -456
  354. package/plugins/gen-markdown/source.go +0 -177
  355. package/plugins/gen-markdown/store.go +0 -201
  356. package/plugins/gen-markdown.wasm +0 -0
  357. package/plugins/gen-mermaid/describe.go +0 -17
  358. package/plugins/gen-mermaid/main.go +0 -22
  359. package/plugins/gen-mermaid/plugin.go +0 -104
  360. package/plugins/gen-mermaid/plugin_test.go +0 -33
  361. package/plugins/gen-mermaid.wasm +0 -0
  362. package/plugins/openapi/ids.go +0 -261
  363. package/plugins/openapi/ids_test.go +0 -98
  364. package/plugins/verify-codeowners/describe.go +0 -19
  365. package/plugins/verify-codeowners/describe_test.go +0 -11
  366. package/plugins/verify-codeowners/main.go +0 -75
  367. package/plugins/verify-codeowners/match.go +0 -85
  368. package/plugins/verify-codeowners/match_test.go +0 -47
  369. package/plugins/verify-codeowners/owners.go +0 -164
  370. package/plugins/verify-codeowners/owners_test.go +0 -225
  371. package/plugins/verify-codeowners/parse.go +0 -90
  372. package/plugins/verify-codeowners/parse_test.go +0 -62
  373. package/plugins/verify-otel/describe.go +0 -19
  374. package/plugins/verify-otel/describe_test.go +0 -11
  375. package/plugins/verify-otel/main.go +0 -53
  376. package/plugins/verify-otel/match.go +0 -336
  377. package/plugins/verify-otel/otlp.go +0 -200
  378. package/plugins/verify-otel/verify.go +0 -734
  379. package/plugins/verify-otel/verify_test.go +0 -460
  380. /package/{plugins/fetch-csr/options.schema.json → scripts/host-plugins/fetch-csr.options.json} +0 -0
  381. /package/{plugins/fetch-git/options.schema.json → scripts/host-plugins/fetch-git.options.json} +0 -0
@@ -1,460 +0,0 @@
1
- package main
2
-
3
- import (
4
- "encoding/json"
5
- "os"
6
- "path/filepath"
7
- "strings"
8
- "testing"
9
-
10
- "github.com/shortlink-org/portolan/catalog"
11
- "github.com/shortlink-org/portolan/plugin"
12
- )
13
-
14
- // The catalog the traces are read against: one service, two aggregates, an
15
- // endpoint flow read from code and a policy flow that opens on the bus.
16
- func estate() catalog.Catalog {
17
- auth := "auth"
18
- step := func(id, from, to string, kind catalog.StepKind, label, ref string) *catalog.Step {
19
- return &catalog.Step{Type: "step", ID: id, From: from, To: to, Kind: kind, Label: label, Ref: ref, Status: catalog.StatusDeclared}
20
- }
21
- lanes := []catalog.Participant{
22
- {ID: "client", Kind: catalog.ParticipantActor},
23
- {ID: "auth.auth", Kind: catalog.ParticipantService, Context: &auth},
24
- {ID: "auth-pg", Kind: catalog.ParticipantStore, Context: &auth},
25
- {ID: "bus", Kind: catalog.ParticipantBroker},
26
- }
27
-
28
- return catalog.Catalog{
29
- GeneratedAt: "2026-01-01T00:00:00Z",
30
- Commit: "abc1234",
31
- Defs: map[string]catalog.TypeDef{},
32
- Adrs: []catalog.Adr{},
33
- Contexts: []catalog.BoundedContext{{
34
- ID: "auth", Slug: "auth", Name: "Authentication",
35
- Services: []catalog.Service{{
36
- ID: "auth.auth", Slug: "auth", Name: "Auth",
37
- Provides: []catalog.RpcService{{
38
- ID: "auth.v1.Sessions",
39
- Methods: []catalog.RpcMethod{
40
- {Name: "login", HTTP: &catalog.HttpRoute{Method: "POST", Path: "/v1/sessions"}},
41
- {Name: "changePassword", HTTP: &catalog.HttpRoute{Method: "POST", Path: "/v1/users/me/password"}},
42
- {Name: "getUser", HTTP: &catalog.HttpRoute{Method: "GET", Path: "/v1/users/{userId}"}},
43
- },
44
- }},
45
- Consumes: []catalog.RpcCall{{ID: "risk.v1.RiskService/Assess", Peer: "risk.v1", Status: catalog.StatusUnresolved, Source: "gen"}},
46
- Aggregates: []catalog.Aggregate{
47
- {ID: "auth.auth.session", Slug: "session", Name: "Session", Root: "Session", Events: []catalog.Event{
48
- {ID: "auth.auth.session.SessionStarted", Slug: "session-started", Name: "SessionStarted", Versions: []catalog.EventVersion{{Version: "v1"}}, Consumers: []catalog.EventConsumer{}},
49
- {ID: "auth.auth.session.SessionEnded", Slug: "session-ended", Name: "SessionEnded", Versions: []catalog.EventVersion{{Version: "v1"}}, Consumers: []catalog.EventConsumer{}},
50
- }},
51
- {ID: "auth.auth.user", Slug: "user", Name: "User", Root: "User", Events: []catalog.Event{
52
- {ID: "auth.auth.user.PasswordChanged", Slug: "password-changed", Name: "PasswordChanged", Versions: []catalog.EventVersion{{Version: "v1"}}, Consumers: []catalog.EventConsumer{{Service: "auth.auth", Status: catalog.StatusDeclared}}},
53
- }},
54
- },
55
- }},
56
- }},
57
- Stores: []catalog.Store{{ID: "auth.auth.pg", Slug: "pg", Name: "Auth database", Kind: catalog.StoreKindPostgres, Owner: "auth.auth"}},
58
- Flows: []catalog.Flow{
59
- {
60
- ID: "flow.auth-login", Slug: "auth-login", Name: "Login", Owner: "auth", Participants: lanes,
61
- Steps: catalog.FlowNodes{
62
- step("s1", "client", "auth.auth", catalog.StepRPC, "login", ""),
63
- step("s2", "auth.auth", "auth-pg", catalog.StepCall, "ByEmail", ""),
64
- &catalog.Step{Type: "step", ID: "s3", From: "auth.auth", To: "risk-v1", Kind: catalog.StepRPC, Label: "Assess", Ref: "risk.v1.RiskService/Assess", Status: catalog.StatusUnresolved},
65
- &catalog.Alt{Type: "alt", ID: "alt4", Branches: []catalog.AltBranch{
66
- {Title: "blocked", Terminal: true, Steps: catalog.FlowNodes{step("s5", "auth.auth", "bus", catalog.StepEvent, "SessionEnded", "auth.auth.session.SessionEnded")}},
67
- {Title: "otherwise", Steps: catalog.FlowNodes{}},
68
- }},
69
- step("s6", "auth.auth", "bus", catalog.StepEvent, "SessionStarted", "auth.auth.session.SessionStarted"),
70
- },
71
- },
72
- {
73
- ID: "flow.auth-revoke", Slug: "auth-revoke", Name: "Revoke", Owner: "auth", Participants: lanes,
74
- Steps: catalog.FlowNodes{
75
- step("s1", "bus", "auth.auth", catalog.StepEvent, "PasswordChanged", "auth.auth.user.PasswordChanged"),
76
- step("s2", "auth.auth", "auth-pg", catalog.StepCall, "Save", ""),
77
- step("s3", "auth.auth", "bus", catalog.StepEvent, "SessionEnded", "auth.auth.session.SessionEnded"),
78
- },
79
- },
80
- },
81
- }
82
- }
83
-
84
- // One batch, written the way a collector writes it: the kinds as numbers
85
- // except one, to show the names are read too.
86
- const recording = `{"resourceSpans":[{"resource":{"attributes":[{"key":"service.name","value":{"stringValue":"auth"}}]},"scopeSpans":[{"spans":[
87
- {"traceId":"t1","spanId":"a1","name":"POST /v1/sessions","kind":2,"startTimeUnixNano":"100","attributes":[{"key":"http.route","value":{"stringValue":"/v1/sessions"}},{"key":"http.request.method","value":{"stringValue":"POST"}}]},
88
- {"traceId":"t1","spanId":"a2","parentSpanId":"a1","name":"SELECT users","kind":3,"startTimeUnixNano":"110","attributes":[{"key":"db.system.name","value":{"stringValue":"postgresql"}},{"key":"db.operation.name","value":{"stringValue":"SELECT"}}]},
89
- {"traceId":"t1","spanId":"a3","parentSpanId":"a2","name":"prepare SELECT users","kind":3,"startTimeUnixNano":"111","attributes":[{"key":"db.system.name","value":{"stringValue":"postgresql"}},{"key":"db.operation.name","value":{"stringValue":"SELECT"}}]},
90
- {"traceId":"t1","spanId":"a4","parentSpanId":"a1","name":"risk.v1.RiskService/Assess","kind":3,"startTimeUnixNano":"120","attributes":[{"key":"rpc.system","value":{"stringValue":"grpc"}},{"key":"rpc.service","value":{"stringValue":"risk.v1.RiskService"}},{"key":"rpc.method","value":{"stringValue":"Assess"}}]},
91
- {"traceId":"t1","spanId":"a6","parentSpanId":"a1","name":"GET","kind":3,"startTimeUnixNano":"125","attributes":[{"key":"http.request.method","value":{"stringValue":"GET"}},{"key":"url.full","value":{"stringValue":"http://auth:8080/v1/users/42?verbose=1"}},{"key":"server.address","value":{"stringValue":"auth"}}]},
92
- {"traceId":"t1","spanId":"a7","parentSpanId":"a1","name":"GET","kind":3,"startTimeUnixNano":"126","attributes":[{"key":"http.request.method","value":{"stringValue":"GET"}},{"key":"url.full","value":{"stringValue":"http://profile:9000/v1/profiles/42"}},{"key":"server.address","value":{"stringValue":"profile"}}]},
93
- {"traceId":"t1","spanId":"a5","parentSpanId":"a1","name":"publish auth.SessionStarted","kind":"SPAN_KIND_PRODUCER","startTimeUnixNano":"130","attributes":[{"key":"messaging.destination.name","value":{"stringValue":"auth_session"}},{"key":"event.name","value":{"stringValue":"auth.SessionStarted"}}]},
94
-
95
- {"traceId":"t2","spanId":"b1","name":"POST /v1/users/me/password","kind":2,"startTimeUnixNano":"200","attributes":[{"key":"http.route","value":{"stringValue":"/v1/users/me/password"}},{"key":"http.request.method","value":{"stringValue":"POST"}}]},
96
- {"traceId":"t2","spanId":"b2","parentSpanId":"b1","name":"publish auth.PasswordChanged","kind":4,"startTimeUnixNano":"210","attributes":[{"key":"event.name","value":{"stringValue":"auth.PasswordChanged"}}]},
97
- {"traceId":"t2","spanId":"b3","parentSpanId":"b2","name":"consume auth.PasswordChanged","kind":5,"startTimeUnixNano":"220","attributes":[{"key":"event.name","value":{"stringValue":"auth.PasswordChanged"}}]},
98
- {"traceId":"t2","spanId":"b4","parentSpanId":"b3","name":"UPDATE sessions","kind":3,"startTimeUnixNano":"230","attributes":[{"key":"db.system.name","value":{"stringValue":"postgresql"}},{"key":"db.operation.name","value":{"stringValue":"UPDATE"}}]},
99
- {"traceId":"t2","spanId":"b5","parentSpanId":"b3","name":"publish auth.SessionEnded","kind":4,"startTimeUnixNano":"240","attributes":[{"key":"event.name","value":{"stringValue":"auth.SessionEnded"}}]},
100
-
101
- {"traceId":"t3","spanId":"c1","name":"GET /v1/health","kind":2,"startTimeUnixNano":"300","attributes":[{"key":"http.route","value":{"stringValue":"/v1/health"}},{"key":"http.request.method","value":{"stringValue":"GET"}}]},
102
- {"traceId":"t3","spanId":"c2","parentSpanId":"c1","name":"SELECT 1","kind":3,"startTimeUnixNano":"310","attributes":[{"key":"db.system.name","value":{"stringValue":"postgresql"}},{"key":"db.operation.name","value":{"stringValue":"SELECT"}}]}
103
- ]}]},
104
- {"resource":{"attributes":[{"key":"service.name","value":{"stringValue":"billing"}}]},"scopeSpans":[{"spans":[
105
- {"traceId":"t4","spanId":"d1","name":"POST /v1/charges","kind":2,"startTimeUnixNano":"400","attributes":[{"key":"http.route","value":{"stringValue":"/v1/charges"}}]}
106
- ]}]}]}`
107
-
108
- func runVerify(t *testing.T, traces string, opts Options) (catalog.Catalog, plugin.Response) {
109
- t.Helper()
110
-
111
- return runVerifyOn(t, estate(), traces, opts)
112
- }
113
-
114
- func runVerifyOn(t *testing.T, cat catalog.Catalog, traces string, opts Options) (catalog.Catalog, plugin.Response) {
115
- t.Helper()
116
- root := t.TempDir()
117
- if err := os.MkdirAll(filepath.Join(root, "telemetry"), 0o755); err != nil {
118
- t.Fatal(err)
119
- }
120
- if err := os.WriteFile(filepath.Join(root, "telemetry/traces.jsonl"), []byte(traces), 0o644); err != nil {
121
- t.Fatal(err)
122
- }
123
- if opts.Traces == nil {
124
- opts.Traces = []string{"telemetry/*.jsonl"}
125
- }
126
-
127
- resp, err := verify(plugin.Request{Catalog: cat, Input: plugin.Input{Root: root, Commit: "abc1234", GeneratedAt: "2026-01-01T00:00:00Z"}}, opts)
128
- if err != nil {
129
- t.Fatal(err)
130
- }
131
- if len(resp.Files) != 1 || resp.Files[0].Name != "observed.json" {
132
- t.Fatalf("files = %+v", resp.Files)
133
- }
134
- var out catalog.Catalog
135
- if err := json.Unmarshal([]byte(resp.Files[0].Contents), &out); err != nil {
136
- t.Fatalf("fragment is not a catalog: %v", err)
137
- }
138
-
139
- return out, resp
140
- }
141
-
142
- func flowNamed(t *testing.T, cat catalog.Catalog, slug string) catalog.Flow {
143
- t.Helper()
144
- for _, f := range cat.Flows {
145
- if f.Slug == slug {
146
- return f
147
- }
148
- }
149
- t.Fatalf("no flow %s in %v", slug, slugs(cat))
150
-
151
- return catalog.Flow{}
152
- }
153
-
154
- func slugs(cat catalog.Catalog) []string {
155
- var out []string
156
- for _, f := range cat.Flows {
157
- out = append(out, f.Slug)
158
- }
159
-
160
- return out
161
- }
162
-
163
- func statuses(flow catalog.Flow) map[string]catalog.Status {
164
- out := map[string]catalog.Status{}
165
- walkSteps(flow.Steps, func(s *catalog.Step) { out[s.ID] = s.Status })
166
-
167
- return out
168
- }
169
-
170
- // A trace that opens on a declared endpoint raises the steps it shows - the
171
- // call in, the events out - and leaves the rest as the code declared them.
172
- // A store call is not raised: a query ran, but nothing in the span says it
173
- // was the repository method the code names.
174
- func TestATraceRaisesTheHopsItShows(t *testing.T) {
175
- out, resp := runVerify(t, recording, Options{})
176
-
177
- login := flowNamed(t, out, "auth-login")
178
- got := statuses(login)
179
- want := map[string]catalog.Status{
180
- "s1": catalog.StatusVerified, // client -> auth.auth login
181
- "s2": catalog.StatusDeclared, // ByEmail: a SELECT ran, which is not the same claim
182
- "s3": catalog.StatusUnresolved, // risk: seen, but its far end is still not in the catalog
183
- "s5": catalog.StatusDeclared, // the blocked arm did not run
184
- "s6": catalog.StatusVerified, // SessionStarted published
185
- }
186
- for id, status := range want {
187
- if got[id] != status {
188
- t.Errorf("%s = %s, want %s", id, got[id], status)
189
- }
190
- }
191
- first := firstStep(login.Steps)
192
- if !strings.Contains(first.Note, "telemetry/traces.jsonl") {
193
- t.Errorf("note = %q, want the recording named", first.Note)
194
- }
195
- if len(login.Participants) != 4 {
196
- t.Errorf("participants changed: %+v", login.Participants)
197
- }
198
- for _, d := range resp.Warnings() {
199
- if strings.Contains(d.Message, "no service in the catalog") && !strings.Contains(d.Message, "billing") && !strings.Contains(d.Message, "profile") {
200
- t.Errorf("unexpected diagnostic: %s", d.Message)
201
- }
202
- }
203
- }
204
-
205
- // An event that declares its wire is matched by that name before any guess
206
- // from the last segment, and a publish span that names a channel other than
207
- // the declared one is reported once - the hop stays, because the event did
208
- // go out.
209
- func TestTheWireIsMatchedByNameAndHeldToItsChannel(t *testing.T) {
210
- cat := estate()
211
- ev := &cat.Contexts[0].Services[0].Aggregates[0].Events[0]
212
- if ev.ID != "auth.auth.session.SessionStarted" {
213
- t.Fatalf("fixture moved: %s", ev.ID)
214
- }
215
- ev.Name = "Started"
216
- ev.Wire = &catalog.EventWire{Name: "auth.SessionStarted", Channel: "sessions"}
217
-
218
- out, resp := runVerifyOn(t, cat, recording, Options{})
219
-
220
- seen := false
221
- for _, f := range out.Flows {
222
- walkSteps(f.Steps, func(s *catalog.Step) {
223
- if s.Ref == ev.ID && s.Status == catalog.StatusVerified {
224
- seen = true
225
- }
226
- })
227
- }
228
- if !seen {
229
- t.Errorf("a publish of the declared wire name should resolve to the event: %+v", out.Flows)
230
- }
231
-
232
- warned := 0
233
- for _, d := range resp.Warnings() {
234
- if strings.Contains(d.Message, `on "auth_session"`) && strings.Contains(d.Message, `it goes on "sessions"`) {
235
- warned++
236
- }
237
- }
238
- if warned != 1 {
239
- t.Errorf("the channel the trace shows should be held against the declared one, once: %+v", resp.Warnings())
240
- }
241
- }
242
-
243
- // A consumer inside a trace is the opening of a flow of its own: the policy
244
- // flow is raised from the middle of the password change, and the consumer
245
- // edge comes out verified.
246
- func TestAConsumerInsideATraceOpensThePolicyFlow(t *testing.T) {
247
- out, _ := runVerify(t, recording, Options{})
248
-
249
- revoke := flowNamed(t, out, "auth-revoke")
250
- got := statuses(revoke)
251
- if got["s1"] != catalog.StatusVerified || got["s3"] != catalog.StatusVerified || got["s2"] != catalog.StatusDeclared {
252
- t.Errorf("statuses = %v", got)
253
- }
254
-
255
- var consumers []catalog.EventConsumer
256
- for _, ctx := range out.Contexts {
257
- for _, svc := range ctx.Services {
258
- for _, agg := range svc.Aggregates {
259
- for _, ev := range agg.Events {
260
- if ev.ID == "auth.auth.user.PasswordChanged" {
261
- consumers = ev.Consumers
262
- }
263
- }
264
- }
265
- }
266
- }
267
- if len(consumers) != 1 || consumers[0].Service != "auth.auth" || consumers[0].Status != catalog.StatusVerified {
268
- t.Errorf("consumers = %+v", consumers)
269
- }
270
- }
271
-
272
- // A call to a service the catalog does not have is recorded as made and still
273
- // unresolved; the peer is the package, on a lane without a dot.
274
- func TestACallToNobodyStaysUnresolved(t *testing.T) {
275
- out, _ := runVerify(t, recording, Options{})
276
-
277
- var calls []catalog.RpcCall
278
- for _, ctx := range out.Contexts {
279
- for _, svc := range ctx.Services {
280
- if svc.ID == "auth.auth" {
281
- calls = svc.Consumes
282
- }
283
- }
284
- }
285
- if len(calls) != 2 || calls[1].ID != "risk.v1.RiskService/Assess" || calls[1].Status != catalog.StatusUnresolved || calls[1].Peer != "risk.v1" {
286
- t.Errorf("consumes = %+v", calls)
287
- }
288
- }
289
-
290
- // A call over HTTP is read back to the operation whose route it hit, by the
291
- // verb and the path's shape; one no service answers on is a hop to a host.
292
- func TestAnHTTPCallIsNamedByTheRouteItHit(t *testing.T) {
293
- out, resp := runVerify(t, recording, Options{})
294
-
295
- var calls []catalog.RpcCall
296
- for _, ctx := range out.Contexts {
297
- for _, svc := range ctx.Services {
298
- if svc.ID == "auth.auth" {
299
- calls = svc.Consumes
300
- }
301
- }
302
- }
303
- if len(calls) != 2 || calls[0].ID != "auth.v1.Sessions/getUser" || calls[0].Peer != "auth.auth" || calls[0].Status != catalog.StatusVerified {
304
- t.Errorf("consumes = %+v", calls)
305
- }
306
- var warned bool
307
- for _, d := range resp.Warnings() {
308
- if strings.Contains(d.Message, "GET /v1/profiles/42") && strings.Contains(d.Message, "profile") {
309
- warned = true
310
- }
311
- }
312
- if !warned {
313
- t.Errorf("a call to a host nobody in the catalog is should be reported: %+v", resp.Warnings())
314
- }
315
- }
316
-
317
- // A sequence no flow opens is written down as seen, once per shape, on the
318
- // route it came in on.
319
- func TestASequenceNobodyDeclaredIsObserved(t *testing.T) {
320
- out, resp := runVerify(t, recording, Options{})
321
-
322
- health := flowNamed(t, out, "observed-auth-get-v1-health")
323
- if health.Owner != "auth" || len(health.Steps) != 2 {
324
- t.Fatalf("flow = %+v", health)
325
- }
326
- first := firstStep(health.Steps)
327
- if first.Label != "GET /v1/health" || first.Status != catalog.StatusVerified || first.From != "client" || first.To != "auth.auth" {
328
- t.Errorf("first = %+v", first)
329
- }
330
- second := health.Steps[1].(*catalog.Step)
331
- if second.To != "auth-pg" || second.Label != "SELECT" {
332
- t.Errorf("second = %+v", second)
333
- }
334
- if !strings.Contains(health.Summary, "1 trace") {
335
- t.Errorf("summary = %q", health.Summary)
336
- }
337
-
338
- var warned bool
339
- for _, d := range resp.Warnings() {
340
- if strings.Contains(d.Message, "GET /v1/health") {
341
- warned = true
342
- }
343
- }
344
- if !warned {
345
- t.Error("a route no interface declares should be reported")
346
- }
347
- }
348
-
349
- // Spans from a service the catalog does not have are reported once and read
350
- // as nothing: a flow for a service that does not exist would be a service.
351
- func TestAnUnknownServiceIsReportedNotInvented(t *testing.T) {
352
- out, resp := runVerify(t, recording, Options{})
353
-
354
- for _, slug := range slugs(out) {
355
- if strings.Contains(slug, "charges") {
356
- t.Errorf("a flow was written for the unknown service: %s", slug)
357
- }
358
- }
359
- var n int
360
- for _, d := range resp.Warnings() {
361
- if strings.Contains(d.Message, `"billing"`) {
362
- n++
363
- }
364
- }
365
- if n != 1 {
366
- t.Errorf("billing was reported %d times, want once", n)
367
- }
368
-
369
- named, _ := runVerify(t, recording, Options{Services: map[string]string{"billing": "auth.auth"}})
370
- if len(named.Flows) <= len(out.Flows) {
371
- t.Error("naming the service under `services` should read its spans")
372
- }
373
- }
374
-
375
- // One batch per line and one value per file are the same recording.
376
- func TestJSONLinesAndOneValueReadAlike(t *testing.T) {
377
- lines := strings.ReplaceAll(recording, "\n\n", "\n")
378
- a, err := parseOTLP([]byte(recording), "x")
379
- if err != nil {
380
- t.Fatal(err)
381
- }
382
- two := strings.Replace(lines, `]}]},
383
- {"resource"`, "]}]}]}\n{\"resourceSpans\":[{\"resource\"", 1)
384
- b, err := parseOTLP([]byte(two), "x")
385
- if err != nil {
386
- t.Fatal(err)
387
- }
388
- if len(a) != len(b) || len(a) != 15 {
389
- t.Errorf("spans = %d and %d, want 15 both ways", len(a), len(b))
390
- }
391
- if a[6].kind != kindProducer {
392
- t.Errorf("kind written as a name = %d", a[6].kind)
393
- }
394
- }
395
-
396
- // The fragment says nothing when there is nothing to read, and says so.
397
- func TestNoRecordingIsAWarningAndAnEmptyFragment(t *testing.T) {
398
- root := t.TempDir()
399
- resp, err := verify(plugin.Request{Catalog: estate(), Input: plugin.Input{Root: root}}, Options{Traces: []string{"telemetry/*.jsonl"}})
400
- if err != nil {
401
- t.Fatal(err)
402
- }
403
- if len(resp.Warnings()) != 1 || !strings.Contains(resp.Warnings()[0].Message, "no trace files") {
404
- t.Errorf("diagnostics = %+v", resp.Warnings())
405
- }
406
- var out catalog.Catalog
407
- if err := json.Unmarshal([]byte(resp.Files[0].Contents), &out); err != nil || len(out.Flows) != 0 {
408
- t.Errorf("fragment = %s", resp.Files[0].Contents)
409
- }
410
- }
411
-
412
- func TestAColonRouteHasTheShapeOfABracedOne(t *testing.T) {
413
- if shapeOf("/v1/baskets/:basketId/items/:sku") != shapeOf("/v1/baskets/{basketId}/items/{sku}") {
414
- t.Fatalf("a Fastify route and the OpenAPI path it serves read as different shapes")
415
- }
416
- if !routeMatches("/v1/baskets/{basketId}", "/v1/baskets/42") || routeMatches("/v1/baskets/{basketId}", "/v1/baskets") {
417
- t.Fatalf("routeMatches does not fill a parameter with exactly one segment")
418
- }
419
- }
420
-
421
- // An outbox writes the event and a relay puts it on the bus, and each opens
422
- // a producer span, the second under the first. They are one publish: the
423
- // event left the service once, and a shape nobody declared must show it
424
- // once too.
425
- const relayed = `{"resourceSpans":[{"resource":{"attributes":[{"key":"service.name","value":{"stringValue":"auth"}}]},"scopeSpans":[{"spans":[
426
- {"traceId":"t9","spanId":"r1","name":"POST /v1/ping","kind":2,"startTimeUnixNano":"900","attributes":[{"key":"http.route","value":{"stringValue":"/v1/ping"}},{"key":"http.request.method","value":{"stringValue":"POST"}}]},
427
- {"traceId":"t9","spanId":"r2","parentSpanId":"r1","name":"publish auth.SessionStarted","kind":4,"startTimeUnixNano":"910","attributes":[{"key":"messaging.system","value":{"stringValue":"outbox"}},{"key":"messaging.destination.name","value":{"stringValue":"auth_session"}},{"key":"messaging.operation.type","value":{"stringValue":"publish"}},{"key":"event.name","value":{"stringValue":"auth.SessionStarted"}}]},
428
- {"traceId":"t9","spanId":"r3","parentSpanId":"r2","name":"publish auth.SessionStarted","kind":4,"startTimeUnixNano":"920","attributes":[{"key":"messaging.system","value":{"stringValue":"nats"}},{"key":"messaging.destination.name","value":{"stringValue":"sessions"}},{"key":"messaging.operation.type","value":{"stringValue":"publish"}},{"key":"event.name","value":{"stringValue":"auth.SessionStarted"}}]}
429
- ]}]}]}`
430
-
431
- func TestARelayedPublishIsOneHop(t *testing.T) {
432
- cat := estate()
433
- ev := &cat.Contexts[0].Services[0].Aggregates[0].Events[0]
434
- ev.Wire = &catalog.EventWire{Name: "auth.SessionStarted", Channel: "auth_session"}
435
-
436
- out, resp := runVerifyOn(t, cat, relayed, Options{})
437
-
438
- ping := flowNamed(t, out, "observed-auth-post-v1-ping")
439
- var events []string
440
- walkSteps(ping.Steps, func(s *catalog.Step) {
441
- if s.Kind == catalog.StepEvent {
442
- events = append(events, s.From+">"+s.To+":"+s.Ref)
443
- }
444
- })
445
- if len(events) != 1 || events[0] != "auth.auth>bus:"+ev.ID {
446
- t.Errorf("one event left, so one hop: %v", events)
447
- }
448
-
449
- // The inner span is the one that says which bus, so it is the one the
450
- // channel is read off.
451
- warned := false
452
- for _, d := range resp.Warnings() {
453
- if strings.Contains(d.Message, `on "sessions"`) {
454
- warned = true
455
- }
456
- }
457
- if !warned {
458
- t.Errorf("the relay's destination should be held against the declared channel: %+v", resp.Warnings())
459
- }
460
- }