使用Testcontainers与Prisma ORM安装PostGIS时出错的问题求助
看起来你遇到的核心问题是:Testcontainers默认拉取的PostgreSQL镜像并没有预装PostGIS扩展,所以当Prisma执行迁移尝试安装PostGIS时,找不到对应的控制文件postgis.control,进而触发了P3006错误。我来帮你一步步解决这个问题:
问题根源
默认的postgres官方镜像只包含基础的PostgreSQL功能,PostGIS属于第三方地理空间扩展,需要单独安装或者使用预装了该扩展的镜像。而Prisma在执行migrate dev时会创建一个影子数据库来验证迁移逻辑,这个影子库和主库共享同一个PostgreSQL实例,只要实例本身没有PostGIS,不管是主库还是影子库都会报错。
解决步骤
1. 切换到预装PostGIS的镜像
改用官方维护的postgis/postgis镜像,这个镜像已经把PostGIS扩展和依赖都预装好了,不需要你手动安装任何额外组件。修改Testcontainers启动容器的代码,指定对应的镜像版本:
container = await new PostgreSqlContainer('postgis/postgis:15-3.3').start();
注意:镜像版本要保持PostgreSQL和PostGIS的兼容性,比如
15-3.3表示PostgreSQL 15 + PostGIS 3.3,你可以根据自己的需求选择合适的版本组合,比如14-3.2、13-3.1等。
2. 确认容器用户权限(可选)
Testcontainers的PostgreSQL容器默认使用postgres用户,这个用户本身就是超级用户,拥有安装扩展的权限,所以这一步通常不需要额外调整。但如果你后续遇到权限相关的错误,可以在连接URL里确认用户是postgres。
3. 重新运行测试
修改镜像后,重新执行你的E2E测试:
- 容器启动时会拉取指定的PostGIS镜像
- Prisma执行
migrate dev时,不管是主库还是影子数据库,都能找到PostGIS的扩展文件,CREATE EXTENSION语句就能正常执行了
修改后的完整测试代码片段
这里把关键修改标注出来,方便你参考:
describe('test POST /user', () => { let prisma: PrismaClient; let app: INestApplication; let container: StartedPostgreSqlContainer; beforeAll(async () => { // 关键修改:使用预装PostGIS的镜像 container = await new PostgreSqlContainer('postgis/postgis:15-3.3').start(); const urlConnection = `postgresql://${container.getUsername()}:${container.getPassword()}@${container.getHost()}:${container.getPort()}/${container.getDatabase()}?schema=public`; process.env.DATABASE_URL = urlConnection; execSync('npx prisma migrate dev', { env: { ...process.env, DATABASE_URL: urlConnection, }, }); const moduleRef = await Test.createTestingModule({ imports: [UserModule], }).compile(); app = moduleRef.createNestApplication(); prisma = new PrismaClient({ datasources: { db: { url: urlConnection, }, }, }); await app.init(); }); });
额外提示
如果后续你需要自定义PostGIS的配置,比如启用特定的子扩展(如postgis_topology),可以在迁移文件里继续添加对应的CREATE EXTENSION语句,因为镜像已经预装了所有依赖,直接执行即可。
备注:内容来源于stack exchange,提问作者wiliamvj

