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: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
},
"scripts": {
"build": "yarn workspaces foreach --all run build",
"test": "yarn workspaces foreach --all --parallel run test",
"test": "yarn workspaces foreach --all run test",
"coverage:permissions": "yarn workspaces foreach --all --parallel run coverage:permissions",
"check-git": "git status -uno --porcelain && [ -z \"$(git status -uno --porcelain)\" ] || (echo 'Git working directory not clean'; false)",
"format": "npx @biomejs/biome format --write packages/**/*.{js,ts,json}",
Expand Down
1 change: 1 addition & 0 deletions packages/core/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,4 @@ export * from './request.js'
export * from './response.js'
export * from './service.js'
export * from './fixtures.js'
export * from './configuration.js'
12 changes: 6 additions & 6 deletions packages/core/src/service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ import { REQUEST_PROPERTIES, requestPropertyMatch } from './properties.js'
import type { CoreRequest } from './request.js'
import type { CoreResponse } from './response.js'

function createError(status: number, message: string): [number, { message: string }] {
export function createServiceError(status: number, message: string): [number, { message: string }] {
return [status, { message: `[FIXTURE SERVER ERROR ${status}]: ${message}` }]
}

Expand Down Expand Up @@ -49,7 +49,7 @@ export function updateServiceConfiguration(
const error = validateConfiguration(data)

if (error) {
return createError(400, error.message)
return createServiceError(400, error.message)
}

const { cors, headers, query, cookies } = data
Expand All @@ -71,13 +71,13 @@ export function createServiceFixture(
const [fixture, validationError] = validateFixture(unsafeFixture, configuration)

if (!fixture) {
return createError(400, validationError)
return createServiceError(400, validationError)
}

const { conflictError, fixtureId } = registerFixture(fixtureStorage, fixture, configuration)

if (conflictError) {
return createError(409, conflictError)
return createServiceError(409, conflictError)
}
return [201, { id: fixtureId }]
}
Expand All @@ -100,15 +100,15 @@ export function createServiceFixtures(
if (!fixture) {
cleanUpOnError()

return createError(400, validationError)
return createServiceError(400, validationError)
}

const { conflictError, fixtureId } = registerFixture(fixtureStorage, fixture, configuration)

if (conflictError) {
cleanUpOnError()

return createError(409, conflictError)
return createServiceError(409, conflictError)
}

fixtureIds.push({ id: fixtureId })
Expand Down
4 changes: 2 additions & 2 deletions packages/dynamock/src/createServer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ export function createServer() {
const service = createService()

app.post('/___fixtures', (req, res) => {
if (typeof req.body?.request === 'object') {
if (typeof req.body?.request === 'object' && req.body?.request !== null) {
req.body.request.origin = `${req.protocol}://${req.get('host')}`
}
const [status, data] = createServiceFixture(service, req.body)
Expand All @@ -39,7 +39,7 @@ export function createServer() {
app.post('/___fixtures/bulk', (req, res) => {
if (Array.isArray(req.body)) {
for (const fixture of req.body) {
if (typeof fixture?.request === 'object') {
if (typeof fixture?.request === 'object' && fixture?.request !== null) {
fixture.request.origin = `${req.protocol}://${req.get('host')}`
}
}
Expand Down
54 changes: 41 additions & 13 deletions packages/dynamock/test/bin.spec.ts
Original file line number Diff line number Diff line change
@@ -1,17 +1,16 @@
import { afterEach, beforeEach, describe, expect, test } from '@jest/globals'
import { spawn, type ChildProcessWithoutNullStreams } from 'node:child_process'
import { getTestFiles } from '@dynamock/test-cases'
import { fetchWithBaseUrl, waitPortFree, waitPortUsed, wrapError } from './config/utils.js'
import { spawn, type ChildProcess } from 'node:child_process'
import { getTestFiles, wrapError } from '@dynamock/test-cases'
import { waitPortFree, waitPortUsed } from './config/utils.js'
import { getServerTestCases } from './config/getTestCases.js'

describe('bin integration tests', () => {
const allTests = getTestFiles()
const port = 5000
const fetchDynamock = fetchWithBaseUrl(fetch, `http://localhost:${port}`)
let process: ChildProcessWithoutNullStreams
const allTests = getTestFiles() //.filter(([filePath]) => filePath.endsWith('create-and-delete-bulk.yml'))
const port = 3000
let process: ChildProcess

beforeEach(async () => {
process = spawn('dynamock', [String(port)])
process = spawn('dynamock', [String(port)] /*, {stdio: 'inherit'}*/)
await waitPortUsed(port)
})

Expand All @@ -27,21 +26,50 @@ describe('bin integration tests', () => {

for (let i = 0; i < testData.length; i++) {
const { action, expectation } = testData[i]
const { path, ...options } = action
const { path, method, headers, body, bodyJSON, query } = action
const fetchOptions: {
method: string
headers: { [key: string]: string }
body: null | string
} = {
method,
headers: headers ?? {},
body: null,
}

const result = await fetchDynamock(path, options)
if (bodyJSON !== undefined) {
fetchOptions.body = JSON.stringify(bodyJSON)
fetchOptions.headers = {
...fetchOptions.headers,
'Content-Type': 'application/json',
}
} else if (body !== undefined) {
fetchOptions.body = String(body)
}

const url = new URL(`http://127.0.0.1:${port}${path}`)

for (const queryKey in query) {
url.searchParams.set(queryKey, query[queryKey])
}

const result = await fetch(url.toString(), fetchOptions)

if (expectation) {
const { status, body, bodyJSON } = expectation
const { status, headers, body, bodyJSON } = expectation

if (status) {
await wrapError(i, () => expect(result.status).toBe(status))
}

if (bodyJSON) {
if (headers) {
await wrapError(i, () => expect(result.headers).toMatchObject(headers))
}

if (bodyJSON !== undefined) {
await wrapError(i, async () => expect(await result.json()).toEqual(bodyJSON))
} else if (body !== undefined) {
await wrapError(i, async () => expect(await result.json()).toEqual(body))
await wrapError(i, async () => expect(await result.text()).toEqual(body))
}
}
}
Expand Down
94 changes: 54 additions & 40 deletions packages/dynamock/test/config/getTestCases.ts
Original file line number Diff line number Diff line change
@@ -1,79 +1,93 @@
import { type AnyYMLTestCase, getYMLTestCases } from '@dynamock/test-cases'
import {
ActionEnum,
type AnyYMLTestCase,
type ApiTest,
getYMLTestCases,
type YMLTestCaseExpectation,
} from '@dynamock/test-cases'

type ServerTestCase = {
action: {
path: string
method: string
headers?: [] | object | null
cookies?: [] | object | null
query?: [] | object | null
options?: object | null
}
expectation: {
status?: number
body?: unknown
bodyJSON?: unknown
}
action: ApiTest
expectation: YMLTestCaseExpectation
}

export function getServerTestCases(absolutePath: string): ServerTestCase[] {
const ymlTestCases: AnyYMLTestCase[] = getYMLTestCases(absolutePath)

return ymlTestCases.map(({ action, expectation }) => {
const testCases: (ServerTestCase | null)[] = ymlTestCases.map(({ action, expectation }) => {
const { name, data } = action

switch (name) {
case 'get_config':
case ActionEnum.get_config:
return {
action: {
...data,
path: '/___config',
method: 'GET',
bodyJSON: data,
},
expectation: {
...expectation,
body: undefined,
bodyJSON: expectation.body,
},
expectation,
}
case 'put_config':
case ActionEnum.put_config:
return {
action: {
...data,
path: '/___config',
method: 'PUT',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(data),
},
expectation: {
...expectation,
body: undefined,
bodyJSON: expectation.body,
bodyJSON: data,
},
expectation,
}
case 'delete_config':
case ActionEnum.delete_config:
return {
action: {
path: '/___config',
method: 'DELETE',
},
expectation,
}
case 'post_fixture':
case ActionEnum.post_fixture:
return {
action: {
...data,
path: '/___fixtures',
method: 'POST',
bodyJSON: data,
},
expectation,
}
case ActionEnum.post_fixtures_bulk:
return {
action: {
path: '/___fixtures/bulk',
method: 'POST',
bodyJSON: data,
},
expectation,
}
case ActionEnum.delete_fixture:
return {
action: {
path: '/___fixtures',
method: 'DELETE',
bodyJSON: data,
},
expectation: {
...expectation,
body: undefined,
bodyJSON: expectation.body,
expectation,
}
case ActionEnum.delete_all_fixtures:
return {
action: {
path: '/___fixtures',
method: 'DELETE',
},
expectation,
}
case ActionEnum.test_fixture:
return {
action: data as ApiTest,
expectation,
}
default:
return null
}
})

return testCases.filter((testCase) => testCase !== null)
}
17 changes: 0 additions & 17 deletions packages/dynamock/test/config/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,20 +37,3 @@ export function waitPortUsed(port: number, reverse = false, retry = 50) {
export function waitPortFree(port: number, retry?: number) {
return waitPortUsed(port, true, retry)
}

export function fetchWithBaseUrl<R>(initialFetch: (url: string, options: object) => Promise<R>, baseUrl: string) {
return (url: string, options: object) => {
return initialFetch(baseUrl + url, options)
}
}

export async function wrapError(testIndex: number, task: () => unknown) {
try {
return await task()
} catch (error: unknown) {
if (error instanceof Error) {
error.message += `\nTest iteration ${testIndex}`
}
throw error
}
}
Loading
Loading