Loading the parity record
Fetching the measured run — the pinned upstream oracle, every symbol and every case.
Fetching the measured run — the pinned upstream oracle, every symbol and every case.
Parity / sequelize
Every number on this page was produced by running both implementations over the same cases: the real sequelize package pinned at 6.37.8 answers first, and its answer is the expectation the Go port is held to. Nothing is a hand-written expectation, so a new upstream release re-scores the port on its own.
Not a generic diagram: every node below names an artefact of this harness — the pinned package it installed, the runner files it started, the case files it streamed, and the counts it wrote out.
| Case group | Cases | Match | Mismatch | Group parity |
|---|---|---|---|---|
| query | 30 | 28 | 0 | 93.3% |
| mutation | 11 | 11 | 0 | 100.0% |
| aggregate | 9 | 9 | 0 | 100.0% |
| types | 6 | 5 | 0 | 83.3% |
| associations | 5 | 5 | 0 | 100.0% |
Every exported symbol of the upstream package, and what the port offers for it. The upstream list is derived mechanically, never from a README: cd node && node -e '
const { Model, Op, DataTypes } = require("sequelize");
console.log(Object.getOwnPropertyNames(Model)
.filter(n => { try { return typeof Object.getOwnPropertyDescriptor(Model,n).value==="function" && !n.startsWith("_") } catch { return false } })
.sort().join("\n"));
console.log(Object.keys(Op).sort().join(" "));
console.log(Object.keys(DataTypes).filter(k=>/^[A-Z]/.test(k)).sort().join(" "));'. A symbol with no case is untested, never a match.
| Upstream symbol | Go symbol | Status | Cases | Note |
|---|---|---|---|---|
| findAll | FindAll | match | find-all-* (23), include-* | |
| findOne | FindOne | differs | find-one-match, find-one-miss (differs: typed error vs null) | |
| findByPk | FindByPk | differs | find-by-pk-match, find-by-pk-miss (differs) | |
| findAndCountAll | FindAndCountAll | untested | — | |
| findOrCreate | FindOrCreate | untested | — | |
| findCreateFind | — | missing | — | |
| findOrBuild / build / bulkBuild | — | missing | — (port is map-based, has no in-memory instance) | |
| count | Count | match | count-all, count-where, count-gt | |
| sum | Sum | match | sum-age, sum-age-where, sum-empty | |
| min | Min | match | min-age, max-empty | |
| max | Max | match | max-age, max-empty | |
| aggregate | (internal) | untested | — | |
| create | Create | match | create-basic, create-default-age-null, create-null-name-fails, create-dup-email-fails, type-bool-create, type-decimal-create (differs) | |
| bulkCreate | BulkCreate | match | bulk-create, bulk-create-null-name-fails | |
| update | Update | match | update-basic, update-none | |
| destroy | Destroy | match | destroy-basic, destroy-none, destroy-all | |
| restore | Restore | untested | — (paranoid, exercised in the port's own doc example) | |
| upsert | Upsert | untested | — | |
| increment | Increment | untested | — | |
| decrement | Decrement | untested | — | |
| truncate | Truncate | untested | — | |
| sync | Sync | match | (setup for every case) | |
| drop | Drop | untested | — | |
| describe | InspectTable | extra | — | |
| getTableName | TableName | untested | — | |
| getAttributes | Attributes | untested | — | |
| removeAttribute / refreshAttributes / init | — | missing | port models are immutable after Define | |
| schema / dropSchema / describe | AlterTable/SchemaDiff/CreateTableSQL | extra | — (migration helpers, no upstream 1:1) | |
| scope / unscoped / addScope | Scope/Unscoped/AddScope | untested | — | |
| beforeCreate … afterUpsert (hooks) | AddHook/RemoveHook | untested | — (registry present, order verified in port unit tests) | |
| hasOne | HasOne | untested | — | |
| hasMany | HasMany | match | include-hasmany-one/-empty/-all | |
| belongsTo | BelongsTo | match | include-belongsto-one/-all | |
| belongsToMany | BelongsToMany | untested | — | |
| getAssociations / getAssociationForAlias | Associations / Association | untested | — | |
| eager include | Query.Include | match | see above (hasMany batched, belongsTo joined) | |
| eq | Eq | match | find-all-where-eq, type-bool-where-* | |
| ne | Ne | match | find-all-where-ne | |
| gt gte lt lte | Gt Gte Lt Lte | match | find-all-where-gt/-gte/-lt/-lte | |
| in | In | match | find-all-where-in, -in-shorthand, -in-empty | |
| notIn | NotIn | match | find-all-where-notin, -notin-empty | |
| like | Like | match | find-all-where-like | |
| notLike | NotLike | match | find-all-where-notlike | |
| between | Between | match | find-all-where-between | |
| notBetween | NotBetween | match | find-all-where-notbetween | |
| is | Is / IsNull | match | find-all-where-is-null, find-all-where-null | |
| not | Not / NotNull | match | find-all-where-not-null, find-all-not | |
| and | And | match | find-all-and | |
| or | Or | match | find-all-or | |
| iLike notILike | ILike NotILike | untested | — (renders ILIKE, SQLite rejects it, so not comparable on this backend) | |
| startsWith endsWith substring | — | missing | expressible via Like | |
| regexp notRegexp iRegexp notIRegexp | — | missing | not portable across dialects | |
| col all any values placeholder join | — | missing | query-builder internals, not user Op values | |
| contains contained overlap adjacent strictLeft strictRight noExtendLeft noExtendRight | — | missing | PostgreSQL range/array operators, no SQLite equivalent | |
| match | — | missing | full-text (tsvector), Postgres-only | |
| INTEGER | INTEGER() | match | pervasive (id, age) | |
| STRING | STRING(n) | match | pervasive (name, email) | |
| BOOLEAN | BOOLEAN() | match | type-bool-* | |
| FLOAT | FLOAT() | match | type-float-sum, type-float-max | |
| DECIMAL NUMERIC | DECIMAL(p,s) | differs | type-decimal-create — string vs number on SQLite (see API-DEVIATIONS) | |
| BIGINT | BIGINT() | untested | — (used internally for COUNT) | |
| TEXT | TEXT() | untested | — | |
| DATE | DATE() | untested | — (kept out of cases: JS Date ISO string vs Go time serialisation is a format difference, not a behaviour) | |
| DATEONLY | DATEONLY() | untested | — | |
| JSON | JSON() | untested | — | |
| BLOB | BLOB() | untested | — | |
| UUID | UUID() | untested | — | |
| ENUM | ENUM(...) | untested | — (renders as TEXT on SQLite, see the port's HANDOFF item 9) | |
| DOUBLE REAL NUMBER | (via FLOAT/DECIMAL) | differs | — | |
| SMALLINT MEDIUMINT TINYINT CHAR TIME NOW | — | missing | not ported in v0.1.0 | |
| ARRAY JSONB HSTORE RANGE GEOMETRY GEOGRAPHY CIDR INET MACADDR CITEXT TSVECTOR VIRTUAL UUIDV1 UUIDV4 ABSTRACT | — | missing | PostgreSQL-specific or abstract, out of the v0.1.0 SQLite scope | |
| new Sequelize / .define / .sync / .transaction / .close / .model | New/Open, Define, Sync, Transaction, Close, Model | match | — | |
| .query (raw) | Query | untested | — | |
| .authenticate | Ping | untested | — | |
| where | Where | match | all find-all-where-* | |
| order | Order | match | find-all-order-desc | |
| limit | Limit | match | find-all-limit | |
| offset | Offset | match | find-all-limit-offset | |
| attributes | Attrs | match | find-all-attributes | |
| include | Include | match | include-* | |
| group / having | Group / Having | untested | — | |
| distinct | Distinct | untested | — | |
| paranoid | Paranoid | untested | — (verified in the port's doc example) | |
| transaction | Tx | untested | — (verified in the port's doc example) |
Every case the harness streamed to both runners, with the exact upstream symbol and Go symbol it exercised. A deliberate, documented difference is a deviation and is counted apart from a mismatch.
| Case | Group | Upstream symbol | Go symbol | Status | Note |
|---|---|---|---|---|---|
| count-all | aggregate | Model.count | Model.Count | match | |
| count-where | aggregate | Model.count | Model.Count | match | |
| count-gt | aggregate | Model.count | Model.Count | match | |
| sum-age | aggregate | Model.sum | Model.Sum | match | |
| sum-age-where | aggregate | Model.sum | Model.Sum | match | |
| sum-empty | aggregate | Model.sum | Model.Sum | match | no rows: both return null |
| max-age | aggregate | Model.max | Model.Max | match | |
| min-age | aggregate | Model.min | Model.Min | match | |
| max-empty | aggregate | Model.max | Model.Max | match | no rows: both return null |
| include-hasmany-one | associations | hasMany + include | HasMany + Include | match | |
| include-hasmany-empty | associations | hasMany + include | HasMany + Include | match | Dave has no posts: posts is an empty array on both |
| include-hasmany-all | associations | hasMany + include | HasMany + Include | match | no N+1: one batched child query |
| include-belongsto-one | associations | belongsTo + include | BelongsTo + Include | match | |
| include-belongsto-all | associations | belongsTo + include | BelongsTo + Include | match | join, one row per post |
| create-basic | mutation | Model.create | Model.Create | match | returned row carries the generated id (5) |
| create-default-age-null | mutation | Model.create | Model.Create | match | omitted nullable columns come back null |
| create-null-name-fails | mutation | Model.create | Model.Create | match | NOT NULL on name: both must fail |
| create-dup-email-fails | mutation | Model.create | Model.Create | match | UNIQUE on email: both must fail |
| bulk-create | mutation | Model.bulkCreate | Model.BulkCreate | match | generated ids 5 and 6 |
| bulk-create-null-name-fails | mutation | Model.bulkCreate | Model.BulkCreate | match | one NOT NULL violation fails the whole batch |
| update-basic | mutation | Model.update | Model.Update | match | affected count |
| update-none | mutation | Model.update | Model.Update | match | |
| destroy-basic | mutation | Model.destroy | Model.Destroy | match | |
| destroy-none | mutation | Model.destroy | Model.Destroy | match | |
| destroy-all | mutation | Model.destroy | Model.Destroy | match | empty where deletes every row on both sides |
| find-all-basic | query | Model.findAll | Model.FindAll | match | |
| find-all-where-eq | query | Op.eq | Op.Eq | match | |
| find-all-where-gt | query | Op.gt | Op.Gt | match | |
| find-all-where-gte | query | Op.gte | Op.Gte | match | |
| find-all-where-lt | query | Op.lt | Op.Lt | match | |
| find-all-where-lte | query | Op.lte | Op.Lte | match | |
| find-all-where-ne | query | Op.ne | Op.Ne | match | |
| find-all-where-in | query | Op.in | Op.In | match | |
| find-all-where-in-shorthand | query | Op.in | Op.In | match | |
| find-all-where-in-empty | query | Op.in | Op.In | match | empty IN matches nothing, not invalid SQL |
| find-all-where-notin | query | Op.notIn | Op.NotIn | match | |
| find-all-where-notin-empty | query | Op.notIn | Op.NotIn | match | empty NOT IN matches everything |
| find-all-where-like | query | Op.like | Op.Like | match | |
| find-all-where-notlike | query | Op.notLike | Op.NotLike | match | |
| find-all-where-null | query | Op.is | Op.IsNull | match | |
| find-all-where-is-null | query | Op.is | Op.Is | match | |
| find-all-where-not-null | query | Op.not | Op.NotNull | match | |
| find-all-where-between | query | Op.between | Op.Between | match | |
| find-all-where-notbetween | query | Op.notBetween | Op.NotBetween | match | |
| find-all-and | query | Op.and | And | match | |
| find-all-or | query | Op.or | Or | match | |
| find-all-not | query | Op.not | Not | match | |
| find-all-order-desc | query | order | Query.Order | match | |
| find-all-limit | query | limit | Query.Limit | match | |
| find-all-limit-offset | query | offset | Query.Offset | match | |
| find-all-attributes | query | attributes | Query.Attrs | match | |
| find-one-match | query | Model.findOne | Model.FindOne | match | |
| find-by-pk-match | query | Model.findByPk | Model.FindByPk | match | |
| find-one-miss | query | Model.findOne | Model.FindOne | deviation | upstream returns null on a miss; the port returns a typed *RecordNotFoundError wrapping ErrRecordNotFound so a missing row cannot be mistaken for an empty record |
| find-by-pk-miss | query | Model.findByPk | Model.FindByPk | deviation | upstream returns null on a miss; the port returns a typed *RecordNotFoundError |
| type-bool-where-true | types | BOOLEAN | BOOLEAN | match | boolean stored as 0/1 in SQLite, read back as true/false on both |
| type-bool-where-false | types | BOOLEAN | BOOLEAN | match | |
| type-bool-create | types | BOOLEAN | BOOLEAN | match | created row round-trips bool true and float 1.25 |
| type-float-sum | types | FLOAT | FLOAT | match | 4.5+3.0+5.0+2.5 = 15 |
| type-float-max | types | FLOAT | FLOAT | match | |
| type-decimal-create | types | DECIMAL | DECIMAL | deviation | on SQLite upstream returns DECIMAL as a JS number (9.99); the port returns it as a string ("9.99") for precision, matching how Sequelize itself returns DECIMAL on Postgres/MySQL. Upstream's SQLite behaviour is the outlier across its own dialects. |