All files / schemas/.history index.spec_20220609152710.ts

0% Statements 0/129
0% Branches 0/1
0% Functions 0/1
0% Lines 0/129

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130                                                                                                                                                                                                                                                                   
/**
 * @license
 * Copyright 2022 Open Ag Data Alliance
 *
 * Use of this source code is governed by an MIT-style
 * license that can be found in the LICENSE file or at
 * https://opensource.org/licenses/MIT.
 */

import test from 'ava';

import { dirname, isAbsolute, join, relative } from 'node:path';

import type { JSONSchema8 as Schema } from 'jsonschema8';
import type { JSONSchema6 } from 'json-schema';
import Ajv from 'ajv';
import $RefParser from '@apidevtools/json-schema-ref-parser';

import schemas from '.';

/**
 * @todo where should this live?
 */
export async function loadSchema(uri: string) {
  const r = /^https:\/\/formats\.openag\.io/i;

  if (r.test(uri)) {
    // Use local version of openag schemas
    const file = uri.replace(r, '.').replace(/\.json$/, '');
    const { default: schema } = await import(file);
    return schema;
  }
  /*
    // Try to fetch schema online
    const { data: schema } = await axios.get<Schema>(uri);
    return schema;
     */
}

const ajv = new Ajv({
  loadSchema,
  /* ProcessCode, */
  inlineRefs: false,
  allErrors: true,
  strict: false,
  // AJV complains about standard formats if this is on
  validateFormats: false,
});
test.before('Initialize JSON Schema validator', async () => {
  const meta = await $RefParser.dereference(
    'https://json-schema.org/draft/2019-09/schema'
  );

  // TODO: Why does compileAsync not work for meta schema?
  ajv.addMetaSchema(meta);
});

// TODO: Figure out less hacky way to make it find the files correctly
let checkReferences: (key: string, schema: Schema) => Promise<any>;
test.before('Initialize $ref checker', () => {
  checkReferences = async (key: string, schema: Schema) => {
    const $refparser = new $RefParser();
    return $refparser.dereference(schema as JSONSchema6, {
      resolve: {
        file: {
          order: 0,
          canRead: true,
          // TODO: Support external $ref
          async read({ url }) {
            const r = /^https:\/\/formats\.openag\.io/;
            const dir = '.';
            const path = url.startsWith('https://formats.openag.io')
              ? url.replace(r, '')
              : relative('', url);
            const file = (
              isAbsolute(path) ? join(dir, path) : join(dir, dirname(key), path)
            ).replace(/\.json$/, '');
            const { default: schema } = await import(`./${file}`);
            return schema;
          },
        },
      },
    });
  };
});

// TODO: Can you make these parallel in ava?
for (const { schema, key } of schemas()) {
  test.before(`Compile schema ${key}`, async () => {
    try {
      await ajv.compileAsync(await schema);
    } catch {
      // Already compiled?
    }
  });

  test(`${key} should be valid JSON Schema`, async (t) => {
    t.assert(ajv.validateSchema(await schema));
  });

  // $id needs to be consistent with file structure or most tools get upset
  test(`${key} should have consistent $id`, async (t) => {
    const { $id } = await schema;
    t.is($id, `https://${join('formats.openag.io/', key)}`);
  });

  test.todo(`${key} should have valid self $ref's`);

  test(`${key} should have valid external $ref's`, async (t) => {
    await t.notThrowsAsync(checkReferences(key, await schema));
  });

  test(`${key} should have valid default`, async (t) => {
    const { default: def } = await schema;
    t.plan(def ? 1 : 0);
    if (def) {
      t.assert(ajv.validate(await schema, def), ajv.errorsText());
    }
  });

  test(`${key} should validate examples`, async (t) => {
    const { examples } = await schema;
    t.plan(examples?.length ?? 0);
    for (const index in examples ?? []) {
      const example = examples![index];
      t.assert(ajv.validate(await schema, example), ajv.errorsText());
    }
  });
}