Skip to content

test(integration-tests): run report scenarios on SQLite, MySQL and D1 across ports - #421

Merged
dmealing merged 5 commits into
mainfrom
fm/mo-1-1-0-report-dialects
Oct 10, 2026
Merged

dmealing merged 5 commits into
mainfrom
fm/mo-1-1-0-report-dialects

Conversation

@dmealing

Copy link
Copy Markdown
Member

Intent

Before 1.1.0-rc.3, close three reporting test gaps ("Yeah do 1, 2, 3"): 1. Databases other than Postgres outside TypeScript: Java, Kotlin, C# and Python never run the report scenarios on SQLite (or MySQL); only TypeScript does. 2. MySQL: no persistence or api-contract report scenarios run on MySQL in any port; only TypeScript's own @spine/@default view value tests read MySQL views, and the Cube MySQL output is golden-compared, never executed. 3. D1: no run against a real Cloudflare D1 (or its local runtime); it is covered only as SQLite. Context: report SQL is produced by TypeScript only (migrate creates the views on Postgres, SQLite and D1; MySQL view bodies come from buildReportViews per docs/recipes/mysql.md). The other ports read the views TypeScript produced and byte-match report-shapes.json. A parallel task (mo-1-1-0-report-coverage) is adding ~10 persistence and ~6 api-contract report scenarios; rebase onto it if it lands first so the new lanes run every scenario.

What Changed

  • Run the report persistence and api-contract scenarios on SQLite in the Java, Kotlin and C# ports (new QueryScenarioSqlite* runners and tests), and on MySQL in TypeScript, Java, Kotlin and C# (new MySQL schema, report views, MySqlContainer/MySqlDatabase and query-scenario-mysql.ts, api-contract-report-mysql-server.ts).
  • Add a D1 lane in TypeScript (query-scenario-d1.ts, d1-kysely.ts) that runs the report scenarios against the local D1 runtime, and a live Cube lane for the MySQL model (cube-model-mysql.live.ts, cube-live-support.ts), with the MySQL and SQLite report and canonical schema artifacts added under fixtures/.
  • Remove stray tmp-* codegen-ts test output directories that were committed by mistake, and update the reporting, Cube export, MySQL recipe and conformance docs to match the new lanes.

Risk Assessment

✅ Low: The fix round fixes all three selected findings correctly and adds no new defects; it only touches test infrastructure, and the Python and Java engine scope-down was accepted earlier as a documented limitation.

Testing

Baseline ts-fast/ts-unit already green. Drove the new report-scenario lanes live: SQLite and D1 (miniflare local workerd) and MySQL (Docker) all pass, plus engine-wire and schema-artifact-engines. Full server bun suite did not finish within the time limit (no failures seen in partial output). The C#, Java and Kotlin lanes that the review fixes touched were not run here, so their MySQL changes are unverified live.

  • Live validation: ⚠️ inconclusive - 7 of 10 scenarios driven live against the product
Scenario Result Live Evidence
SQLite report query scenarios run and read report views (persistence conformance) ✅ pass live bun test test/query-sqlite.test.ts (integration-tests) -> pass
D1 report query scenarios run on Cloudflare D1 local runtime (miniflare workerd) ✅ pass live bun test test/query-d1.test.ts -> pass
MySQL report query scenarios run against a real MySQL in Docker ✅ pass live bun test test/query-mysql.test.ts -> pass
MySQL api-contract report scenarios served and read over HTTP ✅ pass live bun test test/api-contract-report-mysql.test.ts -> pass
MySQL report view bodies created and dropped only by name ✅ pass live bun test test/report-views-mysql.test.ts -> pass
SQLite api-contract report scenarios ✅ pass live bun test test/api-contract-report-sqlite.test.ts -> pass
Engine wiring and schema artifact per engine ✅ pass live bun test test/engine-wire.test.ts test/schema-artifact-engines.test.ts -> pass
C# MySQL report lane (shared container, mysql:// URL accepted) ⏸️ untested no Not driven in this run; needs dotnet test of server/csharp/MetaObjects.IntegrationTests with Docker, time-consuming. Not run.
Java/Kotlin MySQL lanes (drop only schema-artifact objects) ⏸️ untested no Not driven in this run; needs Maven/Gradle reactor plus Testcontainers. Not run.
Full server regression suite passes ⏸️ untested no Run killed at 1500s timeout before completion; partial log shows no failures but no completed result, so no live pass or fail was established.
Evidence: full server bun test log (partial, timed out)
bun test v1.3.14 (0d9b296a)

packages/metadata/test/registry-coverage.test.ts:

[registry-coverage] subtypes: 53/65 exercised (81.5%), 12 UNTESTED
[registry-coverage] untested subtypes:
  attr.boolean
  attr.class
  attr.double
  attr.expression
  attr.filter
  attr.int
  attr.intMap
  attr.long
  validator.atLeastOne
  validator.comparison
  validator.presentIff
  validator.requiredWhen
[registry-coverage] exercised subtypes with untested attrs: 28

packages/codegen-ts/test/format.test.ts:
[codegen-ts] Biome formatter: 2 diagnostics; first: expected `,` but instead found `'x'`

packages/cli/test/migrate-drop-unmanaged.test.ts:
meta: migrate: refusing to drop public.theirs — absent from the committed schema snapshot, so this toolchain never managed it and the migration could not replay against a database where it never existed. Re-run with '--allow drop-unmanaged' if the drop is intended.
meta migrate — sqlite, file:/tmp/drop-unmanaged-zcLHOE/t.db

  Changes: 1 drop-table

  Written:
    /tmp/drop-unmanaged-zcLHOE/.metaobjects/migrations/20261010224218-x/up.sql
    /tmp/drop-unmanaged-zcLHOE/.metaobjects/migrations/20261010224218-x/down.sql

meta migrate — sqlite, file:/tmp/drop-unmanaged-hPAXcf/t.db

  Changes: 1 drop-table

  Written:
    /tmp/drop-unmanaged-hPAXcf/.metaobjects/migrations/20261010224218-x/up.sql
    /tmp/drop-unmanaged-hPAXcf/.metaobjects/migrations/20261010224218-x/down.sql

meta migrate — sqlite, file:/tmp/drop-unmanaged-GcWdWY/t.db

  Changes: 1 drop-table

  Written:
    /tmp/drop-unmanaged-GcWdWY/.metaobjects/migrations/20261010224218-x/up.sql
    /tmp/drop-unmanaged-GcWdWY/.metaobjects/migrations/20261010224218-x/down.sql

meta: migrate: refusing to drop public.orders.orders_job_id_fk — absent from the committed schema snapshot, so this toolchain never managed it and the migration could not replay against a database where it never existed. Re-run with '--allow drop-unmanaged' if the drop is intended.
meta migrate — sqlite, file:/tmp/drop-unmanaged-fk-sXVYLp/t.db

  Changes: 1 drop-fk

  Written:
    /tmp/drop-unmanaged-fk-sXVYLp/.metaobjects/migrations/20261010224218-x/up.sql
    /tmp/drop-unmanaged-fk-sXVYLp/.metaobjects/migrations/20261010224218-x/down.sql

meta migrate — sqlite, file:/tmp/drop-unmanaged-fk-QQaRbr/t.db

  Changes: 1 drop-fk

  Written:
    /tmp/drop-unmanaged-fk-QQaRbr/.metaobjects/migrations/20261010224218-x/up.sql
    /tmp/drop-unmanaged-fk-QQaRbr/.metaobjects/migrations/20261010224218-x/down.sql


packages/cli/test/help-and-exit.test.ts:
meta gen — codegen TS targets from your declared metadata

USAGE:
  meta gen [<entity>...] [flags]

FLAGS:
  --dry-run             Compute and print, don't write
  --list                Print the generator CATALOG (name, layer, tier, what it emits,
                        what to install) and exit. Codegen is opt-in: nothing runs until
                        you wire it, and this is where you choose. No config or metadata
                        required — add --format json for the same catalog, machine-readable.
  --probe               With --list: construct every catalog generator and dry-run it
                        against YOUR model, reporting how many files each would emit.
                        A count per generator beats any category label — and it cannot go
                        stale, because it runs the generators. Needs a project.
  --baseline <default|adopt|fresh>
                        First-time-on-existing-file behavior. Default: refuse a file that
                        cannot be proved to be generated output. "adopt" records the files
                        you have as the merge base and writes NOTHING — the first run a
                        project with no committed .hashes.json can perform. "fresh"
                        overwrites them with fresh output and re-baselines (discards edits).
  --no-antipatterns     Suppress the advisory "hand-rolled what MetaObjects can model" pass
  --limit <n|all>       Advisory lines TEXT output prints before truncating (default 20)
  --format <toon|json|text>   Output format (global flag; default toon off-TTY)
  <entity> [<entity>]   Positional filter on entity names
  --help, -h            Print this help

A real write run (not --dry-run) also runs an ADVISORY anti-pattern pass: it scans
your authored source for hand-rolled aggregates, money-as-float, and CHECK-IN enums
and points you at the construct that models them (origin.aggregate / field.currency /
field.enum). Warnings only — it never fails the build. Opt out with --no-antipatterns
or META_NO_ANTIPATTERNS=1.

Text output caps that list at --limit lines (default 20) and says how many it held
back. --format toon / --format json carry EVERY finding in the payload's
antiPatterns block, uncapped — --limit is a display cap for a terminal, never a
limit on the machine-readable result.

NOTE: outDir, dialect, dbImport, extStyle are read from metaobjects.config.ts

meta migrate — diff metadata vs live DB; emit migration SQL files

USAGE:
  meta migrate [baseline|apply-pending] [flags]

SUBCOMMANDS:
  baseline             Snapshot an EXISTING database's schema as the reference point
                       (use with --from-db). NOTE: for a brand-new/empty database use
                       the greenfield example below, NOT baseline — an offline baseline
                       records your metadata as already-applied and emits no CREATE TABLE.
  apply-pending        Replay committed migration files against --db (no diff).
                       Provisions a fresh/CI database when the chain BUILDS the
                       schema — 'meta verify --replay' proves that it does. A
                       database adopted via 'baseline --from-db' has no such
                       chain. postgres/sqlite only.

MIGRATE FLAGS:
  --db <url>           DB connection URL (required for live-introspect / --apply / --rollback)
                       Supports: file:, libsql:, postgres:, postgresql:
  --dialect sqlite|postgres|d1
                       Optional dialect override (auto-detected from URL scheme)
  --migration-format default|flyway
                       Migration file layout (default: default). 'flyway' emits
                       V<N>__<slug>.sql + U<N>__<slug>.sql for a Flyway runner;
                       --apply/apply-pending/--rollback are refused under it
                       because Flyway owns apply and flyway_schema_history.
                       Also settable once as migrate.format in
                       .metaobjects/config.json.
  --out-dir <path>     Migration directory (default: ./.metaobjects/migrations;
                       flyway: src/main/resources/db/migration)
  --slug <name>        Required when changes are present (e.g., --slug add-user-shipping)
  --allow <csv>        Comma-separated destructive-change permissions:
                       drop-column,drop-table,type-change,drop-index,drop-fk,
                       drop-check,drop-view,drop-view-cascade,
                       adopt-view,nullable-to-not-null,drop-identity-default,
                       drop-unmanaged
                       drop-column DELETES the column and every value in it. If
                       the column was RENAMED, use --rename-column instead: it
                       keeps the data.
                       drop-unmanaged permits dropping a table/view the committed
                       snapshot never contained — one this toolchain never managed.
                       Without it that drop is refused, because the migration it
                       writes cannot replay against a database where the object
                       never existed.
  --on-ambiguous abort|rename|drop-add
                       How to handle ambiguous renames (default: abort)
  --rename-table [schema.]old=new
                       Declare a table rename (repeatable). Resolves the drop+create
                       as ALTER TABLE ... RENAME TO whatever the rename heuristic
                       makes of the names; refused if no such pair is pending.
  --rename-column [schema.]table.old=new
                       Declare a column

... [162271 bytes truncated] ...

nostic(s)"
help: []
antiPatterns:
  status: ran
  total: 0
  rows: []
requirements:
  status: ran
  total: 2
  rows[2]{code,path,severity,source,message}:
    WARN_REQUIREMENT_DEFERRED_UNTRACKED,orderRecord,warn,gate,is deferred but names no @trackedBy issue. Deferring without a ticket is how a known gap becomes an unknown one — nothing will raise it again.
    WARN_REQUIREMENT_OBJECT_UNCLAIMED,"",warn,gate,"no requirement claims 'acme::shop::Order'. Add it to an L4 requirement's 'implementedBy'."
overlays:
  status: ran
  total: 0
  rows: []
names:
  status: ran
  total: 0
  rows: []
deprecations:
  status: ran
  total: 0
  rows: []
fields:
  status: ran
  total: 0
  rows: []
requirementCounts:
  total: 1
  functional: 1
  architectural: 0
  byStatus[1]{status,count}:
    planned,1
  undecided: 0
  entitiesClaimed: 0
  entitiesTotal: 1
  metadataFiles: 2
notRepresented[2]: "per-gate drift detail for every gate but schema (which template variable drifted, which generated file differs, which migration failed to replay) — printed as text on stderr; this payload carries each gate's pass/fail verdict, and the schema gate's differences in schemaDrift",the loader's own warnings and the agent-context/manifest advisories — printed as text on stderr
meta: meta verify — running --templates (default). Explicit subverbs: --templates (prompt drift), --db/--dialect d1 (schema drift), --codegen (codegen drift), --docs (docs drift), --deps (dependency drift), --replay/--replay-snapshot (the committed migration chain replays from empty).
meta: meta verify — no template.* nodes found; the template gate had nothing to check.
meta: meta verify — not run: schema (--db <url>), codegen (--codegen), docs (--docs), deps (--deps), replay (--replay) — a bare 'meta verify' runs only the template gate (plus the requirement ledger); name each gate to run it
verify[7]{gate,ran,ok}:
  templates,true,true
  schema,false,true
  codegen,false,true
  docs,false,true
  deps,false,true
  requirements,true,true
  replay,false,true
exitCode: 0
summary: "2 gate(s) ran, all clean"
help[1]: no drift and nothing advisory to answer — nothing to do
antiPatterns:
  status: ran
  total: 0
  rows: []
requirements:
  status: ran
  total: 0
  rows: []
overlays:
  status: ran
  total: 0
  rows: []
names:
  status: ran
  total: 0
  rows: []
deprecations:
  status: ran
  total: 0
  rows: []
fields:
  status: ran
  total: 0
  rows: []
notRepresented[2]: "per-gate drift detail for every gate but schema (which template variable drifted, which generated file differs, which migration failed to replay) — printed as text on stderr; this payload carries each gate's pass/fail verdict, and the schema gate's differences in schemaDrift",the loader's own warnings and the agent-context/manifest advisories — printed as text on stderr
verify[7]{gate,ran,ok}:
  templates,true,true
  schema,false,true
  codegen,false,true
  docs,false,true
  deps,false,true
  requirements,true,true
  replay,false,true
exitCode: 0
summary: "2 gate(s) ran, all clean"
help[1]: no drift and nothing advisory to answer — nothing to do
antiPatterns:
  status: ran
  total: 0
  rows: []
requirements:
  status: ran
  total: 0
  rows: []
overlays:
  status: ran
  total: 0
  rows: []
names:
  status: ran
  total: 0
  rows: []
deprecations:
  status: ran
  total: 0
  rows: []
fields:
  status: ran
  total: 0
  rows: []
notRepresented[2]: "per-gate drift detail for every gate but schema (which template variable drifted, which generated file differs, which migration failed to replay) — printed as text on stderr; this payload carries each gate's pass/fail verdict, and the schema gate's differences in schemaDrift",the loader's own warnings and the agent-context/manifest advisories — printed as text on stderr

packages/cli/test/collection-routing.test.ts:
meta gen — sqlite, ~/.no-mistakes/worktrees/5b8c6c97e760/01M4KX1RW3QCXBNS3DKW7Z2AVM/server/typescript/packages/cli/test/fixtures/__tmp__/collection-routing-nGMQvG/apps/ui/generated/db

  NEW        generated/db/Order.ts

  1 written


packages/cli/test/migrate-baseline.test.ts:
meta: migrate baseline (offline): recording your metadata's schema as the already-applied baseline — this emits NO CREATE TABLE. Use it only if your database already matches your metadata; for a new/empty database run `meta migrate --from-db --db <url> --dialect postgres --slug init --apply` instead.
migrate: wrote schema snapshot /tmp/mts-cli-eJFLtS/.metaobjects/migrations/.schema.postgres.json
meta: migrate baseline (offline): recording your metadata's schema as the already-applied baseline — this emits NO CREATE TABLE. Use it only if your database already matches your metadata; for a new/empty database run `meta migrate --from-db --db <url> --dialect postgres --slug init --apply` instead.
migrate baseline (dry-run): would write schema snapshot /tmp/mts-cli-8MBFGa/.metaobjects/migrations/.schema.postgres.json

packages/cli/test/migrate-offline-gen.test.ts:
meta: migrate: no schema snapshot at /tmp/mts-gen-srcOue/.metaobjects/migrations/.schema.sqlite.json. For a new project run `meta migrate --from-db --db <url> --dialect sqlite --slug init --apply` to create your tables, or `meta migrate baseline --from-db --db <url>` to adopt an existing database.
meta: migrate baseline (offline): recording your metadata's schema as the already-applied baseline — this emits NO CREATE TABLE. Use it only if your database already matches your metadata; for a new/empty database run `meta migrate --from-db --db <url> --dialect sqlite --slug init --apply` instead.
migrate: wrote schema snapshot /tmp/mts-gen-Tfc0O8/.metaobjects/migrations/.schema.sqlite.json
migrate: no changes
meta: migrate baseline (offline): recording your metadata's schema as the already-applied baseline — this emits NO CREATE TABLE. Use it only if your database already matches your metadata; for a new/empty database run `meta migrate --from-db --db <url> --dialect sqlite --slug init --apply` instead.
migrate: wrote schema snapshot /tmp/mts-gen-Mdf2to/.metaobjects/migrations/.schema.sqlite.json
migrate: wrote /tmp/mts-gen-Mdf2to/.metaobjects/migrations/20261010224234-auto/up.sql
migrate: wrote /tmp/mts-gen-Mdf2to/.metaobjects/migrations/20261010224234-auto/down.sql
migrate: no changes
meta: migrate baseline (offline): recording your metadata's schema as the already-applied baseline — this emits NO CREATE TABLE. Use it only if your database already matches your metadata; for a new/empty database run `meta migrate --from-db --db <url> --dialect sqlite --slug init --apply` instead.
migrate: wrote schema snapshot /tmp/mts-gen-55WuRw/.metaobjects/migrations/.schema.sqlite.json
meta: migrate: --slug <name> required when there are changes (e.g., --slug add-user-shipping)
meta: migrate baseline (offline): recording your metadata's schema as the already-applied baseline — this emits NO CREATE TABLE. Use it only if your database already matches your metadata; for a new/empty database run `meta migrate --from-db --db <url> --dialect sqlite --slug init --apply` instead.
migrate: wrote schema snapshot /tmp/mts-gen-nEierD/.metaobjects/migrations/.schema.sqlite.json
-- UP --
ALTER TABLE "orders" ADD COLUMN "note" TEXT;

-- DOWN --
ALTER TABLE "orders" DROP COLUMN "note";
migrate: wrote /tmp/mts-gen-nEierD/.metaobjects/migrations/20261010224234-auto/up.sql
migrate: wrote /tmp/mts-gen-nEierD/.metaobjects/migrations/20261010224234-auto/down.sql

packages/cli/test/eject-library.test.ts:
{
  "ejected": [],
  "install": {
    "dev": [],
    "runtime": [],
    "command": ""
  },
  "config": {
    "keys": []
  },
  "libraries": [
    {
      "library": "iam",
      "root": "/tmp/eject-lib-zjw4HF/model",
      "files": [
        {
          "ref": "iam/model",
          "path": "meta.iam.model.yaml",
          "status": "created"
        },
        {
          "ref": "iam/requirements",
          "path": "meta.iam.requirements.yaml",
          "status": "created"
        },
        {
          "ref": "iam/db",
          "path": "meta.iam.db.yaml",
          "status": "created"
        }
      ],
      "stillOptedIn": []
    }
  ]
}
EXIT=124
- Outcome: ⚠️ 3 warnings across 1 run (38m4s)

Pipeline

Updates from git push no-mistakes

✅ **intent** - passed

✅ No issues found.

✅ **Rebase** - passed

✅ No issues found.

🔧 **Review** - 4 issues found → auto-fixed ✅
  • ⚠️ docs/features/reporting.md:644 - The intent requires closing gap 1: "Java, Kotlin, C# and Python never run the report scenarios on SQLite (or MySQL); only TypeScript does." The change closes it for C# and Kotlin on both engines and for Java on MySQL only. Python gets no non-Postgres lane at all, and Java gets no SQLite lane. Both are written up as 'stated limitations, not gaps' (reporting.md:643-644: | Java (OMDB) | ✓ | not run: OMDB has no SQLite driver | and | Python | ✓ | not run: the runtime ships a Postgres driver only (pg8000) | not run: same |, plus the matching CONFORMANCE.md row). The reasoning holds up in the source: Python's ObjectManager hardcodes %s placeholders and ships only PostgresDriver, and OMDB has no SQLite driver. Still, this is scoping down a named requirement. Ask the user whether to accept this narrower scope. The alternative is a minimal Python driver (stdlib sqlite3 and/or PyMySQL behind the DB-API seam) and a Java SQLite lane (GenericSQLDriver plus sqlite-jdbc). Either one extends the runtime surface, so the remedy needs the user's authorization.
  • ⚠️ server/java/integration-tests-kotlin/src/test/kotlin/com/metaobjects/integration/kotlin/QueryScenarioRunner.kt:154 - dropMysqlSchema runs SHOW FULL TABLES, sets FOREIGN_KEY_CHECKS = 0, and drops every view and table in the connected database. With METAOBJECTS_TEST_MYSQL_URL set (documented in MySqlContainer.kt), the Kotlin lane wipes every table and view in that database, including ones it did not create. The repo states the opposite rule in report-views-mysql.test.ts: drop 'Only the views and tables THIS file creates, by name: METAOBJECTS_TEST_MYSQL_URL may point at a shared database'. The Java and TS runners in this same change follow that rule: they parse CREATE VIEW/TABLE names from the schema artifact and drop only those. Fix: drop only the names in canonical/schema.mysql.sql, views first, then tables in reverse order, as Java's dropSchema does.
  • ⚠️ server/csharp/MetaObjects.IntegrationTests/QueryScenarioMySqlTests.cs:23 - Each C# MySQL theory case starts its own mysql:8.4 Testcontainers container: 9 persistence report cases here plus 16 api-contract cases (ApiContractReportConformanceTest.cs:56), so about 25 MySQL cold starts per run. CI has no shared MySQL sidecar, unlike Postgres (METAOBJECTS_TEST_PG_URL). The TS, Java and Kotlin MySQL lanes each start one server per file. MySqlDatabase already has a mode that creates a fresh, uniquely named database per scenario on an existing server. Fix: start one container in an xUnit class/collection fixture and give each scenario its own database through that mode. This removes several minutes of container startup per lane run.
  • ℹ️ server/csharp/MetaObjects.IntegrationTests/Runner/MySqlDatabase.cs:39 - C# parses METAOBJECTS_TEST_MYSQL_URL as an ADO.NET connection string. TypeScript reads the same variable as mysql://user:pass@host:port/db, and Java/Kotlin read it as jdbc:mysql://host:port/db. integration-test.sh runs every port in one environment, so setting the variable for one port makes another fail at startup. For example, MySqlConnectionStringBuilder rejects a mysql:// URL. Fix: accept the mysql:// URL form in C# (convert it to a MySqlConnectionStringBuilder), or use a C#-specific variable name.

🔧 Fix applied.
✅ Re-checked - no issues remain.

⚠️ **Test** - 3 warnings
  • ⚠️ Full server regression suite (cd server/typescript &amp;&amp; bun test) did not complete within 1500s; result unknown beyond no failures in partial output. Re-run with a longer timeout to confirm.
  • ⚠️ C# (csharp-mysql shared-container fixture, mysql:// URL acceptance), Java and Kotlin (drop-only-own-objects) lanes were not driven live. Changes from review round 1 for those ports have no live evidence in this run.
  • ⚠️ live validation verdict: inconclusive (7 of 10 scenarios were driven live against the product); untested: C# MySQL report lane (shared container, mysql:// URL accepted), Java/Kotlin MySQL lanes (drop only schema-artifact objects), Full server regression suite passes
  • Live validation: ⚠️ inconclusive - 7 of 10 scenarios driven live against the product
Scenario Result Live Evidence
SQLite report query scenarios run and read report views (persistence conformance) ✅ pass live bun test test/query-sqlite.test.ts (integration-tests) -> pass
D1 report query scenarios run on Cloudflare D1 local runtime (miniflare workerd) ✅ pass live bun test test/query-d1.test.ts -> pass
MySQL report query scenarios run against a real MySQL in Docker ✅ pass live bun test test/query-mysql.test.ts -> pass
MySQL api-contract report scenarios served and read over HTTP ✅ pass live bun test test/api-contract-report-mysql.test.ts -> pass
MySQL report view bodies created and dropped only by name ✅ pass live bun test test/report-views-mysql.test.ts -> pass
SQLite api-contract report scenarios ✅ pass live bun test test/api-contract-report-sqlite.test.ts -> pass
Engine wiring and schema artifact per engine ✅ pass live bun test test/engine-wire.test.ts test/schema-artifact-engines.test.ts -> pass
C# MySQL report lane (shared container, mysql:// URL accepted) ⏸️ untested no Not driven in this run; needs dotnet test of server/csharp/MetaObjects.IntegrationTests with Docker, time-consuming. Not run.
Java/Kotlin MySQL lanes (drop only schema-artifact objects) ⏸️ untested no Not driven in this run; needs Maven/Gradle reactor plus Testcontainers. Not run.
Full server regression suite passes ⏸️ untested no Run killed at 1500s timeout before completion; partial log shows no failures but no completed result, so no live pass or fail was established.
  • scripts/ci-local.sh --only ts-fast --only ts-unit --strict-toolchains
  • cd server/typescript/packages/integration-tests && bun test test/query-sqlite.test.ts test/query-d1.test.ts test/engine-wire.test.ts test/schema-artifact-engines.test.ts test/api-contract-report-sqlite.test.ts -> 72 pass, 0 fail
  • cd server/typescript/packages/integration-tests && bun test test/query-mysql.test.ts test/api-contract-report-mysql.test.ts test/report-views-mysql.test.ts -> 44 pass, 0 fail (Docker testcontainers MySQL)
  • cd server/typescript && bun test (full server suite, regression) -> killed by 1500s timeout, log at ~/.no-mistakes/evidence/01M4KX1RW3QCXBNS3DKW7Z2AVM/full-server-bun-test.log; no '(fail)' lines before cutoff, but run not completed
⚠️ **Document** - 1 warning
  • ⚠️ docs/features/reporting.md:644 - Python and Java SQLite/MySQL scope: confirm narrower scope is accepted. Python runs report scenarios on Postgres only; Java has no SQLite lane. Docs call these stated limitations. The user accepted this in review; confirm it stands for the release.
✅ **Lint** - passed

✅ No issues found.

✅ **Push** - passed

✅ No issues found.

…oss the ports

The FR-044 report scenarios ran on Postgres in every port and on SQLite in
TypeScript only. They now also run where a port's runtime has the engine:

- Persistence report-* scenarios: TypeScript on SQLite, MySQL 8.4 and D1's local
  runtime (Miniflare, `meta migrate --dialect d1`); C# (EF Core, Pomelo) and Kotlin
  (Exposed) on SQLite and MySQL; Java (OMDB) on MySQL.
- api-contract report/ sub-corpus: TypeScript on SQLite, MySQL and D1; C# on SQLite
  and MySQL, against the real views.
- Cube lane: a second live file runs the MySQL path against a real MySQL 8.4 holding
  the report views buildReportViews lowers, comparing each report's Cube query with
  SELECT * from its view.

Each engine executes an artifact TypeScript produced (canonical/schema.sqlite.sql
from meta migrate, canonical/schema.mysql.sql from the adopter tables plus
buildReportViews, and report/ equivalents), held equal to its generator by a test.
Postgres expectations are unchanged; engine spelling is mapped on the actual side,
and report-relative-date gains a per-engine seed (seed-data-engine).

Stated limitations, recorded in docs/CONFORMANCE.md and docs/features/reporting.md:
Java's OMDB has no SQLite driver and Python's runtime ships only a Postgres driver;
the Java/Kotlin/Python api-contract report lane serves rows behind the repository
seam, so no engine reaches it.

Also removes two stray tmp-* test output directories that were committed under
codegen-ts/test and made the workspace typecheck fail, and gives the MySQL test
files' afterAll hook a real timeout.
…nd MySQL lanes; key the D1 binding per server

The SQLite and D1 lanes split a script on every ';', so a seed value holding one would
break there and pass on MySQL. All three lanes now use the end-of-line splitter from
sql-script.ts. The D1 api-contract server hands its binding to the generated db module
through a per-server global key instead of one shared name.
…port lanes

Folds duplicated engine names, corpus paths and schema-reading helpers in the
SQLite, MySQL and D1 report lanes into one definition per language. No behaviour change.
@dmealing

Copy link
Copy Markdown
Member Author

Notes for the reviewer.

Gate runs: 1 no-mistakes run (review fix round 1, then test and document gates answered with local evidence).

Cube MySQL: this takes the smaller option. A second live file runs Cube against a real MySQL 8.4 holding the report views buildReportViews lowers, and compares each report's Cube query with SELECT * from its view, asserting the pre-aggregation. The two MySQL fixture cases stay compile-only.

Stated limitations (documented in docs/CONFORMANCE.md and docs/features/reporting.md): Java's OMDB has no SQLite driver, and Python's runtime ships only a Postgres driver (pg8000), so neither runs those engines. The Java, Kotlin and Python api-contract report lanes serve rows behind the repository seam, so no engine reaches them. This was accepted as a documented limit rather than new driver support.

Port defects: none found. The C# SQLite/MySQL lanes use a view-only test DbContext because the generated context is Postgres-flavoured; that is test mapping, not a port defect. No CHANGELOG line was added for that reason.

Also in this PR: two stray tmp-* test-output directories that had been committed under codegen-ts/test are removed; one of them broke the workspace typecheck on main.

Local evidence on the final head (all green): TypeScript bun test in server/typescript 10442 pass / 0 fail; C# IntegrationTests 230 pass (SQLite and MySQL report lanes, one shared MySQL container); Java integration-tests 179 pass (MySQL persistence 9); Kotlin integration-tests 216 pass (SQLite 9, MySQL 9). TS SQLite, D1 (Miniflare) and MySQL persistence and api-contract report lanes and both Cube live files passed earlier.

@dmealing
dmealing merged commit 70d0c82 into main Oct 10, 2026
1 check passed
@dmealing
dmealing deleted the fm/mo-1-1-0-report-dialects branch October 10, 2026 23:27
dmealing added a commit that referenced this pull request Oct 11, 2026
…pora (#423)

* test(reporting): cover more FR-044 features in the canonical test model and every port

Adds 12 reports to the canonical fitness model (and a Session entity) and
grows the api-contract report corpus to four entities and nine reports, so
the report corpora exercise vocabulary they did not before: multi-hop and
self-referencing dimensions, day/quarter/year/hour/date time grains,
min/max/avg/count-distinct over more column types, negative and decimal
measure defaults over an empty table, measure filters on the remaining
operators, segment and report filters combining like/in/or/ne, a two-hop
@spine, and a TPH base as @from.

- persistence-conformance: 10 new report query scenarios, all five ports
- api-contract-conformance/report: 6 new scenarios (22 in all), all five ports
- cube-model goldens and the live Cube lane cover the new reports
- committed derived artifacts regenerated with the repo's own tools
- test-harness fixes only: date and uuid operand coercion in the Java and C#
  runners, `like` and uuid in the seam lanes' in-memory report repositories,
  and the SQLite convergence test's known inet residue
- removes a stray generated temp directory committed in #420, which turned
  the ts build + typecheck gate red on main

No product port defect was found, so no CHANGELOG entry.

* test(reporting): run the new report scenarios on SQLite, MySQL and D1 and the MySQL Cube lane

The engine lanes added in #421 now run all 19 report scenarios. The MySQL adopter tables gain
the nodes, sessions, measurements, auths and all_types tables, the api-contract MySQL schema
gains the disputed/category/sku columns, and the MySQL Cube lane covers all 21 served reports.
A naive timestamp keeps its wall clock without a zone suffix on the SQLite and D1 wire form;
two scenarios no longer depend on engine-specific NULL ordering or on a Postgres-only clock
expression.

* test(integration-tests-kotlin): coerce a date filter operand on SQLite and read min/max timestamps there

The Kotlin SQLite lane spells a DATE column's SQL type as TEXT, so the string
operand of a date filter reached Exposed uncoerced; key the coercion on the column
type as well. The report-time-min-max seed now spells its timestamps in the form the
SQLite driver's date_string_format parses (fixture seed only; expect blocks unchanged).
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant