Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

issue with generators on the examples provided in the docs #95

Open
alexgherman opened this issue Mar 18, 2024 · 12 comments · May be fixed by #97
Open

issue with generators on the examples provided in the docs #95

alexgherman opened this issue Mar 18, 2024 · 12 comments · May be fixed by #97

Comments

@alexgherman
Copy link

alexgherman commented Mar 18, 2024

There seems to be an issue with the generators, even the examples provided in the docs are not returning the expected values.

TL;DR: run the stackblitz here to see the issue:
https://stackblitz.com/edit/vitejs-vite-yg6ndm?file=src%2Fpreview.ts

Example 1:

  const totalVisitsGenerator = Generator({
    schema: ZodNumber,
    filter: ({ context }) => context.path.at(-1) === 'totalVisits',
    /**
     * The `context` provides a path to the current field
     *
     * {
     *   totalVisits: ...,
     *   nested: {
     *     totalVisits: ...,
     *   }
     * }
     *
     * Would match twice with the following paths:
     *   ['totalVisits']
     *   ['nested', 'totalVisits']
     */

    // returns a more realistic number of visits.
    output: ({ transform }) => transform.utils.random.int({ min: 0, max: 25 }),
  });

  const addressGenerator = Generator({
    schema: ZodObject,
    filter: ({ context }) => context.path.at(-1) === 'address',
    // returns a custom address object
    output: () => ({
      street: 'My Street',
      city: 'My City',
      state: 'My State',
    }),
  });

  const personSchema = z.object({
    name: z.string(),
    birthday: z.date(),
    address: z.object({
      street: z.string(),
      city: z.string(),
      state: z.string(),
    }),
    pets: z.array(z.object({ name: z.string(), breed: z.string() })),
    totalVisits: z.number().int().positive(),
  });

  const fixture = new Fixture({ seed: 38 }).extend([
    addressGenerator,
    totalVisitsGenerator,
  ]);
  const person = fixture.fromSchema(personSchema);
  console.log(person);
  
  // Expected:
// {
// 	address: {
// 		city: 'My City',
// 		state: 'My State',
// 		street: 'My Street',
// 	},
// 	birthday: new Date('1952-01-21T17:32:42.094Z'),
// 	name: 'yxyzyskryqofekd',
// 	pets: [
// 		{
// 			breed: 'dnlwozmxaigobrz',
// 			name: 'vhvlrnsxroqpuma',
// 		},
// 		{
// 			breed: 'ifbgglityarecl-',
// 			name: 'c-lmtvotjcevmyi',
// 		},
// 		{
// 			breed: 'fmylchvprjdgelk',
// 			name: 'ydevqfcctdx-lin',
// 		},
// 	],
// 	totalVisits: 15,
// },

  // Actual:
  // {
  //   "name": "sdnlwozmxaigobr",
  //   "birthday": "2091-09-17T10:54:58.574Z",
  //   "address": { // incorrect address remains untouched
  //     "street": "c-lmtvotjcevmyi",
  //     "city": "ifbgglityarecl-",
  //     "state": "ydevqfcctdx-lin"
  //   },
  //   "pets": [
  //     {
  //       "name": "mylchvprjdgelkq",
  //       "breed": "ivrplyhts-yypas"
  //     },
  //     {
  //       "name": "rcrrkytqrdmzajo",
  //       "breed": "m-rfot-rvbqmlcu"
  //     },
  //     {
  //       "name": "adahtinsiooiwrj",
  //       "breed": "xdxkgurszvshnvg"
  //     }
  //   ],
  //   "totalVisits": 7
  // }

Example 2:

  const pxSchema = z.custom<`${number}px`>((val) => {
    return /^\d+px$/.test(val as string);
  });

  const StringGenerator = Generator({
    schema: ZodString,
    output: () => 'John Doe',
  });

  const PixelGenerator = Generator({
    schema: pxSchema,
    output: () => '100px',
  });

  const developerSchema = z.object({
    name: z.string().max(10),
    resolution: z.object({
      height: pxSchema,
      width: pxSchema,
    }),
  });

  const fixture = new Fixture({ seed: 7 }).extend([
    PixelGenerator,
    StringGenerator,
  ]);
  const developer = fixture.fromSchema(developerSchema);
  console.log(developer);

// Expected:
// {
// 	name: 'John Doe',
// 	resolution: {
// 		height: '100px',
// 		width: '100px',
// 	},
// }

// Actual:
// {
//   "name": "mzzhauqgzw", // incorrect
//   "resolution": {
//     "height": "100px", // correct
//     "width": "100px" // correct
//   }
// }


What am I missing?
And thank you for a great project! =)

@timdeschryver
Copy link
Owner

Hey @alexgherman thanks for the kind words.
It seems like you're right and that there is something off within the StackBlitz, at first I thought it was an outdated version but even after updating to the latest version the issue still occurs.

The main issue is that the generator isn't registered/invoked because the ZodNumber schema can't be matched with zod's type. When you remove the schema property it should work.

    const totalVisitsGenerator = Generator({
//      👇 remove this 
//      schema: ZodNumber,
        filter: ({ context }) => {
            return context.path.at(-1) === 'totalVisits';
        },

Do you also experience this issue outside of StackBlitz, and if so in what environment?
The reason I ask this is because when I copy paste the example to a local project, it just works.

@alexgherman
Copy link
Author

alexgherman commented Mar 19, 2024

ah I see! glad I'm not crazy, thank you @timdeschryver.
I went down a rabbit hole trying different version combinations of zod and zod-fixture with the hope to find a combination that works :)

Either removing the schema altogether or defining the schema separately and sharing it with the generator did it in stackblitz:
https://stackblitz.com/edit/vitejs-vite-mjxbz7?file=src%2Fpreview.ts

However, I see the same issue in my vite project.
here's a reproducible fork, hopefully it will shed some more light on the issue:
https://github.com/alexgherman/zod-fixture-issue-95

Seeing the same output as in stackblitz

[
  {
    "name": "yxyzyskryqofekd",
    "birthday": "1952-01-21T17:32:42.094Z",
    "address": {
      "street": "cvhvlrnsxroqpum",
      "city": "sdnlwozmxaigobr",
      "state": "zc-lmtvotjcevmy"
    },
    "pets": [
      {
        "name": "ifbgglityarecl-",
        "breed": "ydevqfcctdx-lin"
      },
      {
        "name": "fmylchvprjdgelk",
        "breed": "livrplyhts-yypa"
      },
      {
        "name": "jrcrrkytqrdmzaj",
        "breed": "em-rfot-rvbqmlc"
      }
    ],
    "totalVisits": 75
  },
  {
    "name": "gvil-tm-io",
    "resolution": {
      "height": "100px",
      "width": "100px"
    }
  }
]

@timdeschryver
Copy link
Owner

Thanks for the reproduction.
It seems as it's a runtime problem.

Installing vitest in the referenced repository, and copying the example within to a test seems to work as expected.

We probably have to change how we reference Zod's type within generators (https://github.com/timdeschryver/zod-fixture/blob/main/src/internal/zod.ts) to make this compatible with multiple runtimes.

@timdeschryver
Copy link
Owner

I was hoping to find some time to take a look at this but last weeks have been quite busy.
Feel free to take a look at this issue if you want @alexgherman

@dxlbnl
Copy link

dxlbnl commented Jun 17, 2024

I've looked a bit into the issue. And the problem lies in the function isType,

the condition:

schema._def.typeName === target.name

is not valid in compiled situations. as target.name === '_zodString' and schema._def.typeName === 'zodString'

We could compare constructors instead possibly: schema.constructor === target (But this fails a bunch of tests)

Looking into that I see a bit of naughtyness around faking constructors.

@timdeschryver Do you have some pointers in the right direction?

I've pushed this, but it does not seem to work properly (tests pass, but I'm getting errors in my private codebase)
dxlbnl@587e67e

@timdeschryver
Copy link
Owner

@dxlbnl thanks for the investigation.
It seems like this is the root cause of this issue.
It's probably not the cleanest solution, but what if we remove/add the _ when we check the types?

@dxlbnl
Copy link

dxlbnl commented Jun 18, 2024

If that's sufficient, why not. Yet it seems fragile.
I'm not sure how to fix it properly though how to compares the schemas properly.

I did find this repo: https://github.com/lawvs/zod-compare might be a solution.

@timdeschryver
Copy link
Owner

@dxlbnl that seems to be a useful library as it will do most of the heavy lifting for us (and looking at the source code there are many "edge cases"). If you want you can try to incorporate it within this library.

@dxlbnl
Copy link

dxlbnl commented Jul 8, 2024

I'm having difficulty making it work properly. Largely due to the quirkyness regarding the zodTypes stringnames being cast as zodTypes. And also due to the schema types (ZodString, ... ) being tricky to match to schema instances.

I'm not sure how to proceed, but maybe you'd be willing to drop in a call, and discuss it a bit. @timdeschryver

@timdeschryver timdeschryver linked a pull request Jul 13, 2024 that will close this issue
@timdeschryver
Copy link
Owner

@dxlbnl sorry, I missed this message - I also tried to implement a fix.
I tried zod-compare, but I quickly gave up 😅

If you have the time could you take a look at #97 and provide feedback?
I like that the implementation is simpler, but it might be not so clear to implement a custom generator (I assume not many use this feature). The fix gives the desired output within the repo with the reproduction.

@dxlbnl
Copy link

dxlbnl commented Jul 15, 2024

Somehow its now working in my repo, it seems to pick up the right generators. Maybe something has changed with how vite deals with it.

@timdeschryver
Copy link
Owner

Somehow its now working in my repo, it seems to pick up the right generators. Maybe something has changed with how vite deals with it.

Is it working with the current implementation or the implementation of the PR?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

3 participants