From 842dc54a34af058e3b05da1a7bd8fb589def5308 Mon Sep 17 00:00:00 2001 From: Andrew Sidhu Date: Mon, 29 Sep 2025 14:04:28 -0400 Subject: [PATCH 1/8] Refactor Sequelize model definitions and setup --- src/sscce-sequelize-7.ts | 256 +++++++++++++++++++++++++++++++++++---- 1 file changed, 232 insertions(+), 24 deletions(-) diff --git a/src/sscce-sequelize-7.ts b/src/sscce-sequelize-7.ts index 603cb219c..8a38bb0a3 100644 --- a/src/sscce-sequelize-7.ts +++ b/src/sscce-sequelize-7.ts @@ -1,43 +1,251 @@ -import { CreationOptional, DataTypes, InferAttributes, InferCreationAttributes, Model } from '@sequelize/core'; -import { Attribute, NotNull } from '@sequelize/core/decorators-legacy'; -import { createSequelize7Instance } from '../dev/create-sequelize-instance'; -import { expect } from 'chai'; -import sinon from 'sinon'; +import { + CreationOptional, + DataTypes, + InferAttributes, + InferCreationAttributes, + Model, +} from "@sequelize/core"; +import { createSequelize7Instance } from "../dev/create-sequelize-instance"; +import { expect } from "chai"; // if your issue is dialect specific, remove the dialects you don't need to test on. -export const testingOnDialects = new Set(['mssql', 'sqlite', 'mysql', 'mariadb', 'postgres', 'postgres-native']); +export const testingOnDialects = new Set(["postgres"]); -// You can delete this file if you don't want your SSCCE to be tested against Sequelize 7 +class Location extends Model< + InferAttributes, + InferCreationAttributes +> { + declare id: CreationOptional; + declare name: string; + + declare customers?: Customer[]; + declare systems?: System[]; +} + +class Customer extends Model< + InferAttributes, + InferCreationAttributes +> { + declare id: CreationOptional; + declare name: string; + + declare locations?: Location[]; +} + +class System extends Model< + InferAttributes, + InferCreationAttributes +> { + declare id: CreationOptional; + declare name: string; + declare locationId: number; + + declare location?: Location; + declare fuelDeliveries?: FuelDelivery[]; +} + +class FuelDelivery extends Model< + InferAttributes, + InferCreationAttributes +> { + declare id: CreationOptional; + declare product: string; + declare systemId: number; + + declare system?: System; +} + +class LocationCustomer extends Model< + InferAttributes, + InferCreationAttributes +> { + declare locationId: number; + declare customerId: number; +} // Your SSCCE goes inside this function. export async function run() { // This function should be used instead of `new Sequelize()`. // It applies the config for your SSCCE to work on CI. const sequelize = createSequelize7Instance({ - logQueryParameters: true, - benchmark: true, + minifyAliases: true, + dialect: "postgres", define: { - // For less clutter in the SSCCE + // Keep model definitions lean so the regression focus stays on include resolution. timestamps: false, }, }); - class Foo extends Model, InferCreationAttributes> { - declare id: CreationOptional; + Location.init( + { + id: { + type: DataTypes.INTEGER, + autoIncrement: true, + primaryKey: true, + }, + name: { + type: DataTypes.STRING, + allowNull: false, + }, + }, + { + sequelize, + tableName: "locations", + } + ); - @Attribute(DataTypes.TEXT) - @NotNull - declare name: string; - } + Customer.init( + { + id: { + type: DataTypes.INTEGER, + autoIncrement: true, + primaryKey: true, + }, + name: { + type: DataTypes.STRING, + allowNull: false, + }, + }, + { + sequelize, + tableName: "customers", + } + ); - sequelize.addModels([Foo]); + System.init( + { + id: { + type: DataTypes.INTEGER, + autoIncrement: true, + primaryKey: true, + }, + name: { + type: DataTypes.STRING, + allowNull: false, + }, + locationId: { + type: DataTypes.INTEGER, + allowNull: false, + }, + }, + { + sequelize, + tableName: "systems", + } + ); + + FuelDelivery.init( + { + id: { + type: DataTypes.INTEGER, + autoIncrement: true, + primaryKey: true, + }, + product: { + type: DataTypes.STRING, + allowNull: false, + }, + systemId: { + type: DataTypes.INTEGER, + allowNull: false, + }, + }, + { + sequelize, + tableName: "fuel_deliveries", + } + ); + + LocationCustomer.init( + { + locationId: { + type: DataTypes.INTEGER, + allowNull: false, + primaryKey: true, + }, + customerId: { + type: DataTypes.INTEGER, + allowNull: false, + primaryKey: true, + }, + }, + { + sequelize, + tableName: "location_customers", + } + ); + + FuelDelivery.belongsTo(System, { as: "system", foreignKey: "systemId" }); + System.hasMany(FuelDelivery, { + as: "fuelDeliveries", + foreignKey: "systemId", + }); - // You can use sinon and chai assertions directly in your SSCCE. - const spy = sinon.spy(); - sequelize.afterBulkSync(() => spy()); - await sequelize.sync({ force: true }); - expect(spy).to.have.been.called; + System.belongsTo(Location, { as: "location", foreignKey: "locationId" }); + Location.hasMany(System, { as: "systems", foreignKey: "locationId" }); - console.log(await Foo.create({ name: 'TS foo' })); - expect(await Foo.count()).to.equal(1); + Location.belongsToMany(Customer, { + as: "customers", + through: LocationCustomer, + foreignKey: "locationId", + otherKey: "customerId", + }); + Customer.belongsToMany(Location, { + as: "locations", + through: LocationCustomer, + foreignKey: "customerId", + otherKey: "locationId", + }); + + try { + await sequelize.sync({ force: true }); + + const customer = await Customer.create({ name: "Propane Co-op" }); + const location = await Location.create({ name: "Rural Depot" }); + await LocationCustomer.create({ + customerId: customer.id, + locationId: location.id, + }); + + const system = await System.create({ + name: "Delivery System Alpha", + locationId: location.id, + }); + const delivery = await FuelDelivery.create({ + product: "Propane", + systemId: system.id, + }); + + const result = await FuelDelivery.findByPk(delivery.id, { + logging: console.log, + include: [ + { + association: "system", + required: true, + include: [ + { + association: "location", + required: true, + include: [ + { + association: "customers", + required: true, + }, + ], + }, + ], + }, + ], + }); + + expect(result).to.not.be.null; + expect(result!.system).to.not.be.undefined; + expect(result!.system!.location).to.not.be.undefined; + const customers = result!.system!.location!.customers; + expect(customers).to.not.be.undefined; + expect(customers).to.have.length(1); + expect(customers![0].id).to.equal(customer.id); + } finally { + await sequelize.close(); + } } From 77839334175271d6d2183a22a628d3ce8af0c659 Mon Sep 17 00:00:00 2001 From: Andrew Sidhu Date: Mon, 29 Sep 2025 14:05:26 -0400 Subject: [PATCH 2/8] Update createSequelize7Instance to use AbstractDialect --- dev/create-sequelize-instance.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dev/create-sequelize-instance.ts b/dev/create-sequelize-instance.ts index 1ec6d6438..a34e13977 100644 --- a/dev/create-sequelize-instance.ts +++ b/dev/create-sequelize-instance.ts @@ -7,7 +7,7 @@ export function createSequelize6Instance(options?: Sequelize6Options): Sequelize return new Sequelize6(wrapOptions(options)); } -export function createSequelize7Instance(options?: Sequelize7Options): Sequelize7 { +export function createSequelize7Instance(options?: Sequelize7Options): Sequelize7 { // not compatible with node 10 const { Sequelize: Sequelize7Constructor } = require('@sequelize/core'); // @ts-expect-error -- wrapOptions expect sequelize 6. From 49d6c492eeedaaabd77975f44b66e710272a5233 Mon Sep 17 00:00:00 2001 From: Andrew Sidhu Date: Mon, 29 Sep 2025 14:16:47 -0400 Subject: [PATCH 3/8] revert typing --- dev/create-sequelize-instance.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dev/create-sequelize-instance.ts b/dev/create-sequelize-instance.ts index a34e13977..1ec6d6438 100644 --- a/dev/create-sequelize-instance.ts +++ b/dev/create-sequelize-instance.ts @@ -7,7 +7,7 @@ export function createSequelize6Instance(options?: Sequelize6Options): Sequelize return new Sequelize6(wrapOptions(options)); } -export function createSequelize7Instance(options?: Sequelize7Options): Sequelize7 { +export function createSequelize7Instance(options?: Sequelize7Options): Sequelize7 { // not compatible with node 10 const { Sequelize: Sequelize7Constructor } = require('@sequelize/core'); // @ts-expect-error -- wrapOptions expect sequelize 6. From b2eb3a8b1bb1c4e518b6f99bc06b1f212297f1a4 Mon Sep 17 00:00:00 2001 From: Andrew Sidhu Date: Wed, 8 Oct 2025 20:05:26 -0400 Subject: [PATCH 4/8] add v7 wrapper for sscce --- dev/create-sequelize-instance.ts | 58 +++++++++++++++++++++++++++----- 1 file changed, 49 insertions(+), 9 deletions(-) diff --git a/dev/create-sequelize-instance.ts b/dev/create-sequelize-instance.ts index 1ec6d6438..039e98179 100644 --- a/dev/create-sequelize-instance.ts +++ b/dev/create-sequelize-instance.ts @@ -1,15 +1,55 @@ -import type { Options as Sequelize6Options } from 'sequelize'; -import { Sequelize as Sequelize6 } from 'sequelize'; -import type { Options as Sequelize7Options, Sequelize as Sequelize7 } from '@sequelize/core'; -import { wrapOptions } from './wrap-options'; +import type { Options as Sequelize6Options } from "sequelize"; +import { Sequelize as Sequelize6 } from "sequelize"; +import type { + Options as Sequelize7Options, + Sequelize as Sequelize7, +} from "@sequelize/core"; +import { wrapOptions } from "./wrap-options"; +import { CiDbConfigs } from "./ci-db-configs"; +import { log } from "./logging"; -export function createSequelize6Instance(options?: Sequelize6Options): Sequelize6 { +export function createSequelize6Instance( + options?: Sequelize6Options +): Sequelize6 { return new Sequelize6(wrapOptions(options)); } -export function createSequelize7Instance(options?: Sequelize7Options): Sequelize7 { +function wrapOptionsV7(options: Partial> = {}) { + if (!process.env.DIALECT) { + throw new Error("Dialect is not defined! Aborting."); + } + + const isPostgresNative = process.env.DIALECT === "postgres-native"; + const dialect = isPostgresNative ? "postgres" : process.env.DIALECT; + + // Get the CI config for this dialect + const config = CiDbConfigs[dialect as keyof typeof CiDbConfigs] as any; + + // Transform Sequelize v6-style options to v7-style + const transformedConfig = { ...config }; + if (transformedConfig.username) { + transformedConfig.user = transformedConfig.username; + delete transformedConfig.username; + } + + const finalOptions = { + dialect: dialect, + logging: log, + ...transformedConfig, + ...options, + }; + + if (isPostgresNative) { + finalOptions.native = true; + } + + return finalOptions; +} + +export function createSequelize7Instance( + options?: Sequelize7Options +): Sequelize7 { // not compatible with node 10 - const { Sequelize: Sequelize7Constructor } = require('@sequelize/core'); - // @ts-expect-error -- wrapOptions expect sequelize 6. - return new Sequelize7Constructor(wrapOptions(options)); + const { Sequelize: Sequelize7Constructor } = require("@sequelize/core"); + return new Sequelize7Constructor(wrapOptionsV7(options)); } From a454fa9a3595658c3e73cfb988cad7f17586aca7 Mon Sep 17 00:00:00 2001 From: Andrew Sidhu Date: Wed, 8 Oct 2025 20:05:47 -0400 Subject: [PATCH 5/8] update typing --- src/sscce-sequelize-7.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/sscce-sequelize-7.ts b/src/sscce-sequelize-7.ts index 8a38bb0a3..3b8e4f24e 100644 --- a/src/sscce-sequelize-7.ts +++ b/src/sscce-sequelize-7.ts @@ -4,9 +4,11 @@ import { InferAttributes, InferCreationAttributes, Model, + Options, } from "@sequelize/core"; import { createSequelize7Instance } from "../dev/create-sequelize-instance"; import { expect } from "chai"; +import { PostgresDialect } from "@sequelize/postgres"; // if your issue is dialect specific, remove the dialects you don't need to test on. export const testingOnDialects = new Set(["postgres"]); @@ -74,7 +76,7 @@ export async function run() { // Keep model definitions lean so the regression focus stays on include resolution. timestamps: false, }, - }); + } as Options); Location.init( { From cc1ebb9b13448559e94282e721d34c912c8aa8a4 Mon Sep 17 00:00:00 2001 From: Andrew Sidhu Date: Wed, 8 Oct 2025 20:09:48 -0400 Subject: [PATCH 6/8] Do it in the correct place. --- dev/create-sequelize-instance.ts | 36 +------------------- dev/wrap-options.ts | 57 +++++++++++++++++++++++++++----- 2 files changed, 50 insertions(+), 43 deletions(-) diff --git a/dev/create-sequelize-instance.ts b/dev/create-sequelize-instance.ts index 039e98179..72153aba2 100644 --- a/dev/create-sequelize-instance.ts +++ b/dev/create-sequelize-instance.ts @@ -4,9 +4,7 @@ import type { Options as Sequelize7Options, Sequelize as Sequelize7, } from "@sequelize/core"; -import { wrapOptions } from "./wrap-options"; -import { CiDbConfigs } from "./ci-db-configs"; -import { log } from "./logging"; +import { wrapOptions, wrapOptionsV7 } from "./wrap-options"; export function createSequelize6Instance( options?: Sequelize6Options @@ -14,38 +12,6 @@ export function createSequelize6Instance( return new Sequelize6(wrapOptions(options)); } -function wrapOptionsV7(options: Partial> = {}) { - if (!process.env.DIALECT) { - throw new Error("Dialect is not defined! Aborting."); - } - - const isPostgresNative = process.env.DIALECT === "postgres-native"; - const dialect = isPostgresNative ? "postgres" : process.env.DIALECT; - - // Get the CI config for this dialect - const config = CiDbConfigs[dialect as keyof typeof CiDbConfigs] as any; - - // Transform Sequelize v6-style options to v7-style - const transformedConfig = { ...config }; - if (transformedConfig.username) { - transformedConfig.user = transformedConfig.username; - delete transformedConfig.username; - } - - const finalOptions = { - dialect: dialect, - logging: log, - ...transformedConfig, - ...options, - }; - - if (isPostgresNative) { - finalOptions.native = true; - } - - return finalOptions; -} - export function createSequelize7Instance( options?: Sequelize7Options ): Sequelize7 { diff --git a/dev/wrap-options.ts b/dev/wrap-options.ts index e0f950acb..6e958fdb6 100644 --- a/dev/wrap-options.ts +++ b/dev/wrap-options.ts @@ -1,15 +1,24 @@ -import defaults from 'lodash/defaults.js'; -import { CiDbConfigs } from './ci-db-configs'; -import { log } from './logging'; -import type { Dialect, Options } from 'sequelize'; +import defaults from "lodash/defaults.js"; +import { CiDbConfigs } from "./ci-db-configs"; +import { log } from "./logging"; +import type { + Dialect as Sequelize6Dialect, + Options as Sequelize6Options, +} from "sequelize"; +import type { + Options as Sequelize7Options, + Sequelize as Sequelize7, +} from "@sequelize/core"; -export function wrapOptions(options: Options = {}) { +export function wrapOptions(options: Sequelize6Options = {}) { if (!process.env.DIALECT) { - throw new Error('Dialect is not defined! Aborting.'); + throw new Error("Dialect is not defined! Aborting."); } - const isPostgresNative = process.env.DIALECT === 'postgres-native'; - const dialect = (isPostgresNative ? 'postgres' : process.env.DIALECT) as Dialect; + const isPostgresNative = process.env.DIALECT === "postgres-native"; + const dialect = ( + isPostgresNative ? "postgres" : process.env.DIALECT + ) as Sequelize6Dialect; // this fails in the CI due to mismatch between Sequelize 6 & 7. Should be resolved once we drop Sequelize 6. // eslint-disable-next-line @typescript-eslint/prefer-ts-expect-error @@ -31,3 +40,35 @@ export function wrapOptions(options: Options = {}) { return options; } + +export function wrapOptionsV7(options: Partial> = {}) { + if (!process.env.DIALECT) { + throw new Error("Dialect is not defined! Aborting."); + } + + const isPostgresNative = process.env.DIALECT === "postgres-native"; + const dialect = isPostgresNative ? "postgres" : process.env.DIALECT; + + // Get the CI config for this dialect + const config = CiDbConfigs[dialect as keyof typeof CiDbConfigs] as any; + + // Transform Sequelize v6-style options to v7-style + const transformedConfig = { ...config }; + if (transformedConfig.username) { + transformedConfig.user = transformedConfig.username; + delete transformedConfig.username; + } + + const finalOptions = { + dialect: dialect, + logging: log, + ...transformedConfig, + ...options, + }; + + if (isPostgresNative) { + finalOptions.native = true; + } + + return finalOptions; +} From 6acc9ec2df7a3d232113cd8408dd66e5a04f3d13 Mon Sep 17 00:00:00 2001 From: Andrew Sidhu Date: Wed, 8 Oct 2025 23:25:15 -0400 Subject: [PATCH 7/8] Add more tests --- src/sscce-sequelize-7.ts | 294 +++++++++++++++++++++------------------ 1 file changed, 162 insertions(+), 132 deletions(-) diff --git a/src/sscce-sequelize-7.ts b/src/sscce-sequelize-7.ts index 3b8e4f24e..8b2540083 100644 --- a/src/sscce-sequelize-7.ts +++ b/src/sscce-sequelize-7.ts @@ -4,11 +4,24 @@ import { InferAttributes, InferCreationAttributes, Model, + NonAttribute, Options, } from "@sequelize/core"; import { createSequelize7Instance } from "../dev/create-sequelize-instance"; import { expect } from "chai"; import { PostgresDialect } from "@sequelize/postgres"; +import { + Attribute, + AutoIncrement, + BelongsToMany, + ColumnName, + Default, + DeletedAt, + HasMany, + Index, + NotNull, + PrimaryKey, +} from "@sequelize/core/decorators-legacy"; // if your issue is dialect specific, remove the dialects you don't need to test on. export const testingOnDialects = new Set(["postgres"]); @@ -17,32 +30,70 @@ class Location extends Model< InferAttributes, InferCreationAttributes > { + @Attribute(DataTypes.INTEGER) + @AutoIncrement + @PrimaryKey declare id: CreationOptional; - declare name: string; - declare customers?: Customer[]; - declare systems?: System[]; + @Attribute(DataTypes.TEXT) + declare name?: string; + + @HasMany(() => System, { + foreignKey: "locationId", + inverse: "location", + }) + declare systems?: NonAttribute; + + @BelongsToMany(() => Customer, { + through: { + model: () => CustomerLocation, + // Only "active" relationships + scope: { endAt: null }, + }, + foreignKey: "locationId", + otherKey: "customerId", + inverse: { + as: "locations", + }, + }) + declare customers?: NonAttribute; } class Customer extends Model< InferAttributes, InferCreationAttributes > { + @Attribute(DataTypes.INTEGER) + @AutoIncrement + @PrimaryKey declare id: CreationOptional; - declare name: string; - declare locations?: Location[]; + @Attribute(DataTypes.TEXT) + declare name?: CreationOptional; + + declare locations?: NonAttribute; + declare CustomerLocation?: NonAttribute; } class System extends Model< InferAttributes, InferCreationAttributes > { + @Attribute(DataTypes.INTEGER) + @AutoIncrement + @PrimaryKey declare id: CreationOptional; + + @Attribute(DataTypes.TEXT) declare name: string; - declare locationId: number; + declare locationId: number; declare location?: Location; + + @HasMany(() => FuelDelivery, { + foreignKey: "systemId", + inverse: "system", + }) declare fuelDeliveries?: FuelDelivery[]; } @@ -50,19 +101,44 @@ class FuelDelivery extends Model< InferAttributes, InferCreationAttributes > { + @Attribute(DataTypes.INTEGER) + @AutoIncrement + @PrimaryKey declare id: CreationOptional; + + @Attribute(DataTypes.TEXT) declare product: string; + + @Attribute(DataTypes.INTEGER) declare systemId: number; declare system?: System; } -class LocationCustomer extends Model< - InferAttributes, - InferCreationAttributes +class CustomerLocation extends Model< + InferAttributes, + InferCreationAttributes > { - declare locationId: number; + @Attribute(DataTypes.INTEGER) + @PrimaryKey + @NotNull declare customerId: number; + + @Attribute(DataTypes.INTEGER) + @PrimaryKey + @NotNull + declare locationId: number; + + @Attribute(DataTypes.TEXT) + declare relationType: string; + + @Attribute(DataTypes.DATE(6)) + @Index() + declare endAt?: Date | null; + + declare customer?: Customer; + + declare location?: Location; } // Your SSCCE goes inside this function. @@ -76,137 +152,18 @@ export async function run() { // Keep model definitions lean so the regression focus stays on include resolution. timestamps: false, }, + models: [Customer, Location, System, FuelDelivery, CustomerLocation], } as Options); - Location.init( - { - id: { - type: DataTypes.INTEGER, - autoIncrement: true, - primaryKey: true, - }, - name: { - type: DataTypes.STRING, - allowNull: false, - }, - }, - { - sequelize, - tableName: "locations", - } - ); - - Customer.init( - { - id: { - type: DataTypes.INTEGER, - autoIncrement: true, - primaryKey: true, - }, - name: { - type: DataTypes.STRING, - allowNull: false, - }, - }, - { - sequelize, - tableName: "customers", - } - ); - - System.init( - { - id: { - type: DataTypes.INTEGER, - autoIncrement: true, - primaryKey: true, - }, - name: { - type: DataTypes.STRING, - allowNull: false, - }, - locationId: { - type: DataTypes.INTEGER, - allowNull: false, - }, - }, - { - sequelize, - tableName: "systems", - } - ); - - FuelDelivery.init( - { - id: { - type: DataTypes.INTEGER, - autoIncrement: true, - primaryKey: true, - }, - product: { - type: DataTypes.STRING, - allowNull: false, - }, - systemId: { - type: DataTypes.INTEGER, - allowNull: false, - }, - }, - { - sequelize, - tableName: "fuel_deliveries", - } - ); - - LocationCustomer.init( - { - locationId: { - type: DataTypes.INTEGER, - allowNull: false, - primaryKey: true, - }, - customerId: { - type: DataTypes.INTEGER, - allowNull: false, - primaryKey: true, - }, - }, - { - sequelize, - tableName: "location_customers", - } - ); - - FuelDelivery.belongsTo(System, { as: "system", foreignKey: "systemId" }); - System.hasMany(FuelDelivery, { - as: "fuelDeliveries", - foreignKey: "systemId", - }); - - System.belongsTo(Location, { as: "location", foreignKey: "locationId" }); - Location.hasMany(System, { as: "systems", foreignKey: "locationId" }); - - Location.belongsToMany(Customer, { - as: "customers", - through: LocationCustomer, - foreignKey: "locationId", - otherKey: "customerId", - }); - Customer.belongsToMany(Location, { - as: "locations", - through: LocationCustomer, - foreignKey: "customerId", - otherKey: "locationId", - }); - try { await sequelize.sync({ force: true }); const customer = await Customer.create({ name: "Propane Co-op" }); const location = await Location.create({ name: "Rural Depot" }); - await LocationCustomer.create({ + await CustomerLocation.create({ customerId: customer.id, locationId: location.id, + relationType: "primary", }); const system = await System.create({ @@ -247,6 +204,79 @@ export async function run() { expect(customers).to.not.be.undefined; expect(customers).to.have.length(1); expect(customers![0].id).to.equal(customer.id); + + // Test Two + + const result2 = await Customer.findOne({ + include: [ + { + association: "locations", + + include: [ + { + association: "customers", + }, + ], + }, + ], + }); + + expect(result2).to.not.be.null; + expect(result2!.locations).to.not.be.undefined; + const locations = result2!.locations!; + expect(locations).to.have.length.greaterThan(0); + expect(locations[0].customers).to.not.be.undefined; + expect(locations[0].customers).to.have.length.greaterThan(0); + + /// Test Three + + const result3 = await FuelDelivery.findByPk(delivery.id, { + include: [ + { + association: "system", + include: [ + { + association: "location", + include: [ + { + association: "customers", + required: true, + include: [ + { + association: "locations", + required: false, + include: [ + { + association: "systems", + where: { name: "Delivery System Alpha" }, + required: false, + include: [], + }, + ], + }, + ], + }, + ], + }, + ], + }, + ], + }); + + expect(result3).to.not.be.null; + expect(result3!.system).to.not.be.undefined; + expect(result3!.system!.location).to.not.be.undefined; + const customers3 = result3!.system!.location!.customers; + expect(customers3).to.not.be.undefined; + expect(customers3).to.have.length(1); + expect(customers3![0].id).to.equal(customer.id); + expect(customers3![0].locations).to.not.be.undefined; + expect(customers3![0].locations).to.have.length.greaterThan(0); + expect(customers3![0].locations![0].systems).to.not.be.undefined; + expect(customers3![0].locations![0].systems).to.have.length.greaterThan(0); + expect(customers3![0].locations![0].systems![0].name).to.equal( + "Delivery System Alpha" + ); } finally { await sequelize.close(); } From 81e1594617c01145fe643f669a4e92e49a7fd43f Mon Sep 17 00:00:00 2001 From: Andrew Sidhu Date: Tue, 14 Oct 2025 19:54:42 -0400 Subject: [PATCH 8/8] more complex tests --- src/sscce-sequelize-7.ts | 134 +++++++++++++++++++++++++++++++++++---- 1 file changed, 120 insertions(+), 14 deletions(-) diff --git a/src/sscce-sequelize-7.ts b/src/sscce-sequelize-7.ts index 8b2540083..80f9fc057 100644 --- a/src/sscce-sequelize-7.ts +++ b/src/sscce-sequelize-7.ts @@ -14,9 +14,6 @@ import { Attribute, AutoIncrement, BelongsToMany, - ColumnName, - Default, - DeletedAt, HasMany, Index, NotNull, @@ -87,6 +84,7 @@ class System extends Model< @Attribute(DataTypes.TEXT) declare name: string; + @Attribute(DataTypes.INTEGER) declare locationId: number; declare location?: Location; @@ -158,18 +156,27 @@ export async function run() { try { await sequelize.sync({ force: true }); - const customer = await Customer.create({ name: "Propane Co-op" }); - const location = await Location.create({ name: "Rural Depot" }); + const customer = await Customer.create({ name: "Propane Delivery Co" }); + const customer2 = await Customer.create({ name: "Kozy Operations Inc" }); + const location = await Location.create({ name: "Fuel Depot" }); await CustomerLocation.create({ customerId: customer.id, locationId: location.id, relationType: "primary", }); + await CustomerLocation.create({ + customerId: customer2.id, + locationId: location.id, + relationType: "secondary", + }); + const system = await System.create({ - name: "Delivery System Alpha", + name: "Kozy Operations Inc", locationId: location.id, }); + + console.log(system); const delivery = await FuelDelivery.create({ product: "Propane", systemId: system.id, @@ -202,8 +209,9 @@ export async function run() { expect(result!.system!.location).to.not.be.undefined; const customers = result!.system!.location!.customers; expect(customers).to.not.be.undefined; - expect(customers).to.have.length(1); + expect(customers).to.have.length(2); expect(customers![0].id).to.equal(customer.id); + expect(customers![1].id).to.equal(customer2.id); // Test Two @@ -228,8 +236,6 @@ export async function run() { expect(locations[0].customers).to.not.be.undefined; expect(locations[0].customers).to.have.length.greaterThan(0); - /// Test Three - const result3 = await FuelDelivery.findByPk(delivery.id, { include: [ { @@ -248,9 +254,34 @@ export async function run() { include: [ { association: "systems", - where: { name: "Delivery System Alpha" }, - required: false, - include: [], + where: { name: "Kozy Operations Inc" }, + required: true, + include: [ + { + association: "location", + include: [ + { + association: "customers", + required: false, + include: [ + { + association: "locations", + required: false, + include: [ + { + association: "systems", + where: { + name: "Kozy Operations Inc", + }, + required: false, + }, + ], + }, + ], + }, + ], + }, + ], }, ], }, @@ -266,16 +297,91 @@ export async function run() { expect(result3).to.not.be.null; expect(result3!.system).to.not.be.undefined; expect(result3!.system!.location).to.not.be.undefined; + const customers3 = result3!.system!.location!.customers; expect(customers3).to.not.be.undefined; - expect(customers3).to.have.length(1); + expect(customers3).to.have.length(2); expect(customers3![0].id).to.equal(customer.id); expect(customers3![0].locations).to.not.be.undefined; expect(customers3![0].locations).to.have.length.greaterThan(0); expect(customers3![0].locations![0].systems).to.not.be.undefined; expect(customers3![0].locations![0].systems).to.have.length.greaterThan(0); expect(customers3![0].locations![0].systems![0].name).to.equal( - "Delivery System Alpha" + "Kozy Operations Inc" + ); + + const result4 = await FuelDelivery.findByPk(delivery.id, { + include: [ + { + association: "system", + include: [ + { + association: "location", + include: [ + { + association: "customers", + required: true, + include: [ + { + association: "locations", + required: false, + include: [ + { + association: "systems", + where: { name: "Kozy Operations Inc" }, + required: true, + include: [ + { + association: "location", + include: [ + { + association: "customers", + required: false, + include: [ + { + association: "locations", + required: true, + include: [ + { + association: "systems", + where: { + name: "Kozy Operations Inc", + }, + required: false, + }, + ], + }, + ], + }, + ], + }, + ], + }, + ], + }, + ], + }, + ], + }, + ], + }, + ], + }); + + expect(result4).to.not.be.null; + expect(result4!.system).to.not.be.undefined; + expect(result4!.system!.location).to.not.be.undefined; + + const customers4 = result4!.system!.location!.customers; + expect(customers4).to.not.be.undefined; + expect(customers4).to.have.length(2); + expect(customers4![0].id).to.equal(customer.id); + expect(customers4![0].locations).to.not.be.undefined; + expect(customers4![0].locations).to.have.length.greaterThan(0); + expect(customers4![0].locations![0].systems).to.not.be.undefined; + expect(customers4![0].locations![0].systems).to.have.length.greaterThan(0); + expect(customers4![0].locations![0].systems![0].name).to.equal( + "Kozy Operations Inc" ); } finally { await sequelize.close();