Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -209,6 +209,8 @@ web-gate-integration: ## Run Layer 2 web-only integration tests (skipped until o
web-gate-postgres: build-web ## Run fail-loud Postgres-backed web integration tests (requires VARLENS_PG_URL)
@if [ -z "$$VARLENS_PG_URL" ]; then echo "VARLENS_PG_URL is required for web-gate-postgres. This is intentionally opt-in and never part of default desktop CI."; exit 2; fi
npx vitest run --project web-gate tests/web-gate/integration
VARLENS_RUN_POSTGRES_E2E=1 npx vitest run --project main \
tests/main/storage/postgres-cases-query-repository.e2e.test.ts

web-gate-parity: web-data-verify ## Run Layer 3 parity scenarios (opt-in; boots Electron, switches native ABI)
@echo "=== web-gate-parity (opt-in; switches native module to Electron ABI) ==="
Expand Down
11 changes: 10 additions & 1 deletion src/main/storage/postgres/PostgresCasesQueryRepository.ts
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,16 @@ export class PostgresCasesQueryRepository {
LEFT JOIN ${schemaName}."case_cohort_links" ccl ON ccl.case_id = c.id
LEFT JOIN ${schemaName}."cohort_groups" cg ON cg.id = ccl.cohort_id
${whereSql}
GROUP BY c.id, cm.affected_status, cm.sex
GROUP BY
c.id,
c.name,
c.file_path,
c.file_size,
c.variant_count,
c.created_at,
c.genome_build,
cm.affected_status,
cm.sex
ORDER BY ${orderColumn} ${orderDirection}
LIMIT $${values.length + 1}
OFFSET $${values.length + 2}
Expand Down
82 changes: 82 additions & 0 deletions tests/main/storage/postgres-cases-query-repository.e2e.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
import { randomBytes } from 'node:crypto'

import { Client, Pool } from 'pg'
import { afterAll, beforeAll, describe, expect, it } from 'vitest'

import { PostgresCasesQueryRepository } from '../../../src/main/storage/postgres/PostgresCasesQueryRepository'
import { POSTGRES_MIGRATIONS } from '../../../src/main/storage/postgres/migrations/definitions'
import { PostgresMigrationRunner } from '../../../src/main/storage/postgres/migrations/PostgresMigrationRunner'

const RUN = process.env.VARLENS_RUN_POSTGRES_E2E === '1'
const PG_URL =
process.env.VARLENS_PG_URL ??
'postgres://varlens:varlens_dev_password@127.0.0.1:55432/varlens_dev'

describe.skipIf(!RUN)('PostgresCasesQueryRepository — migrated cases view', () => {
const schema = `varlens_test_cases_query_${Date.now()}_${randomBytes(4).toString('hex')}`
let pool: Pool
let probe: Client

beforeAll(async () => {
pool = new Pool({ connectionString: PG_URL, max: 2 })
probe = new Client({ connectionString: PG_URL })
await probe.connect()
await new PostgresMigrationRunner(pool, schema, POSTGRES_MIGRATIONS).migrate()
}, 60_000)

afterAll(async () => {
if (probe) await probe.end()
if (pool) await pool.end()

const cleaner = new Client({ connectionString: PG_URL })
await cleaner.connect()
await cleaner.query(`DROP SCHEMA IF EXISTS "${schema}" CASCADE`)
await cleaner.end()
}, 60_000)

it('queries a ready case with metadata and cohorts through the view', async () => {
const now = Date.now()
const caseResult = await probe.query<{ id: number }>(
`INSERT INTO "${schema}"."cases"
(name, file_path, file_size, variant_count, created_at, genome_build)
VALUES ('view-backed-case', '/tmp/view-backed-case.json', 42, 1, $1, 'GRCh38')
RETURNING id`,
[now]
)
const caseId = Number(caseResult.rows[0].id)
await probe.query(
`INSERT INTO "${schema}"."case_metadata" (case_id, affected_status, sex)
VALUES ($1, 'affected', 'female')`,
[caseId]
)
const cohortResult = await probe.query<{ id: number }>(
`INSERT INTO "${schema}"."cohort_groups" (name, created_at)
VALUES ('view-backed-cohort', $1)
RETURNING id`,
[now]
)
await probe.query(
`INSERT INTO "${schema}"."case_cohort_links" (case_id, cohort_id)
VALUES ($1, $2)`,
[caseId, cohortResult.rows[0].id]
)

const result = await new PostgresCasesQueryRepository(pool, schema).queryCases({
limit: 25,
offset: 0
})

expect(result).toEqual({
data: [
expect.objectContaining({
id: caseId,
name: 'view-backed-case',
affected_status: 'affected',
sex: 'female',
cohort_names: ['view-backed-cohort']
})
],
total_count: 1
})
}, 60_000)
})
13 changes: 13 additions & 0 deletions tests/main/storage/postgres-cases-query-repository.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,19 @@ describe('PostgresCasesQueryRepository', () => {
expect.stringContaining('COUNT(*)::int AS total_count'),
['%new%']
)
const rowsSql = String(pool.query.mock.calls[0]?.[0])
const groupBySql = rowsSql.slice(rowsSql.indexOf('GROUP BY'), rowsSql.indexOf('ORDER BY'))
for (const projectedCaseColumn of [
'c.id',
'c.name',
'c.file_path',
'c.file_size',
'c.variant_count',
'c.created_at',
'c.genome_build'
]) {
expect(groupBySql).toContain(projectedCaseColumn)
}
})

it('runs the no-filter count through the named (prepared) path', async () => {
Expand Down