lodash#fromPairs TypeScript Examples

The following examples show how to use lodash#fromPairs. You can vote up the ones you like or vote down the ones you don't like, and go to the original project or source file by following the links above each example. You may check out the related API usage on the sidebar.
Example #1
Source File: contractWrappers.ts    From webapp with MIT License 6 votes vote down vote up
getRemoveLiquidityReturn = async (
  protectionContract: string,
  id: string,
  ppm: string,
  removeTimestamp: number,
  web3?: Web3
): Promise<PositionReturn> => {
  throwIfNotContract(protectionContract);
  const contract = buildLiquidityProtectionContract(protectionContract, web3);

  const res = await contract.methods
    .removeLiquidityReturn(id, ppm, String(removeTimestamp))
    .call();
  const keys = ["targetAmount", "baseAmount", "networkAmount"];
  const pairs = toPairs(res).map(([, value], index) => [keys[index], value]);

  return fromPairs(pairs) as PositionReturn;

  // targetAmount - expected return amount in the reserve token
  // baseAmount - actual return amount in the reserve token
  // networkAmount - compensation in the network token
}
Example #2
Source File: Input.story.tsx    From grafana-chinese with Apache License 2.0 6 votes vote down vote up
getKnobs = () => {
  return {
    validation: text('Validation regex (will do a partial match if you do not anchor it)', ''),
    validationErrorMessage: text('Validation error message', 'Input not valid'),
    validationEvent: select(
      'Validation event',
      fromPairs(zip(Object.keys(EventsWithValidation), Object.values(EventsWithValidation))),
      EventsWithValidation.onBlur
    ),
  };
}
Example #3
Source File: contractWrappers.ts    From webapp with MIT License 5 votes vote down vote up
fetchPositionsMulti = async (
  positionIds: string[],
  liquidityStore: string
): Promise<ProtectedLiquidity[]> => {
  const positionShapes = positionIds.map(id =>
    protectedPositionShape(liquidityStore, id)
  );

  // @ts-ignore
  const ethMulti = new MultiCall(web3);

  const contract = buildLiquidityProtectionStoreContract(liquidityStore);
  const keys = [
    "owner",
    "poolToken",
    "reserveToken",
    "poolAmount",
    "reserveAmount",
    "reserveRateN",
    "reserveRateD",
    "timestamp",
    "id"
  ];

  try {
    const [multiPositions] = await ethMulti.all([positionShapes]);

    const protectedLiquidity = multiPositions
      // @ts-ignore
      .map(res => ({ ...res.position, "8": res.positionId }))
      // @ts-ignore
      .map(res =>
        fromPairs(keys.map((key, index) => [key, res[index]]))
      ) as ProtectedLiquidity[];

    return protectedLiquidity.filter(pos => typeof pos.owner == "string");
  } catch (e) {
    const response = await mapSeries(positionIds, 3, async id => {
      const raw = await contract.methods.protectedLiquidity(id).call();
      return { ...raw, "8": id };
    });

    const protectedLiquidity = response.filter(Boolean).map(res =>
      // @ts-ignore
      fromPairs(keys.map((key, index) => [key, res[index]]))
    ) as ProtectedLiquidity[];

    return protectedLiquidity;
  }
}
Example #4
Source File: decorateRewarders.ts    From rewarder-list with GNU Affero General Public License v3.0 4 votes vote down vote up
decorateRewarders = async (network: Network): Promise<void> => {
  const dir = `${__dirname}/../../data/${network}/`;
  await fs.mkdir(dir, { recursive: true });

  const rewarderMetas = JSON.parse(
    (await fs.readFile(`${dir}/all-rewarders.json`)).toString()
  ) as Record<string, RewarderMeta>;

  const networkRewarders = KNOWN_REWARDERS.filter((kr) =>
    kr.networks.includes(network)
  );

  const allQuarries = (
    await Promise.all(
      Object.entries(rewarderMetas).map(async ([rewarderKey, meta]) => {
        return await Promise.all(
          meta.quarries.map(async (q) => {
            const [replicaMint] = await findReplicaMintAddress({
              primaryMint: new PublicKey(q.stakedToken.mint),
            });
            const [mergePool] = await findPoolAddress({
              primaryMint: new PublicKey(q.stakedToken.mint),
            });
            return {
              rewarder: rewarderKey,
              token: q.stakedToken,
              quarry: q.quarry,
              slug: q.slug,
              replicaMint: replicaMint.toString(),
              mergePool: mergePool.toString(),
            };
          })
        );
      })
    )
  ).flat();

  const quarriesByMint = groupBy(allQuarries, (el) => el.token.mint);
  const quarriesByReplicaMint = groupBy(allQuarries, (el) => el.replicaMint);
  const rewardersByMint = mapValues(quarriesByMint, (group) =>
    group.map((g) => g.rewarder.toString())
  );

  for (const rewarderInfo of networkRewarders) {
    await fs.writeFile(
      `${dir}/rewarders/${rewarderInfo.address}/info.json`,
      stringify(rewarderInfo)
    );
  }
  const { tokens } = await fetchAllTokens(network);

  const allRewardersWithInfo: Record<string, RewarderMetaWithInfo> = fromPairs(
    await Promise.all(
      Object.entries(rewarderMetas).map(
        async ([rewarderKey, meta]): Promise<
          [string, RewarderMetaWithInfo]
        > => {
          const info = networkRewarders.find(
            (nr) => nr.address === rewarderKey
          );
          const redeemerKeyAndBump = info?.redeemer?.underlyingToken
            ? await findRedeemerKey({
                iouMint: new PublicKey(meta.rewardsToken.mint),
                redemptionMint: new PublicKey(info?.redeemer?.underlyingToken),
              })
            : null;

          const redeemerVaultATA =
            redeemerKeyAndBump && info?.redeemer?.underlyingToken
              ? await getATAAddress({
                  mint: new PublicKey(info.redeemer.underlyingToken),
                  owner: redeemerKeyAndBump[0],
                })
              : redeemerKeyAndBump;

          const quarries = await Promise.all(
            meta.quarries.map(
              async (quarry): Promise<QuarryMetaWithReplicas> => {
                const [mergePool] = await findPoolAddress({
                  primaryMint: new PublicKey(quarry.stakedToken.mint),
                });
                const [replicaMint] = await findReplicaMintAddress({
                  primaryMint: new PublicKey(quarry.stakedToken.mint),
                });

                const primaryQuarries =
                  quarriesByReplicaMint[quarry.stakedToken.mint];
                const otherQuarries = quarriesByMint[quarry.stakedToken.mint];
                const replicaQuarries = quarriesByMint[replicaMint.toString()];

                // It is a replica if primary quarries exist for this as a replica token.
                const firstPrimaryQuarry = primaryQuarries?.[0];
                const isReplica = !!firstPrimaryQuarry;

                const myPrimaryQuarries = isReplica ? primaryQuarries : [];
                const myReplicaQuarries = isReplica
                  ? otherQuarries
                  : replicaQuarries;

                const addRewardsToken = (
                  quarries: {
                    rewarder: string;
                    quarry: string;
                    token: TokenMeta;
                    slug: string;
                  }[]
                ) =>
                  quarries.map(({ quarry, rewarder, slug }) => {
                    const rewardsToken = rewarderMetas[rewarder]?.rewardsToken;
                    invariant(rewardsToken);
                    const displayRewardsToken =
                      info?.redeemer?.underlyingTokenInfo ??
                      tokens[rewardsToken.mint] ??
                      null;
                    return {
                      quarry,
                      rewarder,
                      rewardsToken,
                      slug,
                      displayRewardsToken,
                    };
                  });

                const primaryToken = isReplica
                  ? firstPrimaryQuarry.token
                  : quarry.stakedToken;
                const primaryTokenInfo = tokens[primaryToken.mint] ?? null;

                return {
                  ...quarry,
                  primaryToken,
                  primaryTokenInfo,
                  mergePool: mergePool.toString(),
                  replicaMint: isReplica
                    ? quarry.stakedToken.mint
                    : replicaMint.toString(),
                  primaryQuarries: addRewardsToken(myPrimaryQuarries ?? []),
                  replicaQuarries: addRewardsToken(myReplicaQuarries ?? []),
                  isReplica,
                };
              }
            )
          );
          const rewardsTokenInfo = tokens[meta.rewardsToken.mint] ?? null;
          const result: RewarderMetaWithInfo = {
            ...meta,
            quarries,
            slug: info?.id ?? rewarderKey,
            rewardsTokenInfo,
          };
          if (info) {
            if (info.redeemer && redeemerKeyAndBump) {
              info.redeemer.underlyingTokenInfo =
                tokens[info.redeemer.underlyingToken];
              info.redeemer.redeemerKey = redeemerKeyAndBump[0].toString();
              info.redeemer.redeemerVaultATA = redeemerVaultATA?.toString();
            }
            result.info = info;
          }
          return [rewarderKey, result];
        }
      )
    )
  );

  await fs.writeFile(`${dir}/quarries-by-mint.json`, stringify(quarriesByMint));

  await fs.writeFile(
    `${dir}/all-rewarders-with-info.json`,
    stringify(allRewardersWithInfo)
  );
  await fs.writeFile(
    `${dir}/rewarders-by-mint.json`,
    stringify(rewardersByMint)
  );
  await fs.writeFile(`${dir}/rewarder-list.json`, stringify(networkRewarders));

  for (const [rewarderKey, rewarderInfoFull] of Object.entries(
    allRewardersWithInfo
  )) {
    const rewardsToken = tokens[rewarderInfoFull.rewardsToken.mint];

    await fs.mkdir(`${dir}/rewarders/${rewarderKey}`, { recursive: true });

    // Add the name of the staked token & name of the rewards token to the result

    await fs.writeFile(
      `${dir}/rewarders/${rewarderKey}/full.json`,
      stringify(rewarderInfoFull)
    );

    await fs.mkdir(`${dir}/rewarders/${rewarderKey}/quarries`, {
      recursive: true,
    });
    await Promise.all(
      rewarderInfoFull.quarries.map(async (quarry) => {
        let stakedToken: TokenInfo | null = null;

        const underlyingTokens: TokenInfo[] = [];
        stakedToken = tokens[quarry.stakedToken.mint] ?? null;
        if (stakedToken) {
          underlyingTokens.push(...pushUnderlying(stakedToken, tokens));
        }

        const { quarries: _, ...rewarderInfoWithoutQuarries } =
          rewarderInfoFull;
        const quarryInfoStr = stringify({
          rewarder: rewarderInfoWithoutQuarries,
          rewardsToken,
          quarry,
          stakedToken,
          underlyingTokens,
        });

        await fs.writeFile(
          `${dir}/rewarders/${rewarderKey}/quarries/${quarry.index}.json`,
          quarryInfoStr
        );
        if (quarry.slug && quarry.slug !== quarry.index.toString()) {
          await fs.writeFile(
            `${dir}/rewarders/${rewarderKey}/quarries/${quarry.slug}.json`,
            quarryInfoStr
          );
        }
      })
    );
  }
}
Example #5
Source File: basic.spec.ts    From nestjs-joi with MIT License 4 votes vote down vote up
describe('basic integration', () => {
  describe('with schema as argument', () => {
    it('should validate against the schema', async () => {
      const pipe = new JoiPipe(
        Joi.object().keys({
          prop: Joi.string(),
        }),
      );

      try {
        pipe.transform(
          {
            prop: 1,
          },
          { type: 'query' },
        );
        throw new Error('should not be thrown');
      } catch (error) {
        expect(error.message).toContain('"prop" must be a string');
      }
    });

    it('should validate against the schema, ignoring groups (positive test)', async () => {
      const pipe = new JoiPipe(
        Joi.object().keys({
          prop: Joi.string(),
        }),
        { group: 'group1' },
      );

      let error;
      try {
        pipe.transform(
          {
            prop: '1',
          },
          { type: 'query' },
        );
      } catch (error_) {
        error = error_;
      }

      expect(error).toBeUndefined();
    });

    it('should validate against the schema, ignoring groups (negative test)', async () => {
      const pipe = new JoiPipe(
        Joi.object().keys({
          prop: Joi.string(),
        }),
        { group: 'group1' },
      );

      try {
        pipe.transform(
          {
            prop: 1,
          },
          { type: 'query' },
        );
        throw new Error('should not be thrown');
      } catch (error) {
        expect(error.message).toContain('"prop" must be a string');
      }
    });

    it('should ignore a metatype passed in the metadata', async () => {
      const pipe = new JoiPipe(
        Joi.object().keys({
          prop: Joi.string(),
        }),
      );

      class metatype {
        @JoiSchema(Joi.number())
        prop!: number;
      }

      try {
        pipe.transform(
          {
            prop: 1,
          },
          { type: 'query', metatype },
        );
        throw new Error('should not be thrown');
      } catch (error) {
        expect(error.message).toContain('"prop" must be a string');
      }
    });
  });

  const CASES: {
    [name: string]: {
      fit?: boolean;
      type: Class;
      opts: { group?: string };
      payload: unknown;
      expectErrors: string[];
      notExpectErrors: string[];
    };
  } = {
    'schema constructed from empty type': {
      type: EmptyType,
      opts: {},
      payload: {},
      expectErrors: [],
      notExpectErrors: [],
    },
    'schema constructed from basic type': {
      type: BasicType,
      opts: {},
      payload: {
        prop1: 'foo',
      },
      expectErrors: ['"prop1" must be [basic_prop1]', '"prop2" is required'],
      notExpectErrors: ['"prop0"'],
    },
    'schema constructed from extended type': {
      type: ExtendedType,
      opts: {},
      payload: {
        prop1: 'foo',
        prop2: 'foo',
      },
      expectErrors: [
        '"prop1" must be [basic_prop1]',
        '"prop2" must be [extended_prop2]',
        '"extendedProp" is required',
      ],
      notExpectErrors: ['"prop0"'],
    },
    'schema constructed from decorator-extended type': {
      type: DecoratorExtendedType,
      opts: {},
      payload: {
        prop1: 'foo',
        prop2: 'foo',
      },
      expectErrors: [
        '"prop1" must be [basic_prop1]',
        '"prop2" must be [decorator_extended_prop2]',
        '"extendedProp" is required',
      ],
      notExpectErrors: ['"prop0"'],
    },
    'schema constructed from basic type, respecting groups': {
      type: BasicType,
      opts: { group: 'group1' },
      payload: {
        prop1: 'foo',
        prop2: 'foo',
      },
      expectErrors: [
        '"prop1" must be [basic_prop1_group1]',
        '"prop2" must be [basic_prop2_group1]',
      ],
      notExpectErrors: ['"prop0"'],
    },
    'schema constructed from basic type, falling back to default group': {
      type: BasicType,
      opts: { group: 'groupX' },
      payload: {
        prop1: 'foo',
        prop2: 'foo',
      },
      expectErrors: ['"prop1" must be [basic_prop1]', '"prop2" must be [basic_prop2]'],
      notExpectErrors: ['"prop0"'],
    },
    'schema constructed from extended type, respecting groups': {
      type: ExtendedType,
      opts: { group: 'group1' },
      payload: {
        prop1: 'foo',
        prop2: 'foo',
        extendedProp: 'foo',
      },
      expectErrors: [
        '"prop1" must be [basic_prop1_group1]',
        '"prop2" must be [extended_prop2_group1]',
        '"extendedProp" must be [extended_extendedProp_group1]',
      ],
      notExpectErrors: ['"prop0"'],
    },
    'schema constructed from decorator-extended type, respecting groups': {
      type: DecoratorExtendedType,
      opts: { group: 'group1' },
      payload: {
        prop1: 'foo',
        prop2: 'foo',
        extendedProp: 'foo',
      },
      expectErrors: [
        '"prop1" must be [basic_prop1_group1]',
        '"prop2" must be [decorator_extended_prop2_group1]',
        '"extendedProp" must be [decorator_extended_extendedProp_group1]',
      ],
      notExpectErrors: ['"prop0"'],
    },
    'schema constructed from advanced type (positive, alternative 1)': {
      type: AdvancedType,
      opts: {},
      payload: {
        prop: {
          prop1: 'basic_prop1',
          prop2: 'basic_prop2',
        },
      },
      expectErrors: [],
      notExpectErrors: ['"prop"'],
    },
    'schema constructed from advanced type (positive, alternative 2)': {
      type: AdvancedType,
      opts: {},
      payload: {
        prop: {
          prop1: 'basic_prop1',
          prop2: 'extended_prop2',
          extendedProp: 'extended_extendedProp',
        },
      },
      expectErrors: [],
      notExpectErrors: ['"prop"'],
    },
    'schema constructed from advanced type (negative)': {
      type: AdvancedType,
      opts: {},
      payload: {
        prop: {
          prop1: 'basic_prop1',
        },
      },
      expectErrors: ['"prop" does not match any of the allowed types'],
      notExpectErrors: [],
    },
    'schema constructed from basic type with options': {
      type: BasicTypeWithOptions,
      opts: {},
      payload: {
        prop: 'basicwithoptions_prop',
        unknownProp: 'string',
      },
      expectErrors: ['"unknownProp" is not allowed'],
      notExpectErrors: [],
    },
    'schema constructed from extended type with options': {
      type: ExtendedTypeWithOptions,
      opts: {},
      payload: {
        prop: 'basicwithoptions_prop',
        unknownProp: 'string',
      },
      expectErrors: [],
      notExpectErrors: [],
    },
    'schema constructed from decorator-extended type with options': {
      type: DecoratorExtendedTypeWithOptions,
      opts: {},
      payload: {
        prop: 'basicwithoptions_prop',
        unknownProp: 'string',
      },
      expectErrors: [],
      notExpectErrors: [],
    },
    'schema constructed from basic type with options, respecting groups': {
      type: BasicTypeWithOptions,
      opts: { group: 'group1' },
      payload: {
        prop: 'basicwithoptions_prop',
        unknownProp: 'string',
      },
      expectErrors: [],
      notExpectErrors: [],
    },
    'schema constructed from extended type with options, respecting groups': {
      type: ExtendedTypeWithOptions,
      opts: { group: 'group1' },
      payload: {
        prop: 'basicwithoptions_prop',
        unknownProp: 'string',
      },
      expectErrors: ['"unknownProp" is not allowed'],
      notExpectErrors: [],
    },
    'schema constructed from decorator-extended type with options, respecting groups': {
      type: DecoratorExtendedTypeWithOptions,
      opts: { group: 'group1' },
      payload: {
        prop: 'basicwithoptions_prop',
        unknownProp: 'string',
      },
      expectErrors: ['"unknownProp" is not allowed'],
      notExpectErrors: [],
    },
    'schema constructed from basic type with no default options': {
      type: BasicTypeWithNoDefaultOptions,
      opts: {},
      payload: {
        prop1: 'string',
        prop2: 'string',
      },
      expectErrors: [
        '"prop1" must be [basicwithnodefaultoptions_prop1]',
        '"prop2" must be [basicwithnodefaultoptions_prop2]',
      ],
      notExpectErrors: [],
    },
    'schema constructed from basic type with no default options, respecting groups': {
      type: BasicTypeWithNoDefaultOptions,
      opts: { group: 'group1' },
      payload: {
        prop1: 'string',
        prop2: 'string',
      },
      expectErrors: ['"prop1" must be [basicwithnodefaultoptions_prop1_group1]'],
      notExpectErrors: ['"prop2" must be [basicwithnodefaultoptions_prop2_group1]'],
    },
    'schema constructed from type with nested type (negative)': {
      type: TypeWithNestedType,
      opts: {},
      payload: {
        prop0: 'string',
        prop1: 'string',
        nestedProp: {
          prop1: 'string',
          prop2: 'string',
        },
      },
      expectErrors: [
        '"prop1" must be [nested_prop1]',
        '"nestedProp.prop1" must be [basic_prop1]',
        '"nestedProp.prop2" must be [basic_prop2]',
      ],
      notExpectErrors: ['"prop0"', 'nestedTypeWithFn'],
    },
    'schema constructed from type with nested type (positive)': {
      type: TypeWithNestedType,
      opts: {},
      payload: {
        prop0: 'string',
        prop1: 'nested_prop1',
        nestedProp: {
          prop1: 'basic_prop1',
          prop2: 'basic_prop2',
        },
      },
      expectErrors: [],
      notExpectErrors: ['"prop0"', '"prop1"', '"nestedProp.prop1"', '"nestedProp.prop2"'],
    },
    'schema constructed from type with nested type, respecting groups (negative)': {
      type: TypeWithNestedType,
      opts: { group: 'group1' },
      payload: {
        prop0: 'string',
        prop1: 'string',
        nestedProp: {
          prop1: 'string',
          prop2: 'string',
          extendedProp: 'string',
        },
      },
      expectErrors: [
        '"prop1" must be [nested_prop1_group1]',
        '"nestedProp.prop1" must be [basic_prop1_group1]',
        '"nestedProp.prop2" must be [extended_prop2_group1]',
        '"nestedProp.extendedProp" must be [extended_extendedProp_group1]',
      ],
      notExpectErrors: ['"prop0"', 'nestedTypeWithFn'],
    },
    'schema constructed from type with nested type, respecting groups (positive)': {
      type: TypeWithNestedType,
      opts: { group: 'group1' },
      payload: {
        prop0: 'string',
        prop1: 'nested_prop1_group1',
        nestedProp: {
          prop1: 'basic_prop1_group1',
          prop2: 'extended_prop2_group1',
          extendedProp: 'extended_extendedProp_group1',
        },
      },
      expectErrors: [],
      notExpectErrors: [
        '"prop0"',
        '"prop1"',
        '"nestedProp.prop1"',
        '"nestedProp.prop2"',
        '"nestedProp.extendedProp"',
      ],
    },

    'schema constructed from type with nested type array (negative)': {
      type: TypeWithNestedTypeArray,
      opts: {},
      payload: {
        prop0: 'string',
        prop1: 'string',
        nestedProp: [
          {
            prop1: 'string',
            prop2: 'string',
          },
        ],
      },
      expectErrors: [
        '"prop1" must be [nested_array_prop1]',
        '"nestedProp[0].prop1" must be [basic_prop1]',
        '"nestedProp[0].prop2" must be [basic_prop2]',
      ],
      notExpectErrors: ['"prop0"'],
    },
    'schema constructed from type with nested type array (positive)': {
      type: TypeWithNestedTypeArray,
      opts: {},
      payload: {
        prop0: 'string',
        prop1: 'nested_array_prop1',
        nestedProp: [
          {
            prop1: 'basic_prop1',
            prop2: 'basic_prop2',
          },
        ],
      },
      expectErrors: [],
      notExpectErrors: [
        '"prop0"',
        '"prop1"',
        '"nestedProp[0].prop1"',
        '"nestedProp[0].prop2"',
        '"nestedProp[0].extendedProp"',
      ],
    },
    'schema constructed from type with nested type array, respecting groups (negative)': {
      type: TypeWithNestedTypeArray,
      opts: { group: 'group1' },
      payload: {
        prop0: 'string',
        prop1: 'string',
        nestedProp: [
          {
            prop1: 'string',
            prop2: 'string',
            extendedProp: 'string',
          },
        ],
      },
      expectErrors: [
        '"prop1" must be [nested_array_prop1_group1]',
        '"nestedProp[0].prop1" must be [basic_prop1_group1]',
        '"nestedProp[0].prop2" must be [extended_prop2_group1]',
        '"nestedProp[0].extendedProp" must be [extended_extendedProp_group1]',
      ],
      notExpectErrors: ['"prop0"'],
    },
    'schema constructed from type with nested type array, respecting groups (positive)': {
      type: TypeWithNestedTypeArray,
      opts: { group: 'group1' },
      payload: {
        prop0: 'string',
        prop1: 'nested_array_prop1_group1',
        nestedProp: [
          {
            prop1: 'basic_prop1_group1',
            prop2: 'extended_prop2_group1',
            extendedProp: 'extended_extendedProp_group1',
          },
        ],
      },
      expectErrors: [],
      notExpectErrors: [
        '"prop0"',
        '"prop1"',
        '"nestedProp[0].prop1"',
        '"nestedProp[0].prop2"',
        '"nestedProp[0].extendedProp"',
      ],
    },
    'schema constructed from type with nested type and customizer (positive)': {
      type: TypeWithNestedTypeAndCustomizer,
      opts: {},
      payload: {
        nestedProp: undefined,
      },
      expectErrors: [],
      notExpectErrors: ['"nestedProp"'],
    },
    'schema constructed from type with nested type and customizer (negative)': {
      type: TypeWithNestedTypeAndCustomizer,
      opts: {},
      payload: {
        nestedProp: {
          prop1: 'string',
          prop2: 'string',
        },
      },
      expectErrors: [
        '"nestedProp.prop1" must be [basic_prop1]',
        '"nestedProp.prop2" must be [basic_prop2]',
      ],
      notExpectErrors: [],
    },
    'schema constructed from type with nested type and customizer, respecting groups (negative)': {
      type: TypeWithNestedTypeAndCustomizer,
      opts: { group: 'group1' },
      payload: {
        nestedProp: undefined,
      },
      expectErrors: ['"nestedProp" is required'],
      notExpectErrors: [],
    },
    'schema constructed from type with nested type and customizer, respecting groups (positive)': {
      type: TypeWithNestedTypeAndCustomizer,
      opts: { group: 'group1' },
      payload: {
        nestedProp: {
          prop1: 'basic_prop1_group1',
          prop2: 'basic_prop2_group1',
        },
      },
      expectErrors: [],
      notExpectErrors: ['"nestedProp"'],
    },
    'schema constructed from type with nested type array and array customizer (positive)': {
      type: TypeWithNestedTypeArrayAndArrayCustomizer,
      opts: {},
      payload: {
        nestedProp: undefined,
      },
      expectErrors: [],
      notExpectErrors: ['"nestedProp"'],
    },
    'schema constructed from type with nested type array and array customizer (negative)': {
      type: TypeWithNestedTypeArrayAndArrayCustomizer,
      opts: {},
      payload: {
        nestedProp: [
          {
            prop1: 'string',
            prop2: 'string',
          },
        ],
      },
      expectErrors: [
        '"nestedProp[0].prop1" must be [basic_prop1]',
        '"nestedProp[0].prop2" must be [basic_prop2]',
      ],
      notExpectErrors: [],
    },
    'schema constructed from type with nested type array and array customizer, respecting groups (negative)':
      {
        type: TypeWithNestedTypeArrayAndArrayCustomizer,
        opts: { group: 'group1' },
        payload: {
          nestedProp: undefined,
        },
        expectErrors: ['"nestedProp" is required'],
        notExpectErrors: [],
      },
    'schema constructed from type with nested type array and array customizer, respecting groups (positive)':
      {
        type: TypeWithNestedTypeArrayAndArrayCustomizer,
        opts: { group: 'group1' },
        payload: {
          nestedProp: [
            {
              prop1: 'basic_prop1_group1',
              prop2: 'basic_prop2_group1',
            },
          ],
        },
        expectErrors: [],
        notExpectErrors: ['"nestedProp"'],
      },
    'schema constructed from type with nested type array and customizer (positive)': {
      type: TypeWithNestedTypeArrayAndCustomizer,
      opts: {},
      payload: {
        nestedProp: [],
      },
      expectErrors: [],
      notExpectErrors: ['"nestedProp"'],
    },
    'schema constructed from type with nested type array and customizer (negative)': {
      type: TypeWithNestedTypeArrayAndCustomizer,
      opts: {},
      payload: {
        nestedProp: [
          {
            prop1: 'string',
            prop2: 'string',
          },
        ],
      },
      expectErrors: [
        '"nestedProp[0].prop1" must be [basic_prop1]',
        '"nestedProp[0].prop2" must be [basic_prop2]',
      ],
      notExpectErrors: [],
    },
    'schema constructed from type with nested type array and customizer, respecting groups (negative)':
      {
        type: TypeWithNestedTypeArrayAndCustomizer,
        opts: { group: 'group1' },
        payload: {
          nestedProp: [],
        },
        expectErrors: ['"nestedProp" does not contain 1 required value'],
        notExpectErrors: [],
      },
    'schema constructed from type with nested type array and customizer, respecting groups (positive)':
      {
        type: TypeWithNestedTypeArrayAndCustomizer,
        opts: { group: 'group1' },
        payload: {
          nestedProp: [
            {
              prop1: 'basic_prop1_group1',
              prop2: 'basic_prop2_group1',
            },
          ],
        },
        expectErrors: [],
        notExpectErrors: ['"nestedProp"'],
      },
    'nothing: empty schema without @JoiSchema': {
      type: EmptyType,
      opts: {},
      payload: {
        prop: 'basicwithoptions_prop',
        unknownProp: 'string',
      },
      expectErrors: [],
      notExpectErrors: [],
    },
    // Special inbuilt types: construct cases
    ...fromPairs(
      [String, Object, Number, Array].map(type => [
        'nothing: handle inbuilt type ' + type.name,
        {
          type,
          opts: {},
          payload: {
            prop: 'basicwithoptions_prop',
            unknownProp: 'string',
          },
          expectErrors: [],
        },
      ]),
    ),
  };

  for (const mode of ['type', 'metatype']) {
    describe('with ' + mode + ' as argument', () => {
      for (const [
        caseName,
        { fit: useFit, type, opts, payload, expectErrors, notExpectErrors },
      ] of Object.entries(CASES)) {
        (useFit ? fit : it)('should validate against ' + caseName, async () => {
          const pipe = mode === 'type' ? new JoiPipe(type, opts) : new JoiPipe(opts);

          let error_;
          try {
            pipe.transform(payload, {
              type: 'query',
              metatype: mode === 'metatype' ? type : undefined,
            });

            if (expectErrors && expectErrors.length) {
              throw new Error('should not be thrown');
            }
          } catch (error) {
            error_ = error;

            if (expectErrors && expectErrors.length) {
              expectErrors.map(x => expect(error.stack).toContain(x));
            }
            if (notExpectErrors && notExpectErrors.length) {
              notExpectErrors.map(x => expect(error.stack).not.toContain(x));
            }
          }

          if (!expectErrors || !expectErrors.length) {
            expect(error_).toBeUndefined();
          }
        });
      }
    });
  }
});