@shortlink-org/portolan 0.1.0 → 0.2.1
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.
- package/README.md +69 -205
- package/cli/init.mjs +285 -0
- package/cli/init.test.mjs +158 -0
- package/cli/portolan.mjs +37 -73
- package/index.html +19 -0
- package/package.json +5 -10
- package/plugins/README.md +66 -27
- package/plugins/portolan-go.wasm +0 -0
- package/portolan.json +52 -73
- package/public/landing/cats/bug-hunter.webp +0 -0
- package/public/landing/cats/compass-nap.webp +0 -0
- package/public/landing/cats/diagrammer.webp +0 -0
- package/public/landing/cats/docs-reader.webp +0 -0
- package/public/landing/cats/laptop.webp +0 -0
- package/public/landing/cats/map-inspector.webp +0 -0
- package/public/landing/cats/server-break.webp +0 -0
- package/public/landing/cats/star-mapper.webp +0 -0
- package/public/landing/cats/system-builder.webp +0 -0
- package/public/landing/cats/thread-tangle.webp +0 -0
- package/public/og.png +0 -0
- package/public/readme-header.webp +0 -0
- package/schema/portolan.schema.json +180 -0
- package/scripts/builtin-plugins.mjs +14 -0
- package/scripts/delivery-presets.mjs +353 -0
- package/scripts/gen.mjs +37 -4
- package/scripts/history.mjs +126 -0
- package/scripts/history.test.mjs +110 -0
- package/scripts/host-plugins/fetch-bsr.mjs +351 -0
- package/{plugins/fetch-bsr/options.schema.json → scripts/host-plugins/fetch-bsr.options.json} +4 -0
- package/scripts/host-plugins/fetch-bsr.test.mjs +212 -0
- package/scripts/host-plugins/fetch-csr.mjs +386 -0
- package/scripts/host-plugins/fetch-csr.test.mjs +178 -0
- package/scripts/host-plugins/fetch-git.mjs +379 -0
- package/scripts/host-plugins/fetch-git.test.mjs +210 -0
- package/scripts/local-api.mjs +27 -10
- package/scripts/local-api.test.mjs +85 -2
- package/scripts/manifest.mjs +3 -2
- package/scripts/package-smoke.mjs +24 -3
- package/scripts/plugin-host.mjs +77 -18
- package/scripts/plugin-host.test.mjs +50 -1
- package/scripts/plugin-wasm-worker.mjs +6 -3
- package/scripts/run-builtin.mjs +6 -26
- package/scripts/schema.mjs +6 -1
- package/src/app/App.tsx +24 -429
- package/src/app/Breadcrumbs.test.ts +7 -0
- package/src/app/Breadcrumbs.tsx +15 -2
- package/src/app/CatalogApp.tsx +436 -0
- package/src/app/Sidebar.tsx +0 -1
- package/src/chat/ChatPanel.tsx +98 -41
- package/src/chat/Composer.tsx +4 -2
- package/src/chat/Conversation.tsx +79 -15
- package/src/chat/Header.tsx +43 -9
- package/src/chat/Starter.tsx +64 -3
- package/src/chat/Steps.tsx +1 -1
- package/src/chat/flags.test.ts +51 -14
- package/src/chat/flags.ts +8 -2
- package/src/chat/page-context.test.ts +51 -0
- package/src/chat/page-context.ts +128 -0
- package/src/chat/prompt.test.ts +49 -1
- package/src/chat/prompt.ts +42 -1
- package/src/chat/tools.ts +7 -2
- package/src/chat/transport.ts +16 -4
- package/src/components/ProblemRow.tsx +182 -0
- package/src/data.ts +20 -5
- package/src/er/ErCanvas.tsx +1 -1
- package/src/graph/DependencyGraph.tsx +10 -1
- package/src/graph/FocusedEventGraph.tsx +1 -1
- package/src/graph/GraphToolbar.tsx +2 -4
- package/src/index.css +220 -2
- package/src/landing/DraggableReveal.tsx +168 -0
- package/src/landing/EstateGraph.tsx +28 -0
- package/src/landing/FlowPlayback.tsx +282 -0
- package/src/landing/HeroMap.tsx +128 -0
- package/src/landing/LandingPage.tsx +757 -0
- package/src/landing/ProductFrame.tsx +68 -0
- package/src/landing/ProductTour.tsx +371 -0
- package/src/landing/catalog.ts +25 -0
- package/src/landing/motion.tsx +52 -0
- package/src/lib/all-problems.ts +28 -0
- package/src/lib/local-api.ts +35 -1
- package/src/lib/setup-info.test.ts +8 -0
- package/src/lib/setup-info.ts +3 -2
- package/src/lib/source-code.ts +2 -1
- package/src/likec4/C4View.tsx +5 -0
- package/src/likec4/InteractiveView.tsx +5 -1
- package/src/map/ContextMapGraph.tsx +47 -20
- package/src/pages/ContextMap.tsx +5 -1
- package/src/pages/FlowDetail.tsx +24 -7
- package/src/pages/Overview.tsx +68 -12
- package/src/pages/Problems.tsx +6 -193
- package/src/pages/Settings.tsx +133 -108
- package/src/pages/settings/DeliverySettings.tsx +229 -0
- package/src/pages/settings/PreferencesSettings.tsx +101 -0
- package/src/routes.test.ts +20 -0
- package/src/routes.ts +13 -1
- package/vite.config.ts +17 -9
- package/catalog/enum_test.go +0 -46
- package/catalog/model.go +0 -932
- package/catalog/roundtrip_test.go +0 -185
- package/catalog/via_test.go +0 -38
- package/go.mod +0 -14
- package/go.sum +0 -14
- package/internal/commands/cargo.go +0 -154
- package/internal/commands/commands.go +0 -143
- package/internal/commands/commands_test.go +0 -323
- package/internal/commands/gradle.go +0 -77
- package/internal/commands/justfile.go +0 -82
- package/internal/commands/makefile.go +0 -106
- package/internal/commands/maven.go +0 -93
- package/internal/commands/packagejson.go +0 -159
- package/internal/commands/pyproject.go +0 -169
- package/internal/commands/taskfile.go +0 -101
- package/internal/commands/testdata/estate/.cargo/config.toml +0 -7
- package/internal/commands/testdata/estate/Makefile +0 -33
- package/internal/commands/testdata/estate/Taskfile.yml +0 -21
- package/internal/commands/testdata/estate/build.gradle.kts +0 -21
- package/internal/commands/testdata/estate/justfile +0 -20
- package/internal/commands/testdata/estate/package.json +0 -13
- package/internal/commands/testdata/estate/pom.xml +0 -29
- package/internal/commands/testdata/estate/pyproject.toml +0 -27
- package/internal/commands/testdata/estate/xtask/src/main.rs +0 -27
- package/internal/commands/testdata/golden/commands.json +0 -305
- package/internal/gohttp/analyze.go +0 -2500
- package/internal/gohttp/endpoints.go +0 -1067
- package/internal/gohttp/roots.go +0 -320
- package/internal/gohttp/typed.go +0 -143
- package/internal/goscan/constants.go +0 -85
- package/internal/goscan/goscan_test.go +0 -227
- package/internal/goscan/names.go +0 -52
- package/internal/goscan/parse_test.go +0 -11
- package/internal/goscan/source.go +0 -36
- package/internal/goscan/tree.go +0 -155
- package/internal/goscan/types.go +0 -99
- package/internal/wsdl/ids.go +0 -127
- package/internal/wsdl/ids_test.go +0 -21
- package/internal/wsdl/model.go +0 -62
- package/internal/wsdl/parse.go +0 -920
- package/internal/wsdl/parse_test.go +0 -133
- package/plugin/describe.go +0 -107
- package/plugin/describe_test.go +0 -114
- package/plugin/protocol.go +0 -117
- package/plugin/schematest/schematest.go +0 -126
- package/plugins/extract-adr/describe.go +0 -19
- package/plugins/extract-adr/describe_test.go +0 -11
- package/plugins/extract-adr/extract.go +0 -153
- package/plugins/extract-adr/extract_test.go +0 -350
- package/plugins/extract-adr/history.go +0 -99
- package/plugins/extract-adr/main.go +0 -65
- package/plugins/extract-adr/parse.go +0 -642
- package/plugins/extract-adr/parse_test.go +0 -405
- package/plugins/extract-asyncapi/describe.go +0 -19
- package/plugins/extract-asyncapi/describe_test.go +0 -11
- package/plugins/extract-asyncapi/extract.go +0 -315
- package/plugins/extract-asyncapi/extract_test.go +0 -197
- package/plugins/extract-asyncapi/main.go +0 -48
- package/plugins/extract-asyncapi/spec.go +0 -151
- package/plugins/extract-commands/describe.go +0 -19
- package/plugins/extract-commands/describe_test.go +0 -11
- package/plugins/extract-commands/extract.go +0 -79
- package/plugins/extract-commands/extract_test.go +0 -92
- package/plugins/extract-commands/main.go +0 -55
- package/plugins/extract-csr/avro.go +0 -258
- package/plugins/extract-csr/describe.go +0 -19
- package/plugins/extract-csr/describe_test.go +0 -11
- package/plugins/extract-csr/extract.go +0 -338
- package/plugins/extract-csr/extract_test.go +0 -374
- package/plugins/extract-csr/jsonschema.go +0 -343
- package/plugins/extract-csr/lock.go +0 -25
- package/plugins/extract-csr/main.go +0 -84
- package/plugins/extract-csr/subject.go +0 -102
- package/plugins/extract-flows/describe.go +0 -19
- package/plugins/extract-flows/describe_test.go +0 -11
- package/plugins/extract-flows/extract.go +0 -91
- package/plugins/extract-flows/main.go +0 -45
- package/plugins/extract-flows/parse.go +0 -593
- package/plugins/extract-flows/parse_test.go +0 -204
- package/plugins/extract-glossary/describe.go +0 -19
- package/plugins/extract-glossary/describe_test.go +0 -11
- package/plugins/extract-glossary/extract.go +0 -115
- package/plugins/extract-glossary/extract_test.go +0 -220
- package/plugins/extract-glossary/main.go +0 -59
- package/plugins/extract-glossary/parse.go +0 -214
- package/plugins/extract-glossary/parse_test.go +0 -203
- package/plugins/extract-go/aggregate.go +0 -214
- package/plugins/extract-go/client.go +0 -409
- package/plugins/extract-go/client_test.go +0 -266
- package/plugins/extract-go/describe.go +0 -19
- package/plugins/extract-go/describe_test.go +0 -11
- package/plugins/extract-go/enum.go +0 -195
- package/plugins/extract-go/enum_test.go +0 -82
- package/plugins/extract-go/event.go +0 -99
- package/plugins/extract-go/extract.go +0 -191
- package/plugins/extract-go/extract_test.go +0 -261
- package/plugins/extract-go/flow.go +0 -1441
- package/plugins/extract-go/flow_test.go +0 -609
- package/plugins/extract-go/httpclient.go +0 -174
- package/plugins/extract-go/httpclient_test.go +0 -296
- package/plugins/extract-go/ids.go +0 -92
- package/plugins/extract-go/layout.go +0 -225
- package/plugins/extract-go/layout_test.go +0 -125
- package/plugins/extract-go/lifecycle.go +0 -324
- package/plugins/extract-go/lifecycle_test.go +0 -108
- package/plugins/extract-go/main.go +0 -79
- package/plugins/extract-go/operation.go +0 -157
- package/plugins/extract-go/source.go +0 -316
- package/plugins/extract-go/transport.go +0 -321
- package/plugins/extract-go/transport_test.go +0 -145
- package/plugins/extract-go/wiring.go +0 -434
- package/plugins/extract-go-nats/describe.go +0 -19
- package/plugins/extract-go-nats/describe_test.go +0 -11
- package/plugins/extract-go-nats/extract.go +0 -177
- package/plugins/extract-go-nats/extract_test.go +0 -389
- package/plugins/extract-go-nats/index.go +0 -379
- package/plugins/extract-go-nats/main.go +0 -42
- package/plugins/extract-go-nats/resolve.go +0 -161
- package/plugins/extract-go-nats/sites.go +0 -224
- package/plugins/extract-graphql/describe.go +0 -19
- package/plugins/extract-graphql/describe_test.go +0 -11
- package/plugins/extract-graphql/extract.go +0 -433
- package/plugins/extract-graphql/extract_test.go +0 -256
- package/plugins/extract-graphql/ids.go +0 -49
- package/plugins/extract-graphql/lex.go +0 -237
- package/plugins/extract-graphql/main.go +0 -51
- package/plugins/extract-graphql/parse.go +0 -621
- package/plugins/extract-graphql/parse_test.go +0 -122
- package/plugins/extract-http-clients/describe.go +0 -19
- package/plugins/extract-http-clients/describe_test.go +0 -11
- package/plugins/extract-http-clients/extract.go +0 -705
- package/plugins/extract-http-clients/extract_test.go +0 -1263
- package/plugins/extract-http-clients/main.go +0 -42
- package/plugins/extract-openapi/describe.go +0 -19
- package/plugins/extract-openapi/describe_test.go +0 -11
- package/plugins/extract-openapi/discover.go +0 -243
- package/plugins/extract-openapi/extract.go +0 -526
- package/plugins/extract-openapi/extract_test.go +0 -545
- package/plugins/extract-openapi/main.go +0 -75
- package/plugins/extract-openapi/spec.go +0 -350
- package/plugins/extract-project/describe.go +0 -19
- package/plugins/extract-project/describe_test.go +0 -11
- package/plugins/extract-project/extract.go +0 -221
- package/plugins/extract-project/extract_test.go +0 -109
- package/plugins/extract-project/main.go +0 -41
- package/plugins/extract-proto/ast.go +0 -125
- package/plugins/extract-proto/consumes.go +0 -77
- package/plugins/extract-proto/describe.go +0 -19
- package/plugins/extract-proto/extract.go +0 -293
- package/plugins/extract-proto/extract_test.go +0 -459
- package/plugins/extract-proto/ids.go +0 -89
- package/plugins/extract-proto/ids_test.go +0 -57
- package/plugins/extract-proto/lex.go +0 -285
- package/plugins/extract-proto/main.go +0 -100
- package/plugins/extract-proto/module.go +0 -120
- package/plugins/extract-proto/parse.go +0 -720
- package/plugins/extract-proto/parse_test.go +0 -307
- package/plugins/extract-proto/provides.go +0 -236
- package/plugins/extract-proto/resolve.go +0 -222
- package/plugins/extract-proto/resolve_test.go +0 -119
- package/plugins/extract-redis/describe.go +0 -19
- package/plugins/extract-redis/describe_test.go +0 -11
- package/plugins/extract-redis/extract.go +0 -183
- package/plugins/extract-redis/extract_test.go +0 -168
- package/plugins/extract-redis/keyspaces.go +0 -469
- package/plugins/extract-redis/main.go +0 -44
- package/plugins/extract-river/describe.go +0 -19
- package/plugins/extract-river/describe_test.go +0 -11
- package/plugins/extract-river/extract.go +0 -507
- package/plugins/extract-river/extract_test.go +0 -132
- package/plugins/extract-river/main.go +0 -42
- package/plugins/extract-sql/ddl.go +0 -893
- package/plugins/extract-sql/ddl_test.go +0 -401
- package/plugins/extract-sql/describe.go +0 -19
- package/plugins/extract-sql/describe_test.go +0 -11
- package/plugins/extract-sql/layout.go +0 -221
- package/plugins/extract-sql/layout_test.go +0 -55
- package/plugins/extract-sql/lineage.go +0 -107
- package/plugins/extract-sql/main.go +0 -141
- package/plugins/extract-sql/maps.go +0 -564
- package/plugins/extract-sql/maps_java.go +0 -117
- package/plugins/extract-sql/maps_rust.go +0 -333
- package/plugins/extract-sql/maps_rust_test.go +0 -70
- package/plugins/extract-sql/maps_test.go +0 -204
- package/plugins/extract-sql/maps_ts.go +0 -398
- package/plugins/extract-sql/maps_ts_test.go +0 -136
- package/plugins/extract-sql/projection.go +0 -70
- package/plugins/extract-sql/projection_test.go +0 -50
- package/plugins/extract-sql/store.go +0 -420
- package/plugins/extract-sql/store_test.go +0 -233
- package/plugins/extract-sql/view.go +0 -295
- package/plugins/extract-watermill/describe.go +0 -19
- package/plugins/extract-watermill/describe_test.go +0 -11
- package/plugins/extract-watermill/extract.go +0 -1228
- package/plugins/extract-watermill/extract_test.go +0 -234
- package/plugins/extract-watermill/main.go +0 -41
- package/plugins/extract-wsdl/describe.go +0 -19
- package/plugins/extract-wsdl/describe_test.go +0 -11
- package/plugins/extract-wsdl/extract.go +0 -148
- package/plugins/extract-wsdl/extract_test.go +0 -45
- package/plugins/extract-wsdl/main.go +0 -53
- package/plugins/fetch-bsr/auth.go +0 -114
- package/plugins/fetch-bsr/bsr.go +0 -240
- package/plugins/fetch-bsr/cache.go +0 -66
- package/plugins/fetch-bsr/describe.go +0 -19
- package/plugins/fetch-bsr/fetch.go +0 -192
- package/plugins/fetch-bsr/fetch_test.go +0 -465
- package/plugins/fetch-bsr/lock.go +0 -71
- package/plugins/fetch-bsr/main.go +0 -117
- package/plugins/fetch-csr/auth.go +0 -95
- package/plugins/fetch-csr/cache.go +0 -69
- package/plugins/fetch-csr/describe.go +0 -19
- package/plugins/fetch-csr/describe_test.go +0 -11
- package/plugins/fetch-csr/fetch.go +0 -254
- package/plugins/fetch-csr/fetch_test.go +0 -494
- package/plugins/fetch-csr/lock.go +0 -95
- package/plugins/fetch-csr/main.go +0 -122
- package/plugins/fetch-csr/registry.go +0 -209
- package/plugins/fetch-git/cache.go +0 -65
- package/plugins/fetch-git/describe.go +0 -19
- package/plugins/fetch-git/describe_test.go +0 -11
- package/plugins/fetch-git/fetch.go +0 -181
- package/plugins/fetch-git/fetch_test.go +0 -332
- package/plugins/fetch-git/git.go +0 -169
- package/plugins/fetch-git/lock.go +0 -72
- package/plugins/fetch-git/main.go +0 -127
- package/plugins/fetch-git/offline.go +0 -39
- package/plugins/fetch-git/pin.go +0 -90
- package/plugins/fetch-git/pin_test.go +0 -74
- package/plugins/gen-backstage/describe.go +0 -17
- package/plugins/gen-backstage/main.go +0 -22
- package/plugins/gen-backstage/plugin.go +0 -473
- package/plugins/gen-backstage/plugin_test.go +0 -145
- package/plugins/gen-backstage.wasm +0 -0
- package/plugins/gen-markdown/adr.go +0 -162
- package/plugins/gen-markdown/aggregate.go +0 -362
- package/plugins/gen-markdown/canonical.go +0 -93
- package/plugins/gen-markdown/context.go +0 -90
- package/plugins/gen-markdown/coverage_test.go +0 -89
- package/plugins/gen-markdown/describe.go +0 -19
- package/plugins/gen-markdown/describe_test.go +0 -11
- package/plugins/gen-markdown/external.go +0 -71
- package/plugins/gen-markdown/flow.go +0 -277
- package/plugins/gen-markdown/glossary.go +0 -76
- package/plugins/gen-markdown/glossary_test.go +0 -148
- package/plugins/gen-markdown/llms.go +0 -302
- package/plugins/gen-markdown/main.go +0 -22
- package/plugins/gen-markdown/markdown.go +0 -254
- package/plugins/gen-markdown/markdown_test.go +0 -100
- package/plugins/gen-markdown/module.go +0 -107
- package/plugins/gen-markdown/plugin.go +0 -39
- package/plugins/gen-markdown/quality_test.go +0 -176
- package/plugins/gen-markdown/redis_test.go +0 -38
- package/plugins/gen-markdown/render.go +0 -344
- package/plugins/gen-markdown/render_test.go +0 -186
- package/plugins/gen-markdown/service.go +0 -456
- package/plugins/gen-markdown/source.go +0 -177
- package/plugins/gen-markdown/store.go +0 -201
- package/plugins/gen-markdown.wasm +0 -0
- package/plugins/gen-mermaid/describe.go +0 -17
- package/plugins/gen-mermaid/main.go +0 -22
- package/plugins/gen-mermaid/plugin.go +0 -104
- package/plugins/gen-mermaid/plugin_test.go +0 -33
- package/plugins/gen-mermaid.wasm +0 -0
- package/plugins/openapi/ids.go +0 -261
- package/plugins/openapi/ids_test.go +0 -98
- package/plugins/verify-codeowners/describe.go +0 -19
- package/plugins/verify-codeowners/describe_test.go +0 -11
- package/plugins/verify-codeowners/main.go +0 -75
- package/plugins/verify-codeowners/match.go +0 -85
- package/plugins/verify-codeowners/match_test.go +0 -47
- package/plugins/verify-codeowners/owners.go +0 -164
- package/plugins/verify-codeowners/owners_test.go +0 -225
- package/plugins/verify-codeowners/parse.go +0 -90
- package/plugins/verify-codeowners/parse_test.go +0 -62
- package/plugins/verify-otel/describe.go +0 -19
- package/plugins/verify-otel/describe_test.go +0 -11
- package/plugins/verify-otel/main.go +0 -53
- package/plugins/verify-otel/match.go +0 -336
- package/plugins/verify-otel/otlp.go +0 -200
- package/plugins/verify-otel/verify.go +0 -734
- package/plugins/verify-otel/verify_test.go +0 -460
- /package/{plugins/fetch-csr/options.schema.json → scripts/host-plugins/fetch-csr.options.json} +0 -0
- /package/{plugins/fetch-git/options.schema.json → scripts/host-plugins/fetch-git.options.json} +0 -0
|
@@ -1,609 +0,0 @@
|
|
|
1
|
-
package main
|
|
2
|
-
|
|
3
|
-
import (
|
|
4
|
-
"go/ast"
|
|
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 flows themselves are checked where every other fragment is: the committed
|
|
15
|
-
// examples/auth/portolan/domain.json, which `npm run gen:check` holds to the
|
|
16
|
-
// source. What is worth pinning here is the reading that gets there - the order
|
|
17
|
-
// calls come out in, what a signature says came back, and the two places where
|
|
18
|
-
// a wrong answer would be a plausible one.
|
|
19
|
-
|
|
20
|
-
const useCaseSource = `package end_after_credential_change
|
|
21
|
-
|
|
22
|
-
import (
|
|
23
|
-
"context"
|
|
24
|
-
|
|
25
|
-
"github.com/example/auth/internal/domain/session"
|
|
26
|
-
"github.com/example/auth/internal/domain/session/event"
|
|
27
|
-
)
|
|
28
|
-
|
|
29
|
-
type UseCase struct {
|
|
30
|
-
repo session.Repository
|
|
31
|
-
now func() time.Time
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
func (uc *UseCase) Handle(ctx context.Context, in dto.Input) error {
|
|
35
|
-
sessions, err := uc.repo.ByUserID(ctx, in.UserID)
|
|
36
|
-
if err != nil {
|
|
37
|
-
return err
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
for _, doomed := range change.Ends(sessions, uc.now()) {
|
|
41
|
-
if err := uc.end(ctx, doomed.ID); err != nil {
|
|
42
|
-
return err
|
|
43
|
-
}
|
|
44
|
-
}
|
|
45
|
-
return nil
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
func (uc *UseCase) end(ctx context.Context, id string) error {
|
|
49
|
-
for attempt := range retries {
|
|
50
|
-
current, err := uc.repo.ByID(ctx, id)
|
|
51
|
-
if err != nil {
|
|
52
|
-
return err
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
ev, ended := current.Revoke(event.ReasonPasswordChanged, uc.now())
|
|
56
|
-
if !ended {
|
|
57
|
-
return nil
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
return uc.repo.Save(ctx, current, ev)
|
|
61
|
-
}
|
|
62
|
-
return nil
|
|
63
|
-
}
|
|
64
|
-
`
|
|
65
|
-
|
|
66
|
-
func handleOf(t *testing.T, source, name string) (*pkg, *ast.FuncDecl) {
|
|
67
|
-
t.Helper()
|
|
68
|
-
|
|
69
|
-
pkg, err := parseSource("usecase.go", source)
|
|
70
|
-
if err != nil {
|
|
71
|
-
t.Fatal(err)
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
fn := pkg.methods("UseCase")[name]
|
|
75
|
-
if fn == nil {
|
|
76
|
-
t.Fatalf("no method %s on UseCase", name)
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
return pkg, fn
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
// The order of the calls is the order of the flow, and it has to be the order
|
|
83
|
-
// they are written in - including a call in an `if` initialiser, which runs
|
|
84
|
-
// before the block it guards.
|
|
85
|
-
func TestCallSitesAreInSourceOrder(t *testing.T) {
|
|
86
|
-
_, handle := handleOf(t, useCaseSource, "Handle")
|
|
87
|
-
|
|
88
|
-
var got []string
|
|
89
|
-
for _, site := range callSites(handle) {
|
|
90
|
-
if selector, ok := site.call.Fun.(*ast.SelectorExpr); ok {
|
|
91
|
-
got = append(got, selector.Sel.Name)
|
|
92
|
-
}
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
want := []string{"ByUserID", "Ends", "now", "end"}
|
|
96
|
-
if len(got) != len(want) {
|
|
97
|
-
t.Fatalf("calls = %v, want %v", got, want)
|
|
98
|
-
}
|
|
99
|
-
for i := range want {
|
|
100
|
-
if got[i] != want[i] {
|
|
101
|
-
t.Errorf("call %d = %q, want %q", i, got[i], want[i])
|
|
102
|
-
}
|
|
103
|
-
}
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
// A step inside a loop says so. The frame is deliberately not built - the
|
|
107
|
-
// condition is written for a compiler - but a reader who is not told that Save
|
|
108
|
-
// runs once per session has been told something false. The note travels from
|
|
109
|
-
// the loop in Handle, through the helper it calls, onto the step.
|
|
110
|
-
func TestAStepInsideALoopSaysSo(t *testing.T) {
|
|
111
|
-
d, _ := walkSource(t, useCaseSource, nil)
|
|
112
|
-
|
|
113
|
-
save := stepLabelled(t, d.steps, "Save")
|
|
114
|
-
want := "inside a loop over `change.Ends(sessions, uc.now())`, inside a loop over `retries`."
|
|
115
|
-
if save.Note != want {
|
|
116
|
-
t.Fatalf("Save note = %q, want %q", save.Note, want)
|
|
117
|
-
}
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
// walkSource reads Handle of an in-memory use case the way extractFlows would,
|
|
121
|
-
// with the store on its own lane and the given peers.
|
|
122
|
-
func walkSource(t *testing.T, source string, peers map[string]string) (*flowDraft, *flowReader) {
|
|
123
|
-
t.Helper()
|
|
124
|
-
|
|
125
|
-
pkg, handle := handleOf(t, source, "Handle")
|
|
126
|
-
r := newFlowReader(t.TempDir(), flowOptions{context: "auth", svcID: "auth.auth", service: "auth", store: "pg", peers: peers}, &plugin.Builder{})
|
|
127
|
-
r.module = "github.com/example/auth"
|
|
128
|
-
|
|
129
|
-
d := newDraft()
|
|
130
|
-
d.lane(r.serviceLane())
|
|
131
|
-
r.walkBody(d, &scope{
|
|
132
|
-
pkg: pkg,
|
|
133
|
-
key: "session/login",
|
|
134
|
-
fields: useCaseFields(pkg),
|
|
135
|
-
imports: importsOf(pkg),
|
|
136
|
-
vars: map[string]domainRef{},
|
|
137
|
-
recv: receiverIdent(handle),
|
|
138
|
-
recvType: "UseCase",
|
|
139
|
-
}, handle, 0)
|
|
140
|
-
|
|
141
|
-
return d, r
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
func stepLabelled(t *testing.T, nodes catalog.FlowNodes, label string) *catalog.Step {
|
|
145
|
-
t.Helper()
|
|
146
|
-
for _, node := range nodes {
|
|
147
|
-
switch x := node.(type) {
|
|
148
|
-
case *catalog.Step:
|
|
149
|
-
if x.Label == label {
|
|
150
|
-
return x
|
|
151
|
-
}
|
|
152
|
-
case *catalog.Alt:
|
|
153
|
-
for _, branch := range x.Branches {
|
|
154
|
-
for _, inner := range branch.Steps {
|
|
155
|
-
if step, ok := inner.(*catalog.Step); ok && step.Label == label {
|
|
156
|
-
return step
|
|
157
|
-
}
|
|
158
|
-
}
|
|
159
|
-
}
|
|
160
|
-
}
|
|
161
|
-
}
|
|
162
|
-
t.Fatalf("no step labelled %s", label)
|
|
163
|
-
|
|
164
|
-
return nil
|
|
165
|
-
}
|
|
166
|
-
|
|
167
|
-
const branchingSource = `package login
|
|
168
|
-
|
|
169
|
-
import (
|
|
170
|
-
"context"
|
|
171
|
-
|
|
172
|
-
"github.com/example/auth/internal/domain/session"
|
|
173
|
-
)
|
|
174
|
-
|
|
175
|
-
type UseCase struct {
|
|
176
|
-
repo session.Repository
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
func (uc *UseCase) Handle(ctx context.Context, in dto.Input) error {
|
|
180
|
-
u, err := uc.repo.ByID(ctx, in.ID)
|
|
181
|
-
if err != nil {
|
|
182
|
-
return err
|
|
183
|
-
}
|
|
184
|
-
|
|
185
|
-
if in.Force {
|
|
186
|
-
if err := uc.repo.Save(ctx, u); err != nil {
|
|
187
|
-
return err
|
|
188
|
-
}
|
|
189
|
-
return nil
|
|
190
|
-
} else if in.Dry {
|
|
191
|
-
uc.repo.ByToken(ctx, in.Token)
|
|
192
|
-
}
|
|
193
|
-
|
|
194
|
-
return uc.repo.Save(ctx, u)
|
|
195
|
-
}
|
|
196
|
-
`
|
|
197
|
-
|
|
198
|
-
// An if with a hop inside it is a choice the reader has to see, and an arm
|
|
199
|
-
// that returns is one the flow does not continue past. The if with nothing in
|
|
200
|
-
// it - the error check - is not drawn at all.
|
|
201
|
-
func TestAnIfWithAHopInsideItIsAnAlt(t *testing.T) {
|
|
202
|
-
d, _ := walkSource(t, branchingSource, nil)
|
|
203
|
-
|
|
204
|
-
if len(d.steps) != 3 {
|
|
205
|
-
t.Fatalf("top-level nodes = %d, want 3 (ByID, alt, Save): %s", len(d.steps), dump(d.steps))
|
|
206
|
-
}
|
|
207
|
-
alt, ok := d.steps[1].(*catalog.Alt)
|
|
208
|
-
if !ok {
|
|
209
|
-
t.Fatalf("second node is %T, want alt", d.steps[1])
|
|
210
|
-
}
|
|
211
|
-
if alt.ID != "alt4" {
|
|
212
|
-
t.Errorf("alt id = %q; ids number every node, frames included", alt.ID)
|
|
213
|
-
}
|
|
214
|
-
|
|
215
|
-
type arm struct {
|
|
216
|
-
title string
|
|
217
|
-
steps int
|
|
218
|
-
terminal bool
|
|
219
|
-
}
|
|
220
|
-
var got []arm
|
|
221
|
-
for _, b := range alt.Branches {
|
|
222
|
-
got = append(got, arm{b.Title, len(b.Steps), b.Terminal})
|
|
223
|
-
}
|
|
224
|
-
want := []arm{{"in.Force", 1, true}, {"in.Dry", 1, false}, {"otherwise", 0, false}}
|
|
225
|
-
if len(got) != len(want) {
|
|
226
|
-
t.Fatalf("arms = %+v, want %+v", got, want)
|
|
227
|
-
}
|
|
228
|
-
for i := range want {
|
|
229
|
-
if got[i] != want[i] {
|
|
230
|
-
t.Errorf("arm %d = %+v, want %+v", i, got[i], want[i])
|
|
231
|
-
}
|
|
232
|
-
}
|
|
233
|
-
|
|
234
|
-
last, ok := d.steps[2].(*catalog.Step)
|
|
235
|
-
if !ok || last.Label != "Save" || last.ID != "s5" {
|
|
236
|
-
t.Errorf("last node = %+v, want the Save after the alt as s5", d.steps[2])
|
|
237
|
-
}
|
|
238
|
-
}
|
|
239
|
-
|
|
240
|
-
// Two arms that both end the flow are not a choice about what comes next, and
|
|
241
|
-
// the catalog refuses an alt whose every arm leaves. The arms stay drawn; the
|
|
242
|
-
// mark comes off.
|
|
243
|
-
func TestArmsThatAllLeaveLoseTheMark(t *testing.T) {
|
|
244
|
-
const source = `package login
|
|
245
|
-
|
|
246
|
-
import "github.com/example/auth/internal/domain/session"
|
|
247
|
-
|
|
248
|
-
type UseCase struct {
|
|
249
|
-
repo session.Repository
|
|
250
|
-
}
|
|
251
|
-
|
|
252
|
-
func (uc *UseCase) Handle(ctx context.Context, in dto.Input) error {
|
|
253
|
-
if in.A {
|
|
254
|
-
return uc.repo.Save(ctx, nil)
|
|
255
|
-
} else {
|
|
256
|
-
return uc.repo.ByID(ctx, in.ID)
|
|
257
|
-
}
|
|
258
|
-
}
|
|
259
|
-
`
|
|
260
|
-
d, _ := walkSource(t, source, nil)
|
|
261
|
-
alt, ok := d.steps[0].(*catalog.Alt)
|
|
262
|
-
if !ok {
|
|
263
|
-
t.Fatalf("node = %T", d.steps[0])
|
|
264
|
-
}
|
|
265
|
-
for _, branch := range alt.Branches {
|
|
266
|
-
if branch.Terminal {
|
|
267
|
-
t.Errorf("arm %q is terminal", branch.Title)
|
|
268
|
-
}
|
|
269
|
-
}
|
|
270
|
-
}
|
|
271
|
-
|
|
272
|
-
func dump(nodes catalog.FlowNodes) string {
|
|
273
|
-
out := ""
|
|
274
|
-
for _, node := range nodes {
|
|
275
|
-
switch x := node.(type) {
|
|
276
|
-
case *catalog.Step:
|
|
277
|
-
out += x.ID + ":" + x.Label + " "
|
|
278
|
-
case *catalog.Alt:
|
|
279
|
-
out += x.ID + "{" + dump(altSteps(x)) + "} "
|
|
280
|
-
}
|
|
281
|
-
}
|
|
282
|
-
|
|
283
|
-
return out
|
|
284
|
-
}
|
|
285
|
-
|
|
286
|
-
func altSteps(alt *catalog.Alt) catalog.FlowNodes {
|
|
287
|
-
var out catalog.FlowNodes
|
|
288
|
-
for _, b := range alt.Branches {
|
|
289
|
-
out = append(out, b.Steps...)
|
|
290
|
-
}
|
|
291
|
-
|
|
292
|
-
return out
|
|
293
|
-
}
|
|
294
|
-
|
|
295
|
-
// The loops enclosing a call travel into the body it reaches: a use case run
|
|
296
|
-
// once per session does everything it does once per session, and the fact
|
|
297
|
-
// belongs to its steps rather than to the call that got there.
|
|
298
|
-
func TestLoopsTravelIntoTheBodyTheyReach(t *testing.T) {
|
|
299
|
-
d := &flowDraft{seen: map[string]bool{}}
|
|
300
|
-
d.enter("inside a loop over `sessions`")
|
|
301
|
-
d.enter("inside a loop over `retries`")
|
|
302
|
-
d.add(catalog.Step{Label: "Save"})
|
|
303
|
-
d.leave()
|
|
304
|
-
d.leave()
|
|
305
|
-
|
|
306
|
-
step, ok := d.steps[0].(*catalog.Step)
|
|
307
|
-
if !ok {
|
|
308
|
-
t.Fatalf("step is %T", d.steps[0])
|
|
309
|
-
}
|
|
310
|
-
want := "inside a loop over `sessions`, inside a loop over `retries`."
|
|
311
|
-
if step.Note != want {
|
|
312
|
-
t.Errorf("note = %q, want %q", step.Note, want)
|
|
313
|
-
}
|
|
314
|
-
}
|
|
315
|
-
|
|
316
|
-
// What came back is read off the signature, by position: a name bound to the
|
|
317
|
-
// second result of `Start` holds an event, and one bound to the first holds a
|
|
318
|
-
// session. Reading the body instead would be reading an implementation to find
|
|
319
|
-
// out what a declaration already says.
|
|
320
|
-
func TestResultsAreReadOffTheSignature(t *testing.T) {
|
|
321
|
-
const domainSource = `package session
|
|
322
|
-
|
|
323
|
-
import "github.com/example/auth/internal/domain/session/event"
|
|
324
|
-
|
|
325
|
-
func Start(id, userID string, now time.Time) (*Session, event.SessionStarted, error) {
|
|
326
|
-
return nil, event.SessionStarted{}, nil
|
|
327
|
-
}
|
|
328
|
-
`
|
|
329
|
-
|
|
330
|
-
pkg, err := parseSource("session.go", domainSource)
|
|
331
|
-
if err != nil {
|
|
332
|
-
t.Fatal(err)
|
|
333
|
-
}
|
|
334
|
-
|
|
335
|
-
var start *ast.FuncDecl
|
|
336
|
-
for _, decl := range pkg.files[0].Decls {
|
|
337
|
-
if fn, ok := decl.(*ast.FuncDecl); ok && fn.Name.Name == "Start" {
|
|
338
|
-
start = fn
|
|
339
|
-
}
|
|
340
|
-
}
|
|
341
|
-
if start == nil {
|
|
342
|
-
t.Fatal("no Start")
|
|
343
|
-
}
|
|
344
|
-
|
|
345
|
-
r := &flowReader{}
|
|
346
|
-
got := r.resultRefs(pkg, "session", start.Type)
|
|
347
|
-
|
|
348
|
-
if len(got) != 3 {
|
|
349
|
-
t.Fatalf("results = %d, want 3 - a result this cannot place has to keep its position", len(got))
|
|
350
|
-
}
|
|
351
|
-
if got[0].name != "Session" || got[0].event {
|
|
352
|
-
t.Errorf("first result = %+v", got[0])
|
|
353
|
-
}
|
|
354
|
-
if got[1].name != "SessionStarted" || !got[1].event || got[1].aggregate != "session" {
|
|
355
|
-
t.Errorf("second result = %+v", got[1])
|
|
356
|
-
}
|
|
357
|
-
if got[2].name != "" {
|
|
358
|
-
t.Errorf("error was read as %+v", got[2])
|
|
359
|
-
}
|
|
360
|
-
}
|
|
361
|
-
|
|
362
|
-
// The binding lives in assembly because neither side of it can: login declares
|
|
363
|
-
// the port so that it does not import the package that satisfies it.
|
|
364
|
-
func TestPortBindingsAreReadFromAssembly(t *testing.T) {
|
|
365
|
-
root := t.TempDir()
|
|
366
|
-
dir := filepath.Join(root, "internal/di/provider")
|
|
367
|
-
if err := os.MkdirAll(dir, 0o755); err != nil {
|
|
368
|
-
t.Fatal(err)
|
|
369
|
-
}
|
|
370
|
-
|
|
371
|
-
const provider = `package provider
|
|
372
|
-
|
|
373
|
-
import (
|
|
374
|
-
"github.com/example/auth/internal/application/session/usecases/login"
|
|
375
|
-
"github.com/example/auth/internal/application/user/usecases/authenticate"
|
|
376
|
-
)
|
|
377
|
-
|
|
378
|
-
func ProvideAuthenticator(uc *authenticate.UseCase) login.Authenticator {
|
|
379
|
-
return authenticator{uc: uc}
|
|
380
|
-
}
|
|
381
|
-
|
|
382
|
-
// Two results is not a binding: the port is not the whole answer.
|
|
383
|
-
func ProvideSomethingElse(uc *authenticate.UseCase) (login.Authenticator, error) {
|
|
384
|
-
return nil, nil
|
|
385
|
-
}
|
|
386
|
-
`
|
|
387
|
-
if err := os.WriteFile(filepath.Join(dir, "authenticator.go"), []byte(provider), 0o644); err != nil {
|
|
388
|
-
t.Fatal(err)
|
|
389
|
-
}
|
|
390
|
-
|
|
391
|
-
got := portBindings(root)
|
|
392
|
-
if want := "user/authenticate"; got["session/login.Authenticator"] != want {
|
|
393
|
-
t.Errorf("binding = %q, want %q", got["session/login.Authenticator"], want)
|
|
394
|
-
}
|
|
395
|
-
if len(got) != 1 {
|
|
396
|
-
t.Errorf("bindings = %v, want exactly the one that binds two use cases", got)
|
|
397
|
-
}
|
|
398
|
-
}
|
|
399
|
-
|
|
400
|
-
func TestDirectWireBindingsMarkConsumerOwnedAdapterPorts(t *testing.T) {
|
|
401
|
-
root := t.TempDir()
|
|
402
|
-
dir := filepath.Join(root, "internal/user/di")
|
|
403
|
-
if err := os.MkdirAll(dir, 0o755); err != nil {
|
|
404
|
-
t.Fatal(err)
|
|
405
|
-
}
|
|
406
|
-
|
|
407
|
-
const assembly = `package di
|
|
408
|
-
|
|
409
|
-
import (
|
|
410
|
-
"github.com/google/wire"
|
|
411
|
-
"github.com/example/auth/internal/user/application/register"
|
|
412
|
-
"github.com/example/auth/internal/user/infrastructure/password"
|
|
413
|
-
)
|
|
414
|
-
|
|
415
|
-
var Set = wire.NewSet(
|
|
416
|
-
password.NewHasher,
|
|
417
|
-
wire.Bind(new(register.PasswordHasher), new(*password.Hasher)),
|
|
418
|
-
)
|
|
419
|
-
`
|
|
420
|
-
if err := os.WriteFile(filepath.Join(dir, "infrastructure.go"), []byte(assembly), 0o644); err != nil {
|
|
421
|
-
t.Fatal(err)
|
|
422
|
-
}
|
|
423
|
-
|
|
424
|
-
got := wireBoundPorts(root)
|
|
425
|
-
if !got["user/register.PasswordHasher"] {
|
|
426
|
-
t.Errorf("bindings = %v, want user/register.PasswordHasher", got)
|
|
427
|
-
}
|
|
428
|
-
}
|
|
429
|
-
|
|
430
|
-
// A port filled by several use cases, one per method, is bound per method:
|
|
431
|
-
// the signature names the use cases, the adapter's methods say which is which.
|
|
432
|
-
func TestAPortOverSeveralUseCasesIsBoundPerMethod(t *testing.T) {
|
|
433
|
-
root := t.TempDir()
|
|
434
|
-
dir := filepath.Join(root, "internal/di/provider")
|
|
435
|
-
if err := os.MkdirAll(dir, 0o755); err != nil {
|
|
436
|
-
t.Fatal(err)
|
|
437
|
-
}
|
|
438
|
-
|
|
439
|
-
const provider = `package provider
|
|
440
|
-
|
|
441
|
-
import (
|
|
442
|
-
"context"
|
|
443
|
-
|
|
444
|
-
"github.com/example/auth/internal/application/lockout/usecases/check"
|
|
445
|
-
checkdto "github.com/example/auth/internal/application/lockout/usecases/check/dto"
|
|
446
|
-
"github.com/example/auth/internal/application/lockout/usecases/record_failure"
|
|
447
|
-
failuredto "github.com/example/auth/internal/application/lockout/usecases/record_failure/dto"
|
|
448
|
-
"github.com/example/auth/internal/application/user/usecases/authenticate"
|
|
449
|
-
)
|
|
450
|
-
|
|
451
|
-
func ProvideLockout(check *check.UseCase, failed *record_failure.UseCase) authenticate.Lockout {
|
|
452
|
-
return lockoutAdapter{check: check, failed: failed}
|
|
453
|
-
}
|
|
454
|
-
|
|
455
|
-
type lockoutAdapter struct {
|
|
456
|
-
check *check.UseCase
|
|
457
|
-
failed *record_failure.UseCase
|
|
458
|
-
}
|
|
459
|
-
|
|
460
|
-
func (l lockoutAdapter) Allowed(ctx context.Context, id string) (bool, error) {
|
|
461
|
-
out, err := l.check.Handle(ctx, checkdto.Input{UserID: id})
|
|
462
|
-
if err != nil {
|
|
463
|
-
return false, err
|
|
464
|
-
}
|
|
465
|
-
return out.Allowed, nil
|
|
466
|
-
}
|
|
467
|
-
|
|
468
|
-
func (l lockoutAdapter) Failed(ctx context.Context, id string) error {
|
|
469
|
-
return l.failed.Handle(ctx, failuredto.Input{UserID: id})
|
|
470
|
-
}
|
|
471
|
-
`
|
|
472
|
-
if err := os.WriteFile(filepath.Join(dir, "lockout.go"), []byte(provider), 0o644); err != nil {
|
|
473
|
-
t.Fatal(err)
|
|
474
|
-
}
|
|
475
|
-
|
|
476
|
-
got := portBindings(root)
|
|
477
|
-
want := map[string]string{
|
|
478
|
-
"user/authenticate.Lockout.Allowed": "lockout/check",
|
|
479
|
-
"user/authenticate.Lockout.Failed": "lockout/record_failure",
|
|
480
|
-
}
|
|
481
|
-
for key, useCase := range want {
|
|
482
|
-
if got[key] != useCase {
|
|
483
|
-
t.Errorf("%s = %q, want %q", key, got[key], useCase)
|
|
484
|
-
}
|
|
485
|
-
}
|
|
486
|
-
if _, bound := got["user/authenticate.Lockout"]; bound {
|
|
487
|
-
t.Errorf("bindings = %v, want no port-level binding when the methods say which use case is which", got)
|
|
488
|
-
}
|
|
489
|
-
}
|
|
490
|
-
|
|
491
|
-
// An import of the domain is a lane; the event package under it is a fact.
|
|
492
|
-
// Nothing else is either, and a reader that guessed would put a helper package
|
|
493
|
-
// on a diagram as if it were a database.
|
|
494
|
-
func TestPackagesAreToldApart(t *testing.T) {
|
|
495
|
-
imports := map[string]string{
|
|
496
|
-
"session": "github.com/example/auth/internal/domain/session",
|
|
497
|
-
"event": "github.com/example/auth/internal/domain/session/event",
|
|
498
|
-
"uc": "github.com/example/auth/internal/application/user/usecases/authenticate",
|
|
499
|
-
"time": "time",
|
|
500
|
-
}
|
|
501
|
-
|
|
502
|
-
if aggregate, name, ok := domainSelector("session.Repository", imports); !ok || aggregate != "session" || name != "Repository" {
|
|
503
|
-
t.Errorf("domainSelector = %q %q %v", aggregate, name, ok)
|
|
504
|
-
}
|
|
505
|
-
if _, _, ok := domainSelector("time.Time", imports); ok {
|
|
506
|
-
t.Error("time.Time was read as a domain port")
|
|
507
|
-
}
|
|
508
|
-
if aggregate, ok := eventPackage(imports["event"]); !ok || aggregate != "session" {
|
|
509
|
-
t.Errorf("eventPackage = %q %v", aggregate, ok)
|
|
510
|
-
}
|
|
511
|
-
if _, ok := eventPackage(imports["session"]); ok {
|
|
512
|
-
t.Error("the aggregate package was read as its event package")
|
|
513
|
-
}
|
|
514
|
-
if key, ok := useCaseSelector("*uc.UseCase", imports); !ok || key != "user/authenticate" {
|
|
515
|
-
t.Errorf("useCaseSelector = %q %v", key, ok)
|
|
516
|
-
}
|
|
517
|
-
}
|
|
518
|
-
|
|
519
|
-
func TestSentence(t *testing.T) {
|
|
520
|
-
if got := sentence("revoke-sessions-on-password-change"); got != "Revoke sessions on password change" {
|
|
521
|
-
t.Errorf("sentence = %q", got)
|
|
522
|
-
}
|
|
523
|
-
}
|
|
524
|
-
|
|
525
|
-
// A switch is a choice too. Its arms are titled by what the case says, with
|
|
526
|
-
// the subject in front; default is otherwise; an arm that returns is
|
|
527
|
-
// terminal; and, like an if, nothing is drawn unless some arm has a hop.
|
|
528
|
-
func TestASwitchWithAHopIsAnAlt(t *testing.T) {
|
|
529
|
-
const source = `package login
|
|
530
|
-
|
|
531
|
-
import (
|
|
532
|
-
"context"
|
|
533
|
-
|
|
534
|
-
"github.com/example/auth/internal/domain/session"
|
|
535
|
-
)
|
|
536
|
-
|
|
537
|
-
type UseCase struct {
|
|
538
|
-
repo session.Repository
|
|
539
|
-
}
|
|
540
|
-
|
|
541
|
-
func (uc *UseCase) Handle(ctx context.Context, in dto.Input) error {
|
|
542
|
-
u, err := uc.repo.ByID(ctx, in.ID)
|
|
543
|
-
if err != nil {
|
|
544
|
-
return err
|
|
545
|
-
}
|
|
546
|
-
switch in.Mode {
|
|
547
|
-
case "soft", "gentle":
|
|
548
|
-
return uc.repo.Save(ctx, u)
|
|
549
|
-
case "hard":
|
|
550
|
-
uc.repo.ByToken(ctx, in.Token)
|
|
551
|
-
default:
|
|
552
|
-
return nil
|
|
553
|
-
}
|
|
554
|
-
switch {
|
|
555
|
-
case in.A > 1:
|
|
556
|
-
return nil
|
|
557
|
-
case in.B:
|
|
558
|
-
return nil
|
|
559
|
-
}
|
|
560
|
-
return nil
|
|
561
|
-
}
|
|
562
|
-
`
|
|
563
|
-
d, _ := walkSource(t, source, nil)
|
|
564
|
-
if got := dump(d.steps); got != "s1:ByID alt4{s2:Save s3:ByToken } " {
|
|
565
|
-
t.Fatalf("nodes = %q; the second switch has no hop and is not drawn", got)
|
|
566
|
-
}
|
|
567
|
-
alt := d.steps[1].(*catalog.Alt)
|
|
568
|
-
var titles []string
|
|
569
|
-
for _, b := range alt.Branches {
|
|
570
|
-
titles = append(titles, b.Title)
|
|
571
|
-
}
|
|
572
|
-
if strings.Join(titles, "|") != `in.Mode is "soft", "gentle"|in.Mode is "hard"|otherwise` {
|
|
573
|
-
t.Errorf("titles = %q", titles)
|
|
574
|
-
}
|
|
575
|
-
if !alt.Branches[0].Terminal || alt.Branches[1].Terminal || !alt.Branches[2].Terminal {
|
|
576
|
-
t.Errorf("terminal = %v %v %v", alt.Branches[0].Terminal, alt.Branches[1].Terminal, alt.Branches[2].Terminal)
|
|
577
|
-
}
|
|
578
|
-
}
|
|
579
|
-
|
|
580
|
-
// A type switch asks about one value, and every arm says what it turned out
|
|
581
|
-
// to be.
|
|
582
|
-
func TestATypeSwitchNamesWhatItAsksAbout(t *testing.T) {
|
|
583
|
-
const source = `package login
|
|
584
|
-
|
|
585
|
-
import "github.com/example/auth/internal/domain/session"
|
|
586
|
-
|
|
587
|
-
type UseCase struct {
|
|
588
|
-
repo session.Repository
|
|
589
|
-
}
|
|
590
|
-
|
|
591
|
-
func (uc *UseCase) Handle(ctx context.Context, in dto.Input) error {
|
|
592
|
-
switch e := in.Event.(type) {
|
|
593
|
-
case *Started, *Renewed:
|
|
594
|
-
return uc.repo.Save(ctx, nil)
|
|
595
|
-
case *Ended:
|
|
596
|
-
uc.repo.ByID(ctx, e.ID)
|
|
597
|
-
}
|
|
598
|
-
return nil
|
|
599
|
-
}
|
|
600
|
-
`
|
|
601
|
-
d, _ := walkSource(t, source, nil)
|
|
602
|
-
alt, ok := d.steps[0].(*catalog.Alt)
|
|
603
|
-
if !ok {
|
|
604
|
-
t.Fatalf("node = %T", d.steps[0])
|
|
605
|
-
}
|
|
606
|
-
if alt.Branches[0].Title != "in.Event is *Started, *Renewed" || alt.Branches[1].Title != "in.Event is *Ended" || alt.Branches[2].Title != "otherwise" {
|
|
607
|
-
t.Errorf("titles = %q %q %q", alt.Branches[0].Title, alt.Branches[1].Title, alt.Branches[2].Title)
|
|
608
|
-
}
|
|
609
|
-
}
|