From 79c9a91b59d56e7123df6c6f2405deea964dd47a Mon Sep 17 00:00:00 2001 From: konojunya Date: Wed, 2 Sep 2026 14:43:58 +0900 Subject: [PATCH 1/2] test: run canonical conformance suite --- .github/dependabot.yml | 4 + .github/workflows/ci.yml | 20 ++ AGENTS.md | 1 + Cargo.lock | 98 +++++++ Cargo.toml | 11 + README.md | 13 + ...0004-consume-a-pinned-conformance-suite.md | 61 +++++ docs/specs/compiler-frontend.md | 8 +- tests/compiler.rs | 46 +--- tests/conformance.rs | 248 ++++++++++++++++++ tests/fixtures/valid/01-minimal.stack | 7 - tests/fixtures/valid/02-node-semantics.stack | 21 -- .../fixtures/valid/03-groups-and-layout.stack | 52 ---- .../fixtures/valid/04-commerce-platform.stack | 127 --------- tests/specification-revision | 1 + 15 files changed, 470 insertions(+), 248 deletions(-) create mode 100644 docs/decisions/0004-consume-a-pinned-conformance-suite.md create mode 100644 tests/conformance.rs delete mode 100644 tests/fixtures/valid/01-minimal.stack delete mode 100644 tests/fixtures/valid/02-node-semantics.stack delete mode 100644 tests/fixtures/valid/03-groups-and-layout.stack delete mode 100644 tests/fixtures/valid/04-commerce-platform.stack create mode 100644 tests/specification-revision diff --git a/.github/dependabot.yml b/.github/dependabot.yml index ca79ca5..b3eb7c8 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -4,3 +4,7 @@ updates: directory: / schedule: interval: weekly + - package-ecosystem: cargo + directory: / + schedule: + interval: weekly diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index cbd450f..3834fb4 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -16,6 +16,21 @@ jobs: - name: Check out repository uses: actions/checkout@v7 + - name: Read supported specification revision + id: specification + shell: bash + run: | + grep -Eq '^[0-9a-f]{40}$' tests/specification-revision + revision=$(tr -d '\n' < tests/specification-revision) + printf 'revision=%s\n' "$revision" >> "$GITHUB_OUTPUT" + + - name: Check out canonical specification + uses: actions/checkout@v7 + with: + repository: stack-sh/specification + ref: ${{ steps.specification.outputs.revision }} + path: .stack-specification + - name: Install latest stable Rust toolchain run: rustup toolchain install stable --profile minimal --component clippy,rustfmt,llvm-tools-preview @@ -25,6 +40,11 @@ jobs: - name: Run tests run: cargo +stable test + - name: Run canonical conformance suite + env: + STACK_SPECIFICATION_DIR: .stack-specification + run: cargo +stable test --features conformance --test conformance + - name: Run Clippy run: cargo +stable clippy --all-targets --all-features -- -D warnings diff --git a/AGENTS.md b/AGENTS.md index 3742795..b531331 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -16,6 +16,7 @@ The canonical language contract is `stack-sh/specification`. Never introduce syn - Format: `cargo fmt --check` - Test: `cargo test` +- Conformance: `STACK_SPECIFICATION_DIR=../specification cargo test --features conformance --test conformance` - Unit-test coverage: `cargo llvm-cov --lib --all-features --workspace --fail-under-lines 95 --fail-under-functions 95 --fail-under-regions 95` - Lint: `cargo clippy --all-targets --all-features -- -D warnings` - Documentation: `cargo doc --no-deps` diff --git a/Cargo.lock b/Cargo.lock index a6b9f70..5d53077 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,6 +2,104 @@ # It is not intended for manual editing. version = 4 +[[package]] +name = "itoa" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682" + +[[package]] +name = "memchr" +version = "2.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf8baf1c55e62ffcace7a9f06f4bd9cd3f0c4beb022d3b367256b91b87513d98" + +[[package]] +name = "proc-macro2" +version = "1.0.107" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "985e7ec9bb745e6ce6535b544d84d6cd6f7ad8bd711c398938ae983b91a766d9" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.47" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fbf4db142a473a8d80c26bbf18454ed458bf8d26c8219c331daecfdbd079001" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "serde" +version = "1.0.229" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4148590afebada386688f18773da617792bf2ef03ffc1e4cbd2b1d45b023e0ba" +dependencies = [ + "serde_core", +] + +[[package]] +name = "serde_core" +version = "1.0.229" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67dca2c9c51e58a4791a4b1ed58308b39c64224d349a935ab5039aa360942a48" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.229" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7a5d71263a5a7d47b41f6b3f06ba276f10cc18b0931f1799f710578e2309348" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_json" +version = "1.0.151" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c841b55ecdae098c80dcae9cf767f6f8a0c2cdb3416bbef72181df4d0fe73f14" +dependencies = [ + "itoa", + "memchr", + "serde", + "serde_core", + "zmij", +] + [[package]] name = "stack-compiler" version = "0.1.0" +dependencies = [ + "serde_json", +] + +[[package]] +name = "syn" +version = "3.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6275cddf4610d1775e6d1fe9469b2e77d0f39fd98fb7450901b821e0c53649f" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "unicode-ident" +version = "1.0.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75" + +[[package]] +name = "zmij" +version = "1.0.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29666d0abbfad1e3dc4dcf6144730dd3a3ab225bbbdac83319345b1b44ccfc1b" diff --git a/Cargo.toml b/Cargo.toml index 0f86909..8d7aa20 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -13,6 +13,17 @@ categories = ["parser-implementations"] [lib] path = "src/lib.rs" +[features] +conformance = [] + +[[test]] +name = "conformance" +path = "tests/conformance.rs" +required-features = ["conformance"] + +[dev-dependencies] +serde_json = "1.0.151" + [lints.clippy] expect_used = "deny" panic = "deny" diff --git a/README.md b/README.md index 17c7dfe..e060e79 100644 --- a/README.md +++ b/README.md @@ -34,11 +34,24 @@ Install [`cargo-llvm-cov`](https://github.com/taiki-e/cargo-llvm-cov) before run Package-level Clippy lints reject panic-producing `unwrap`, `expect`, `panic`, `unreachable`, `todo`, and `unimplemented` calls in library and test targets. +## Conformance + +The language-independent schemas and fixtures live in [`stack-sh/specification`](https://github.com/stack-sh/specification). This repository records its tested specification commit in [`tests/specification-revision`](./tests/specification-revision). + +Run the canonical suite against a local specification checkout: + +```sh +STACK_SPECIFICATION_DIR=../specification cargo test --features conformance --test conformance +``` + +JSON support is development-only and does not add a runtime dependency to the compiler library. CI checks out the recorded specification revision before running the suite. + ## Architecture - [`docs/decisions/0001-build-a-portable-rust-compiler-core.md`](./docs/decisions/0001-build-a-portable-rust-compiler-core.md) - [`docs/decisions/0002-separate-syntax-ast-from-normalized-ir.md`](./docs/decisions/0002-separate-syntax-ast-from-normalized-ir.md) - [`docs/decisions/0003-use-a-handwritten-parser.md`](./docs/decisions/0003-use-a-handwritten-parser.md) +- [`docs/decisions/0004-consume-a-pinned-conformance-suite.md`](./docs/decisions/0004-consume-a-pinned-conformance-suite.md) - [`docs/specs/compiler-frontend.md`](./docs/specs/compiler-frontend.md) ## License diff --git a/docs/decisions/0004-consume-a-pinned-conformance-suite.md b/docs/decisions/0004-consume-a-pinned-conformance-suite.md new file mode 100644 index 0000000..ded277d --- /dev/null +++ b/docs/decisions/0004-consume-a-pinned-conformance-suite.md @@ -0,0 +1,61 @@ +# ADR-0004: Consume a Pinned Canonical Conformance Suite + +## Status + +Accepted + +## Date + +2026-09-02 + +## Context + +`stack-sh/specification` now owns portable normalized IR, diagnostic schemas, and implementation-independent conformance fixtures. The Rust compiler must prove that its native IR and diagnostics map to those contracts without copying canonical fixture data into this repository. + +Tracking the specification default branch implicitly would make compiler CI non-reproducible. A specification change could break an unchanged compiler commit, and passing CI would no longer identify the language revision actually supported. Vendoring fixtures would avoid network access but would create two sources of truth. + +The compiler core is intentionally dependency-free. JSON is needed only by the conformance adapter and must not become part of source parsing or runtime compilation. + +## Decision + +Record the exact supported specification commit in `tests/specification-revision`. Compiler CI checks out that immutable revision and passes its path explicitly to a feature-gated integration test through `STACK_SPECIFICATION_DIR`. + +The conformance test discovers canonical valid and invalid case directories. It maps native Rust IR and diagnostics to semantic JSON values and compares them with expected documents. Diagnostic comparison includes code, severity, and end-exclusive source range but excludes human-readable message, help, and related-information wording. + +`serde_json` is a development-only dependency. The library keeps no runtime dependencies and exposes no JSON-specific public API. The conformance test is gated by the `conformance` Cargo feature so a normal package checkout can run `cargo test` without fetching another repository. + +When the compiler adopts a newer language revision, the fixture runner must pass locally before `tests/specification-revision` changes. Once the specification publishes stable releases, a release commit may replace draft commit pins while retaining exact reproducibility. + +## Alternatives Considered + +### Copy fixtures into the compiler repository + +- Pros: Tests are self-contained and offline. +- Cons: Expected data can drift from the canonical specification. +- Rejected: Language fixtures must have one owner. + +### Track `stack-sh/specification` main automatically + +- Pros: Immediate detection of specification changes. +- Cons: An unchanged compiler revision can begin failing without any local change. +- Rejected: Compatibility claims must identify an exact tested revision. + +### Add serialization dependencies to the compiler library + +- Pros: Downstream tools could request JSON directly from the core crate. +- Cons: Expands the public API and runtime dependency surface before WASM and package boundaries are designed. +- Rejected: A test-only adapter proves the contract without committing to a runtime transport API. + +### Use a Git submodule + +- Pros: Git records an exact external revision. +- Cons: Adds clone and contributor workflow complexity for a test suite that normal builds do not require. +- Rejected: A revision file and explicit CI checkout provide the same reproducibility with less repository coupling. + +## Consequences + +- Compiler CI states exactly which public specification revision it supports. +- Canonical fixtures remain owned by one repository. +- Conformance runs require a specification checkout and explicit environment variable. +- Updating specification support is a deliberate reviewed change. +- The compiler lockfile includes development-only JSON dependencies, while the built library remains dependency-free. diff --git a/docs/specs/compiler-frontend.md b/docs/specs/compiler-frontend.md index 3103207..55f9be0 100644 --- a/docs/specs/compiler-frontend.md +++ b/docs/specs/compiler-frontend.md @@ -35,6 +35,8 @@ src/validation/ Focused validation unit tests src/ir.rs Renderer-independent normalized model src/lib.rs Public parse and compile APIs tests/ Public API and conformance-oriented tests +tests/specification-revision + Exact canonical specification revision tested by CI docs/decisions/ Architectural decisions ``` @@ -71,7 +73,7 @@ match operator { - Unit tests cover lexer escapes, positions, parser productions, defaults, and each implemented diagnostic. - Integration tests exercise public `parse` and `compile` APIs. -- Valid fixtures mirror the canonical specification examples. +- Feature-gated integration tests execute the canonical conformance suite from the recorded specification revision. - Invalid cases assert stable diagnostic codes rather than entire prose messages. - Library unit-test line, function, and region coverage must each remain at or above 95 percent. - Every compiler change must pass formatting, tests, coverage, Clippy, and documentation builds. @@ -102,7 +104,7 @@ match operator { ## Initial Success Criteria -- All four canonical Stack examples parse and compile. +- All valid cases in the pinned canonical conformance suite parse and compile. - Specification-defined defaults appear in normalized IR. - UTF-8, BOM, invalid string, syntax, name-resolution, semantic, layout-scope, and complexity diagnostics use their assigned codes. - Independent semantic errors are collected in one validation pass. @@ -117,4 +119,4 @@ match operator { - Theme and icon resolution - Layout and renderer integration - Multi-error syntax recovery -- Stable serialization schema and package versioning +- Native Rust API stabilization and package versioning diff --git a/tests/compiler.rs b/tests/compiler.rs index 05d6f45..585dd11 100644 --- a/tests/compiler.rs +++ b/tests/compiler.rs @@ -1,50 +1,20 @@ use stack_compiler::{compile, compile_bytes, diagnostic::Severity, ir}; -const VALID_EXAMPLES: &[(&str, &str)] = &[ - ("minimal", include_str!("fixtures/valid/01-minimal.stack")), - ( - "node semantics", - include_str!("fixtures/valid/02-node-semantics.stack"), - ), - ( - "groups and layout", - include_str!("fixtures/valid/03-groups-and-layout.stack"), - ), - ( - "commerce platform", - include_str!("fixtures/valid/04-commerce-platform.stack"), - ), -]; - -#[test] -fn canonical_examples_compile_without_diagnostics() { - for (name, source) in VALID_EXAMPLES { - let output = compile(source); - assert!( - output.diagnostics.is_empty(), - "{name}: {:?}", - output.diagnostics - ); - assert!(output.diagram.is_some(), "{name}"); - } -} - #[test] -fn canonical_commerce_example_has_expected_normalized_shape() { - let output = compile(VALID_EXAMPLES[3].1); +fn public_api_applies_defaults_to_a_valid_document() { + let output = compile("stack 1.0 diagram \"API\" { node api \"API\" }"); assert!(output.diagram.is_some(), "{:?}", output.diagnostics); let Some(diagram) = output.diagram else { return; }; assert_eq!(diagram.theme_id, "default"); - assert_eq!(diagram.nodes.len(), 13); - assert_eq!(diagram.groups.len(), 5); - assert_eq!(diagram.edges.len(), 12); - assert_eq!( - diagram.layout.as_ref().and_then(|layout| layout.direction), - Some(ir::Direction::Right), - ); + assert_eq!(diagram.nodes.len(), 1); + let Some(node) = diagram.nodes.first() else { + return; + }; + assert_eq!(node.kind, ir::NodeKind::Service); + assert!(diagram.edges.is_empty()); } #[test] diff --git a/tests/conformance.rs b/tests/conformance.rs new file mode 100644 index 0000000..8863ed9 --- /dev/null +++ b/tests/conformance.rs @@ -0,0 +1,248 @@ +use std::{ + error::Error, + fs, + path::{Path, PathBuf}, +}; + +use serde_json::{Value, json}; +use stack_compiler::{compile_bytes, diagnostic, ir}; + +#[test] +fn valid_cases_match_normalized_ir() -> Result<(), Box> { + let root = specification_root()?.join("conformance/valid"); + for case in case_directories(&root)? { + let source = fs::read(case.join("source.stack"))?; + let expected_ir = read_json(&case.join("expected.ir.json"))?; + let output = compile_bytes(&source); + + let Some(diagram) = output.diagram else { + return Err(test_error(format!( + "{} did not produce IR: {:?}", + case.display(), + output.diagnostics + ))); + }; + assert_eq!( + diagram_json(&diagram), + expected_ir, + "IR mismatch in {}", + case.display() + ); + + let expected_diagnostics_path = case.join("expected.diagnostics.json"); + let expected_diagnostics = if expected_diagnostics_path.exists() { + read_json(&expected_diagnostics_path)? + } else { + json!({ "schemaVersion": "1.0", "diagnostics": [] }) + }; + assert_eq!( + diagnostics_json(&output.diagnostics), + expected_diagnostics, + "diagnostic mismatch in {}", + case.display() + ); + } + Ok(()) +} + +#[test] +fn invalid_cases_match_portable_diagnostics() -> Result<(), Box> { + let root = specification_root()?.join("conformance/invalid"); + for case in case_directories(&root)? { + let source = fs::read(case.join("source.stack"))?; + let expected = read_json(&case.join("expected.diagnostics.json"))?; + let output = compile_bytes(&source); + + assert!( + output.diagram.is_none(), + "invalid case produced IR: {}", + case.display() + ); + assert_eq!( + diagnostics_json(&output.diagnostics), + expected, + "diagnostic mismatch in {}", + case.display() + ); + } + Ok(()) +} + +fn case_directories(root: &Path) -> Result, Box> { + let mut cases = Vec::new(); + for entry in fs::read_dir(root)? { + let entry = entry?; + if entry.file_type()?.is_dir() { + cases.push(entry.path()); + } + } + cases.sort(); + if cases.is_empty() { + return Err(test_error(format!( + "no conformance cases found in {}", + root.display() + ))); + } + Ok(cases) +} + +fn read_json(path: &Path) -> Result> { + Ok(serde_json::from_slice(&fs::read(path)?)?) +} + +fn test_error(message: String) -> Box { + Box::new(std::io::Error::other(message)) +} + +fn diagnostics_json(diagnostics: &[diagnostic::Diagnostic]) -> Value { + json!({ + "schemaVersion": "1.0", + "diagnostics": diagnostics + .iter() + .map(diagnostic_expectation_json) + .collect::>(), + }) +} + +fn diagnostic_expectation_json(diagnostic: &diagnostic::Diagnostic) -> Value { + json!({ + "code": diagnostic.code, + "severity": severity_name(diagnostic.severity), + "range": range_json(diagnostic.span), + }) +} + +fn severity_name(severity: diagnostic::Severity) -> &'static str { + match severity { + diagnostic::Severity::Error => "error", + diagnostic::Severity::Warning => "warning", + } +} + +fn range_json(span: diagnostic::Span) -> Value { + json!({ + "start": position_json(span.start), + "end": position_json(span.end), + }) +} + +fn position_json(position: diagnostic::SourcePosition) -> Value { + json!({ + "byteOffset": position.byte_offset, + "line": position.line, + "column": position.column, + }) +} + +fn diagram_json(diagram: &ir::Diagram) -> Value { + json!({ + "schemaVersion": "1.0", + "languageVersion": { + "major": diagram.language_version.major, + "minor": diagram.language_version.minor, + }, + "title": diagram.title, + "themeId": diagram.theme_id, + "children": diagram.children.iter().map(element_json).collect::>(), + "nodes": diagram.nodes.iter().map(node_json).collect::>(), + "groups": diagram.groups.iter().map(group_json).collect::>(), + "edges": diagram.edges.iter().map(edge_json).collect::>(), + "layout": diagram.layout.as_ref().map(layout_json), + }) +} + +fn element_json(element: &ir::ElementId) -> Value { + match element { + ir::ElementId::Node(id) => json!({ "type": "node", "id": id }), + ir::ElementId::Group(id) => json!({ "type": "group", "id": id }), + } +} + +fn node_json(node: &ir::Node) -> Value { + json!({ + "id": node.id, + "label": node.label, + "kind": node_kind_name(node.kind), + "iconId": node.icon_id, + "detail": node.detail, + "parentGroupId": node.parent_group_id, + }) +} + +fn node_kind_name(kind: ir::NodeKind) -> &'static str { + match kind { + ir::NodeKind::Actor => "actor", + ir::NodeKind::Client => "client", + ir::NodeKind::Service => "service", + ir::NodeKind::Function => "function", + ir::NodeKind::Worker => "worker", + ir::NodeKind::Database => "database", + ir::NodeKind::Cache => "cache", + ir::NodeKind::Queue => "queue", + ir::NodeKind::Storage => "storage", + ir::NodeKind::External => "external", + } +} + +fn group_json(group: &ir::Group) -> Value { + json!({ + "id": group.id, + "label": group.label, + "parentGroupId": group.parent_group_id, + "children": group.children.iter().map(element_json).collect::>(), + "layout": group.layout.as_ref().map(layout_json), + }) +} + +fn edge_json(edge: &ir::Edge) -> Value { + json!({ + "from": edge.from, + "to": edge.to, + "direction": edge_direction_name(edge.direction), + "kind": edge_kind_name(edge.kind), + "label": edge.label, + }) +} + +fn edge_direction_name(direction: ir::EdgeDirection) -> &'static str { + match direction { + ir::EdgeDirection::Forward => "forward", + ir::EdgeDirection::Bidirectional => "bidirectional", + ir::EdgeDirection::Association => "association", + } +} + +fn edge_kind_name(kind: ir::EdgeKind) -> &'static str { + match kind { + ir::EdgeKind::Flow => "flow", + ir::EdgeKind::Request => "request", + ir::EdgeKind::Event => "event", + ir::EdgeKind::Data => "data", + ir::EdgeKind::Dependency => "dependency", + } +} + +fn layout_json(layout: &ir::Layout) -> Value { + json!({ + "direction": layout.direction.map(direction_name), + "sameRanks": layout.same_ranks, + "order": layout.order, + }) +} + +fn direction_name(direction: ir::Direction) -> &'static str { + match direction { + ir::Direction::Right => "right", + ir::Direction::Down => "down", + } +} + +fn specification_root() -> Result> { + let path = std::env::var_os("STACK_SPECIFICATION_DIR").ok_or_else(|| { + std::io::Error::new( + std::io::ErrorKind::NotFound, + "STACK_SPECIFICATION_DIR must identify a specification checkout", + ) + })?; + Ok(PathBuf::from(path)) +} diff --git a/tests/fixtures/valid/01-minimal.stack b/tests/fixtures/valid/01-minimal.stack deleted file mode 100644 index 6a1b623..0000000 --- a/tests/fixtures/valid/01-minimal.stack +++ /dev/null @@ -1,7 +0,0 @@ -stack 1.0 - -diagram "Hello Stack" { - node web "Web app" - node api "API" - edge web -> api -} diff --git a/tests/fixtures/valid/02-node-semantics.stack b/tests/fixtures/valid/02-node-semantics.stack deleted file mode 100644 index b28e806..0000000 --- a/tests/fixtures/valid/02-node-semantics.stack +++ /dev/null @@ -1,21 +0,0 @@ -stack 1.0 - -diagram "Application and datastore" { - theme light - - node app "Application" { - kind service - icon "service" - detail "Business logic" - } - - node db "Primary database" { - kind database - icon "postgresql" - detail "PostgreSQL" - } - - edge app -> db "SQL" { - kind data - } -} diff --git a/tests/fixtures/valid/03-groups-and-layout.stack b/tests/fixtures/valid/03-groups-and-layout.stack deleted file mode 100644 index b2310aa..0000000 --- a/tests/fixtures/valid/03-groups-and-layout.stack +++ /dev/null @@ -1,52 +0,0 @@ -stack 1.0 - -diagram "Public application" { - layout { - direction right - } - - group clients "Clients" { - layout { - direction down - rank same [browser, mobile] - order [browser, mobile] - } - - node browser "Browser" { - kind client - icon "browser" - } - - node mobile "Mobile app" { - kind client - icon "mobile" - } - } - - node gateway "Edge gateway" { - icon "gateway" - } - - group platform "Platform" { - node api "Application API" - node db "Primary database" { - kind database - } - } - - edge browser -> gateway "HTTPS" { - kind request - } - - edge mobile -> gateway "HTTPS" { - kind request - } - - edge gateway -> api "HTTPS" { - kind request - } - - edge api -> db "SQL" { - kind data - } -} diff --git a/tests/fixtures/valid/04-commerce-platform.stack b/tests/fixtures/valid/04-commerce-platform.stack deleted file mode 100644 index b3a5029..0000000 --- a/tests/fixtures/valid/04-commerce-platform.stack +++ /dev/null @@ -1,127 +0,0 @@ -stack 1.0 - -diagram "Commerce platform" { - layout { - direction right - } - - node customer "Customer" { - kind actor - } - - group storefront "Storefront" { - node web "Web storefront" { - kind client - icon "nextjs" - detail "Next.js" - } - - node gateway "Edge gateway" { - icon "gateway" - } - } - - group commerce "Commerce services" { - layout { - direction down - rank same [catalog, checkout] - order [catalog, checkout] - } - - node catalog "Catalog API" { - detail "Products and pricing" - } - - node checkout "Checkout API" { - detail "Order orchestration" - } - } - - group asynchronous "Asynchronous processing" { - node events "Event bus" { - kind queue - } - - node fulfillment "Fulfillment worker" { - kind worker - } - - node notifications "Notification worker" { - kind worker - } - } - - group data "Data" { - node products "Product database" { - kind database - icon "postgresql" - } - - node orders "Order database" { - kind database - icon "postgresql" - } - - node assets "Product media" { - kind storage - } - } - - group partners "External systems" { - node payment "Payment provider" { - kind external - } - - node email "Email provider" { - kind external - } - } - - edge customer -> web "Browse and buy" { - kind request - } - - edge web -> gateway "HTTPS" { - kind request - } - - edge gateway -> catalog "Catalog requests" { - kind request - } - - edge gateway -> checkout "Checkout requests" { - kind request - } - - edge catalog -> products "SQL" { - kind data - } - - edge catalog -> assets "Media URLs" { - kind data - } - - edge checkout -> orders "Transactions" { - kind data - } - - edge checkout -> payment "Payment API" { - kind request - } - - edge checkout -> events "OrderPlaced" { - kind event - } - - edge events -> fulfillment "OrderPlaced" { - kind event - } - - edge events -> notifications "OrderPlaced" { - kind event - } - - edge notifications -> email "Send receipt" { - kind request - } -} diff --git a/tests/specification-revision b/tests/specification-revision new file mode 100644 index 0000000..13fe103 --- /dev/null +++ b/tests/specification-revision @@ -0,0 +1 @@ +79f8e5599c99fe1bd7f3e7a0102ba12c01bc024b From 1cbe06c9dea30c2950ce06d74a769dfdf2b6dcda Mon Sep 17 00:00:00 2001 From: konojunya Date: Wed, 2 Sep 2026 14:45:17 +0900 Subject: [PATCH 2/2] test: advance specification revision --- tests/specification-revision | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/specification-revision b/tests/specification-revision index 13fe103..ec75a9c 100644 --- a/tests/specification-revision +++ b/tests/specification-revision @@ -1 +1 @@ -79f8e5599c99fe1bd7f3e7a0102ba12c01bc024b +23e8748201c831e0e9ef94e7d84501e630a4096f