test: fix cockroach tests

This commit is contained in:
Lucian Mocanu 2025-01-27 23:31:41 +01:00
parent 206af0aa39
commit 3044e7a233
3 changed files with 451 additions and 540 deletions

View File

@ -224,10 +224,7 @@ export class CockroachQueryRunner
)
} else {
this.storeQueries = false
this.transactionDepth -= 1
// This was disabled because it failed tests after update to CRDB 24.2
// https://github.com/typeorm/typeorm/pull/11190
// await this.query("RELEASE SAVEPOINT cockroach_restart")
await this.query("RELEASE SAVEPOINT cockroach_restart")
await this.query("COMMIT")
this.queries = []
this.isTransactionActive = false

View File

@ -39,21 +39,22 @@ describe("repository > find options > locking", () => {
DriverUtils.isSQLiteFamily(connection.driver) ||
connection.driver.options.type === "sap" ||
connection.driver.options.type === "spanner"
)
) {
return
}
if (connection.driver.options.type === "cockroachdb") {
return Promise.all([
connection
.getRepository(PostWithVersion)
.findOne({
where: { id: 1 },
lock: { mode: "pessimistic_write" },
})
.should.be.rejectedWith(
PessimisticLockTransactionRequiredError,
),
])
await connection
.getRepository(PostWithVersion)
.findOne({
where: { id: 1 },
lock: { mode: "pessimistic_write" },
})
.should.be.rejectedWith(
PessimisticLockTransactionRequiredError,
)
return
}
return Promise.all([
@ -91,20 +92,16 @@ describe("repository > find options > locking", () => {
return
if (connection.driver.options.type === "cockroachdb") {
return connection.manager.transaction((entityManager) => {
return Promise.all([
entityManager
.getRepository(PostWithVersion)
.findOne({
where: { id: 1 },
lock: { mode: "pessimistic_write" },
}).should.not.be.rejected,
])
})
return connection.manager.transaction((entityManager) =>
entityManager.getRepository(PostWithVersion).findOne({
where: { id: 1 },
lock: { mode: "pessimistic_write" },
}),
).should.not.be.rejected
}
return connection.manager.transaction((entityManager) => {
return Promise.all([
return connection.manager.transaction((entityManager) =>
Promise.all([
entityManager.getRepository(PostWithVersion).find({
where: { id: 1 },
lock: { mode: "pessimistic_read" },
@ -114,8 +111,8 @@ describe("repository > find options > locking", () => {
where: { id: 1 },
lock: { mode: "pessimistic_write" },
}).should.not.be.rejected,
])
})
]),
)
}),
))
@ -150,19 +147,15 @@ describe("repository > find options > locking", () => {
})
if (DriverUtils.isMySQLFamily(connection.driver)) {
expect(executedSql[0].indexOf("LOCK IN SHARE MODE") !== -1)
.to.be.true
expect(executedSql[0]).to.contain("LOCK IN SHARE MODE")
} else if (connection.driver.options.type === "postgres") {
expect(executedSql[0].indexOf("FOR SHARE") !== -1).to.be
.true
expect(executedSql[0]).to.contain("FOR SHARE")
} else if (connection.driver.options.type === "oracle") {
expect(executedSql[0].indexOf("FOR UPDATE") !== -1).to.be
.true
expect(executedSql[0]).to.contain("FOR UPDATE")
} else if (connection.driver.options.type === "mssql") {
expect(
executedSql[0].indexOf("WITH (HOLDLOCK, ROWLOCK)") !==
-1,
).to.be.true
expect(executedSql[0]).to.contain(
"WITH (HOLDLOCK, ROWLOCK)",
)
}
}),
))
@ -170,7 +163,9 @@ describe("repository > find options > locking", () => {
it("should attach for no key update lock statement on query if locking enabled", () =>
Promise.all(
connections.map(async (connection) => {
if (!(connection.driver.options.type === "postgres")) return
if (connection.driver.options.type !== "postgres") {
return
}
const executedSql: string[] = []
@ -193,8 +188,7 @@ describe("repository > find options > locking", () => {
})
})
expect(executedSql.join(" ").includes("FOR NO KEY UPDATE")).to
.be.true
expect(executedSql.join(" ")).to.contain("FOR NO KEY UPDATE")
}),
))
@ -224,8 +218,7 @@ describe("repository > find options > locking", () => {
})
})
expect(executedSql.join(" ").includes("FOR KEY SHARE")).to.be
.true
expect(executedSql.join(" ")).to.contain("FOR KEY SHARE")
}),
))
@ -241,8 +234,9 @@ describe("repository > find options > locking", () => {
"8.0.0",
))
)
)
) {
return
}
const executedSql: string[] = []
@ -268,8 +262,9 @@ describe("repository > find options > locking", () => {
})
})
expect(executedSql.join(" ").includes("FOR SHARE SKIP LOCKED"))
.to.be.true
expect(executedSql.join(" ")).to.contain(
"FOR SHARE SKIP LOCKED",
)
}),
))
@ -285,8 +280,9 @@ describe("repository > find options > locking", () => {
"8.0.0",
))
)
)
) {
return
}
const executedSql: string[] = []
@ -312,8 +308,7 @@ describe("repository > find options > locking", () => {
})
})
expect(executedSql.join(" ").includes("FOR UPDATE NOWAIT")).to
.be.true
expect(executedSql.join(" ")).to.contain("FOR UPDATE NOWAIT")
}),
))
@ -324,8 +319,9 @@ describe("repository > find options > locking", () => {
DriverUtils.isSQLiteFamily(connection.driver) ||
connection.driver.options.type === "sap" ||
connection.driver.options.type === "spanner"
)
) {
return
}
const executedSql: string[] = []
@ -351,13 +347,9 @@ describe("repository > find options > locking", () => {
connection.driver.options.type === "postgres" ||
connection.driver.options.type === "oracle"
) {
expect(executedSql[0].indexOf("FOR UPDATE") !== -1).to.be
.true
expect(executedSql[0]).to.contain("FOR UPDATE")
} else if (connection.driver.options.type === "mssql") {
expect(
executedSql[0].indexOf("WITH (UPDLOCK, ROWLOCK)") !==
-1,
).to.be.true
expect(executedSql[0]).to.contain("WITH (UPDLOCK, ROWLOCK)")
}
}),
))
@ -388,15 +380,14 @@ describe("repository > find options > locking", () => {
})
})
expect(executedSql[0].indexOf("WITH (NOLOCK)") !== -1).to.be
.true
expect(executedSql[0]).to.contain("WITH (NOLOCK)")
}),
))
it("should throw error if optimistic lock used with `find` method", () =>
Promise.all(
connections.map(async (connection) => {
return connection
await connection
.getRepository(PostWithVersion)
.find({ lock: { mode: "optimistic", version: 1 } })
.should.be.rejectedWith(OptimisticLockCanNotBeUsedError)
@ -406,7 +397,7 @@ describe("repository > find options > locking", () => {
it("should not throw error if optimistic lock used with `findOne` method", () =>
Promise.all(
connections.map(async (connection) => {
return connection.getRepository(PostWithVersion).findOne({
await connection.getRepository(PostWithVersion).findOne({
where: { id: 1 },
lock: { mode: "optimistic", version: 1 },
}).should.not.be.rejected
@ -420,7 +411,7 @@ describe("repository > find options > locking", () => {
post.title = "New post"
await connection.manager.save(post)
return connection
await connection
.getRepository(PostWithoutVersionAndUpdateDate)
.findOne({
where: { id: 1 },
@ -437,7 +428,7 @@ describe("repository > find options > locking", () => {
post.title = "New post"
await connection.manager.save(post)
return connection
await connection
.getRepository(PostWithVersion)
.findOne({
where: { id: 1 },
@ -454,7 +445,7 @@ describe("repository > find options > locking", () => {
post.title = "New post"
await connection.manager.save(post)
return connection.getRepository(PostWithVersion).findOne({
await connection.getRepository(PostWithVersion).findOne({
where: { id: 1 },
lock: { mode: "optimistic", version: 1 },
}).should.not.be.rejected
@ -465,13 +456,15 @@ describe("repository > find options > locking", () => {
Promise.all(
connections.map(async (connection) => {
// skipped because inserted milliseconds are not always equal to what we say it to insert, unskip when needed
if (connection.driver.options.type === "mssql") return
if (connection.driver.options.type === "mssql") {
return
}
const post = new PostWithUpdateDate()
post.title = "New post"
await connection.manager.save(post)
return connection
await connection
.getRepository(PostWithUpdateDate)
.findOne({
where: { id: 1 },
@ -488,13 +481,15 @@ describe("repository > find options > locking", () => {
Promise.all(
connections.map(async (connection) => {
// skipped because inserted milliseconds are not always equal to what we say it to insert, unskip when needed
if (connection.driver.options.type === "mssql") return
if (connection.driver.options.type === "mssql") {
return
}
const post = new PostWithUpdateDate()
post.title = "New post"
await connection.manager.save(post)
return connection.getRepository(PostWithUpdateDate).findOne({
await connection.getRepository(PostWithUpdateDate).findOne({
where: { id: 1 },
lock: { mode: "optimistic", version: post.updateDate },
}).should.not.be.rejected
@ -505,13 +500,15 @@ describe("repository > find options > locking", () => {
Promise.all(
connections.map(async (connection) => {
// skipped because inserted milliseconds are not always equal to what we say it to insert, unskip when needed
if (connection.driver.options.type === "mssql") return
if (connection.driver.options.type === "mssql") {
return
}
const post = new PostWithVersionAndUpdatedDate()
post.title = "New post"
await connection.manager.save(post)
return Promise.all([
await Promise.all([
connection
.getRepository(PostWithVersionAndUpdatedDate)
.findOne({
@ -538,29 +535,26 @@ describe("repository > find options > locking", () => {
DriverUtils.isSQLiteFamily(connection.driver) ||
connection.driver.options.type === "sap"
)
return connection.manager.transaction((entityManager) => {
return Promise.all([
entityManager
.getRepository(PostWithVersion)
.findOne({
where: { id: 1 },
lock: { mode: "pessimistic_read" },
})
.should.be.rejectedWith(
LockNotSupportedOnGivenDriverError,
),
entityManager
.getRepository(PostWithVersion)
.findOne({
where: { id: 1 },
lock: { mode: "pessimistic_write" },
})
.should.be.rejectedWith(
LockNotSupportedOnGivenDriverError,
),
])
})
await connection.manager
.transaction((entityManager) =>
Promise.all([
entityManager
.getRepository(PostWithVersion)
.findOne({
where: { id: 1 },
lock: { mode: "pessimistic_read" },
}),
entityManager
.getRepository(PostWithVersion)
.findOne({
where: { id: 1 },
lock: { mode: "pessimistic_write" },
}),
]),
)
.should.be.rejectedWith(
LockNotSupportedOnGivenDriverError,
)
return
}),
@ -570,26 +564,22 @@ describe("repository > find options > locking", () => {
Promise.all(
connections.map(async (connection) => {
if (
!(
connection.driver.options.type === "postgres" ||
connection.driver.options.type === "cockroachdb"
)
)
connection.driver.options.type !== "postgres" &&
connection.driver.options.type !== "cockroachdb"
) {
return
}
return connection.manager.transaction((entityManager) => {
return Promise.all([
entityManager
.getRepository(Post)
.findOne({
where: { id: 1 },
lock: { mode: "pessimistic_write", tables: [] },
})
.should.be.rejectedWith(
"lockTables cannot be an empty array",
),
])
})
await connection.manager
.transaction((entityManager) =>
entityManager.getRepository(Post).findOne({
where: { id: 1 },
lock: { mode: "pessimistic_write", tables: [] },
}),
)
.should.be.rejectedWith(
"lockTables cannot be an empty array",
)
}),
))
@ -597,39 +587,40 @@ describe("repository > find options > locking", () => {
Promise.all(
connections.map(async (connection) => {
if (
!(
connection.driver.options.type === "postgres" ||
connection.driver.options.type === "cockroachdb"
)
)
connection.driver.options.type !== "postgres" &&
connection.driver.options.type !== "cockroachdb"
) {
return
}
return connection.manager.transaction((entityManager) => {
return Promise.all([
entityManager
.getRepository(Post)
.findOne({
where: { id: 1 },
relations: { author: true },
lock: {
mode: "pessimistic_write",
tables: ["img"],
},
})
.should.be.rejectedWith(
'"img" is not part of this query',
),
])
})
await connection.manager
.transaction((entityManager) =>
entityManager.getRepository(Post).findOne({
where: { id: 1 },
relations: { author: true },
lock: {
mode: "pessimistic_write",
tables: ["img"],
},
}),
)
.should.be.rejectedWith('"img" is not part of this query')
}),
))
it("should allow on a left join", () =>
Promise.all(
connections.map(async (connection) => {
if (connection.driver.options.type === "cockroachdb") {
return connection.manager.transaction((entityManager) => {
return Promise.all([
if (
connection.driver.options.type !== "postgres" &&
connection.driver.options.type !== "cockroachdb"
) {
return
}
await connection.manager
.transaction((entityManager) =>
Promise.all([
entityManager.getRepository(Post).findOne({
where: { id: 1 },
relations: { author: true },
@ -638,56 +629,28 @@ describe("repository > find options > locking", () => {
tables: ["post"],
},
}),
entityManager
.getRepository(Post)
.findOne({
where: { id: 1 },
relations: { author: true },
lock: { mode: "pessimistic_write" },
})
.should.be.rejectedWith(
"FOR UPDATE cannot be applied to the nullable side of an outer join",
),
])
})
}
if (connection.driver.options.type === "postgres") {
return connection.manager.transaction((entityManager) => {
return Promise.all([
entityManager.getRepository(Post).findOne({
where: { id: 1 },
relations: { author: true },
lock: {
mode: "pessimistic_write",
tables: ["post"],
},
lock: { mode: "pessimistic_write" },
}),
entityManager
.getRepository(Post)
.findOne({
where: { id: 1 },
relations: { author: true },
lock: { mode: "pessimistic_write" },
})
.should.be.rejectedWith(
"FOR UPDATE cannot be applied to the nullable side of an outer join",
),
])
})
}
return
]),
)
.should.be.rejectedWith(
"FOR UPDATE cannot be applied to the nullable side of an outer join",
)
}),
))
it("should allow using lockTables on all types of locking", () =>
Promise.all(
connections.map(async (connection) => {
if (!(connection.driver.options.type === "postgres")) return
if (connection.driver.options.type !== "postgres") {
return
}
return connection.manager.transaction((entityManager) => {
return Promise.all([
await connection.manager.transaction((entityManager) =>
Promise.all([
entityManager.getRepository(Post).findOne({
where: { id: 1 },
relations: { author: true },
@ -736,8 +699,8 @@ describe("repository > find options > locking", () => {
tables: ["post"],
},
}),
])
})
]),
)
}),
))
@ -745,31 +708,28 @@ describe("repository > find options > locking", () => {
Promise.all(
connections.map(async (connection) => {
if (
!(
connection.driver.options.type === "postgres" ||
connection.driver.options.type === "cockroachdb"
)
)
connection.driver.options.type !== "postgres" &&
connection.driver.options.type !== "cockroachdb"
) {
return
}
return connection.manager.transaction((entityManager) => {
return Promise.all([
entityManager.getRepository(Post).findOne({
where: { id: 1 },
join: {
alias: "post",
innerJoinAndSelect: {
categorys: "post.categories",
images: "categorys.images",
},
await connection.manager.transaction((entityManager) =>
entityManager.getRepository(Post).findOne({
where: { id: 1 },
join: {
alias: "post",
innerJoinAndSelect: {
categorys: "post.categories",
images: "categorys.images",
},
lock: {
mode: "pessimistic_write",
tables: ["image"],
},
}),
])
})
},
lock: {
mode: "pessimistic_write",
tables: ["image"],
},
}),
)
}),
))
})