UNPKG

@sprucelabs/spruce-cli

Version:

Command line interface for building Spruce skills.

319 lines • 16.6 kB
"use strict"; var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; return c > 3 && r && Object.defineProperty(target, key, r), r; }; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); const path_1 = __importDefault(require("path")); const mercury_types_1 = require("@sprucelabs/mercury-types"); const spruce_event_utils_1 = require("@sprucelabs/spruce-event-utils"); const spruce_skill_utils_1 = require("@sprucelabs/spruce-skill-utils"); const test_utils_1 = require("@sprucelabs/test-utils"); const EventWriter_1 = require("../../../features/event/writers/EventWriter"); const LintService_1 = __importDefault(require("../../../services/LintService")); const AbstractEventTest_1 = __importDefault(require("../../../tests/AbstractEventTest")); const test_utility_1 = __importDefault(require("../../../tests/utilities/test.utility")); class KeepingEventsInSyncTest extends AbstractEventTest_1.default { static randomVersion = 'v2020_01_01'; static get todaysVersion() { return spruce_skill_utils_1.versionUtil.generateVersion(); } static async syncingSchemasWithDisconnectedStopsWithError() { await this.FeatureFixture().installCachedFeatures('events'); await this.syncCoreEventsPretendingToBeMercuryTypes(); const results = await this.Action('schema', 'sync').execute({}); const match = test_utility_1.default.assertFileByNameInGeneratedFiles('sendMessageEmitPayload.schema.ts', results.files); test_utils_1.assert.isTrue(spruce_skill_utils_1.diskUtil.doesFileExist(match)); const client = await this.getMercuryFixture().connectToApi({ shouldAuthAsCurrentSkill: true, }); await client.disconnect(); const results2 = await this.Action('schema', 'sync').execute({}); test_utils_1.assert.isTruthy(results2.errors); test_utils_1.assert.isTrue(spruce_skill_utils_1.diskUtil.doesFileExist(match)); } static async syncingSchemasDoesNotSyncEventSchemasIfEventsNotInstalled() { await this.FeatureFixture().installCachedFeatures('schemas'); const results = await this.Action('schema', 'sync').execute({}); test_utils_1.assert.doesThrow(() => this.assertCorePayloadSchemasAreCreated(results)); } static async canGetNumberOfEventsBackFromHealthCheck() { const cli = await this.FeatureFixture().installCachedFeatures('events'); LintService_1.default.enableLinting(); const results = await this.Action('event', 'sync').execute({}); test_utils_1.assert.isFalsy(results.errors); await this.Service('build').build(); const health = await cli.checkHealth({ shouldRunOnSourceFiles: false }); test_utils_1.assert.isTruthy(health.skill); test_utils_1.assert.isFalsy(health.skill.errors); test_utils_1.assert.isTruthy(health.event); test_utils_1.assert.isEqual(health.event.status, 'passed'); test_utils_1.assert.isTruthy(health.event.contracts); test_utils_1.assert.isAbove(health.event.contracts.length, 0); } static async syncsEventsFromOtherSkills() { const { skillFixture, skill2 } = await this.seedDummySkillRegisterCurrentSkillAndInstallToOrg(); const eventName = `my-new-event::${this.todaysVersion.constValue}`; const fqen = `${skill2.slug}.my-new-event::${this.todaysVersion.constValue}`; const description = (0, test_utils_1.generateId)() + '"' + '"'; const aiInstructions = (0, test_utils_1.generateId)() + '"' + '"'; await skillFixture.registerEventContract(skill2, { eventSignatures: { [eventName]: { description, aiInstructions, emitPayloadSchema: (0, spruce_event_utils_1.buildEmitTargetAndPayloadSchema)({ eventName: 'my-new-event', targetSchema: spruce_event_utils_1.eventTargetSchema, payloadSchema: { id: 'myNewEventEmitPayloadId', fields: { onlyField: { type: 'text' } }, }, }), responsePayloadSchema: { id: 'myNewEventResponsePayloadId', fields: {}, }, emitPermissionContract: (0, mercury_types_1.buildPermissionContract)({ id: 'myNewEventEmitPermissionContract', name: 'My new event emit permissionContract', permissions: [ { id: 'can-emit', name: 'Can emit my new event', defaults: { guest: { default: true, }, }, }, ], }), listenPermissionContract: (0, mercury_types_1.buildPermissionContract)({ id: 'myNewEventListenPermissionContract', name: 'My new event listen permissionContract', permissions: [ { id: 'can-listen', name: 'Can emit my new event', defaults: { guest: { default: true, }, }, }, ], }), }, }, }); this.Service('dependency').add({ id: skill2.id, namespace: skill2.slug, }); const results = await this.Action('event', 'sync').execute({}); const match = test_utility_1.default.assertFileByNameInGeneratedFiles(`myNewEvent.${this.todaysVersion.dirValue}.contract.ts`, results.files); test_utils_1.assert.doesInclude(match, `${spruce_skill_utils_1.namesUtil.toCamel(skill2.slug)}${path_1.default.sep}myNewEvent.${this.todaysVersion.dirValue}.contract.ts`); const contract = (await this.Service('import').importDefault(match)); const sig = spruce_event_utils_1.eventContractUtil.getSignatureByName(contract, fqen); test_utils_1.assert.isTruthy(sig.emitPayloadSchema); test_utils_1.assert.isTruthy( //@ts-ignore sig.emitPayloadSchema.fields?.payload?.options?.schema?.id, 'myNewEventEmitPayloadId'); test_utils_1.assert.isTruthy(sig.responsePayloadSchema?.id, 'myNewEventResponsePayloadId'); test_utils_1.assert.isTruthy(sig.emitPermissionContract); test_utils_1.assert.isEqual(sig.emitPermissionContract.id, `${skill2.slug}.myNewEventEmitPermissionContract`); test_utils_1.assert.isEqual(sig.emitPermissionContract.permissions[0].id, 'can-emit'); test_utils_1.assert.isTruthy(sig.listenPermissionContract); test_utils_1.assert.isEqual(sig.listenPermissionContract.id, `${skill2.slug}.myNewEventListenPermissionContract`); test_utils_1.assert.isEqual(sig.listenPermissionContract.permissions[0].id, 'can-listen'); test_utils_1.assert.isEqual(sig.description, description, 'description missing'); test_utils_1.assert.isEqual(sig.aiInstructions, aiInstructions, 'aiInstructions missing'); } static async twoSkillsWithSameEventCanBeSynced() { const { skill2, skillFixture, orgFixture, org } = await this.seedDummySkillRegisterCurrentSkillAndInstallToOrg(); const skill3 = await skillFixture.seedDemoSkill({ name: 'a third skill', }); await orgFixture.installSkillAtOrganization(skill3.id, org.id); const eventName = `my-new-event::${this.todaysVersion.constValue}`; await skillFixture.registerEventContract(skill2, { eventSignatures: { [eventName]: { emitPayloadSchema: (0, spruce_event_utils_1.buildEmitTargetAndPayloadSchema)({ eventName: 'my-new-event', targetSchema: spruce_event_utils_1.eventTargetSchema, }), }, }, }); await skillFixture.registerEventContract(skill3, { eventSignatures: { [eventName]: { emitPayloadSchema: (0, spruce_event_utils_1.buildEmitTargetAndPayloadSchema)({ eventName: 'my-new-event', targetSchema: spruce_event_utils_1.eventTargetSchema, }), }, }, }); await this.Service('dependency').set([ { id: skill2.id, namespace: skill2.slug, }, { id: skill3.id, namespace: skill3.slug, }, ]); const results = await this.Action('event', 'sync').execute({}); const contract = test_utility_1.default.assertFileByNameInGeneratedFiles('events.contract.ts', results.files); await this.Service('typeChecker').check(contract); } static async skillWithSameEventNameButDifferentVersionsCanBeSynced() { const { skill2, skillFixture } = await this.seedDummySkillRegisterCurrentSkillAndInstallToOrg(); const eventName = `my-new-event::${this.todaysVersion.constValue}`; const eventName2 = `my-new-event::${this.randomVersion}`; await skillFixture.registerEventContract(skill2, { eventSignatures: { [eventName]: { emitPayloadSchema: (0, spruce_event_utils_1.buildEmitTargetAndPayloadSchema)({ eventName: 'my-new-event', targetSchema: spruce_event_utils_1.eventTargetSchema, }), }, [eventName2]: { emitPayloadSchema: (0, spruce_event_utils_1.buildEmitTargetAndPayloadSchema)({ eventName: 'my-new-event', targetSchema: spruce_event_utils_1.eventTargetSchema, }), }, }, }); await this.Service('dependency').add({ id: skill2.id, namespace: skill2.slug, }); const results = await this.Action('event', 'sync').execute({}); const contract = test_utility_1.default.assertFileByNameInGeneratedFiles('events.contract.ts', results.files); await this.Service('typeChecker').check(contract); } static async unRegisteredEventsAreRemoved() { const { skillFixture, syncResults, skill2, contractFileName } = await this.seedSkillsAndRegisterAndSyncEvents(); await this.assertValidActionResponseFiles(syncResults); await skillFixture.unRegisterEvents(skill2, { shouldUnregisterAll: true, }); const eventContract = test_utility_1.default.assertFileByNameInGeneratedFiles(contractFileName, syncResults.files); test_utils_1.assert.isTrue(spruce_skill_utils_1.diskUtil.doesFileExist(eventContract)); await this.Action('event', 'sync').execute({}); test_utils_1.assert.isFalse(spruce_skill_utils_1.diskUtil.doesFileExist(eventContract)); const dirname = path_1.default.dirname(eventContract); test_utils_1.assert.isFalse(spruce_skill_utils_1.diskUtil.doesDirExist(dirname)); } static async syncCoreEventsPretendingToBeMercuryTypes() { const results = await this.Action('event', 'sync').execute({ shouldSyncOnlyCoreEvents: true, eventBuilderFile: '../../../builder', skillEventContractTypesFile: '../../builder', }); const builder = ` export function buildEventContract(..._: any[]):any { return _[0] } export function buildPermissionContract(..._: any[]):any { return _[0] } `; spruce_skill_utils_1.diskUtil.writeFile(this.resolvePath('src', 'builder.ts'), builder); await this.Service('pkg').uninstall([ '@sprucelabs/mercury-types', '@sprucelabs/mercury-client', '@sprucelabs/spruce-event-plugin', ]); return results; } static async seedSkillsAndRegisterAndSyncEvents(signature) { const { skill2, skillFixture, cli } = await this.seedDummySkillRegisterCurrentSkillAndInstallToOrg(); const { results, filename } = await this.registerEventAndSync(skill2, signature); return { skillFixture, syncResults: results, cli, skill2, contractFileName: filename, }; } static async registerEventAndSync(skill, signature) { const skillFixture = this.getSkillFixture(); const stamp = new Date().getTime(); const eventName = `cleanup-event-test-${stamp}::${this.todaysVersion.constValue}`; const filename = (0, EventWriter_1.generateEventContractFileName)({ nameCamel: `cleanupEventTest${stamp}`, version: this.todaysVersion.constValue, }); await skillFixture.registerEventContract(skill, { eventSignatures: { [eventName]: { emitPayloadSchema: (0, spruce_event_utils_1.buildEmitTargetAndPayloadSchema)({ eventName, targetSchema: spruce_event_utils_1.eventTargetSchema, }), ...signature, }, }, }); await this.Service('dependency').add({ id: skill.id, namespace: skill.slug, }); const results = await this.Action('event', 'sync').execute({}); return { results, filename }; } static assertCorePayloadSchemasAreCreated(results, shouldHaveWritten = true) { const filesToCheck = [ { name: `unregisterListenersEmitTargetAndPayload.schema.ts`, path: `schemas${path_1.default.sep}${spruce_skill_utils_1.MERCURY_API_NAMESPACE}`, }, ]; this.assertFilesWereGenerated(filesToCheck, results, shouldHaveWritten); } static assertFilesWereGenerated(filesToCheck, results, shouldHaveWritten = true) { for (const file of filesToCheck) { const expected = this.resolveHashSprucePath('schemas/mercury/v2020_12_25', file.name); const doesExist = spruce_skill_utils_1.diskUtil.doesFileExist(expected); if (shouldHaveWritten) { test_utils_1.assert.isTrue(doesExist, `Expected to find ${file} on the filesystem.`); } else { test_utils_1.assert.isFalse(doesExist, `Should not have written ${file}.`); } } } } exports.default = KeepingEventsInSyncTest; __decorate([ test_utils_1.test.skip('find how to simulate connection errors') ], KeepingEventsInSyncTest, "syncingSchemasWithDisconnectedStopsWithError", null); __decorate([ (0, test_utils_1.test)() ], KeepingEventsInSyncTest, "syncingSchemasDoesNotSyncEventSchemasIfEventsNotInstalled", null); __decorate([ (0, test_utils_1.test)() ], KeepingEventsInSyncTest, "canGetNumberOfEventsBackFromHealthCheck", null); __decorate([ (0, test_utils_1.test)() ], KeepingEventsInSyncTest, "syncsEventsFromOtherSkills", null); __decorate([ (0, test_utils_1.test)() ], KeepingEventsInSyncTest, "twoSkillsWithSameEventCanBeSynced", null); __decorate([ (0, test_utils_1.test)() ], KeepingEventsInSyncTest, "skillWithSameEventNameButDifferentVersionsCanBeSynced", null); __decorate([ (0, test_utils_1.test)() ], KeepingEventsInSyncTest, "unRegisteredEventsAreRemoved", null); //# sourceMappingURL=KeepingEventsInSync2.test.js.map