reqon-dsl 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.
- package/.claude/settings.local.json +31 -0
- package/.claude/skills/api-integration.md +125 -0
- package/.claude/skills/database-schema.md +51 -0
- package/.claude/skills/dsl-design.md +80 -0
- package/.claude/skills/property-testing.md +143 -0
- package/.claude/skills/reqon/SKILL.md +44 -0
- package/.claude/skills/reqon/references/examples.md +206 -0
- package/.claude/skills/reqon/references/syntax.md +263 -0
- package/.claude/skills/vscode-extension.md +113 -0
- package/.github/dependabot.yml +32 -0
- package/.github/pull_request_template.md +21 -0
- package/.github/workflows/ci.yml +174 -0
- package/.github/workflows/release.yml +73 -0
- package/CLAUDE.md +72 -0
- package/CONTRIBUTING.md +161 -0
- package/README.md +235 -0
- package/TODO.md +51 -0
- package/dist/ast/index.d.ts +1 -0
- package/dist/ast/index.js +1 -0
- package/dist/ast/nodes.d.ts +237 -0
- package/dist/ast/nodes.js +12 -0
- package/dist/auth/auth.test.d.ts +1 -0
- package/dist/auth/auth.test.js +255 -0
- package/dist/auth/circuit-breaker.d.ts +115 -0
- package/dist/auth/circuit-breaker.js +267 -0
- package/dist/auth/credentials.d.ts +91 -0
- package/dist/auth/credentials.js +169 -0
- package/dist/auth/index.d.ts +5 -0
- package/dist/auth/index.js +8 -0
- package/dist/auth/oauth2-provider.d.ts +41 -0
- package/dist/auth/oauth2-provider.js +131 -0
- package/dist/auth/rate-limiter.d.ts +61 -0
- package/dist/auth/rate-limiter.js +380 -0
- package/dist/auth/token-store.d.ts +30 -0
- package/dist/auth/token-store.js +148 -0
- package/dist/auth/types.d.ts +142 -0
- package/dist/auth/types.js +1 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +270 -0
- package/dist/errors/errors.test.d.ts +1 -0
- package/dist/errors/errors.test.js +165 -0
- package/dist/errors/index.d.ts +83 -0
- package/dist/errors/index.js +159 -0
- package/dist/execution/execution.test.d.ts +1 -0
- package/dist/execution/execution.test.js +246 -0
- package/dist/execution/index.d.ts +4 -0
- package/dist/execution/index.js +2 -0
- package/dist/execution/state.d.ts +136 -0
- package/dist/execution/state.js +82 -0
- package/dist/execution/store.d.ts +52 -0
- package/dist/execution/store.js +120 -0
- package/dist/index.d.ts +27 -0
- package/dist/index.js +57 -0
- package/dist/integration.test.d.ts +1 -0
- package/dist/integration.test.js +168 -0
- package/dist/interpreter/context.d.ts +15 -0
- package/dist/interpreter/context.js +29 -0
- package/dist/interpreter/evaluator.d.ts +5 -0
- package/dist/interpreter/evaluator.js +223 -0
- package/dist/interpreter/evaluator.test.d.ts +1 -0
- package/dist/interpreter/evaluator.test.js +512 -0
- package/dist/interpreter/executor.d.ts +131 -0
- package/dist/interpreter/executor.js +663 -0
- package/dist/interpreter/fetch-handler.d.ts +43 -0
- package/dist/interpreter/fetch-handler.js +203 -0
- package/dist/interpreter/http.d.ts +57 -0
- package/dist/interpreter/http.js +210 -0
- package/dist/interpreter/http.test.d.ts +1 -0
- package/dist/interpreter/http.test.js +299 -0
- package/dist/interpreter/index.d.ts +7 -0
- package/dist/interpreter/index.js +7 -0
- package/dist/interpreter/pagination.d.ts +63 -0
- package/dist/interpreter/pagination.js +155 -0
- package/dist/interpreter/progress.test.d.ts +1 -0
- package/dist/interpreter/progress.test.js +216 -0
- package/dist/interpreter/schema-matcher.d.ts +16 -0
- package/dist/interpreter/schema-matcher.js +136 -0
- package/dist/interpreter/schema-matcher.test.d.ts +1 -0
- package/dist/interpreter/schema-matcher.test.js +122 -0
- package/dist/interpreter/signals.d.ts +57 -0
- package/dist/interpreter/signals.js +73 -0
- package/dist/interpreter/step-handlers/for-handler.d.ts +17 -0
- package/dist/interpreter/step-handlers/for-handler.js +51 -0
- package/dist/interpreter/step-handlers/index.d.ts +8 -0
- package/dist/interpreter/step-handlers/index.js +8 -0
- package/dist/interpreter/step-handlers/map-handler.d.ts +10 -0
- package/dist/interpreter/step-handlers/map-handler.js +20 -0
- package/dist/interpreter/step-handlers/match-handler.d.ts +27 -0
- package/dist/interpreter/step-handlers/match-handler.js +61 -0
- package/dist/interpreter/step-handlers/store-handler.d.ts +13 -0
- package/dist/interpreter/step-handlers/store-handler.js +66 -0
- package/dist/interpreter/step-handlers/types.d.ts +15 -0
- package/dist/interpreter/step-handlers/types.js +1 -0
- package/dist/interpreter/step-handlers/validate-handler.d.ts +10 -0
- package/dist/interpreter/step-handlers/validate-handler.js +26 -0
- package/dist/interpreter/step-handlers/webhook-handler.d.ts +36 -0
- package/dist/interpreter/step-handlers/webhook-handler.js +104 -0
- package/dist/lexer/index.d.ts +10 -0
- package/dist/lexer/index.js +12 -0
- package/dist/lexer/lexer.d.ts +24 -0
- package/dist/lexer/lexer.js +264 -0
- package/dist/lexer/lexer.test.d.ts +1 -0
- package/dist/lexer/lexer.test.js +259 -0
- package/dist/lexer/tokens.d.ts +69 -0
- package/dist/lexer/tokens.js +146 -0
- package/dist/loader/index.d.ts +36 -0
- package/dist/loader/index.js +220 -0
- package/dist/loader/loader.test.d.ts +1 -0
- package/dist/loader/loader.test.js +287 -0
- package/dist/oas/index.d.ts +4 -0
- package/dist/oas/index.js +2 -0
- package/dist/oas/loader.d.ts +21 -0
- package/dist/oas/loader.js +82 -0
- package/dist/oas/oas.test.d.ts +1 -0
- package/dist/oas/oas.test.js +218 -0
- package/dist/oas/validator.d.ts +12 -0
- package/dist/oas/validator.js +227 -0
- package/dist/parser/base.d.ts +33 -0
- package/dist/parser/base.js +97 -0
- package/dist/parser/expressions.d.ts +27 -0
- package/dist/parser/expressions.js +248 -0
- package/dist/parser/expressions.test.d.ts +1 -0
- package/dist/parser/expressions.test.js +378 -0
- package/dist/parser/index.d.ts +3 -0
- package/dist/parser/index.js +3 -0
- package/dist/parser/match.test.d.ts +1 -0
- package/dist/parser/match.test.js +254 -0
- package/dist/parser/parser.d.ts +68 -0
- package/dist/parser/parser.js +1229 -0
- package/dist/parser/parser.test.d.ts +1 -0
- package/dist/parser/parser.test.js +333 -0
- package/dist/parser/schedule.test.d.ts +1 -0
- package/dist/parser/schedule.test.js +241 -0
- package/dist/plugin.d.ts +35 -0
- package/dist/plugin.js +68 -0
- package/dist/scheduler/cron-parser.d.ts +32 -0
- package/dist/scheduler/cron-parser.js +198 -0
- package/dist/scheduler/cron-parser.test.d.ts +1 -0
- package/dist/scheduler/cron-parser.test.js +188 -0
- package/dist/scheduler/index.d.ts +3 -0
- package/dist/scheduler/index.js +2 -0
- package/dist/scheduler/scheduler.d.ts +81 -0
- package/dist/scheduler/scheduler.js +376 -0
- package/dist/scheduler/types.d.ts +65 -0
- package/dist/scheduler/types.js +1 -0
- package/dist/stores/factory.d.ts +36 -0
- package/dist/stores/factory.js +73 -0
- package/dist/stores/file.d.ts +60 -0
- package/dist/stores/file.js +173 -0
- package/dist/stores/file.test.d.ts +1 -0
- package/dist/stores/file.test.js +165 -0
- package/dist/stores/index.d.ts +6 -0
- package/dist/stores/index.js +5 -0
- package/dist/stores/memory.d.ts +19 -0
- package/dist/stores/memory.js +51 -0
- package/dist/stores/memory.test.d.ts +1 -0
- package/dist/stores/memory.test.js +157 -0
- package/dist/stores/postgrest.d.ts +55 -0
- package/dist/stores/postgrest.js +217 -0
- package/dist/stores/stores.test.d.ts +1 -0
- package/dist/stores/stores.test.js +158 -0
- package/dist/stores/types.d.ts +31 -0
- package/dist/stores/types.js +26 -0
- package/dist/sync/index.d.ts +4 -0
- package/dist/sync/index.js +2 -0
- package/dist/sync/state.d.ts +69 -0
- package/dist/sync/state.js +66 -0
- package/dist/sync/store.d.ts +49 -0
- package/dist/sync/store.js +93 -0
- package/dist/sync/sync.test.d.ts +1 -0
- package/dist/sync/sync.test.js +221 -0
- package/dist/utils/async.d.ts +7 -0
- package/dist/utils/async.js +9 -0
- package/dist/utils/file.d.ts +38 -0
- package/dist/utils/file.js +92 -0
- package/dist/utils/index.d.ts +4 -0
- package/dist/utils/index.js +4 -0
- package/dist/utils/logger.d.ts +34 -0
- package/dist/utils/logger.js +39 -0
- package/dist/utils/path.d.ts +12 -0
- package/dist/utils/path.js +41 -0
- package/dist/webhook/index.d.ts +8 -0
- package/dist/webhook/index.js +7 -0
- package/dist/webhook/server.d.ts +84 -0
- package/dist/webhook/server.js +319 -0
- package/dist/webhook/store.d.ts +67 -0
- package/dist/webhook/store.js +193 -0
- package/dist/webhook/types.d.ts +88 -0
- package/dist/webhook/types.js +6 -0
- package/docusaurus/README.md +41 -0
- package/docusaurus/docs/advanced/execution-state.md +283 -0
- package/docusaurus/docs/advanced/extending-reqon.md +388 -0
- package/docusaurus/docs/advanced/multi-file-missions.md +250 -0
- package/docusaurus/docs/advanced/parallel-execution.md +353 -0
- package/docusaurus/docs/api-reference.md +443 -0
- package/docusaurus/docs/authentication/api-key.md +339 -0
- package/docusaurus/docs/authentication/basic.md +276 -0
- package/docusaurus/docs/authentication/bearer.md +282 -0
- package/docusaurus/docs/authentication/oauth2.md +317 -0
- package/docusaurus/docs/authentication/overview.md +251 -0
- package/docusaurus/docs/cli.md +229 -0
- package/docusaurus/docs/core-concepts/actions.md +286 -0
- package/docusaurus/docs/core-concepts/missions.md +264 -0
- package/docusaurus/docs/core-concepts/schemas.md +353 -0
- package/docusaurus/docs/core-concepts/sources.md +339 -0
- package/docusaurus/docs/core-concepts/stores.md +332 -0
- package/docusaurus/docs/dsl-syntax/expressions.md +361 -0
- package/docusaurus/docs/dsl-syntax/fetch.md +293 -0
- package/docusaurus/docs/dsl-syntax/for-loops.md +324 -0
- package/docusaurus/docs/dsl-syntax/map.md +345 -0
- package/docusaurus/docs/dsl-syntax/match.md +387 -0
- package/docusaurus/docs/dsl-syntax/pipelines.md +397 -0
- package/docusaurus/docs/dsl-syntax/validate.md +401 -0
- package/docusaurus/docs/error-handling/dead-letter-queues.md +399 -0
- package/docusaurus/docs/error-handling/flow-control.md +337 -0
- package/docusaurus/docs/error-handling/retry-strategies.md +368 -0
- package/docusaurus/docs/examples.md +488 -0
- package/docusaurus/docs/getting-started.md +256 -0
- package/docusaurus/docs/http/circuit-breaker.md +401 -0
- package/docusaurus/docs/http/incremental-sync.md +394 -0
- package/docusaurus/docs/http/pagination.md +361 -0
- package/docusaurus/docs/http/rate-limiting.md +383 -0
- package/docusaurus/docs/http/requests.md +328 -0
- package/docusaurus/docs/http/retry.md +402 -0
- package/docusaurus/docs/intro.md +90 -0
- package/docusaurus/docs/openapi/loading-specs.md +305 -0
- package/docusaurus/docs/openapi/operation-calls.md +314 -0
- package/docusaurus/docs/openapi/overview.md +212 -0
- package/docusaurus/docs/openapi/response-validation.md +344 -0
- package/docusaurus/docs/scheduling/cron.md +305 -0
- package/docusaurus/docs/scheduling/daemon-mode.md +317 -0
- package/docusaurus/docs/scheduling/intervals.md +289 -0
- package/docusaurus/docs/scheduling/overview.md +231 -0
- package/docusaurus/docs/stores/custom-adapters.md +376 -0
- package/docusaurus/docs/stores/file.md +236 -0
- package/docusaurus/docs/stores/memory.md +193 -0
- package/docusaurus/docs/stores/overview.md +274 -0
- package/docusaurus/docs/stores/postgrest.md +316 -0
- package/docusaurus/docusaurus.config.ts +148 -0
- package/docusaurus/package-lock.json +18029 -0
- package/docusaurus/package.json +47 -0
- package/docusaurus/sidebars.ts +155 -0
- package/docusaurus/src/components/HomepageFeatures/index.tsx +105 -0
- package/docusaurus/src/components/HomepageFeatures/styles.module.css +12 -0
- package/docusaurus/src/css/custom.css +169 -0
- package/docusaurus/src/pages/index.module.css +48 -0
- package/docusaurus/src/pages/index.tsx +110 -0
- package/docusaurus/src/pages/markdown-page.md +7 -0
- package/docusaurus/static/.nojekyll +0 -0
- package/docusaurus/static/img/docusaurus-social-card.jpg +0 -0
- package/docusaurus/static/img/docusaurus.png +0 -0
- package/docusaurus/static/img/favicon.ico +0 -0
- package/docusaurus/static/img/logo.svg +10 -0
- package/docusaurus/static/img/undraw_docusaurus_mountain.svg +171 -0
- package/docusaurus/static/img/undraw_docusaurus_react.svg +170 -0
- package/docusaurus/static/img/undraw_docusaurus_tree.svg +40 -0
- package/docusaurus/tsconfig.json +8 -0
- package/examples/README.md +112 -0
- package/examples/error-handling/README.md +150 -0
- package/examples/error-handling/payment-processor.vague +287 -0
- package/examples/github-sync/README.md +74 -0
- package/examples/github-sync/fetch-issues.vague +47 -0
- package/examples/github-sync/fetch-prs.vague +40 -0
- package/examples/github-sync/mission.vague +101 -0
- package/examples/github-sync/normalize.vague +70 -0
- package/examples/jsonplaceholder/README.md +28 -0
- package/examples/jsonplaceholder/posts.vague +48 -0
- package/examples/petstore/README.md +35 -0
- package/examples/petstore/openapi.yaml +97 -0
- package/examples/petstore/sync.vague +52 -0
- package/examples/temporal-comparison/README.md +297 -0
- package/examples/temporal-comparison/reconciliation.vague +355 -0
- package/examples/temporal-comparison/temporal/activities/index.ts +8 -0
- package/examples/temporal-comparison/temporal/activities/shipstation.ts +225 -0
- package/examples/temporal-comparison/temporal/activities/shopify.ts +257 -0
- package/examples/temporal-comparison/temporal/activities/storage.ts +198 -0
- package/examples/temporal-comparison/temporal/activities/stripe.ts +169 -0
- package/examples/temporal-comparison/temporal/activities/validation.ts +205 -0
- package/examples/temporal-comparison/temporal/client/schedule.ts +218 -0
- package/examples/temporal-comparison/temporal/config/retry.ts +63 -0
- package/examples/temporal-comparison/temporal/types/index.ts +129 -0
- package/examples/temporal-comparison/temporal/workers/main.ts +130 -0
- package/examples/temporal-comparison/temporal/workflows/orderReconciliation.ts +262 -0
- package/examples/xero/README.md +88 -0
- package/examples/xero/invoices.vague +189 -0
- package/package.json +40 -0
- package/src/api-integration.test.ts +954 -0
- package/src/ast/index.ts +1 -0
- package/src/ast/nodes.ts +310 -0
- package/src/auth/auth.test.ts +326 -0
- package/src/auth/circuit-breaker.test.ts +390 -0
- package/src/auth/circuit-breaker.ts +379 -0
- package/src/auth/credentials.test.ts +273 -0
- package/src/auth/credentials.ts +246 -0
- package/src/auth/index.ts +40 -0
- package/src/auth/oauth2-provider.ts +177 -0
- package/src/auth/rate-limiter.ts +459 -0
- package/src/auth/token-store.ts +177 -0
- package/src/auth/types.ts +159 -0
- package/src/benchmark/e2e.bench.ts +288 -0
- package/src/benchmark/evaluator.bench.ts +331 -0
- package/src/benchmark/fixtures.ts +295 -0
- package/src/benchmark/index.ts +108 -0
- package/src/benchmark/lexer.bench.ts +69 -0
- package/src/benchmark/parser.bench.ts +103 -0
- package/src/benchmark/resilience.bench.ts +193 -0
- package/src/benchmark/store.bench.ts +147 -0
- package/src/benchmark/utils.ts +230 -0
- package/src/cli.ts +313 -0
- package/src/errors/errors.test.ts +234 -0
- package/src/errors/index.ts +223 -0
- package/src/execution/execution.test.ts +307 -0
- package/src/execution/index.ts +21 -0
- package/src/execution/state.ts +207 -0
- package/src/execution/store.ts +188 -0
- package/src/index.ts +169 -0
- package/src/integration.test.ts +192 -0
- package/src/interpreter/context.ts +57 -0
- package/src/interpreter/evaluator.test.ts +796 -0
- package/src/interpreter/evaluator.ts +245 -0
- package/src/interpreter/executor.ts +946 -0
- package/src/interpreter/fetch-handler.ts +302 -0
- package/src/interpreter/http.test.ts +423 -0
- package/src/interpreter/http.ts +308 -0
- package/src/interpreter/index.ts +32 -0
- package/src/interpreter/pagination.ts +207 -0
- package/src/interpreter/progress.test.ts +276 -0
- package/src/interpreter/schema-matcher.test.ts +160 -0
- package/src/interpreter/schema-matcher.ts +168 -0
- package/src/interpreter/signals.ts +73 -0
- package/src/interpreter/step-handlers/for-handler.ts +65 -0
- package/src/interpreter/step-handlers/index.ts +17 -0
- package/src/interpreter/step-handlers/map-handler.ts +24 -0
- package/src/interpreter/step-handlers/match-handler.ts +101 -0
- package/src/interpreter/step-handlers/store-handler.ts +78 -0
- package/src/interpreter/step-handlers/types.ts +17 -0
- package/src/interpreter/step-handlers/validate-handler.ts +30 -0
- package/src/interpreter/step-handlers/webhook-handler.ts +142 -0
- package/src/lexer/index.ts +18 -0
- package/src/lexer/lexer.test.ts +316 -0
- package/src/lexer/tokens.ts +179 -0
- package/src/loader/index.ts +288 -0
- package/src/loader/loader.test.ts +360 -0
- package/src/oas/index.ts +4 -0
- package/src/oas/loader.ts +126 -0
- package/src/oas/oas.test.ts +254 -0
- package/src/oas/validator.ts +299 -0
- package/src/parser/base.ts +124 -0
- package/src/parser/expressions.test.ts +525 -0
- package/src/parser/expressions.ts +314 -0
- package/src/parser/index.ts +3 -0
- package/src/parser/match.test.ts +296 -0
- package/src/parser/parser.test.ts +739 -0
- package/src/parser/parser.ts +1469 -0
- package/src/parser/schedule.test.ts +287 -0
- package/src/parser/webhook.test.ts +248 -0
- package/src/plugin.ts +83 -0
- package/src/scheduler/cron-parser.test.ts +236 -0
- package/src/scheduler/cron-parser.ts +236 -0
- package/src/scheduler/index.ts +10 -0
- package/src/scheduler/scheduler.ts +443 -0
- package/src/scheduler/types.ts +71 -0
- package/src/stores/factory.ts +104 -0
- package/src/stores/file.test.ts +276 -0
- package/src/stores/file.ts +211 -0
- package/src/stores/index.ts +6 -0
- package/src/stores/memory.test.ts +238 -0
- package/src/stores/memory.ts +63 -0
- package/src/stores/postgrest.test.ts +488 -0
- package/src/stores/postgrest.ts +263 -0
- package/src/stores/stores.test.ts +197 -0
- package/src/stores/types.ts +58 -0
- package/src/sync/index.ts +16 -0
- package/src/sync/state.ts +126 -0
- package/src/sync/store.ts +139 -0
- package/src/sync/sync.test.ts +271 -0
- package/src/utils/async.ts +10 -0
- package/src/utils/file.ts +106 -0
- package/src/utils/index.ts +14 -0
- package/src/utils/logger.ts +53 -0
- package/src/utils/path.ts +47 -0
- package/src/webhook/index.ts +15 -0
- package/src/webhook/server.test.ts +253 -0
- package/src/webhook/server.ts +389 -0
- package/src/webhook/store.ts +239 -0
- package/src/webhook/types.ts +93 -0
- package/tsconfig.json +17 -0
- package/vitest.config.ts +39 -0
|
@@ -0,0 +1,378 @@
|
|
|
1
|
+
import { describe, it, expect } from 'vitest';
|
|
2
|
+
import { ReqonLexer } from '../lexer/index.js';
|
|
3
|
+
import { ReqonExpressionParser } from './expressions.js';
|
|
4
|
+
describe('ReqonExpressionParser', () => {
|
|
5
|
+
function parseExpr(source) {
|
|
6
|
+
const lexer = new ReqonLexer(source);
|
|
7
|
+
const tokens = lexer.tokenize();
|
|
8
|
+
const parser = new TestableExpressionParser(tokens);
|
|
9
|
+
return parser.parseExpression();
|
|
10
|
+
}
|
|
11
|
+
// Create a testable subclass that exposes parseExpression
|
|
12
|
+
class TestableExpressionParser extends ReqonExpressionParser {
|
|
13
|
+
constructor(tokens) {
|
|
14
|
+
super(tokens);
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
describe('literals', () => {
|
|
18
|
+
it('parses number literals', () => {
|
|
19
|
+
const expr = parseExpr('42');
|
|
20
|
+
expect(expr.type).toBe('Literal');
|
|
21
|
+
if (expr.type === 'Literal') {
|
|
22
|
+
expect(expr.value).toBe(42);
|
|
23
|
+
expect(expr.dataType).toBe('number');
|
|
24
|
+
}
|
|
25
|
+
});
|
|
26
|
+
it('parses decimal number literals', () => {
|
|
27
|
+
const expr = parseExpr('3.14');
|
|
28
|
+
expect(expr.type).toBe('Literal');
|
|
29
|
+
if (expr.type === 'Literal') {
|
|
30
|
+
expect(expr.value).toBe(3.14);
|
|
31
|
+
}
|
|
32
|
+
});
|
|
33
|
+
it('parses string literals', () => {
|
|
34
|
+
const expr = parseExpr('"hello world"');
|
|
35
|
+
expect(expr.type).toBe('Literal');
|
|
36
|
+
if (expr.type === 'Literal') {
|
|
37
|
+
expect(expr.value).toBe('hello world');
|
|
38
|
+
expect(expr.dataType).toBe('string');
|
|
39
|
+
}
|
|
40
|
+
});
|
|
41
|
+
it('parses true literal', () => {
|
|
42
|
+
const expr = parseExpr('true');
|
|
43
|
+
expect(expr.type).toBe('Literal');
|
|
44
|
+
if (expr.type === 'Literal') {
|
|
45
|
+
expect(expr.value).toBe(true);
|
|
46
|
+
expect(expr.dataType).toBe('boolean');
|
|
47
|
+
}
|
|
48
|
+
});
|
|
49
|
+
it('parses false literal', () => {
|
|
50
|
+
const expr = parseExpr('false');
|
|
51
|
+
expect(expr.type).toBe('Literal');
|
|
52
|
+
if (expr.type === 'Literal') {
|
|
53
|
+
expect(expr.value).toBe(false);
|
|
54
|
+
expect(expr.dataType).toBe('boolean');
|
|
55
|
+
}
|
|
56
|
+
});
|
|
57
|
+
it('parses null literal', () => {
|
|
58
|
+
const expr = parseExpr('null');
|
|
59
|
+
expect(expr.type).toBe('Literal');
|
|
60
|
+
if (expr.type === 'Literal') {
|
|
61
|
+
expect(expr.value).toBe(null);
|
|
62
|
+
expect(expr.dataType).toBe('null');
|
|
63
|
+
}
|
|
64
|
+
});
|
|
65
|
+
});
|
|
66
|
+
describe('identifiers', () => {
|
|
67
|
+
it('parses simple identifiers', () => {
|
|
68
|
+
const expr = parseExpr('myVariable');
|
|
69
|
+
expect(expr.type).toBe('Identifier');
|
|
70
|
+
if (expr.type === 'Identifier') {
|
|
71
|
+
expect(expr.name).toBe('myVariable');
|
|
72
|
+
}
|
|
73
|
+
});
|
|
74
|
+
it('parses dot-prefixed field shorthand', () => {
|
|
75
|
+
const expr = parseExpr('.fieldName');
|
|
76
|
+
expect(expr.type).toBe('Identifier');
|
|
77
|
+
if (expr.type === 'Identifier') {
|
|
78
|
+
expect(expr.name).toBe('fieldName');
|
|
79
|
+
}
|
|
80
|
+
});
|
|
81
|
+
});
|
|
82
|
+
describe('binary arithmetic expressions', () => {
|
|
83
|
+
it('parses addition', () => {
|
|
84
|
+
const expr = parseExpr('1 + 2');
|
|
85
|
+
expect(expr.type).toBe('BinaryExpression');
|
|
86
|
+
if (expr.type === 'BinaryExpression') {
|
|
87
|
+
expect(expr.operator).toBe('+');
|
|
88
|
+
expect(expr.left).toMatchObject({ type: 'Literal', value: 1 });
|
|
89
|
+
expect(expr.right).toMatchObject({ type: 'Literal', value: 2 });
|
|
90
|
+
}
|
|
91
|
+
});
|
|
92
|
+
it('parses subtraction', () => {
|
|
93
|
+
const expr = parseExpr('10 - 5');
|
|
94
|
+
expect(expr.type).toBe('BinaryExpression');
|
|
95
|
+
if (expr.type === 'BinaryExpression') {
|
|
96
|
+
expect(expr.operator).toBe('-');
|
|
97
|
+
}
|
|
98
|
+
});
|
|
99
|
+
it('parses multiplication', () => {
|
|
100
|
+
const expr = parseExpr('3 * 4');
|
|
101
|
+
expect(expr.type).toBe('BinaryExpression');
|
|
102
|
+
if (expr.type === 'BinaryExpression') {
|
|
103
|
+
expect(expr.operator).toBe('*');
|
|
104
|
+
}
|
|
105
|
+
});
|
|
106
|
+
it('parses division', () => {
|
|
107
|
+
const expr = parseExpr('20 / 4');
|
|
108
|
+
expect(expr.type).toBe('BinaryExpression');
|
|
109
|
+
if (expr.type === 'BinaryExpression') {
|
|
110
|
+
expect(expr.operator).toBe('/');
|
|
111
|
+
}
|
|
112
|
+
});
|
|
113
|
+
it('respects operator precedence (multiplication before addition)', () => {
|
|
114
|
+
const expr = parseExpr('1 + 2 * 3');
|
|
115
|
+
expect(expr.type).toBe('BinaryExpression');
|
|
116
|
+
if (expr.type === 'BinaryExpression') {
|
|
117
|
+
expect(expr.operator).toBe('+');
|
|
118
|
+
expect(expr.left).toMatchObject({ type: 'Literal', value: 1 });
|
|
119
|
+
expect(expr.right.type).toBe('BinaryExpression');
|
|
120
|
+
if (expr.right.type === 'BinaryExpression') {
|
|
121
|
+
expect(expr.right.operator).toBe('*');
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
});
|
|
125
|
+
it('respects parentheses', () => {
|
|
126
|
+
const expr = parseExpr('(1 + 2) * 3');
|
|
127
|
+
expect(expr.type).toBe('BinaryExpression');
|
|
128
|
+
if (expr.type === 'BinaryExpression') {
|
|
129
|
+
expect(expr.operator).toBe('*');
|
|
130
|
+
expect(expr.left.type).toBe('BinaryExpression');
|
|
131
|
+
expect(expr.right).toMatchObject({ type: 'Literal', value: 3 });
|
|
132
|
+
}
|
|
133
|
+
});
|
|
134
|
+
});
|
|
135
|
+
describe('comparison expressions', () => {
|
|
136
|
+
it('parses equality', () => {
|
|
137
|
+
const expr = parseExpr('x == 5');
|
|
138
|
+
expect(expr.type).toBe('BinaryExpression');
|
|
139
|
+
if (expr.type === 'BinaryExpression') {
|
|
140
|
+
expect(expr.operator).toBe('==');
|
|
141
|
+
}
|
|
142
|
+
});
|
|
143
|
+
it('parses less than', () => {
|
|
144
|
+
const expr = parseExpr('x < 10');
|
|
145
|
+
expect(expr.type).toBe('BinaryExpression');
|
|
146
|
+
if (expr.type === 'BinaryExpression') {
|
|
147
|
+
expect(expr.operator).toBe('<');
|
|
148
|
+
}
|
|
149
|
+
});
|
|
150
|
+
it('parses greater than', () => {
|
|
151
|
+
const expr = parseExpr('x > 0');
|
|
152
|
+
expect(expr.type).toBe('BinaryExpression');
|
|
153
|
+
if (expr.type === 'BinaryExpression') {
|
|
154
|
+
expect(expr.operator).toBe('>');
|
|
155
|
+
}
|
|
156
|
+
});
|
|
157
|
+
it('parses less than or equal', () => {
|
|
158
|
+
const expr = parseExpr('x <= 100');
|
|
159
|
+
expect(expr.type).toBe('BinaryExpression');
|
|
160
|
+
if (expr.type === 'BinaryExpression') {
|
|
161
|
+
expect(expr.operator).toBe('<=');
|
|
162
|
+
}
|
|
163
|
+
});
|
|
164
|
+
it('parses greater than or equal', () => {
|
|
165
|
+
const expr = parseExpr('x >= 0');
|
|
166
|
+
expect(expr.type).toBe('BinaryExpression');
|
|
167
|
+
if (expr.type === 'BinaryExpression') {
|
|
168
|
+
expect(expr.operator).toBe('>=');
|
|
169
|
+
}
|
|
170
|
+
});
|
|
171
|
+
});
|
|
172
|
+
describe('logical expressions', () => {
|
|
173
|
+
it('parses and expression', () => {
|
|
174
|
+
const expr = parseExpr('a and b');
|
|
175
|
+
expect(expr.type).toBe('LogicalExpression');
|
|
176
|
+
if (expr.type === 'LogicalExpression') {
|
|
177
|
+
expect(expr.operator).toBe('and');
|
|
178
|
+
}
|
|
179
|
+
});
|
|
180
|
+
it('parses or expression', () => {
|
|
181
|
+
const expr = parseExpr('a or b');
|
|
182
|
+
expect(expr.type).toBe('LogicalExpression');
|
|
183
|
+
if (expr.type === 'LogicalExpression') {
|
|
184
|
+
expect(expr.operator).toBe('or');
|
|
185
|
+
}
|
|
186
|
+
});
|
|
187
|
+
it('parses not expression', () => {
|
|
188
|
+
const expr = parseExpr('not a');
|
|
189
|
+
expect(expr.type).toBe('NotExpression');
|
|
190
|
+
if (expr.type === 'NotExpression') {
|
|
191
|
+
expect(expr.operand).toMatchObject({ type: 'Identifier', name: 'a' });
|
|
192
|
+
}
|
|
193
|
+
});
|
|
194
|
+
it('parses combined logical expressions', () => {
|
|
195
|
+
const expr = parseExpr('a and b or c');
|
|
196
|
+
// 'or' has lower precedence, so it's the root
|
|
197
|
+
expect(expr.type).toBe('LogicalExpression');
|
|
198
|
+
if (expr.type === 'LogicalExpression') {
|
|
199
|
+
expect(expr.operator).toBe('or');
|
|
200
|
+
expect(expr.left.type).toBe('LogicalExpression');
|
|
201
|
+
if (expr.left.type === 'LogicalExpression') {
|
|
202
|
+
expect(expr.left.operator).toBe('and');
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
});
|
|
206
|
+
it('parses double negation', () => {
|
|
207
|
+
const expr = parseExpr('not not a');
|
|
208
|
+
expect(expr.type).toBe('NotExpression');
|
|
209
|
+
if (expr.type === 'NotExpression') {
|
|
210
|
+
expect(expr.operand.type).toBe('NotExpression');
|
|
211
|
+
}
|
|
212
|
+
});
|
|
213
|
+
});
|
|
214
|
+
describe('ternary expressions', () => {
|
|
215
|
+
it('parses simple ternary', () => {
|
|
216
|
+
const expr = parseExpr('true ? 1 : 0');
|
|
217
|
+
expect(expr.type).toBe('TernaryExpression');
|
|
218
|
+
if (expr.type === 'TernaryExpression') {
|
|
219
|
+
expect(expr.condition).toMatchObject({ type: 'Literal', value: true });
|
|
220
|
+
expect(expr.consequent).toMatchObject({ type: 'Literal', value: 1 });
|
|
221
|
+
expect(expr.alternate).toMatchObject({ type: 'Literal', value: 0 });
|
|
222
|
+
}
|
|
223
|
+
});
|
|
224
|
+
it('parses nested ternary', () => {
|
|
225
|
+
const expr = parseExpr('a ? b ? 1 : 2 : 3');
|
|
226
|
+
expect(expr.type).toBe('TernaryExpression');
|
|
227
|
+
if (expr.type === 'TernaryExpression') {
|
|
228
|
+
expect(expr.consequent.type).toBe('TernaryExpression');
|
|
229
|
+
}
|
|
230
|
+
});
|
|
231
|
+
});
|
|
232
|
+
describe('unary expressions', () => {
|
|
233
|
+
it('parses negative number', () => {
|
|
234
|
+
const expr = parseExpr('-5');
|
|
235
|
+
expect(expr.type).toBe('UnaryExpression');
|
|
236
|
+
if (expr.type === 'UnaryExpression') {
|
|
237
|
+
expect(expr.operator).toBe('-');
|
|
238
|
+
expect(expr.operand).toMatchObject({ type: 'Literal', value: 5 });
|
|
239
|
+
}
|
|
240
|
+
});
|
|
241
|
+
it('parses positive number', () => {
|
|
242
|
+
const expr = parseExpr('+5');
|
|
243
|
+
expect(expr.type).toBe('UnaryExpression');
|
|
244
|
+
if (expr.type === 'UnaryExpression') {
|
|
245
|
+
expect(expr.operator).toBe('+');
|
|
246
|
+
}
|
|
247
|
+
});
|
|
248
|
+
it('parses double negative', () => {
|
|
249
|
+
const expr = parseExpr('--x');
|
|
250
|
+
expect(expr.type).toBe('UnaryExpression');
|
|
251
|
+
if (expr.type === 'UnaryExpression') {
|
|
252
|
+
expect(expr.operand.type).toBe('UnaryExpression');
|
|
253
|
+
}
|
|
254
|
+
});
|
|
255
|
+
});
|
|
256
|
+
describe('member access (qualified names)', () => {
|
|
257
|
+
it('parses single property access', () => {
|
|
258
|
+
const expr = parseExpr('obj.prop');
|
|
259
|
+
expect(expr.type).toBe('QualifiedName');
|
|
260
|
+
if (expr.type === 'QualifiedName') {
|
|
261
|
+
expect(expr.parts).toEqual(['obj', 'prop']);
|
|
262
|
+
}
|
|
263
|
+
});
|
|
264
|
+
it('parses chained property access', () => {
|
|
265
|
+
const expr = parseExpr('a.b.c.d');
|
|
266
|
+
expect(expr.type).toBe('QualifiedName');
|
|
267
|
+
if (expr.type === 'QualifiedName') {
|
|
268
|
+
expect(expr.parts).toEqual(['a', 'b', 'c', 'd']);
|
|
269
|
+
}
|
|
270
|
+
});
|
|
271
|
+
});
|
|
272
|
+
describe('function calls', () => {
|
|
273
|
+
it('parses function call without arguments', () => {
|
|
274
|
+
const expr = parseExpr('fn()');
|
|
275
|
+
expect(expr.type).toBe('CallExpression');
|
|
276
|
+
if (expr.type === 'CallExpression') {
|
|
277
|
+
expect(expr.callee).toBe('fn');
|
|
278
|
+
expect(expr.arguments).toEqual([]);
|
|
279
|
+
}
|
|
280
|
+
});
|
|
281
|
+
it('parses function call with single argument', () => {
|
|
282
|
+
const expr = parseExpr('length(items)');
|
|
283
|
+
expect(expr.type).toBe('CallExpression');
|
|
284
|
+
if (expr.type === 'CallExpression') {
|
|
285
|
+
expect(expr.callee).toBe('length');
|
|
286
|
+
expect(expr.arguments).toHaveLength(1);
|
|
287
|
+
}
|
|
288
|
+
});
|
|
289
|
+
it('parses function call with multiple arguments', () => {
|
|
290
|
+
const expr = parseExpr('func(1, 2, 3)');
|
|
291
|
+
expect(expr.type).toBe('CallExpression');
|
|
292
|
+
if (expr.type === 'CallExpression') {
|
|
293
|
+
expect(expr.arguments).toHaveLength(3);
|
|
294
|
+
}
|
|
295
|
+
});
|
|
296
|
+
});
|
|
297
|
+
describe('match expressions', () => {
|
|
298
|
+
it('parses simple match expression', () => {
|
|
299
|
+
const expr = parseExpr('match x { 1 => "one", 2 => "two" }');
|
|
300
|
+
expect(expr.type).toBe('MatchExpression');
|
|
301
|
+
if (expr.type === 'MatchExpression') {
|
|
302
|
+
expect(expr.arms).toHaveLength(2);
|
|
303
|
+
expect(expr.arms[0].pattern).toMatchObject({ type: 'Literal', value: 1 });
|
|
304
|
+
expect(expr.arms[0].result).toMatchObject({ type: 'Literal', value: 'one' });
|
|
305
|
+
}
|
|
306
|
+
});
|
|
307
|
+
it('parses match with wildcard', () => {
|
|
308
|
+
const expr = parseExpr('match status { "A" => "active", _ => "other" }');
|
|
309
|
+
expect(expr.type).toBe('MatchExpression');
|
|
310
|
+
if (expr.type === 'MatchExpression') {
|
|
311
|
+
expect(expr.arms).toHaveLength(2);
|
|
312
|
+
expect(expr.arms[1].pattern).toMatchObject({ type: 'Identifier', name: '_' });
|
|
313
|
+
}
|
|
314
|
+
});
|
|
315
|
+
});
|
|
316
|
+
describe('any of expressions', () => {
|
|
317
|
+
it('parses any of expression', () => {
|
|
318
|
+
const expr = parseExpr('any of items');
|
|
319
|
+
expect(expr.type).toBe('AnyOfExpression');
|
|
320
|
+
if (expr.type === 'AnyOfExpression') {
|
|
321
|
+
expect(expr.collection).toMatchObject({ type: 'Identifier', name: 'items' });
|
|
322
|
+
expect(expr.condition).toBeUndefined();
|
|
323
|
+
}
|
|
324
|
+
});
|
|
325
|
+
it('parses any of with where condition', () => {
|
|
326
|
+
const expr = parseExpr('any of items where active');
|
|
327
|
+
expect(expr.type).toBe('AnyOfExpression');
|
|
328
|
+
if (expr.type === 'AnyOfExpression') {
|
|
329
|
+
expect(expr.condition).toBeDefined();
|
|
330
|
+
}
|
|
331
|
+
});
|
|
332
|
+
});
|
|
333
|
+
describe('range expressions', () => {
|
|
334
|
+
it('parses range expression', () => {
|
|
335
|
+
const expr = parseExpr('1..10');
|
|
336
|
+
expect(expr.type).toBe('RangeExpression');
|
|
337
|
+
if (expr.type === 'RangeExpression') {
|
|
338
|
+
expect(expr.min).toMatchObject({ type: 'Literal', value: 1 });
|
|
339
|
+
expect(expr.max).toMatchObject({ type: 'Literal', value: 10 });
|
|
340
|
+
}
|
|
341
|
+
});
|
|
342
|
+
it('parses open-ended range', () => {
|
|
343
|
+
const expr = parseExpr('5..');
|
|
344
|
+
expect(expr.type).toBe('RangeExpression');
|
|
345
|
+
if (expr.type === 'RangeExpression') {
|
|
346
|
+
expect(expr.min).toMatchObject({ type: 'Literal', value: 5 });
|
|
347
|
+
expect(expr.max).toBeUndefined();
|
|
348
|
+
}
|
|
349
|
+
});
|
|
350
|
+
});
|
|
351
|
+
describe('complex expressions', () => {
|
|
352
|
+
it('parses complex arithmetic with comparisons', () => {
|
|
353
|
+
const expr = parseExpr('(a + b) * 2 > 100');
|
|
354
|
+
expect(expr.type).toBe('BinaryExpression');
|
|
355
|
+
if (expr.type === 'BinaryExpression') {
|
|
356
|
+
expect(expr.operator).toBe('>');
|
|
357
|
+
}
|
|
358
|
+
});
|
|
359
|
+
it('parses logical expression with comparisons', () => {
|
|
360
|
+
const expr = parseExpr('x > 0 and x < 100');
|
|
361
|
+
expect(expr.type).toBe('LogicalExpression');
|
|
362
|
+
if (expr.type === 'LogicalExpression') {
|
|
363
|
+
expect(expr.operator).toBe('and');
|
|
364
|
+
expect(expr.left.type).toBe('BinaryExpression');
|
|
365
|
+
expect(expr.right.type).toBe('BinaryExpression');
|
|
366
|
+
}
|
|
367
|
+
});
|
|
368
|
+
it('parses ternary with function call', () => {
|
|
369
|
+
const expr = parseExpr('length(items) > 0 ? first(items) : null');
|
|
370
|
+
expect(expr.type).toBe('TernaryExpression');
|
|
371
|
+
if (expr.type === 'TernaryExpression') {
|
|
372
|
+
expect(expr.condition.type).toBe('BinaryExpression');
|
|
373
|
+
expect(expr.consequent.type).toBe('CallExpression');
|
|
374
|
+
expect(expr.alternate).toMatchObject({ type: 'Literal', value: null });
|
|
375
|
+
}
|
|
376
|
+
});
|
|
377
|
+
});
|
|
378
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,254 @@
|
|
|
1
|
+
import { describe, it, expect } from 'vitest';
|
|
2
|
+
import { ReqonLexer } from '../lexer/index.js';
|
|
3
|
+
import { ReqonParser } from './parser.js';
|
|
4
|
+
describe('Match Step Parsing', () => {
|
|
5
|
+
function parseAction(source) {
|
|
6
|
+
const lexer = new ReqonLexer(source);
|
|
7
|
+
const tokens = lexer.tokenize();
|
|
8
|
+
const parser = new ReqonParser(tokens, source);
|
|
9
|
+
const program = parser.parse();
|
|
10
|
+
const action = program.statements.find((s) => s.type === 'ActionDefinition');
|
|
11
|
+
if (!action) {
|
|
12
|
+
throw new Error('No action found in program');
|
|
13
|
+
}
|
|
14
|
+
return action;
|
|
15
|
+
}
|
|
16
|
+
describe('basic match step', () => {
|
|
17
|
+
it('parses match with single schema and flow directive', () => {
|
|
18
|
+
const action = parseAction(`
|
|
19
|
+
action HandleResponse {
|
|
20
|
+
match response {
|
|
21
|
+
SuccessResponse -> continue
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
`);
|
|
25
|
+
expect(action.steps).toHaveLength(1);
|
|
26
|
+
const matchStep = action.steps[0];
|
|
27
|
+
expect(matchStep.type).toBe('MatchStep');
|
|
28
|
+
expect(matchStep.arms).toHaveLength(1);
|
|
29
|
+
expect(matchStep.arms[0].schema).toBe('SuccessResponse');
|
|
30
|
+
expect(matchStep.arms[0].flow).toEqual({ type: 'continue' });
|
|
31
|
+
});
|
|
32
|
+
it('parses match with multiple schemas', () => {
|
|
33
|
+
const action = parseAction(`
|
|
34
|
+
action HandleResponse {
|
|
35
|
+
match response {
|
|
36
|
+
SuccessResponse -> continue,
|
|
37
|
+
NotFoundError -> skip,
|
|
38
|
+
ServerError -> abort "Something went wrong"
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
`);
|
|
42
|
+
expect(action.steps).toHaveLength(1);
|
|
43
|
+
const matchStep = action.steps[0];
|
|
44
|
+
expect(matchStep.arms).toHaveLength(3);
|
|
45
|
+
expect(matchStep.arms[0].schema).toBe('SuccessResponse');
|
|
46
|
+
expect(matchStep.arms[0].flow).toEqual({ type: 'continue' });
|
|
47
|
+
expect(matchStep.arms[1].schema).toBe('NotFoundError');
|
|
48
|
+
expect(matchStep.arms[1].flow).toEqual({ type: 'skip' });
|
|
49
|
+
expect(matchStep.arms[2].schema).toBe('ServerError');
|
|
50
|
+
expect(matchStep.arms[2].flow).toEqual({
|
|
51
|
+
type: 'abort',
|
|
52
|
+
message: 'Something went wrong',
|
|
53
|
+
});
|
|
54
|
+
});
|
|
55
|
+
});
|
|
56
|
+
describe('flow directives', () => {
|
|
57
|
+
it('parses continue directive', () => {
|
|
58
|
+
const action = parseAction(`
|
|
59
|
+
action Test {
|
|
60
|
+
match response {
|
|
61
|
+
Schema -> continue
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
`);
|
|
65
|
+
const matchStep = action.steps[0];
|
|
66
|
+
expect(matchStep.arms[0].flow).toEqual({ type: 'continue' });
|
|
67
|
+
});
|
|
68
|
+
it('parses skip directive', () => {
|
|
69
|
+
const action = parseAction(`
|
|
70
|
+
action Test {
|
|
71
|
+
match response {
|
|
72
|
+
Schema -> skip
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
`);
|
|
76
|
+
const matchStep = action.steps[0];
|
|
77
|
+
expect(matchStep.arms[0].flow).toEqual({ type: 'skip' });
|
|
78
|
+
});
|
|
79
|
+
it('parses abort directive without message', () => {
|
|
80
|
+
const action = parseAction(`
|
|
81
|
+
action Test {
|
|
82
|
+
match response {
|
|
83
|
+
Schema -> abort
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
`);
|
|
87
|
+
const matchStep = action.steps[0];
|
|
88
|
+
expect(matchStep.arms[0].flow).toEqual({ type: 'abort' });
|
|
89
|
+
});
|
|
90
|
+
it('parses abort directive with message', () => {
|
|
91
|
+
const action = parseAction(`
|
|
92
|
+
action Test {
|
|
93
|
+
match response {
|
|
94
|
+
Schema -> abort "Error occurred"
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
`);
|
|
98
|
+
const matchStep = action.steps[0];
|
|
99
|
+
expect(matchStep.arms[0].flow).toEqual({
|
|
100
|
+
type: 'abort',
|
|
101
|
+
message: 'Error occurred',
|
|
102
|
+
});
|
|
103
|
+
});
|
|
104
|
+
it('parses queue directive without target', () => {
|
|
105
|
+
const action = parseAction(`
|
|
106
|
+
action Test {
|
|
107
|
+
match response {
|
|
108
|
+
Schema -> queue
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
`);
|
|
112
|
+
const matchStep = action.steps[0];
|
|
113
|
+
expect(matchStep.arms[0].flow).toEqual({ type: 'queue' });
|
|
114
|
+
});
|
|
115
|
+
it('parses queue directive with target', () => {
|
|
116
|
+
const action = parseAction(`
|
|
117
|
+
action Test {
|
|
118
|
+
match response {
|
|
119
|
+
Schema -> queue deadLetterQueue
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
`);
|
|
123
|
+
const matchStep = action.steps[0];
|
|
124
|
+
expect(matchStep.arms[0].flow).toEqual({
|
|
125
|
+
type: 'queue',
|
|
126
|
+
target: 'deadLetterQueue',
|
|
127
|
+
});
|
|
128
|
+
});
|
|
129
|
+
it('parses jump directive without then', () => {
|
|
130
|
+
const action = parseAction(`
|
|
131
|
+
action Test {
|
|
132
|
+
match response {
|
|
133
|
+
Schema -> jump RefreshToken
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
`);
|
|
137
|
+
const matchStep = action.steps[0];
|
|
138
|
+
expect(matchStep.arms[0].flow).toEqual({
|
|
139
|
+
type: 'jump',
|
|
140
|
+
action: 'RefreshToken',
|
|
141
|
+
});
|
|
142
|
+
});
|
|
143
|
+
it('parses jump directive with then retry', () => {
|
|
144
|
+
const action = parseAction(`
|
|
145
|
+
action Test {
|
|
146
|
+
match response {
|
|
147
|
+
Schema -> jump RefreshToken then retry
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
`);
|
|
151
|
+
const matchStep = action.steps[0];
|
|
152
|
+
expect(matchStep.arms[0].flow).toEqual({
|
|
153
|
+
type: 'jump',
|
|
154
|
+
action: 'RefreshToken',
|
|
155
|
+
then: 'retry',
|
|
156
|
+
});
|
|
157
|
+
});
|
|
158
|
+
it('parses jump directive with then continue', () => {
|
|
159
|
+
const action = parseAction(`
|
|
160
|
+
action Test {
|
|
161
|
+
match response {
|
|
162
|
+
Schema -> jump RefreshToken then continue
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
`);
|
|
166
|
+
const matchStep = action.steps[0];
|
|
167
|
+
expect(matchStep.arms[0].flow).toEqual({
|
|
168
|
+
type: 'jump',
|
|
169
|
+
action: 'RefreshToken',
|
|
170
|
+
then: 'continue',
|
|
171
|
+
});
|
|
172
|
+
});
|
|
173
|
+
it('parses retry directive without config', () => {
|
|
174
|
+
const action = parseAction(`
|
|
175
|
+
action Test {
|
|
176
|
+
match response {
|
|
177
|
+
Schema -> retry
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
`);
|
|
181
|
+
const matchStep = action.steps[0];
|
|
182
|
+
expect(matchStep.arms[0].flow).toEqual({ type: 'retry' });
|
|
183
|
+
});
|
|
184
|
+
it('parses retry directive with config', () => {
|
|
185
|
+
const action = parseAction(`
|
|
186
|
+
action Test {
|
|
187
|
+
match response {
|
|
188
|
+
Schema -> retry { maxAttempts: 5, backoff: exponential, initialDelay: 1000 }
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
`);
|
|
192
|
+
const matchStep = action.steps[0];
|
|
193
|
+
expect(matchStep.arms[0].flow?.type).toBe('retry');
|
|
194
|
+
if (matchStep.arms[0].flow?.type === 'retry') {
|
|
195
|
+
expect(matchStep.arms[0].flow.backoff).toEqual({
|
|
196
|
+
maxAttempts: 5,
|
|
197
|
+
backoff: 'exponential',
|
|
198
|
+
initialDelay: 1000,
|
|
199
|
+
});
|
|
200
|
+
}
|
|
201
|
+
});
|
|
202
|
+
});
|
|
203
|
+
describe('match with steps', () => {
|
|
204
|
+
it('parses match with single inline step', () => {
|
|
205
|
+
const action = parseAction(`
|
|
206
|
+
action Test {
|
|
207
|
+
match response {
|
|
208
|
+
SuccessResponse -> store response -> cache
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
`);
|
|
212
|
+
const matchStep = action.steps[0];
|
|
213
|
+
expect(matchStep.arms[0].schema).toBe('SuccessResponse');
|
|
214
|
+
expect(matchStep.arms[0].steps).toHaveLength(1);
|
|
215
|
+
expect(matchStep.arms[0].steps[0].type).toBe('StoreStep');
|
|
216
|
+
});
|
|
217
|
+
it('parses match with step block', () => {
|
|
218
|
+
const action = parseAction(`
|
|
219
|
+
action Test {
|
|
220
|
+
match response {
|
|
221
|
+
SuccessResponse -> {
|
|
222
|
+
store response -> cache,
|
|
223
|
+
store response -> backup
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
`);
|
|
228
|
+
const matchStep = action.steps[0];
|
|
229
|
+
expect(matchStep.arms[0].schema).toBe('SuccessResponse');
|
|
230
|
+
expect(matchStep.arms[0].steps).toHaveLength(2);
|
|
231
|
+
expect(matchStep.arms[0].steps[0].type).toBe('StoreStep');
|
|
232
|
+
expect(matchStep.arms[0].steps[1].type).toBe('StoreStep');
|
|
233
|
+
});
|
|
234
|
+
});
|
|
235
|
+
describe('wildcard pattern', () => {
|
|
236
|
+
it('parses wildcard pattern at end', () => {
|
|
237
|
+
const action = parseAction(`
|
|
238
|
+
action Test {
|
|
239
|
+
match response {
|
|
240
|
+
SuccessResponse -> continue,
|
|
241
|
+
_ -> abort "Unexpected response"
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
`);
|
|
245
|
+
const matchStep = action.steps[0];
|
|
246
|
+
expect(matchStep.arms).toHaveLength(2);
|
|
247
|
+
expect(matchStep.arms[1].schema).toBe('_');
|
|
248
|
+
expect(matchStep.arms[1].flow).toEqual({
|
|
249
|
+
type: 'abort',
|
|
250
|
+
message: 'Unexpected response',
|
|
251
|
+
});
|
|
252
|
+
});
|
|
253
|
+
});
|
|
254
|
+
});
|