使用pg-mem测试时遇RelationNotFound:pg_enum不存在的解决求助
问题
在Jest测试TypeScript项目仓库层时,使用pg-mem创建内存PostgreSQL数据库,ORM采用sequelize-typescript。部分模型包含枚举类型,执行sequelize.sync({ force: true })时报错:RelationNotFound [Error]: relation "pg_enum" does not exist——原因是sequelize会查询PostgreSQL的pg_enum系统目录处理枚举,但pg-mem默认未实现该系统表。
失败的查询语句:
select t.typname enum_name, array_agg(e.enumlabel order by enumsortorder) enum_value from pg_type t join pg_enum e on t.oid = e.enumtypid join pg_catalog.pg_namespace n on n.oid = t.typnamespace where n.nspname = 'public' and t.typname = 'enum_assets_status' group by 1;
注:测试用例复用了sequelize-typescript创建的现有模型声明。
当前配置代码:
import { DataType, newDb } from "pg-mem"; import { Sequelize } from "sequelize-typescript"; import { modelA, modelB } from "../../database/models"; describe("test", () => { const db = newDb(); const sequelize = new Sequelize({ dialect: "postgres", dialectModule: db.adapters.createPg(), models: [ modelA, modelB ] }); sequelize.sync({ force: true }); //////////// Test Cases ////////////// ////////////////////////////////////// ////////////////////////////////////// });
已尝试的无效方法:
手动创建pg_enum表但未生效:
db.registerLanguage("plpgsql", ({ code, args, returns }) => { return () => { db.public.none(`CREATE TABLE pg_enum ( oid OID, enumtypid OID, enumsortorder NUMBER, enumlabel TEXT )`); db.public.none(`INSERT INTO pg_enum ("oid",enumtypid,enumsortorder,enumlabel) VALUES (29188,29184,2.0,'INCOMPLETE');`); }; });
解决方法
方案1:模拟pg_catalog下的pg_enum系统表
pg_enum属于pg_catalog命名空间而非public,需在对应命名空间下创建表并插入枚举数据,同时补充pg_type的对应记录:
describe("test", () => { const db = newDb(); // 在pg_catalog下创建pg_enum表 db.pgCatalog.none(`CREATE TABLE pg_enum ( enumtypid OID, enumsortorder FLOAT, enumlabel TEXT )`); // 为目标枚举类型插入数据(示例为enum_assets_status) const enumOid = 10001; // 可指定任意有效OID db.pgCatalog.none(`INSERT INTO pg_enum (enumtypid, enumsortorder, enumlabel) VALUES ($1, 1.0, 'INCOMPLETE'), ($1, 2.0, 'COMPLETE'), ($1, 3.0, 'ARCHIVED')`, [enumOid]); // 向pg_type插入枚举类型的元数据 db.pgCatalog.none(`INSERT INTO pg_type (oid, typname, typnamespace, typtype) VALUES ($1, 'enum_assets_status', (SELECT oid FROM pg_namespace WHERE nspname='public'), 'e')`, [enumOid]); const sequelize = new Sequelize({ dialect: "postgres", dialectModule: db.adapters.createPg(), models: [modelA, modelB] }); await sequelize.sync({ force: true }); // 测试用例... });
方案2:拦截pg_enum查询并返回模拟结果
通过pg-mem的查询拦截功能,直接返回sequelize期望的枚举数据:
describe("test", () => { const db = newDb(); // 拦截pg_enum相关查询 db.public.interceptQueries((query) => { if (query.text.includes('pg_enum') && query.text.includes('enum_assets_status')) { return { rows: [{ enum_name: 'enum_assets_status', enum_value: ['INCOMPLETE', 'COMPLETE', 'ARCHIVED'] }] }; } return null; // 其他查询正常处理 }); const sequelize = new Sequelize({ dialect: "postgres", dialectModule: db.adapters.createPg(), models: [modelA, modelB] }); await sequelize.sync({ force: true }); // 测试用例... });
方案3:临时替换枚举为字符串类型
若测试无需严格验证枚举逻辑,可在测试中覆盖模型的枚举字段类型:
describe("test", () => { const db = newDb(); const sequelize = new Sequelize({ dialect: "postgres", dialectModule: db.adapters.createPg() }); // 覆盖模型中的枚举字段为STRING类型 modelA.init( { // 保留其他原有字段 status: { type: DataType.STRING, allowNull: false } }, { sequelize, modelName: 'modelA' } ); // 同理处理modelB的枚举字段 modelB.init(/* 覆盖枚举字段 */, { sequelize, modelName: 'modelB' }); await sequelize.sync({ force: true }); // 测试用例... });
内容的提问来源于stack exchange,提问作者Azman Amin
相关产品推荐
相关产品推荐

