UNPKG

@sprucelabs/spruce-cli

Version:

Command line interface for building Spruce skills.

377 lines (315 loc) • 10.8 kB
import globby from '@sprucelabs/globby' import { buildSchema, Schema, SchemaTypesRenderer } from '@sprucelabs/schema' import { CORE_NAMESPACE, diskUtil, namesUtil, randomUtil, versionUtil, } from '@sprucelabs/spruce-skill-utils' import { test, assert, generateId } from '@sprucelabs/test-utils' import CommandServiceImpl from '../../../services/CommandService' import LintService from '../../../services/LintService' import SchemaTemplateItemBuilder from '../../../templateItemBuilders/SchemaTemplateItemBuilder' import AbstractSkillTest from '../../../tests/AbstractSkillTest' import testUtil from '../../../tests/utilities/test.utility' import { GeneratedFile } from '../../../types/cli.types' export default class SyncingSchemasInGoTest extends AbstractSkillTest { protected static skillCacheKey = 'schemas' private static goDir: string private static builder1Name = 'aSchemaIBuilt' private static builder2Name = 'anotherSchemaIBuilt' private static renderer: SchemaTypesRenderer private static goModuleName = randomUtil.rand([ 'my-skill', 'awesome-skill', 'super-skill', ]) private static version = versionUtil.generateVersion().constValue private static skillNamespace: string = CORE_NAMESPACE private static schemaTemplateItemBuilder: SchemaTemplateItemBuilder private static goFullModuleName: string protected static async beforeEach(): Promise<void> { await super.beforeEach() if (!this.goDir) { this.goDir = this.resolvePath(this.skillDir, 'go-schemas') diskUtil.createDir(this.goDir) } CommandServiceImpl.fakeCommand(/yarn.*?/, { code: 1, }) this.renderer = SchemaTypesRenderer.Renderer() this.schemaTemplateItemBuilder = new SchemaTemplateItemBuilder( CORE_NAMESPACE ) } @test() protected static async canSyncCoreSchemasWithoutError() { this.moveToGoDir() this.goFullModuleName = await this.go.initGoProject(this.goModuleName) await this.sync() const tsFiles = await globby('**/*.ts', { cwd: this.cwd, ignore: ['go/pkg/**'], }) assert.isLength(tsFiles, 0, 'Expected no .ts files to be generated.') } @test() protected static async syncingGeneratesCoreGoFileIfSchemaDoesExist() { await this.createSchema(this.builder1Name) this.moveToGoDir() const syncResults = await this.sync() testUtil.assertFileByPathInGeneratedFiles( this.coreSchemaGoFilepath, syncResults.files ) assert.isTrue( diskUtil.doesFileExist(this.coreSchemaGoFilepath), 'Expected core_schemas.go file to exist.' ) } @test() protected static async coreSchemasFileHasStructBasedOnSchema() { await this.assertCoreFileIncludesSchema(this.builder1Name) } @test() protected static async coreShowsDifferentSchema() { await this.createSchema(this.builder2Name) this.moveToGoDir() await this.sync() await this.assertCoreFileIncludesSchema(this.builder2Name) } @test() protected static async doesNotIncludeSchemaIfDeleted() { diskUtil.deleteFile(this.getBuilderFilepath(this.builder1Name)) this.moveToGoDir() await this.sync() const contents = this.loadCoreSchemasFile() assert.doesNotInclude( contents, namesUtil.toPascal(this.builder1Name), 'Expected core_schemas.go to not include deleted schema.' ) } @test() protected static async coreSchemasFileIncludesPackageDeclaration() { this.assertCoreSchemaFileIncludes('package schemas') } @test() protected static async goFileIsValid() { this.moveToGoDir() await this.go.exec('build', './...') } @test() protected static async canRenderNestedSchemas() { const schema2 = await this.importBuilder(this.builder2Name) schema2.fields = { ...schema2.fields, hasNestedSchema: { type: 'schema', options: { schema: nestedSchema, }, }, dateOfBirth: { type: 'date', }, } diskUtil.writeFile( this.getBuilderFilepath(this.builder2Name), `import { buildSchema } from '@sprucelabs/schema' export default buildSchema(${JSON.stringify(schema2, null, 4)})` ) await this.lintBuilders() schema2.namespace = this.skillNamespace const expected = this.renderSchemaAsStruct(schema2) this.moveToGoDir() await this.sync() this.assertCoreSchemaFileIncludes(expected) } @test() protected static async generatedFileShouldPassVet() { this.moveToGoDir() await this.go.vet() } @test() protected static async writesSchemaFileForEachSchemaFound() { this.moveToGoDir() const results = await this.sync() const generated = ['nestedSchema', 'anotherSchemaIBuilt'] for (const schema of generated) { this.assertSchemaFileWrittenWithExpectedPackage( schema, results.files ) } } @test() protected static async canActuallyUseGeneratedSchemasInGoTest() { this.moveToGoDir() const testSrc = buildGoTest({ pwd: this.goFullModuleName, name: generateId(), age: Date.now() % 100, version: this.version, }) diskUtil.writeFile( this.resolvePath(this.cwd, 'test_schema.go'), testSrc ) await this.go.exec('test', './...') } @test() protected static async creatingSchemaWithDifferentVersionAlsoWorks() { this.version = versionUtil.generateVersion('2020_01_01').constValue await this.createSchema('versionedSchema') this.moveToGoDir() const results = await this.sync() this.assertSchemaFileWrittenWithExpectedPackage( 'versionedSchema', results.files ) } private static assertSchemaFileWrittenWithExpectedPackage( schema: string, files: GeneratedFile[] = [] ) { assert.isAbove(files.length, 0, 'Expected at least one generated file.') const path = testUtil.assertFileByPathInGeneratedFiles( `schemas/${this.skillNamespace.toLowerCase()}/${this.version}/${namesUtil.toSnake(schema)}.go`, files ) const contents = diskUtil.readFile(path) assert.doesInclude( contents, `package ${this.version}`, 'Did not include expected package declaration.' ) } private static async lintBuilders() { LintService.enableLinting() await this.Service('lint').fix(`**/*.builder.ts`) } private static async assertCoreFileIncludesSchema(name: string) { const struct = await this.importStructForSchema(name) this.assertCoreSchemaFileIncludes(struct) } private static assertCoreSchemaFileIncludes(needle: string) { const contents = this.loadCoreSchemasFile() assert.doesInclude( contents, needle, 'core_schemas.go missing expected content' ) } private static async importStructForSchema(name: string) { const imported = await this.importBuilder(name) imported.namespace = this.skillNamespace const struct = this.renderSchemaAsStruct(imported) return struct } private static loadCoreSchemasFile() { return diskUtil.readFile(this.coreSchemaGoFilepath) } private static renderSchemaAsStruct(imported: Schema) { const templateItems = this.schemaTemplateItemBuilder.buildTemplateItems( { [CORE_NAMESPACE]: [imported], } ) return this.renderer.render(imported, { language: 'go', schemaTemplateItems: templateItems, }) } private static async importBuilder(name: string) { return (await this.Service('import').importDefault( this.getBuilderFilepath(name) )) as Schema } private static async createSchema(name: string) { const results = await this.Action('schema', 'create').execute({ nameReadable: 'A schema i built!', nameCamel: name, version: this.version, }) assert.isFalsy( results.errors, 'Expected no errors when creating schema.' ) } private static getBuilderFilepath(name: string) { return this.resolvePath( this.skillDir, 'src', 'schemas', versionUtil.generateVersion().dirValue, `${name}.builder.ts` ) } private static get coreSchemaGoFilepath() { return diskUtil.resolvePath(this.goDir, 'schemas/core_schemas.go') } private static async sync() { const results = await this.Action('schema', 'sync', { shouldAutoHandleDependencies: true, }).execute({ shouldGenerateCoreSchemaTypes: true, schemaLookupDir: this.resolvePath(this.skillDir, 'src'), }) assert.isFalsy( results.errors, 'Expected no errors when syncing core schemas in a go project.' ) assert.isTruthy( results.files, 'Expected some generated files when syncing core schemas in a go project.' ) return results } private static moveToGoDir() { this.cwd = this.goDir this.setCwd(this.cwd) } } const nestedSchema = buildSchema({ id: 'nestedSchema', fields: { name: { type: 'text', }, age: { type: 'number', }, }, }) const buildGoTest = (options: { pwd: string name: string age: number version: string }) => ` package goschemas import ( "testing" spruce "${options.pwd}/schemas/spruce/${options.version}" ) func TestMakeASchemaIBuilt(t *testing.T) { data := map[string]interface{}{ "name": "${options.name}", "age": ${options.age}, } result, err := spruce.MakeNestedSchema(data) if err != nil { t.Fatalf("MakeNestedSchema failed: %v", err) } if result.Name != "${options.name}" { t.Errorf("Expected Name to be '${options.name}', got '%s'", result.Name) } if result.Age != ${options.age} { t.Errorf("Expected Age to be ${options.age}, got %f", result.Age) } } `