orval icon indicating copy to clipboard operation
orval copied to clipboard

Mock: `override.mock.properties` needs to recurse inside arrrays

Open thany opened this issue 2 months ago • 1 comments

Please consider this OpenAPI spec (simplified for brevity)

paths:
  /users:
    get:
      operationId: getUsers
      responses:
        '200':
          description: User listing success
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/retrievedUsers'
  /user/{id}:
    get:
      operationId: getUser
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
      responses:
        '200':
          description: User get success
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/retrievedUser'

components:
  schemas:
    baseUser:
      type: object
      description: Base user object
      properties:
        firstName:
          type: string
        lastName:
          type: string
        email:
          type: string
    retrievedUser:
      description: User object as it shall be retrieved
      allOf:
        - $ref: '#/components/schemas/baseUser'
        - type: object
          required: ["id", "lastName", "email"]
          properties:
            id:
              type: string
    retrievedUsers:
      description: Array of `retrievedUser` objects
      type: array
      items:
        $ref: '#/components/schemas/retrievedUser'

And this config (also simplified for brevity):

export default defineConfig({
  myproject: {
    input: {
      target: 'spec/myproject.yml',
    },
    output: {
      mode: 'split',
      target: 'src/gen/myproject.ts',
      client: 'fetch',
      mock: true,
      override: {
        mock: {
          properties: {
            firstName: () => faker.person.firstName(),
            lastName: () => faker.person.lastName(),
            email: () => faker.internet.email(),
          },
        },
      },
    }
});

The faker calls are used in the getUser mocks, but they are completely ignored in the getUsers mock. Just because they are objects in an array?... Why does that matter?

Compare a correctly generated mock:

export const getGetUserResponseMock = (): RetrievedUser => ({
  ...{
    firstName: (() => faker.person.firstName())(),
    lastName: (() => faker.person.lastName())(),
    email: (() => faker.internet.email())(),
  }, ...{ id: faker.string.alpha({ length: { min: 10, max: 20 } }) },
});

With the one where it goes wrong:

export const getGetUsersResponseMock = (): RetrievedUsers => (
  Array.from({ length: faker.number.int({ min: 1, max: 10 }) }, (_, i) => i + 1).map(() => ({
    ...{
      firstName: faker.helpers.arrayElement([faker.string.alpha({ length: { min: 10, max: 20 } }), undefined]),
      lastName: faker.helpers.arrayElement([faker.string.alpha({ length: { min: 10, max: 20 } }), undefined]),
      email: faker.helpers.arrayElement([faker.string.alpha({ length: { min: 10, max: 20 } }), undefined]),
    },
  }))
);

Why is it not using the functions I told it to use? The properties do have the exact same name in both cases, so what mechanism decides when exactly to use (or not use) override.mock.properties functions?

If this is intended behaviour in some way, please update the documentation to describe how exactly this decision is made per property, and how to write it in such a way that it always picks up these properties. Otherwise, it would be a bug that needs fixing.

I'm still using Orval 7.11.2 (because of #2464) on Node.js 22.20.0.

thany avatar Oct 23 '25 14:10 thany

Also note how 50% of the properties in a generated array are allowed to be undefined, despite the spec clearly stating they are required. Why is that?

This is partially fixed by adding override.mock.required = true but that requires ALL properties, not just the actually required ones. And then there still an optional property (firstName) that is never allowed to be undefined when a override.mock.properties exists for it.

Do you need a separate issue # for this?

thany avatar Oct 23 '25 14:10 thany