Skip to content

feat: add orm-test package — M:N + mega query integration tests against real PostgreSQL#869

Open
pyramation wants to merge 6 commits intomainfrom
devin/1774056352-orm-test
Open

feat: add orm-test package — M:N + mega query integration tests against real PostgreSQL#869
pyramation wants to merge 6 commits intomainfrom
devin/1774056352-orm-test

Conversation

@pyramation
Copy link
Contributor

@pyramation pyramation commented Mar 21, 2026

Summary

Adds a new graphql/orm-test package that tests generated ORM models against a live PostgreSQL database via graphile-test. Both test suites run the full codegen pipeline at test runtime (introspection → inferTablesFromIntrospectiongenerateOrm → compile → dynamic import) and exercise the resulting ORM client.

Codegen infrastructure (__tests__/helpers/)

  • codegen-helper.ts — orchestrates the full codegen pipeline against a live graphile-test schema: runs introspection, infers tables, generates ORM files, compiles TypeScript to JS, and returns the createClient factory. Each test suite passes a unique name parameter to get its own __generated__/<name>/ directory (avoids collisions when Jest runs suites in parallel).
  • graphile-adapter.ts — implements the GraphQLAdapter interface by wrapping graphile-test's query() function, converting responses to QueryResult<T> discriminated unions for the ORM client.

M:N integration tests (orm-m2n.test.ts) — 12 tests

  • Seeds a real Postgres DB with posts, tags, and a post_tags junction table (with @behavior +manyToMany)
  • Builds a PostGraphile schema with ManyToManyOptInPreset to enable M:N connection fields
  • Uses ORM findMany and create methods for queries and junction row creation
  • Uses raw GraphQL for junction row deletion (composite PK not inferred by inferTablesFromIntrospection)
  • Verifies junction rows are created/deleted and M:N connections reflect the changes

Mega query tests (mega-query.test.ts) — 40 tests

Exercises all 7 ConstructivePreset plugins simultaneously via the ORM against a real Postgres DB seeded with NYC location data (mega-seed.sql):

  1. tsvector — full-text search on tsv column, tsvRank score field with concrete range assertions (0.05–0.1)
  2. BM25 (pg_textsearch) — relevance search on body column, bodyBm25Score with ranking-order assertions (Prospect Park > Central Park Cafe > Brooklyn Bridge Park > High Line for "park")
  3. pg_trgm — fuzzy trigram matching on name, nameTrgmSimilarity with threshold assertions (Prospect Park > 0.35 for "park", Met Museum ≈ 0.7 for "museum"), typo handling ("Broklyn" → Brooklyn Bridge Park)
  4. pgvector — cosine distance filter on embedding column (flat VectorNearbyInput: {vector, distance}), cluster tests for restaurant/park/museum directions, search_locations() function
  5. PostGIS — spatial geom column with GeoJSON bbox intersection filter
  6. Connection filter — scalar (isActive, rating), logical (and/or/not), relation (category, tags, tagsExist)
  7. M:Nlocation_amenities junction table with add/remove via serial PK

Crown jewel: a single ORM findMany combining all 7 filter types with multi-signal orderBy: [BODY_BM25_SCORE_ASC, NAME_TRGM_SIMILARITY_DESC].

52 total tests (12 M:N + 40 mega), all passing locally.

Updates since last revision

  • Improved seed data for dramatic score separation — Body texts tuned so BM25 produces clear ranking: "park" → Prospect Park dominates (3× "park" in short body), "museum art" → MoMA edges out Met Museum, "cafe coffee" → sole match on Central Park Cafe. Vector embeddings semantically clustered: dim 0 = restaurant, dim 1 = park, dim 2 = museum.
  • Concrete score-based assertions — Tests now assert BM25 ranking order across locations, trgm similarity thresholds (e.g. Prospect Park > 0.35 for "park"), tsvRank ranges (0.05–0.1), and relative ordering rather than weak "is negative" / "greater than 0" checks.
  • 3 new pgvector cluster tests — Verify [1,0,0] finds restaurants, [0,1,0] finds parks, [0,0,1] finds museums using cosine distance threshold.
  • searchScore composite signal test — Combines tsvector + BM25 + trgm in one query and verifies all three individual signals plus the composite searchScore are populated and in expected ranges.
  • Fixed vectorEmbedding filter shapeVectorNearbyInput is flat {vector: [1,0,0], distance: 0.5}, NOT nested {nearby: {embedding: [...]}}. The ORM maps where → GraphQL where arg (unified search plugin), which correctly applies distance filtering.
  • Rewrote both test files to use the codegen pipeline + ORM models instead of raw GraphQL queries via query().
  • Adjusted BM25 assertions: BM25 does not filter rows — it returns ALL rows with scores (negative = match, 0 = no match). Tests assert on score population, match/non-match counts, and relative ranking order.
  • Junction delete uses raw GraphQL: inferTablesFromIntrospection reports primaryKey: undefined for the post_tags junction table, so the codegen skips delete method generation. Delete tests fall back to raw GraphQL mutations.

Review & Testing Checklist for Human

  • Concrete score thresholds are empirical — BM25 scores (e.g. < -0.5, < -2, < -3), trgm similarities (e.g. > 0.35, ≈ 0.7), and tsvRank ranges (0.05–0.1) were calibrated against postgres-plus:18. Different Postgres versions, extension versions, or text configs could shift these values and cause test failures. Verify the thresholds have enough margin.
  • Plugin-specific filters are not type-safe in the ORMtsvTsv, bm25Body, trgmName, vectorEmbedding, category, tags, tagsExist, and geom.intersects are added by Graphile plugins at runtime, not captured by inferTablesFromIntrospection. The ORM passes them through as untyped properties. Verify this is acceptable or whether the codegen should be enhanced to capture plugin filter fields.
  • CI must use postgres-plus:18 Docker imagemega-seed.sql requires pg_textsearch (BM25 index), postgis, vector, and pg_trgm extensions. If CI uses a different Postgres image, the mega query tests will fail at CREATE EXTENSION IF NOT EXISTS pg_textsearch.
  • Junction table delete falls back to raw GraphQLinferTablesFromIntrospection doesn't detect the composite PK on post_tags, so the ORM skips generating delete methods. Verify whether this is a codegen gap to fix separately.

Recommended test plan: cd graphql/orm-test && pnpm test — all 52 tests should pass against a local Postgres running postgres-plus:18.

Notes

  • SQL fixtures in orm-m2n use fixed UUIDs (e.g., 11111111-..., aaaaaaaa-...) for deterministic assertions.
  • The anonymous/PUBLIC role gets full CRUD grants in both test schemas; appropriate for test isolation, not production.
  • The ivfflat index uses lists = 1 which is only valid for the small (7-row) seed dataset.
  • Lockfile diff is mostly formatting changes (multi-line resolution: collapsed to single-line) from a pnpm version difference, plus minor version bumps (semver 7.7.3→7.7.4, pg-protocol 1.12.0→1.13.0).

Link to Devin session: https://app.devin.ai/sessions/745d2d10b699452091e24131ba5edef2
Requested by: @pyramation

…reSQL

New graphql/orm-test package that exercises ORM M:N patterns against a
live PostGraphile schema via graphile-test:

- SQL fixtures: posts, tags, post_tags junction with @behavior +manyToMany
- ManyToManyOptInPreset enabled for M:N connection fields
- Tests: addTag (createPostTag), removeTag (deletePostTagByPostIdAndTagId,
  deletePostTagByRowId), CRUD, reverse M:N direction, unique constraint
- 10/10 tests pass locally
@devin-ai-integration
Copy link
Contributor

🤖 Devin AI Engineer

I'll be helping with this pull request! Here's what you should know:

✅ I will automatically:

  • Address comments on this PR. Add '(aside)' to your comment to have me ignore it.
  • Look at CI failures and help fix them

Note: I can only respond to comments from users who have write access to this repository.

⚙️ Control Options:

  • Disable automatic comment and CI monitoring

devin-ai-integration bot and others added 2 commits March 21, 2026 02:04
Exercises tsvector, BM25 (pg_textsearch), pg_trgm, pgvector, PostGIS,
relation filters, scalar filters, and M:N junctions in one test suite.

Crown jewel: a single GraphQL request combining all 7 plugin types
with multi-signal ORDER BY (BM25 score + trgm similarity).

36 mega-query tests + 10 M:N tests = 46 total, all passing.
@devin-ai-integration devin-ai-integration bot changed the title feat: add orm-test package — M:N integration tests against real PostgreSQL feat: add orm-test package — M:N + mega query integration tests against real PostgreSQL Mar 21, 2026
devin-ai-integration bot and others added 3 commits March 21, 2026 05:18
- Add codegen-helper.ts: orchestrates introspection → inferTables → generateOrm → compile → load at test runtime
- Add graphile-adapter.ts: wraps graphile-test query() as GraphQLAdapter for ORM client
- Rewrite orm-m2n.test.ts: uses generated ORM for findMany/create, raw GraphQL for delete (junction PK not inferred)
- Rewrite mega-query.test.ts: uses generated ORM for all 7 ConstructivePreset plugins
- Fix vectorEmbedding filter shape: VectorNearbyInput is flat {vector, distance}, not nested {nearby: {embedding}}
- Fix BM25 assertions: BM25 returns all rows with scores (0 for non-matches), doesn't filter
- Each test suite gets isolated __generated__/<name> directory to avoid parallel collisions
- Add @constructive-io/graphql-codegen dependency to orm-test package.json
- 48 tests pass (12 M:N + 36 mega query)
…er seed data

- Richer seed body texts for dramatic BM25 score separation
- Semantic vector embeddings clustered by category (restaurant/park/museum)
- Concrete assertions: BM25 ranking order, trgm similarity thresholds, tsvRank ranges
- Fix vectorEmbedding filter shape: use flat {vector, distance} (not nested nearby/embedding)
- 3 new pgvector cluster tests (restaurant, park, museum directions)
- searchScore composite signal test combining tsvector + BM25 + trgm
- 52 tests total, all passing
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