Repository navigation
test smoke CRUD, Python/Go smoke and migrate guard, table override in Python/Go (supersedes #119) - #150
Merged
Conversation
…#107) Parity with #98/#106 (TypeScript). The Python and Go generators derived the table name as snakeCase(entity.name) and never read the per-entity `table` override, so an entity named `Order` with "table": "order_record" generated the SQL reserved word `order`. - Both generators now emit `tableName = entity.table || snakeCase(name)`. - Python template uses it for `__tablename__`, the self-referencing ManyToMany ForeignKey, join-table names, and index names, so every reference to this entity's own table follows the override consistently. - Go template uses it in `TableName()`. Without an override tableName === snakeCase(name), so output is unchanged for the common case. Added override + fallback tests to both suites. Full suite: 318 passed, 0 failed. fixes #107
…table PR #119 moves __tablename__ to the per-entity table override, but child models still wrote ForeignKey("<parent name lowercased>.id"). With an override on the parent, or any multi-word parent (UserProfile -> user_profile, FK said userprofile), the FK named a table that does not exist. Pass every entity's table name into the model template and use it.
apso test smoke only called GET /health and each list route. It now builds a payload per entity from the .apsorc (required fields only: not nullable, no default), creates entities parents first so required ManyToOne foreign keys (<camelCase(to_name or parent)>Id) can be set from the parent's id, reads each row back by id, and deletes children before parents. Entities it cannot fill (unsupported required types, cycles of required relationships, children of skipped entities) get a skip step with the reason instead of failing. The planner lives in src/lib/verify/crud-plan.ts with unit tests for the payload values and the ordering.
apso test smoke for Python and Go projects starts the generated server (uvicorn from .venv, or a go build ./cmd binary so stopping it cannot orphan the server) and runs the same HTTP checks against DATABASE_URL from the environment or .env. It never creates a database or tables; with no DATABASE_URL, or nothing listening at its host and port, the check is skipped with a message and reports "Smoke test skipped.". Routes follow each generator: /api/<plural> (Python) and /api/<kebab-plural> (Go). apso migrate generate and verify fail fast for Python and Go and name the native tool (Alembic, goose). The baseline, run and drift flow needs a scratch database; TypeScript has PGlite, the others do not. scaffold-smoke.sh: the go leg runs apso test smoke when SMOKE_DATABASE_URL points at an empty Postgres (opt in, off in CI).
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Four commits for three changes. Supersedes #119 (its commit is included with authorship kept, plus one fix on top).
1. Per-entity
tableoverride in Python and Go (supersedes #119)__tablename__to the override, but child models still wroteForeignKey("<parent name lowercased>.id"). With an override on the parent (Order->order_record) the FK named a table that no longer exists. The same expression was already wrong for any multi-word parent before fix(cli): honor per-entity table override in Python and Go generators (#107) #119 (UserProfilehas tableuser_profile, FK saiduserprofile). The model template now gets every entity's real table name and uses it for ManyToOne FKs and the ManyToMany inverse FK. Output is byte-identical for single-word entities without an override.TableName().2.
apso test smokecreate/read/deletesrc/lib/verify/crud-plan.tsbuilds one row per entity from the .apsorc (required = not nullable, no default, not a user primary key; text/string/varchar -> "smoke" cut tolength, int types -> 1, decimal/numeric/float -> 1.5, boolean -> true, date/timestamp(tz)/datetime ->2024-01-01T00:00:00.000Z, enum -> first value, uuid -> random uuid, json/jsonb -> {},is_email-> an email). Required ManyToOne parents come from the parsed relationship map (bothManyToOneandOneToManydeclarations,to_namerespected viagetRelationshipIdField), and entities are ordered parents first. Cycles, unsupported required types, and children of skipped entities becomeskip <Entity>steps with the reason, not failures.The smoke run is now: GET list per entity, then POST (expect 200/201 and an
id), GET/<route>/<id>(200), and finally DELETE in reverse order (200/204). OneStepper call, same as before. Auth-guarded projects still only check /health.3. Python and Go
apso test smokefor python/go starts the generated server (.venv/bin/python -m uvicorn app.main:app, or ago build ./cmdbinary so killing it can't orphan the server) and runs the same HTTP checks againstDATABASE_URL(environment first, then.env). It does not create tables or invent a database: tables come from the project's ownDATABASE_SYNC/ migrations. With noDATABASE_URL, or nothing listening at its host:port, the check is skipped with a message ("Smoke test skipped.")./<Plural>, Python/api/<plural lowercased>, Go/api/<kebab-plural>.apso migrate generate/apso migrate verifyfail fast for Python and Go with "supports TypeScript services only for now" plus the native tool to use (Alembic; goose). The baseline/run/drift flow needs a scratch database to build the deployed schema on; TypeScript has PGlite, Python and Go do not, and running a baseline against the user's database is destructive. Not straightforward, so not wired.scripts/scaffold-smoke.shgo leg: opt-inSMOKE_DATABASE_URL=<empty Postgres>runsapso test smokeafter the compile. Nothing added to the python leg: a fresh Python scaffold cannot pass the smoke test today (template bugs below). CI is unchanged (no Postgres service there, so the go step does not run in CI).Verified (all on this branch, macOS, Node 22, Python 3.12, Go 1.25, local Postgres 17)
npm run lint0 errors (562 warnings, unchanged),npx tsc --noEmit -p .clean, full jest suite 390 passed / 0 failed. The intermediate commit for part 2 also typechecks and passes the verify tests on its own.ForeignKey("order_record.id")andForeignKey("user_profile.id"). In a real generated Python project the child model emittedForeignKey("order_record.id"), and the Python smoke run below created rows inorder_record.bin/run init --name smoke-app --language typescript --skip-platform, then an .apsorc (v2) with Customer (text with length 5, is_email, enum, boolean, integer with default, nullable text), Order (table: order_record, decimal, timestamptz, nullable json, required ManyToOne Customer) and LineItem (integer, date, required ManyToOne Order).bin/run generate,npm install,npm run build,bin/run test smoke --skip-build: all 14 steps ok (3 lists, POST/GET for Customer, Order, LineItem in that order, DELETE LineItem, Order, Customer).GET /LineItemsandPOST /LineItemsFAIL with 404, the command exits 1, and the other entities still pass.test/lib/verify/crud-plan.test.ts): sample values per type, required-only payload, parents-first order via both ManyToOne and OneToMany declarations,to_nameFK naming, nullable parents ignored, cycles / unsupported types / children of skipped entities skipped with reasons, per-language routes,.envparsing.scripts/scaffold-smoke.shrun locally: typescript leg PASS (smoke now shows POST/GET/DELETE /SmokeWidgets, then migrate generate + verify), go leg withSMOKE_DATABASE_URLset to an empty local database PASS (build, start, list, POST/GET/DELETE /api/smoke-widgets), python leg (PYTHON=python3.12) PASS unchanged.DATABASE_SYNC=true: all steps ok, including the required ManyToOne chain on/api/ordersand/api/line-items.DATABASE_SYNC=true, after the two manual template workarounds listed below: same schema without relationships, all 13 steps ok. With relationships,POST /api/ordersfails with 500 (a real generator bug, below), which is the smoke test doing its job.DATABASE_URLat a closed port, and with no.env: oneskippedstep with the reason, "Smoke test skipped.", exit 0.apso migrate verify/generatein Python and Go projects: exit 2 with the TypeScript-only message.Found while verifying (not fixed here)
Each of these makes a fresh Python or Go service fail at runtime. None is caught today because the python/go legs only compile.
app/main.pynever includesautogen_router, so no entity route is served andcreate_allsees no models. Fresh scaffold smoke: every entity route 404s.pyproject.tomlneedssqlalchemy[asyncio]: SQLAlchemy 2.1 no longer pulls in greenlet, so the app crashes on import.package.jsonpins@electric-sql/pglite-socket@^0.3.0, which does not exist (latest is 0.2.11), sonpm run db/apso devwith PGlite cannot install. Same in the Go template.create_type=False, soDATABASE_SYNC=truefails withtype "customer_tier_enum" does not exist.customer: Optional[Any]and pydantic cannot serialize the SQLAlchemy object (PydanticSerializationError).enums.TierEnum, but enums.go definesCustomerTierEnum.map[string]interface{}and*map[string]interface{}..apsorcships"version": 1, so top-levelrelationshipsare ignored until a user changes it to 2.To get the green Python run I added
app.include_router(autogen_router)to main.py, installed greenlet, and (for the run with relationships and an enum) created the enum type by hand. To get the green Go run I dropped the enum and json fields.Not done