@shortlink-org/portolan 0.2.4 → 0.4.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 (291) hide show
  1. package/README.md +174 -6
  2. package/catalog/enum_test.go +46 -0
  3. package/catalog/evidence_test.go +35 -0
  4. package/catalog/model.go +1213 -0
  5. package/catalog/roundtrip_test.go +210 -0
  6. package/catalog/via_test.go +38 -0
  7. package/cli/init.test.mjs +6 -1
  8. package/cli/portolan.mjs +14 -1
  9. package/cli/portolan.test.mjs +49 -0
  10. package/go.mod +21 -0
  11. package/go.sum +34 -0
  12. package/internal/gocall/README.md +19 -0
  13. package/internal/gocall/analyze.go +189 -0
  14. package/internal/gocall/analyze_test.go +107 -0
  15. package/internal/gohttp/analyze.go +2562 -0
  16. package/internal/gohttp/destination.go +373 -0
  17. package/internal/gohttp/endpoints.go +1067 -0
  18. package/internal/gohttp/roots.go +320 -0
  19. package/internal/gohttp/typed.go +96 -0
  20. package/internal/goscan/constants.go +85 -0
  21. package/internal/goscan/goscan_test.go +227 -0
  22. package/internal/goscan/index.go +629 -0
  23. package/internal/goscan/index_test.go +66 -0
  24. package/internal/goscan/names.go +52 -0
  25. package/internal/goscan/parse_test.go +11 -0
  26. package/internal/goscan/source.go +37 -0
  27. package/internal/goscan/tree.go +284 -0
  28. package/internal/goscan/types.go +99 -0
  29. package/internal/wsdl/ids.go +127 -0
  30. package/internal/wsdl/ids_test.go +21 -0
  31. package/internal/wsdl/model.go +70 -0
  32. package/internal/wsdl/parse.go +949 -0
  33. package/internal/wsdl/parse_test.go +170 -0
  34. package/package.json +28 -15
  35. package/plugin/describe.go +156 -0
  36. package/plugin/describe_test.go +114 -0
  37. package/plugin/protocol.go +141 -0
  38. package/plugin/schematest/schematest.go +126 -0
  39. package/plugins/README.md +404 -50
  40. package/plugins/cmd/portolan-http-clients/main.go +19 -0
  41. package/plugins/extract-argocd/options.schema.json +44 -0
  42. package/plugins/extract-celery/extract.py +0 -2
  43. package/plugins/extract-celery/extract_test.py +1 -1
  44. package/plugins/extract-celery/main.py +1 -0
  45. package/plugins/extract-csharp-ddd/README.md +213 -0
  46. package/plugins/extract-csharp-ddd/options.schema.json +55 -0
  47. package/plugins/extract-django/README.md +52 -19
  48. package/plugins/extract-django/choices.py +47 -0
  49. package/plugins/extract-django/domain.py +30 -18
  50. package/plugins/extract-django/extract.py +21 -7
  51. package/plugins/extract-django/extract_test.py +68 -2
  52. package/plugins/extract-django/lifecycle.py +4 -28
  53. package/plugins/extract-django/main.py +1 -0
  54. package/plugins/extract-django/operations.py +1 -1
  55. package/plugins/extract-django/routing_test.py +109 -1
  56. package/plugins/extract-django/rules.py +154 -0
  57. package/plugins/extract-django/rules_test.py +158 -0
  58. package/plugins/extract-django/store.py +1 -1
  59. package/plugins/extract-django/transport.py +101 -55
  60. package/plugins/extract-django/verbs.py +241 -0
  61. package/plugins/extract-go/README.md +47 -0
  62. package/plugins/extract-go-sqs/options.schema.json +21 -0
  63. package/plugins/extract-http-clients/describe.go +20 -0
  64. package/plugins/extract-http-clients/describe_test.go +11 -0
  65. package/plugins/extract-http-clients/extract.go +740 -0
  66. package/plugins/extract-http-clients/extract_test.go +1561 -0
  67. package/plugins/extract-http-clients/main.go +41 -0
  68. package/plugins/extract-java/build/org/portolan/extract/Extract.class +0 -0
  69. package/plugins/extract-java/build/org/portolan/extract/Main.class +0 -0
  70. package/plugins/extract-java/build/org/portolan/extract/Protocol$Builder.class +0 -0
  71. package/plugins/extract-java/build/org/portolan/extract/Protocol$Input.class +0 -0
  72. package/plugins/extract-java/build/org/portolan/extract/Protocol$Options.class +0 -0
  73. package/plugins/extract-k8s/options.schema.json +32 -0
  74. package/plugins/extract-laravel/Cargo.lock +962 -0
  75. package/plugins/extract-laravel/Cargo.toml +20 -0
  76. package/plugins/extract-laravel/README.md +200 -0
  77. package/plugins/extract-laravel/options.schema.json +68 -0
  78. package/plugins/extract-laravel/rustfmt.toml +1 -0
  79. package/plugins/extract-php-ddd/Cargo.lock +972 -0
  80. package/plugins/extract-php-ddd/Cargo.toml +22 -0
  81. package/plugins/extract-php-ddd/README.md +141 -0
  82. package/plugins/extract-php-ddd/options.schema.json +50 -0
  83. package/plugins/extract-php-ddd/rustfmt.toml +1 -0
  84. package/plugins/extract-python-kafka/extract.py +0 -2
  85. package/plugins/extract-python-kafka/extract_test.py +1 -1
  86. package/plugins/extract-python-kafka/main.py +1 -0
  87. package/plugins/extract-terraform/options.schema.json +26 -0
  88. package/plugins/extract-ts/extract.test.ts +2 -2
  89. package/plugins/extract-ts/extract.ts +4 -5
  90. package/plugins/extract-ts/graphql.test.ts +1 -1
  91. package/plugins/extract-ts/main.ts +1 -0
  92. package/plugins/openapi/ids.go +261 -0
  93. package/plugins/openapi/ids_test.go +98 -0
  94. package/plugins/phpscan/Cargo.lock +953 -0
  95. package/plugins/phpscan/Cargo.toml +19 -0
  96. package/plugins/phpscan/rustfmt.toml +1 -0
  97. package/plugins/portolan-go.wasm +0 -0
  98. package/plugins/pyplugin/catalog.py +24 -1
  99. package/plugins/pyplugin/protocol.py +1 -5
  100. package/plugins/verify-otel/options.schema.json +12 -0
  101. package/portolan.json +132 -4
  102. package/rules/builtin.json +218 -0
  103. package/schema/portolan.schema.json +905 -4
  104. package/scripts/README.md +21 -13
  105. package/scripts/catalog-sources.mjs +6 -0
  106. package/scripts/delivery-presets.mjs +57 -14
  107. package/scripts/diff.mjs +5 -1
  108. package/scripts/django-aggregates.test.mjs +58 -0
  109. package/scripts/gen-likec4.mjs +150 -17
  110. package/scripts/gen-likec4.test.mjs +96 -0
  111. package/scripts/gen.mjs +148 -118
  112. package/scripts/gitops-example.test.mjs +108 -0
  113. package/scripts/go-discovery.test.mjs +30 -0
  114. package/scripts/history.mjs +186 -3
  115. package/scripts/history.test.mjs +1 -1
  116. package/scripts/host-plugins/fetch-argocd.mjs +338 -0
  117. package/scripts/host-plugins/fetch-argocd.options.json +49 -0
  118. package/scripts/host-plugins/fetch-argocd.test.mjs +274 -0
  119. package/scripts/host-plugins/fetch-bsr.mjs +1 -0
  120. package/scripts/host-plugins/fetch-csr.mjs +1 -0
  121. package/scripts/host-plugins/fetch-git.mjs +78 -21
  122. package/scripts/host-plugins/fetch-git.test.mjs +62 -8
  123. package/scripts/host-plugins/fetch-k8s.mjs +263 -0
  124. package/scripts/host-plugins/fetch-k8s.options.json +50 -0
  125. package/scripts/host-plugins/fetch-k8s.test.mjs +259 -0
  126. package/scripts/host-plugins/k8s-topology.mjs +183 -0
  127. package/scripts/json-format.mjs +192 -0
  128. package/scripts/json-format.test.mjs +97 -0
  129. package/scripts/local-api.mjs +287 -12
  130. package/scripts/local-api.test.mjs +150 -5
  131. package/scripts/local-discovery.mjs +94 -9
  132. package/scripts/manifest.mjs +14 -3
  133. package/scripts/manifest.test.mjs +24 -0
  134. package/scripts/output-diff.mjs +94 -0
  135. package/scripts/output-diff.test.mjs +36 -0
  136. package/scripts/package-smoke.mjs +62 -4
  137. package/scripts/plugin-host.mjs +45 -3
  138. package/scripts/plugin-host.test.mjs +9 -0
  139. package/scripts/plugin-wasm-worker.mjs +4 -1
  140. package/scripts/plugins-fresh.mjs +97 -0
  141. package/scripts/plugins-fresh.test.mjs +64 -0
  142. package/scripts/provenance.mjs +72 -0
  143. package/scripts/provenance.test.mjs +149 -0
  144. package/scripts/run-builtin.mjs +62 -7
  145. package/scripts/schema.mjs +167 -19
  146. package/scripts/trace-trials.mjs +176 -0
  147. package/scripts/trace-trials.test.mjs +142 -0
  148. package/scripts/warning-policy.mjs +167 -0
  149. package/scripts/warning-policy.test.mjs +93 -0
  150. package/src/app/Breadcrumbs.test.ts +3 -0
  151. package/src/app/Breadcrumbs.tsx +3 -0
  152. package/src/app/CatalogApp.tsx +2 -0
  153. package/src/app/Sidebar.tsx +3 -3
  154. package/src/app/SidebarFooter.tsx +20 -4
  155. package/src/catalog-docs.test.ts +64 -0
  156. package/src/catalog-docs.ts +35 -0
  157. package/src/catalog-error.test.ts +15 -0
  158. package/src/catalog-index.ts +25 -0
  159. package/src/catalog-model.ts +293 -5
  160. package/src/catalog-validation.ts +113 -2
  161. package/src/catalog.test.ts +40 -1
  162. package/src/chat/Starter.tsx +5 -11
  163. package/src/chat/tools.test.ts +27 -0
  164. package/src/chat/tools.ts +5 -9
  165. package/src/components/CatalogStamp.tsx +10 -8
  166. package/src/components/ChannelRows.test.tsx +76 -1
  167. package/src/components/ChannelRows.tsx +223 -60
  168. package/src/components/DeploymentRows.tsx +131 -0
  169. package/src/components/DocsLinks.test.tsx +27 -0
  170. package/src/components/DocsLinks.tsx +56 -0
  171. package/src/components/FieldTree.tsx +5 -2
  172. package/src/components/HTTPDestinationEvidence.test.tsx +23 -0
  173. package/src/components/HTTPDestinationEvidence.tsx +31 -0
  174. package/src/components/Integrations.tsx +1 -1
  175. package/src/components/MachineDocs.tsx +6 -5
  176. package/src/components/MethodRows.tsx +9 -2
  177. package/src/components/PluginIcon.tsx +77 -0
  178. package/src/components/ProblemRow.tsx +173 -153
  179. package/src/components/RelationEvidence.test.tsx +14 -0
  180. package/src/components/RelationEvidence.tsx +53 -0
  181. package/src/components/RuleMarks.tsx +22 -0
  182. package/src/components/ShapeRows.tsx +24 -20
  183. package/src/data.ts +46 -7
  184. package/src/enrich.test.ts +459 -4
  185. package/src/enrich.ts +308 -7
  186. package/src/er/ErCanvas.tsx +218 -12
  187. package/src/er/GroupNode.tsx +57 -0
  188. package/src/er/StoreHeader.tsx +1 -0
  189. package/src/er/layout.test.ts +85 -2
  190. package/src/er/layout.ts +140 -5
  191. package/src/er/spec.test.ts +17 -0
  192. package/src/er/spec.ts +23 -10
  193. package/src/flow/Recordings.test.tsx +52 -0
  194. package/src/flow/Recordings.tsx +236 -0
  195. package/src/flow/StepDetail.tsx +59 -0
  196. package/src/flow/TraceTrial.tsx +419 -0
  197. package/src/flow/evidence.test.ts +16 -0
  198. package/src/flow/evidence.ts +34 -0
  199. package/src/flow/examples.test.ts +33 -0
  200. package/src/flow/examples.ts +37 -0
  201. package/src/flow/outline.test.ts +28 -0
  202. package/src/flow/outline.ts +7 -2
  203. package/src/flow/trace-trial-resume.test.ts +42 -0
  204. package/src/flow/trace-trial-resume.ts +74 -0
  205. package/src/graph/elk.ts +78 -0
  206. package/src/index.css +68 -0
  207. package/src/landing/DraggableReveal.tsx +3 -2
  208. package/src/landing/EvidencePipeline.tsx +105 -0
  209. package/src/landing/LandingPage.tsx +17 -70
  210. package/src/landing/ProductTour.tsx +6 -6
  211. package/src/lib/all-problems.ts +27 -17
  212. package/src/lib/catalog-diff.ts +1 -1
  213. package/src/lib/centrality.test.ts +251 -0
  214. package/src/lib/centrality.ts +232 -0
  215. package/src/lib/confluence.test.ts +37 -0
  216. package/src/lib/confluence.ts +41 -0
  217. package/src/lib/context-color.ts +1 -1
  218. package/src/lib/deployment-drift.ts +22 -0
  219. package/src/lib/derive.ts +43 -69
  220. package/src/lib/django-aggregates.d.mts +9 -0
  221. package/src/lib/django-aggregates.mjs +36 -0
  222. package/src/lib/django-aggregates.test.ts +29 -0
  223. package/src/lib/django-aggregates.ts +5 -0
  224. package/src/lib/environments.test.ts +83 -0
  225. package/src/lib/environments.ts +57 -0
  226. package/src/lib/integration-url.test.ts +30 -0
  227. package/src/lib/integration-url.ts +63 -0
  228. package/src/lib/kafka-ui.ts +3 -45
  229. package/src/lib/local-api.ts +116 -4
  230. package/src/lib/notion.ts +13 -0
  231. package/src/lib/plugin-index.json +2765 -0
  232. package/src/lib/plugins.test.ts +68 -0
  233. package/src/lib/plugins.ts +264 -0
  234. package/src/lib/problem-flows.test.ts +61 -0
  235. package/src/lib/problem-flows.ts +78 -0
  236. package/src/lib/problem-rules-cel.d.mts +43 -0
  237. package/src/lib/problem-rules-cel.mjs +407 -0
  238. package/src/lib/problem-rules.test.ts +287 -0
  239. package/src/lib/problem-rules.ts +271 -0
  240. package/src/lib/problem-subjects.ts +737 -0
  241. package/src/lib/rule-entries.ts +39 -0
  242. package/src/lib/{data-problems.test.ts → rules-data.test.ts} +18 -16
  243. package/src/lib/rules-deploy.test.ts +133 -0
  244. package/src/lib/{problems.test.ts → rules-edges.test.ts} +8 -3
  245. package/src/lib/{proto-problems.test.ts → rules-proto.test.ts} +9 -25
  246. package/src/lib/{wire-problems.test.ts → rules-wire.test.ts} +13 -11
  247. package/src/lib/rules.test.ts +51 -0
  248. package/src/lib/rules.ts +86 -0
  249. package/src/lib/setup-info.test.ts +17 -0
  250. package/src/lib/setup-info.ts +58 -0
  251. package/src/lib/shape.test.ts +32 -0
  252. package/src/lib/shape.ts +30 -6
  253. package/src/lib/tech.ts +16 -0
  254. package/src/lib/trace-project.test.ts +34 -0
  255. package/src/lib/trace-project.ts +50 -0
  256. package/src/lib/use-problems.ts +23 -0
  257. package/src/lib/warnings.test.ts +63 -0
  258. package/src/lib/warnings.ts +260 -0
  259. package/src/likec4/ids.test.ts +6 -2
  260. package/src/likec4/ids.ts +43 -0
  261. package/src/main.tsx +23 -0
  262. package/src/map/ContextMapGraph.tsx +76 -32
  263. package/src/merge-deployments.test.ts +127 -0
  264. package/src/merge.test.ts +82 -0
  265. package/src/merge.ts +189 -18
  266. package/src/pages/AggregatePage.tsx +65 -14
  267. package/src/pages/ContextMap.tsx +45 -3
  268. package/src/pages/ContextPage.tsx +8 -5
  269. package/src/pages/EventPage.tsx +15 -5
  270. package/src/pages/FlowDetail.tsx +23 -2
  271. package/src/pages/GraphPage.tsx +40 -3
  272. package/src/pages/Overview.tsx +152 -12
  273. package/src/pages/PluginIndex.tsx +190 -0
  274. package/src/pages/Problems.tsx +396 -128
  275. package/src/pages/ServicePage.tsx +62 -5
  276. package/src/pages/Settings.tsx +217 -43
  277. package/src/pages/settings/AboutSettings.tsx +8 -1
  278. package/src/pages/settings/DjangoAggregateChoices.tsx +79 -0
  279. package/src/pages/settings/IntegrationsSettings.tsx +63 -17
  280. package/src/pages/settings/RecordingSettings.tsx +138 -0
  281. package/src/pages/settings/RulesSettings.tsx +825 -0
  282. package/src/routes.test.ts +9 -0
  283. package/src/routes.ts +22 -1
  284. package/src/selection/DetailPanel.tsx +15 -0
  285. package/src/virtual-provenance.d.ts +11 -0
  286. package/vite.config.ts +5 -0
  287. package/scripts/vendor-lock.mjs +0 -58
  288. package/scripts/vendor-lock.test.mjs +0 -69
  289. package/src/lib/data-problems.ts +0 -314
  290. package/src/lib/proto-problems.ts +0 -237
  291. package/src/lib/wire-problems.ts +0 -342
@@ -0,0 +1,227 @@
1
+ package goscan
2
+
3
+ import (
4
+ "go/ast"
5
+ "os"
6
+ "path/filepath"
7
+ "reflect"
8
+ "strings"
9
+ "testing"
10
+ )
11
+
12
+ func write(t *testing.T, root, name, contents string) {
13
+ t.Helper()
14
+ full := filepath.Join(root, name)
15
+ if err := os.MkdirAll(filepath.Dir(full), 0o755); err != nil {
16
+ t.Fatal(err)
17
+ }
18
+ if err := os.WriteFile(full, []byte(contents), 0o644); err != nil {
19
+ t.Fatal(err)
20
+ }
21
+ }
22
+
23
+ func tree(t *testing.T) *Tree {
24
+ t.Helper()
25
+ root := t.TempDir()
26
+ write(t, root, "go.mod", "module example.com/m\n\ngo 1.22\n")
27
+ write(t, root, "main.go", `package main
28
+
29
+ import (
30
+ other "example.com/m/sub"
31
+ "example.com/m/vendor/lib"
32
+ )
33
+
34
+ const (
35
+ A = "a"
36
+ B = A
37
+ C = other.D
38
+ Inherited
39
+ Self1 = Self2
40
+ Self2 = Self1
41
+ Foreign = lib.Default
42
+ Number = 3
43
+ )
44
+
45
+ type Args struct {
46
+ ID string `+"`json:\"id\"`"+`
47
+ Secret string `+"`json:\"-\"`"+`
48
+ Plain int
49
+ hidden bool
50
+ }
51
+ `)
52
+ write(t, root, "sub/b.go", "package sub\n\nconst D = \"d\"\n")
53
+ write(t, root, "sub/b_test.go", "package sub\n\nconst Test = \"never\"\n")
54
+ write(t, root, "sub/gen.gen.go", "package sub\n\nconst Generated = \"never\"\n")
55
+ write(t, root, "vendor/lib/lib.go", "package lib\n\nconst Default = \"never\"\n")
56
+ write(t, root, ".hidden/h.go", "package hidden\n\nconst Hidden = \"never\"\n")
57
+ write(t, root, "node_modules/x/x.go", "package x\n\nconst X = \"never\"\n")
58
+
59
+ out, err := Read(root)
60
+ if err != nil {
61
+ t.Fatal(err)
62
+ }
63
+ return out
64
+ }
65
+
66
+ func TestReadKeepsTheSourceAndLeavesTheRest(t *testing.T) {
67
+ out := tree(t)
68
+
69
+ var names []string
70
+ for _, file := range out.Files {
71
+ names = append(names, file.Name+" "+file.Pkg)
72
+ }
73
+ want := []string{"main.go example.com/m", "sub/b.go example.com/m/sub"}
74
+ if !reflect.DeepEqual(names, want) {
75
+ t.Errorf("files: got %v, want %v", names, want)
76
+ }
77
+ if out.Module != "example.com/m" {
78
+ t.Errorf("module: got %q", out.Module)
79
+ }
80
+ for _, key := range []string{"example.com/m/sub.Test", "example.com/m/sub.Generated", "example.com/m/vendor/lib.Default", "example.com/m/.hidden.Hidden"} {
81
+ if _, found := out.Constants[key]; found {
82
+ t.Errorf("%s was read", key)
83
+ }
84
+ }
85
+ }
86
+
87
+ func TestImportsAreByLocalName(t *testing.T) {
88
+ out := tree(t)
89
+ got := out.Files[0].Imports
90
+ want := map[string]string{"other": "example.com/m/sub", "lib": "example.com/m/vendor/lib"}
91
+ if !reflect.DeepEqual(got, want) {
92
+ t.Errorf("imports: got %v, want %v", got, want)
93
+ }
94
+ }
95
+
96
+ func TestADottedImportSegmentIsKnownByItsPackageName(t *testing.T) {
97
+ root := t.TempDir()
98
+ write(t, root, "go.mod", "module example.com/m\n")
99
+ write(t, root, "main.go", "package main\n\nimport (\n\t\"github.com/nats-io/nats.go\"\n\t\"gopkg.in/yaml.v3\"\n\t\"github.com/nats-io/nats.go/jetstream\"\n)\n")
100
+ out, err := Read(root)
101
+ if err != nil {
102
+ t.Fatal(err)
103
+ }
104
+ got := out.Files[0].Imports
105
+ want := map[string]string{
106
+ "nats.go": "github.com/nats-io/nats.go",
107
+ "nats": "github.com/nats-io/nats.go",
108
+ "yaml.v3": "gopkg.in/yaml.v3",
109
+ "yaml": "gopkg.in/yaml.v3",
110
+ "jetstream": "github.com/nats-io/nats.go/jetstream",
111
+ }
112
+ if !reflect.DeepEqual(got, want) {
113
+ t.Errorf("imports: got %v, want %v", got, want)
114
+ }
115
+ }
116
+
117
+ func TestConstantsResolveThroughEachOther(t *testing.T) {
118
+ out := tree(t)
119
+ cases := map[string]string{
120
+ "example.com/m.A": "a",
121
+ "example.com/m.B": "a",
122
+ "example.com/m.C": "d",
123
+ "example.com/m.Inherited": "d",
124
+ "example.com/m.Self1": "",
125
+ "example.com/m.Foreign": "",
126
+ "example.com/m.Number": "",
127
+ "example.com/m.Missing": "",
128
+ }
129
+ for key, want := range cases {
130
+ if got := out.ConstantString(key, nil); got != want {
131
+ t.Errorf("%s: got %q, want %q", key, got, want)
132
+ }
133
+ }
134
+ }
135
+
136
+ func TestForeignAnswersWhatTheTreeCannot(t *testing.T) {
137
+ out := tree(t)
138
+ out.Foreign = func(importPath, name string) (string, bool) {
139
+ if importPath == "example.com/m/vendor/lib" && name == "Default" {
140
+ return "default", true
141
+ }
142
+ return "", false
143
+ }
144
+ if got := out.ConstantString("example.com/m.Foreign", nil); got != "default" {
145
+ t.Errorf("got %q, want %q", got, "default")
146
+ }
147
+ }
148
+
149
+ func TestTypeKey(t *testing.T) {
150
+ out := tree(t)
151
+ file := out.Files[0]
152
+ parse := func(src string) ast.Expr {
153
+ expr, err := parseExpr(src)
154
+ if err != nil {
155
+ t.Fatal(err)
156
+ }
157
+ return expr
158
+ }
159
+ cases := map[string]string{
160
+ "Args": "example.com/m.Args",
161
+ "*Args": "example.com/m.Args",
162
+ "string": "string",
163
+ "other.Thing": "example.com/m/sub.Thing",
164
+ "[]other.Thing": "[]example.com/m/sub.Thing",
165
+ "map[string]Args": "map[string]example.com/m.Args",
166
+ "interface{ M() }": "interface",
167
+ "func()": "",
168
+ "a.b.C": "",
169
+ }
170
+ for src, want := range cases {
171
+ if got := out.TypeKey(parse(src), file); got != want {
172
+ t.Errorf("%s: got %q, want %q", src, got, want)
173
+ }
174
+ }
175
+ if got := out.TypeKey(nil, file); got != "" {
176
+ t.Errorf("nil: got %q", got)
177
+ }
178
+ }
179
+
180
+ func TestFieldsOfFollowTheJSONTag(t *testing.T) {
181
+ out := tree(t)
182
+ var body *ast.StructType
183
+ ast.Inspect(out.Files[0].Node, func(node ast.Node) bool {
184
+ if spec, ok := node.(*ast.TypeSpec); ok && spec.Name.Name == "Args" {
185
+ body = spec.Type.(*ast.StructType)
186
+ }
187
+ return body == nil
188
+ })
189
+ got := out.FieldsOf(body)
190
+ want := []string{"id string", "Plain int", "hidden bool"}
191
+ if !reflect.DeepEqual(got, want) {
192
+ t.Errorf("got %v, want %v", got, want)
193
+ }
194
+ }
195
+
196
+ func TestAtIsRelativeToTheRoot(t *testing.T) {
197
+ out := tree(t)
198
+ file := out.Files[1]
199
+ at := out.At(file.Node.Decls[0].Pos())
200
+ if at.String() != "sub/b.go:3" {
201
+ t.Errorf("got %q", at.String())
202
+ }
203
+ if (Source{File: "x.go"}).String() != "x.go" {
204
+ t.Error("a source with no line prints its line")
205
+ }
206
+ }
207
+
208
+ func TestNames(t *testing.T) {
209
+ if got := LastSegment("example.com/m.Args"); got != "Args" {
210
+ t.Errorf("LastSegment: %q", got)
211
+ }
212
+ if got := LastSegment("plain"); got != "plain" {
213
+ t.Errorf("LastSegment plain: %q", got)
214
+ }
215
+ if got := Slug(" Send Welcome_Mail!! "); got != "send-welcome-mail" {
216
+ t.Errorf("Slug: %q", got)
217
+ }
218
+ if got := Title("send-welcome_mail.v1"); got != "Send Welcome Mail V1" {
219
+ t.Errorf("Title: %q", got)
220
+ }
221
+ if got := FirstNonEmpty(" ", "", "x", "y"); got != "x" {
222
+ t.Errorf("FirstNonEmpty: %q", got)
223
+ }
224
+ if got := strings.TrimSpace(FirstNonEmpty(" ", "")); got != "" {
225
+ t.Errorf("FirstNonEmpty empty: %q", got)
226
+ }
227
+ }