Skip to content

Commit

Permalink
ci: refactor tests to use local database (#311)
Browse files Browse the repository at this point in the history
* test: fix not closed timeout handle

* test: use local database for kitchen sink test

* test: remode debug output

* ci: add github actions postress service container for tests

* style: fix formatting

* test: extract logic for timeout race into helper function

* ci: add support to run tests without database setup

* style: format code

* test: add missing return

* ci: differentiate titles for tests execution with/without db

* test: switch to inline snapshot to differentiate with/without db executions

* test: remove reduntant comment

* ci: unify test commit status check title
  • Loading branch information
rostislav-simonik authored Dec 18, 2022
1 parent f6efee4 commit 7559995
Show file tree
Hide file tree
Showing 4 changed files with 143 additions and 46 deletions.
82 changes: 79 additions & 3 deletions .github/workflows/pull-request.yml
Original file line number Diff line number Diff line change
Expand Up @@ -31,17 +31,74 @@ jobs:
- name: Run es-lint
run: yarn -s lint:check

test-latest-prisma:
test-latest-prisma-without-database:
needs: [validate]
name: Test with latest Prisma
timeout-minutes: 20

strategy:
matrix:
os: ['ubuntu-latest', 'macos-latest', 'windows-latest']
os: ['macos-latest', 'windows-latest']
node-version: [14, 16]
database: ['no-db']

runs-on: ${{ matrix.os }}

steps:
- name: Checkout repository
uses: actions/checkout@v3

- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v3
with:
node-version: ${{ matrix.node-version }}
cache: 'yarn'

- name: Install dependencies
run: yarn --frozen-lockfile

- name: Set E2E DB Schema for Windows
if: ${{ matrix.os == 'windows-latest' }}
run: yarn -s ts-node scripts/get-e2e-db-schema --os ${{ matrix.os }} --node-version ${{ matrix.node-version }} --github-env $env:GITHUB_ENV

- name: Set E2E DB Schema for other operating systems
if: ${{ matrix.os != 'windows-latest' }}
run: yarn -s ts-node scripts/get-e2e-db-schema --os ${{ matrix.os }} --node-version ${{ matrix.node-version }} --github-env $GITHUB_ENV

- name: Build
run: yarn -s build

- name: Test
run: yarn -s test:ci
env:
DATABASE: ${{ matrix.database }}

test-latest-prisma-with-database:
needs: [validate]
name: Test with latest Prisma
timeout-minutes: 20

strategy:
matrix:
os: ['ubuntu-latest']
node-version: [14, 16]
database: ['db']

runs-on: ${{ matrix.os }}

services:
postgres:
image: postgres
env:
POSTGRES_PASSWORD: postgres
options: >-
--health-cmd pg_isready
--health-interval 10s
--health-timeout 5s
--health-retries 5
ports:
- 5432:5432

steps:
- name: Checkout repository
uses: actions/checkout@v3
Expand All @@ -68,8 +125,10 @@ jobs:

- name: Test
run: yarn -s test:ci
env:
DATABASE: ${{ matrix.database }}

test-past-prisma:
test-past-prisma-with-database:
needs: [validate]
name: Test with past Prisma
timeout-minutes: 20
Expand All @@ -78,9 +137,24 @@ jobs:
matrix:
os: ['ubuntu-latest']
node-version: [16]
database: ['db']
prisma-base-version: ['4.0']

runs-on: ${{ matrix.os }}

services:
postgres:
image: postgres
env:
POSTGRES_PASSWORD: postgres
options: >-
--health-cmd pg_isready
--health-interval 10s
--health-timeout 5s
--health-retries 5
ports:
- 5432:5432

steps:
- name: Checkout repository
uses: actions/checkout@v3
Expand All @@ -107,3 +181,5 @@ jobs:
run: yarn -s add @prisma/generator-helper@${{ matrix.prisma-base-version }}
- name: Test
run: yarn -s test:ci
env:
DATABASE: ${{ matrix.database }}
19 changes: 19 additions & 0 deletions tests/__helpers__/helpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,25 @@ import dedent from 'dindist'
import * as fs from 'fs-jetpack'
import * as Path from 'path'

export const timeoutRace = async <T>(
values: Iterable<T | PromiseLike<T>>,
timeout: number
): Promise<Awaited<T> | 'timeout'> => {
let timeoutHandle: NodeJS.Timeout | undefined

const result = await Promise.race<'timeout' | T>([
...values,
new Promise((res) => {
timeoutHandle = setTimeout(() => res('timeout'), timeout)
}),
])

if (result !== 'timeout' && timeoutHandle) {
clearTimeout(timeoutHandle)
}
return result
}

export function assertBuildPresent() {
if (fs.exists(Path.join(__dirname, '../../dist-esm')) === false)
throw new Error(`Please run build ESM before running this test`)
Expand Down
25 changes: 0 additions & 25 deletions tests/e2e/__snapshots__/kitchen-sink.test.ts.snap
Original file line number Diff line number Diff line change
@@ -1,30 +1,5 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`A full-featured project type checks, generates expected GraphQL schema, and successfully resolves received GraphQL documents: client request 1 1`] = `
Object {
"bars": Array [
Object {
"foo": Object {
"BigIntManually": null,
"BytesManually": null,
"DateTimeManually": null,
"DecimalManually": null,
"JsonManually": null,
"someBigIntField": 9007199254740991,
"someBytesField": Object {
"data": Array [],
"type": "Buffer",
},
"someDateTimeField": "2021-05-10T20:42:46.609Z",
"someDecimalField": "24.454545",
"someEnumA": "alpha",
"someJsonField": Object {},
},
},
],
}
`;

exports[`A full-featured project type checks, generates expected GraphQL schema, and successfully resolves received GraphQL documents: graphql schema 1`] = `
"### This file was generated by Nexus Schema
### Do not make changes to this file directly
Expand Down
63 changes: 45 additions & 18 deletions tests/e2e/kitchen-sink.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import stripAnsi from 'strip-ansi'
import { inspect } from 'util'

import { envarSpecs } from '../../src/lib/peerDepValidator'
import { createPrismaSchema } from '../__helpers__/helpers'
import { createPrismaSchema, timeoutRace } from '../__helpers__/helpers'
import { graphQLClient } from '../__providers__/graphqlClient'
import { project } from '../__providers__/project'

Expand Down Expand Up @@ -261,9 +261,9 @@ it('A full-featured project type checks, generates expected GraphQL schema, and
filePath: `.env`,
// prettier-ignore
content: dindist`
DB_URL="postgres://bcnfshogmxsukp:e31b6ddc8b9d85f8964b6671e4b578c58f0d13e15f637513207d44268eabc950@ec2-54-196-33-23.compute-1.amazonaws.com:5432/d17vadgam0dtao?schema=${process.env.E2E_DB_SCHEMA ?? 'local'}"
DB_URL="postgresql://postgres:postgres@localhost/nexus-prisma?schema=${process.env.E2E_DB_SCHEMA ?? 'local'}"
${envarSpecs.NO_PEER_DEPENDENCY_CHECK.name}="true"
`,
`,
},
]

Expand All @@ -277,7 +277,7 @@ it('A full-featured project type checks, generates expected GraphQL schema, and
const results = runTestProjectBuild()

// uncomment this to see the raw results (helpful for debugging)
console.log(`e2e output:\n`, inspect(results, { depth: 10, colors: true }))
// console.log(`e2e output:\n`, inspect(results, { depth: 10, colors: true }))

/**
* Sanity checks around buildtime
Expand Down Expand Up @@ -330,6 +330,11 @@ it('A full-featured project type checks, generates expected GraphQL schema, and
/.*"prismaClientImportId": "@prisma\/client".*/
)

if (process.env.DATABASE === 'no-db') {
d(`database not available, skipping runtime test`)
return
}

/**
* Sanity check the runtime
*/
Expand All @@ -343,23 +348,22 @@ it('A full-featured project type checks, generates expected GraphQL schema, and
const serverProcess = ctx.runAsync(`node build/server`, { reject: false })
serverProcess.stdout!.pipe(process.stdout)

const result = await Promise.race<'timeout' | 'server_started'>([
new Promise((res) =>
serverProcess.stdout!.on('data', (data: Buffer) => {
if (data.toString().match(SERVER_READY_MESSAGE)) res('server_started')
})
),
new Promise((res) => {
setTimeout(() => res('timeout'), 10_000)
}),
])
const result = await timeoutRace<'server_started'>(
[
new Promise((res) =>
serverProcess.stdout!.on('data', (data: Buffer) => {
if (data.toString().match(SERVER_READY_MESSAGE)) res('server_started')
})
),
],
10_000
)

if (result === 'timeout') {
throw new Error(
`server was not ready after 10 seconds. The output from child process was:\n\n${serverProcess.stdio}\n\n`
)
}

d(`starting client queries`)

const data = await ctx.graphQLClient.request(gql`
Expand Down Expand Up @@ -387,11 +391,34 @@ it('A full-featured project type checks, generates expected GraphQL schema, and
serverProcess.cancel()
// On Windows the serverProcess never completes the promise so we do an ugly timeout here
// and rely on jest --forceExit to terminate the process
await Promise.race([serverProcess, new Promise((res) => setTimeout(res, 2000))])

d(`stopped server`)
await timeoutRace([serverProcess], 2_000)

expect(data).toMatchSnapshot('client request 1')
d(`stopped server`)
expect(data).toMatchInlineSnapshot(`
Object {
"bars": Array [
Object {
"foo": Object {
"BigIntManually": null,
"BytesManually": null,
"DateTimeManually": null,
"DecimalManually": null,
"JsonManually": null,
"someBigIntField": 9007199254740991,
"someBytesField": Object {
"data": Array [],
"type": "Buffer",
},
"someDateTimeField": "2021-05-10T20:42:46.609Z",
"someDecimalField": "24.454545",
"someEnumA": "alpha",
"someJsonField": Object {},
},
},
],
}
`)

const [{ foo }] = data.bars

Expand Down

0 comments on commit 7559995

Please sign in to comment.