mirror of
https://github.com/typeorm/typeorm.git
synced 2025-12-08 21:26:23 +00:00
fix(redis): version detection logic (#11815)
Co-authored-by: AdolfodelSel <adolfo.selllano@gmail.com> Co-authored-by: Giorgio Boa <35845425+gioboa@users.noreply.github.com> Co-authored-by: Mike Guida <mike@mguida.com>
This commit is contained in:
parent
38715bbd41
commit
6f486e5a67
28
src/cache/RedisQueryResultCache.ts
vendored
28
src/cache/RedisQueryResultCache.ts
vendored
@ -300,26 +300,22 @@ export class RedisQueryResultCache implements QueryResultCache {
|
||||
}
|
||||
|
||||
/**
|
||||
* Detects the Redis version based on the connected client's API characteristics
|
||||
* without creating test keys in the database
|
||||
* Detects the Redis package version by reading the installed package.json
|
||||
* and sets the appropriate API version (3 for callback-based, 5 for Promise-based).
|
||||
*/
|
||||
private detectRedisVersion(): void {
|
||||
if (this.clientType !== "redis") return
|
||||
|
||||
try {
|
||||
// Detect version by examining the client's method signatures
|
||||
// This avoids creating test keys in the database
|
||||
const setMethod = this.client.set
|
||||
if (setMethod && setMethod.length <= 3) {
|
||||
// Redis 5+ set method accepts fewer parameters (key, value, options)
|
||||
this.redisMajorVersion = 5
|
||||
} else {
|
||||
// Redis 3/4 set method requires more parameters (key, value, flag, duration, callback)
|
||||
this.redisMajorVersion = 3
|
||||
}
|
||||
} catch {
|
||||
// Default to Redis 3/4 for maximum compatibility
|
||||
const version = PlatformTools.readPackageVersion("redis")
|
||||
const major = parseInt(version.split(".")[0], 10)
|
||||
if (isNaN(major)) {
|
||||
throw new TypeORMError(`Invalid Redis version format: ${version}`)
|
||||
}
|
||||
if (major <= 4) {
|
||||
// Redis 3/4 uses callback-based API
|
||||
this.redisMajorVersion = 3
|
||||
} else {
|
||||
// Redis 5+ uses Promise-based API
|
||||
this.redisMajorVersion = 5
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -27,6 +27,20 @@ export class PlatformTools {
|
||||
return global
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads the version string from package.json of the given package.
|
||||
* This operation is only supported in node.
|
||||
*/
|
||||
static readPackageVersion(name: string): string {
|
||||
try {
|
||||
return require(`${name}/package.json`).version
|
||||
} catch (err) {
|
||||
throw new TypeError(
|
||||
`Failed to read package.json for "${name}": ${err.message}`,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads ("require"-s) given file or package.
|
||||
* This operation only supports on node platform
|
||||
|
||||
108
test/unit/cache/redis-query-result-cache.ts
vendored
Normal file
108
test/unit/cache/redis-query-result-cache.ts
vendored
Normal file
@ -0,0 +1,108 @@
|
||||
/* eslint-disable @typescript-eslint/no-unused-expressions */
|
||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||
import { expect } from "chai"
|
||||
import * as sinon from "sinon"
|
||||
import { RedisQueryResultCache } from "../../../src/cache/RedisQueryResultCache"
|
||||
import { PlatformTools } from "../../../src/platform/PlatformTools"
|
||||
import { DataSource } from "../../../src/data-source/DataSource"
|
||||
|
||||
describe("RedisQueryResultCache", () => {
|
||||
describe("detectRedisVersion", () => {
|
||||
let sandbox: sinon.SinonSandbox
|
||||
let mockDataSource: sinon.SinonStubbedInstance<DataSource>
|
||||
let readPackageVersionStub: sinon.SinonStub
|
||||
|
||||
beforeEach(() => {
|
||||
sandbox = sinon.createSandbox()
|
||||
|
||||
// Create a mock DataSource
|
||||
mockDataSource = {
|
||||
options: {},
|
||||
logger: {
|
||||
log: sandbox.stub(),
|
||||
},
|
||||
} as any
|
||||
|
||||
// Stub PlatformTools.readPackageVersion
|
||||
readPackageVersionStub = sandbox.stub(
|
||||
PlatformTools,
|
||||
"readPackageVersion",
|
||||
)
|
||||
|
||||
// Stub PlatformTools.load to prevent actual redis loading
|
||||
sandbox.stub(PlatformTools, "load").returns({})
|
||||
})
|
||||
|
||||
afterEach(() => {
|
||||
sandbox.restore()
|
||||
})
|
||||
|
||||
it("should detect Redis v3.x and set redisMajorVersion to 3", () => {
|
||||
readPackageVersionStub.returns("3.1.2")
|
||||
|
||||
const cache = new RedisQueryResultCache(
|
||||
mockDataSource as any,
|
||||
"redis",
|
||||
)
|
||||
|
||||
// Access the private method via any cast for testing
|
||||
;(cache as any).detectRedisVersion()
|
||||
|
||||
expect((cache as any).redisMajorVersion).to.equal(3)
|
||||
expect(readPackageVersionStub.calledOnceWith("redis")).to.be.true
|
||||
})
|
||||
|
||||
it("should detect Redis v4.x and set redisMajorVersion to 3 (callback-based)", () => {
|
||||
readPackageVersionStub.returns("4.6.13")
|
||||
|
||||
const cache = new RedisQueryResultCache(
|
||||
mockDataSource as any,
|
||||
"redis",
|
||||
)
|
||||
|
||||
;(cache as any).detectRedisVersion()
|
||||
|
||||
expect((cache as any).redisMajorVersion).to.equal(3)
|
||||
})
|
||||
|
||||
it("should detect Redis v5.x and set redisMajorVersion to 5 (Promise-based)", () => {
|
||||
readPackageVersionStub.returns("5.0.0")
|
||||
|
||||
const cache = new RedisQueryResultCache(
|
||||
mockDataSource as any,
|
||||
"redis",
|
||||
)
|
||||
|
||||
;(cache as any).detectRedisVersion()
|
||||
|
||||
expect((cache as any).redisMajorVersion).to.equal(5)
|
||||
expect(readPackageVersionStub.calledOnceWith("redis")).to.be.true
|
||||
})
|
||||
|
||||
it("should detect Redis v6.x and set redisMajorVersion to 5 (Promise-based)", () => {
|
||||
readPackageVersionStub.returns("6.2.3")
|
||||
|
||||
const cache = new RedisQueryResultCache(
|
||||
mockDataSource as any,
|
||||
"redis",
|
||||
)
|
||||
|
||||
;(cache as any).detectRedisVersion()
|
||||
|
||||
expect((cache as any).redisMajorVersion).to.equal(5)
|
||||
})
|
||||
|
||||
it("should detect Redis v7.x and set redisMajorVersion to 5 (Promise-based)", () => {
|
||||
readPackageVersionStub.returns("7.0.0")
|
||||
|
||||
const cache = new RedisQueryResultCache(
|
||||
mockDataSource as any,
|
||||
"redis",
|
||||
)
|
||||
|
||||
;(cache as any).detectRedisVersion()
|
||||
|
||||
expect((cache as any).redisMajorVersion).to.equal(5)
|
||||
})
|
||||
})
|
||||
})
|
||||
62
test/unit/platform/platform-tools.ts
Normal file
62
test/unit/platform/platform-tools.ts
Normal file
@ -0,0 +1,62 @@
|
||||
import { expect } from "chai"
|
||||
import * as sinon from "sinon"
|
||||
import { PlatformTools } from "../../../src/platform/PlatformTools"
|
||||
|
||||
describe("PlatformTools", () => {
|
||||
describe("readPackageVersion", () => {
|
||||
let sandbox: sinon.SinonSandbox
|
||||
|
||||
beforeEach(() => {
|
||||
sandbox = sinon.createSandbox()
|
||||
})
|
||||
|
||||
afterEach(() => {
|
||||
sandbox.restore()
|
||||
})
|
||||
|
||||
it("should successfully read version from an installed package", () => {
|
||||
// Test with a package we know exists in node_modules (chai is in devDependencies)
|
||||
const version = PlatformTools.readPackageVersion("chai")
|
||||
|
||||
expect(version).to.be.a("string")
|
||||
expect(version).to.match(/^\d+\.\d+\.\d+/)
|
||||
})
|
||||
|
||||
it("should return correct version format with major.minor.patch", () => {
|
||||
const version = PlatformTools.readPackageVersion("chai")
|
||||
|
||||
expect(version).to.be.a("string")
|
||||
expect(version.split(".").length).to.be.at.least(3)
|
||||
})
|
||||
|
||||
it("should throw TypeError when package does not exist", () => {
|
||||
expect(() => {
|
||||
PlatformTools.readPackageVersion(
|
||||
"this-package-definitely-does-not-exist-12345",
|
||||
)
|
||||
}).to.throw(
|
||||
TypeError,
|
||||
/Failed to read package\.json for "this-package-definitely-does-not-exist-12345"/,
|
||||
)
|
||||
})
|
||||
|
||||
it("should handle scoped package names", () => {
|
||||
const version = PlatformTools.readPackageVersion("@types/node")
|
||||
|
||||
expect(version).to.be.a("string")
|
||||
expect(version).to.match(/^\d+/)
|
||||
})
|
||||
|
||||
it("should throw error for empty package name", () => {
|
||||
expect(() => {
|
||||
PlatformTools.readPackageVersion("")
|
||||
}).to.throw(TypeError, /Failed to read package\.json/)
|
||||
})
|
||||
|
||||
it("should throw error for whitespace-only package name", () => {
|
||||
expect(() => {
|
||||
PlatformTools.readPackageVersion(" ")
|
||||
}).to.throw(TypeError, /Failed to read package\.json/)
|
||||
})
|
||||
})
|
||||
})
|
||||
Loading…
x
Reference in New Issue
Block a user