test: add automated test suite (Vitest) for the SQL import/render pipeline - #8
Conversation
Introduce automated testing to a project that had no test runner, no test script and no CI. Vitest fits the existing Vite setup with near-zero config; a standalone vitest.config.ts wires the @/ alias via vite-tsconfig-paths and runs in the node environment, avoiding the app's Tailwind/React/WASM plugins that pure-logic tests do not need. Cover the cheapest, highest-value surface first: pure helpers with no DOM, WASM or database. getNextSequence and cloneField (field.ts), the charset/collation and SQLite integer-column reordering plus enum naming (render-uttils.ts), and orderTables including the CircularDependencyError cycle path that backs the Foreign Key Cycle Detection feature. The build's tsc step is unaffected (plain tsc no-ops on the root config) and the new files typecheck clean under strict and lint clean.
Cover getImporter().parseSql across MySQL, MariaDB, PostgreSQL, SQLite, Oracle and SQL Server. Each dialect gets a representative CREATE TABLE + foreign key + index fixture, asserting table and column counts, primary key, NOT NULL, UNIQUE and DEFAULT parsing, data-type mapping, and the captured foreign key (direction, cardinality and ON DELETE action). Also parse the bundled PostgreSQL dump as a realistic case and verify malformed input surfaces errors without dropping valid tables or throwing. Data-type fixtures are built from the seed arrays via the same transform the app uses in seedDataTypes, so tests run without booting the WASM SQLite database. The parser is a WASM module whose init() is async; the importer constructor does not await it, so the suite awaits init() once in beforeAll, after which every synchronous parseSql call resolves.
Cover getRenderer().renderDDL across MySQL, MariaDB, PostgreSQL, SQLite, Oracle and SQL Server. A shared DatabaseType fixture (users + posts with a primary key, auto-increment, NOT NULL UNIQUE column with a length, DEFAULT and a posts to users foreign key) is rendered per dialect, asserting the emitted DDL creates both tables and columns and carries the primary key, unique constraint, dialect-specific auto-increment spelling (AUTO_INCREMENT, SERIAL, AUTOINCREMENT, IDENTITY), variable-length text type, DEFAULT value and the foreign key with ON DELETE CASCADE. The fixture is built from the seed data types so field type ids hydrate the way the app hydrates them, and embeds relationship source/target objects because the SQLite renderer orders tables from the raw database before the migration step re-hydrates. Assertions match quote-agnostic patterns rather than exact strings, since identifier quoting and formatting differ by dialect.
Add the Phase 4 import -> render -> import round-trip: for each of the six dialects, parse a CREATE TABLE + foreign key schema into a model, render it back to DDL, parse the rendered DDL again, and assert the two models are equal. Comparison is on a normalized model (table and column names, resolved type names, key/constraint flags, relationships), not raw SQL, since formatting and identifier quoting legitimately differ. A small in-memory adapter assembles the DatabaseType the renderer consumes from the importer output, which the app normally reconstructs via the database. Primary keys are canonicalized to non-nullable in the comparison: SQL Server emits its primary key as a table-level constraint and the importer only forces NOT NULL for inline primary keys, so the flag would otherwise differ on the round trip although the schemas are equivalent. Add a GitHub Actions workflow running npm ci + npm test on pushes to main and on pull requests. Typecheck and lint are intentionally left out for now: the current codebase does not pass a strict project typecheck or a clean lint, so gating on them would fail CI on pre-existing, unrelated issues.
KarimTamani
left a comment
There was a problem hiding this comment.
I reviewed the PR and tested it locally. Everything looks good .
This is a great addition to the project.
|
Thanks @albertoarena for the detailed notes , they're very helpful. Regarding the As for the SQL Server round-trip asymmetry, I agree that this looks like a separate issue rather than something that should block this PR. The normalization in the round-trip comparison is a reasonable way to keep the tests focused on schema equivalence. We can address the importer behavior in a follow-up PR without delaying the introduction of the test suite. Thanks for putting this together! Having a solid automated test suite and CI in place is a big improvement for StackRender. I really appreciate the effort and the well-structured PR. Looking forward to more contributions! |
What
Adds the first automated test suite to StackRender. There is currently no test runner, no
testscript and no CI, so this lands the harness plus a meaningful suite covering the core SQL import/export pipeline across all six dialects.88 tests, all green (
npm test). One commit per phase for easy review.Why
The SQL import and render code is pure, dialect-heavy and the most bug-prone part of the app. It is also the cheapest to test (no DOM, no WASM SQLite, no PowerSync). This suite locks in current behavior and gives a safety net for future dialect work.
Tooling
vitest.config.ts(node environment,vite-tsconfig-pathsfor the@/alias). It does not reuse the app Vite config, so the Tailwind/React/WASM plugins are not pulled into pure-logic tests.test(vitest run) andtest:watch.What is covered
getNextSequence/cloneField, the charset/collation and SQLite integer-column reordering plus enum naming inrender-uttils.ts, andorderTablesincluding theCircularDependencyErrorcycle path behind the Foreign Key Cycle Detection feature.getImporter().parseSqlfor MySQL, MariaDB, PostgreSQL, SQLite, Oracle and SQL Server. RepresentativeCREATE TABLE+ foreign key + index fixtures assert table/column counts, primary key, NOT NULL, UNIQUE, DEFAULT, type mapping and the captured foreign key. Also parses the bundled PostgreSQL dump and checks malformed input surfaces errors without dropping valid tables.getRenderer().renderDDLfor all six dialects from a sharedDatabaseTypefixture, asserting the emitted DDL creates both tables and carries the primary key, unique constraint, dialect-specific auto-increment spelling (AUTO_INCREMENT / SERIAL / AUTOINCREMENT / IDENTITY), length-carrying text type, DEFAULT and the foreign key with ON DELETE CASCADE.Data-type fixtures are built from the seed arrays via the same transform the app uses in
seedDataTypes, so tests run without booting the WASM SQLite database.Notes for reviewers
@guanmingchiu/sqlparser-ts)init()is async and the importer constructor does not await it (correct in the browser, where it resolves before any import). The suite awaitsinit()once inbeforeAll..github/workflows/ci.yml) runsnpm ci+npm teston pushes tomainand on pull requests. Typecheck and lint are intentionally left out for now: the current codebase does not pass a strict project typecheck or a clean lint, so gating on them would fail CI on pre-existing, unrelated issues. Happy to add them in a separate cleanup PR.Verifying
npm install npm test