Skip to content

Header expectation from OpenApi url is failing because it seems to don't understand it as String #1940

@benjamin-richard-sogexia

Description

Describe the issue
The mock server should return the right expectation even if i use a header like Accept-Language that is describe from openApi

What you are trying to do
I have an OpenApi specifying header

  "components": {
    "parameters": {
      "LanguageV1": {
        "in": "header",
        "name": "Accept-Language",
        "schema": {
          "type": "string",
          "minLength": 5,
          "maxLength": 5,
          "examples": [
            "fr-FR",
            "en-EN"
          ]
        },
        "required": true
      },

Then i send http request to the mock server like this :

curl -X 'POST' \
  'http://172.23.0.14:1080/v1/my-route' \
  -H 'Accept-Language: fr-FR' \
  -H 'Content-Type: application/json' \
  -d '{
  "level": "log",
  "message": "User completed step 1 of process"
}'

I expect a match from the mock server, but it fails to use the right expectation because of this :

method matched
path matched
body matched
headers didn't match: 
  schema match failed expect:
    {
      "format" : "string",
      "maxLength" : 5,
      "minLength" : 5
    }
   found:
    fr-FR
   errors:
    JsonParseException - Unrecognized token 'fr': was expecting (JSON String, Number (or 'NaN'/'INF'/'+INF'), Array, Object or token 'null', 'true' or 'false')
     at [Source: (String)"fr-FR"; line: 1, column: 3]
  multimap matching key match failed for key:
    Accept-Language
  multimap match failed expected:
    {
      "keyMatchStyle" : "MATCHING_KEY",
      "Accept-Language" : {
        "parameterStyle" : "SIMPLE",
        "values" : [ {
          "schema" : {
            "format" : "string",
            "maxLength" : 5,
            "minLength" : 5
          }
        } ]
      }
    }
   found:
    {
      "Content-Type" : [ "application/json" ],
      "Content-Length" : [ "72" ],
      "Connection" : [ "keep-alive" ],
      "Accept-Language" : [ "fr-FR" ]
    }
   failed because:
    multimap values don't match

MockServer version
5.15.0

To Reproduce
Steps to reproduce the issue:
Juste add the LanguageV1 component and use it inside your own paths from an OpenApi

  1. How you are running MockServer (i.e maven plugin, docker, etc) : i use Javascript server
  2. Code you used to create expectations
import mockserver from 'mockserver-node'
import { mockServerClient } from 'mockserver-client'
import { overrideMockServer } from './overrides/override-mock-server.js'

import express from 'express'
import swaggerUi from 'swagger-ui-express'
import fs from 'fs'
import path from 'path'
import { fileURLToPath } from 'url'

const mockServerUrl = 'http://172.23.0.14';
const openAPIFilePath = '../open-api/openapi.json';
const appPort = 1080;
const swaggerUiExpressAppPort = '1081';
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);

const clientMockServer = await startMockServer();
await startSwaggerUIExpressApp(clientMockServer);

async function startMockServer() {
  await mockserver.stop_mockserver({ serverPort: appPort }

  );
  await mockserver.start_mockserver({
    serverPort: appPort,
    verbose: true,
    jvmOptions: [
      '-Dmockserver.enableCORSForAllResponses=true',
      '-Dmockserver.corsAllowOrigins="*"',
      '-Dmockserver.corsAllowMethods="CONNECT, DELETE, GET, HEAD, OPTIONS, POST, PUT, PATCH, TRACE"',
      '-Dmockserver.corsAllowHeaders="Allow, Content-Encoding, Content-Length, Content-Type, ETag, Expires, Last-Modified, Location, Server, Vary, Authorization"',
      '-Dmockserver.corsAllowCredentials=true',
    ],
  });
  console.log(`MockServer started on port ${appPort}`)
  const client = await mockServerClient('localhost', appPort)

  // Read and modify the OpenAPI specification to fix the Accept-Language header issue
  let openApiSpec = JSON.parse(fs.readFileSync(path.join(__dirname, openAPIFilePath), 'utf8'))

  // Use the modified OpenAPI specification
  await client.openAPIExpectation({ specUrlOrPayload: openApiSpec })
  await overrideMockServer(client);
  return client;
}

async function startSwaggerUIExpressApp(clientMockServer) {
  const expressApp = express();
  let openApiSpecification = JSON.parse(fs.readFileSync(path.join(__dirname, openAPIFilePath), 'utf8'));
  openApiSpecification = await enhanceOpenAPISpecificationWithMockServerOverrides(clientMockServer, openApiSpecification);
  openApiSpecification.servers = [
    {
      url: `${mockServerUrl}:${appPort}`, // URL of your mock server
      description: 'Mock Server',
    },
  ];
  expressApp.use('/api-docs', swaggerUi.serve, swaggerUi.setup(openApiSpecification));
  expressApp.listen(swaggerUiExpressAppPort, () => {
    console.log(`Swagger UI is available at ${mockServerUrl}:${swaggerUiExpressAppPort}/api-docs`);
  });
}

async function enhanceOpenAPISpecificationWithMockServerOverrides(clientMockServer, openApiSpec) {
  try {
    const expectations = await clientMockServer.retrieveActiveExpectations({});
    expectations.forEach((expectation) => {
      if (expectation.httpRequest.specUrlOrPayload !== openAPIFilePath) {
        const httpRequest = expectation.httpRequest;
        const httpResponse = expectation.httpResponse;
        const httpResponseObjectCallback = expectation.httpResponseObjectCallback;
        if (!httpRequest || (!httpResponse && !httpResponseObjectCallback)) return;
        const path = httpRequest.path;
        const method = httpRequest.method.toLowerCase();
        if (!openApiSpec.paths[path]) {
          openApiSpec.paths[path] = {};
        }
        if (!openApiSpec.paths[path][method]) {
          openApiSpec.paths[path][method] = {
            summary: `MockServer expectation for ${method.toUpperCase()} ${path}`,
            responses: {},
          };
        }
        if (httpResponse) {
          openApiSpec.paths[path][method].responses[httpResponse.statusCode] = {
            description: httpResponse.reasonPhrase + ' [Override MockServer]',
            content: {
              'application/json': {
                schema: {
                  type: 'object',
                  example: JSON.stringify(httpResponse.body),
                },
              },
            },
          };
        }
        if (httpResponseObjectCallback) {
          openApiSpec.paths[path][method].summary = openApiSpec.paths[path][method].summary + ' [Callback MockServer]';
        }

        openApiSpec.paths[path][method]['x-mockserver-expectation'] = expectation;
      }
    });
    return openApiSpec;
  } catch (error) {
    console.error('Error fetching MockServer expectations:', error);
    return openApiSpec;
  }
}

  1. What error you saw : see above

Expected behaviour
it should return a valid expectation

MockServer Log
Log output, as INFO level (or lower)

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions