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

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

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 131 132 133 134 135                                                                                                                                                                                                                                                                             
/**
 * @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 $RefParser from '@apidevtools/json-schema-ref-parser';
import Ajv from 'ajv';
import type { JSONSchema6 } from 'json-schema';
import type { JSONSchema8 as Schema } from 'jsonschema8';

import schemas, { requireSchema } from './';

/**
 * @todo where should this live?
 */
export 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$/, '');
    return requireSchema(file);
  }

  throw new Error(`Unknown schema URI: ${uri}`);
  /*
    // Try to fetch schema online
    const { data: schema } = await axios.get<Schema>(uri);
    return schema;
     */
}

const ajv = new Ajv({
  async loadSchema(uri) {
    return loadSchema(uri);
  },
  /* 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'
  );

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

// FIXME: Figure out less hacky way to make it find the files correctly
let checkReferences: (key: string, schema: Schema) => Promise<unknown>;
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 directory = '.';
            const path = url.startsWith('https://formats.openag.io')
              ? url.replace(r, '')
              : relative('', url);
            const file = (
              isAbsolute(path)
                ? join(directory, path)
                : join(directory, dirname(key), path)
            ).replace(/\.json$/, '.cjs');
            return requireSchema(file);
          },
        },
      },
    });
  };
});

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

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

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

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

  // eslint-disable-next-line @typescript-eslint/no-loop-func
  test(`${key} should have valid external $ref's`, async (t) => {
    await t.notThrowsAsync(checkReferences(key, schema));
  });

  test(`${key} should have valid default`, async (t) => {
    // eslint-disable-next-line unicorn/prevent-abbreviations
    const { default: def } = schema;
    t.plan(def ? 1 : 0);
    if (def) {
      t.assert(ajv.validate(schema, def), ajv.errorsText());
    }
  });

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