UNPKG

@sprucelabs/spruce-cli

Version:

Command line interface for building Spruce skills.

316 lines (258 loc) • 9.62 kB
import { buildSchema, normalizeSchemaToIdWithVersion, Schema, validateSchema, } from '@sprucelabs/schema' import { CORE_NAMESPACE, diskUtil, namesUtil, } from '@sprucelabs/spruce-skill-utils' import { test, assert } from '@sprucelabs/test-utils' import { errorAssert } from '@sprucelabs/test-utils' import fieldClassMap from '#spruce/schemas/fields/fieldClassMap' import { coreSchemas } from '../../features/schema/stores/SchemaStore' import AbstractSchemaTest from '../../tests/AbstractSchemaTest' const LOCAL_NAMESPACE = 'TestSkill' export default class SchemaStoreTest extends AbstractSchemaTest { @test() protected static async canInstantiateSchemaStore() { assert.isTruthy(this.Store('schema')) } @test() protected static async hasFetchSchemaTemplateItemsMethod() { assert.isFunction(this.Store('schema').fetchSchemas) assert.isFunction(this.Store('schema').fetchFields) } @test() protected static async fetchesCoreSchemas() { const results = await this.Store('schema').fetchSchemas({ localNamespace: LOCAL_NAMESPACE, }) const { schemasByNamespace, errors } = results assert.isEqual(errors.length, 0) assert.isTruthy(schemasByNamespace[CORE_NAMESPACE]) const coreSchemas = schemasByNamespace[CORE_NAMESPACE] assert.isAbove(coreSchemas.length, 0) this.validateSchemas(coreSchemas) assert.doesInclude(coreSchemas, { id: 'person' }) assert.doesInclude(coreSchemas, { id: 'skill' }) } @test() protected static async fetchesLocalSchemas() { const results = await this.copySchemasAndFetchSchemas({ localSchemaDir: this.resolvePath('src', 'schemas'), }) const { schemasByNamespace, errors } = results assert.isEqual(errors.length, 0) assert.isTruthy(schemasByNamespace[LOCAL_NAMESPACE]) assert.isLength(Object.keys(schemasByNamespace), 2) const localSchemas = schemasByNamespace[LOCAL_NAMESPACE] this.validateSchemas(localSchemas) assert.isLength(localSchemas, 3) } @test.skip( "Skipped because schemas are bulk imported and it's all or nothing." ) protected static async canHandleABadSchema() { const results = await this.copySchemasAndFetchSchemas( { localSchemaDir: this.resolvePath('src', 'schemas'), }, 'test_builders_one_bad' ) assert.isEqual(results.errors.length, 1) assert.doesInclude(results.errors[0].message, 'badSchema') const { schemasByNamespace } = results const localSchemas = schemasByNamespace[LOCAL_NAMESPACE] this.validateSchemas(localSchemas) assert.isLength(localSchemas, 3) } @test() protected static async canFetchCoreFields() { const results = await SchemaStoreTest.copySchemasAndFieldsThenFetchFields() const fieldTypes = Object.keys(fieldClassMap) for (const type of fieldTypes) { assert.doesInclude(results, { 'fields[].registration.type': namesUtil.toPascal(type), }) } } @test() protected static async canFetchLocalFields() { const results = await this.copySchemasAndFieldsThenFetchFields({ localAddonsDir: this.resolvePath('src', 'addons'), }) assert.isLength(results.errors, 0) assert.isLength(results.fields, Object.keys(fieldClassMap).length + 1) assert.doesInclude(results, { 'fields[].registration.type': 'Test' }) assert.doesInclude(results, { 'fields[].registration.description': 'A test for us all', }) } @test() protected static async canHandleBadFields() { const results = await this.copySchemasAndFieldsThenFetchFields( { localAddonsDir: this.resolvePath('src', 'addons'), }, 'field_registrations_one_bad' ) assert.isLength(results.errors, 1) assert.doesInclude(results.errors[0].message, 'badField') } @test() protected static async wontLetYouSpecifyANamespaceNorVersion() { const results = await this.copySchemasAndFetchSchemas( {}, 'test_builders_with_namespace_and_version' ) assert.isTruthy(results.errors) assert.isLength(results.errors, 2) errorAssert.assertError(results.errors[0], 'SCHEMA_FAILED_TO_IMPORT') errorAssert.assertError(results.errors[1], 'SCHEMA_FAILED_TO_IMPORT') results.errors = results.errors.sort((a, b) => //@ts-ignore a.originalError.options.schemaId > b.originalError.options.schemaId ? 1 : -1 ) errorAssert.assertError( // @ts-ignore results.errors[0].originalError, // @ts-ignore 'INVALID_SCHEMA', { schemaId: 'schemaWithNamespace', errors: ['namespace_should_not_be_set'], } ) errorAssert.assertError( // @ts-ignore results.errors[1].originalError, // @ts-ignore 'INVALID_SCHEMA', { schemaId: 'schemaWithVersion', errors: ['version_should_not_be_set'], } ) } @test() protected static async emitsDidFetchEvent() { const cli = await this.installSchemaFeature('schemas') let wasFired = false await cli.on('schema.did-fetch-schemas', () => { wasFired = true return {} }) await this.Store('schema').fetchSchemas({ localNamespace: LOCAL_NAMESPACE, }) assert.isTrue(wasFired) } @test() protected static async fetchEventPayloadIncludesAllOtherSchemas() { const cli = await this.installSchemaFeature('schemas') let emitPayload: any await cli.on('schema.did-fetch-schemas', (payload) => { emitPayload = payload return {} }) await this.Store('schema').fetchSchemas({ localNamespace: LOCAL_NAMESPACE, }) for (const name in coreSchemas) { assert.doesInclude( emitPayload.schemas, //@ts-ignore normalizeSchemaToIdWithVersion(coreSchemas[name]) ) } } @test() protected static async canListenToFetchEventToDropInAdditionalSchemas() { const cli = await this.installSchemaFeature('schemas') await cli.on('schema.did-fetch-schemas', () => { return { schemas: [ buildSchema({ id: 'test', namespace: 'MyCoolNamespace', fields: {}, }), ], } }) const results = await this.Store('schema').fetchSchemas({ localNamespace: LOCAL_NAMESPACE, }) assert.isLength(results.errors, 0) const { schemasByNamespace } = results assert.isTruthy(schemasByNamespace.MyCoolNamespace) } @test() protected static async multipleSchemasWithSameIdAndVersionReturnOnce() { const cli = await this.installSchemaFeature('schemas') await this.copySchemas() await cli.on('schema.did-fetch-schemas', () => { return { schemas: [ { id: 'schemaOne', name: 'First schema', namespace: 'TestSkill', version: 'v2020_06_23', description: 'It is going to be great!', fields: { name: { type: 'text', isRequired: true, }, }, }, ], } }) const results = await this.Store('schema').fetchSchemas({ localNamespace: LOCAL_NAMESPACE, }) assert.isLength(results.schemasByNamespace.TestSkill, 3) } private static validateSchemas(schemas: Schema[]) { for (const schema of schemas) { validateSchema(schema) } } private static async copySchemasAndFetchSchemas( options?: Record<string, any>, testBuilderDir = 'test_builders' ) { await this.syncSchemas('schemas') await this.copySchemas(testBuilderDir) const results = await this.Store('schema').fetchSchemas({ localNamespace: LOCAL_NAMESPACE, ...(options || {}), }) return results } private static async copySchemas(testBuilderDir = 'test_builders') { const schemasDir = this.resolvePath('src', 'schemas') await diskUtil.copyDir(this.resolveTestPath(testBuilderDir), schemasDir) } private static async copySchemasAndFieldsThenFetchFields( options?: Record<string, any>, registrationsDir = 'field_registrations' ) { await this.syncSchemas('schemas') const addonsDir = this.resolvePath('src', 'addons') await diskUtil.copyDir( this.resolveTestPath(registrationsDir), addonsDir ) const results = await this.Store('schema').fetchFields(options) return results } }