diff --git a/sequencing-server/cdict/channel_banananation.xml b/sequencing-server/cdict/channel_banananation.xml
new file mode 100644
index 0000000000..0a3e214a1d
--- /dev/null
+++ b/sequencing-server/cdict/channel_banananation.xml
@@ -0,0 +1,35 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+ OVEN-21
+ OVEN-26
+
+
+
+
+ 1
+ String of the oven
+
+ None
+
+ oven_sc
+ BAKE
+
+
+
+
+
diff --git a/sequencing-server/cdict/parameter_banananation.xml b/sequencing-server/cdict/parameter_banananation.xml
new file mode 100644
index 0000000000..03f9033a86
--- /dev/null
+++ b/sequencing-server/cdict/parameter_banananation.xml
@@ -0,0 +1,34 @@
+
+
+
+
+
+
+
+
+
+
+
+
+ The rate that the banana changes form green to yellow
+
+
+ banana_mgr
+ BANANA
+
+
+
+
+
+
+
+
+
+ 1
+ This is the rate that the banana can change color for all bananas
+
+
diff --git a/sequencing-server/test/batchLoaders/commandDictionaryTypescriptBatchLoader.spec.ts b/sequencing-server/test/batchLoaders/DictionaryTypescriptBatchLoader.spec.ts
similarity index 100%
rename from sequencing-server/test/batchLoaders/commandDictionaryTypescriptBatchLoader.spec.ts
rename to sequencing-server/test/batchLoaders/DictionaryTypescriptBatchLoader.spec.ts
diff --git a/sequencing-server/test/batchLoaders/expansionBatchLoader.spec.ts b/sequencing-server/test/batchLoaders/expansionBatchLoader.spec.ts
index a4d709658c..b9b53e8562 100644
--- a/sequencing-server/test/batchLoaders/expansionBatchLoader.spec.ts
+++ b/sequencing-server/test/batchLoaders/expansionBatchLoader.spec.ts
@@ -2,18 +2,31 @@ import type { GraphQLClient } from 'graphql-request';
import { insertExpansion, removeExpansion } from '../testUtils/Expansion';
import { expansionBatchLoader } from '../../src/lib/batchLoaders/expansionBatchLoader';
import { getGraphQLClient } from '../testUtils/testUtils';
-import { insertCommandDictionary, removeCommandDictionary } from '../testUtils/CommandDictionary';
+import { insertDictionary, removeDictionary } from '../testUtils/Dictionary';
import { insertParcel, removeParcel } from '../testUtils/Parcel';
+import { DictionaryType } from '../../src/types/types';
let graphqlClient: GraphQLClient;
let expansionId: number;
let commandDictionaryId: number;
+let channelDictionaryId: number;
+let parameterDictionaryId: number;
let parcelId: number;
beforeAll(async () => {
graphqlClient = await getGraphQLClient();
- commandDictionaryId = (await insertCommandDictionary(graphqlClient)).id;
- parcelId = (await insertParcel(graphqlClient, commandDictionaryId, 'expansionBatchLoaderTestParcel')).parcelId;
+ commandDictionaryId = (await insertDictionary(graphqlClient, DictionaryType.COMMAND)).id;
+ channelDictionaryId = (await insertDictionary(graphqlClient, DictionaryType.CHANNEL)).id;
+ parameterDictionaryId = (await insertDictionary(graphqlClient, DictionaryType.PARAMETER)).id;
+ parcelId = (
+ await insertParcel(
+ graphqlClient,
+ commandDictionaryId,
+ channelDictionaryId,
+ parameterDictionaryId,
+ 'expansionBatchLoaderTestParcel',
+ )
+ ).parcelId;
expansionId = await insertExpansion(
graphqlClient,
@@ -32,7 +45,9 @@ beforeAll(async () => {
afterAll(async () => {
await removeExpansion(graphqlClient, expansionId);
await removeParcel(graphqlClient, parcelId);
- await removeCommandDictionary(graphqlClient, commandDictionaryId);
+ await removeDictionary(graphqlClient, commandDictionaryId, DictionaryType.COMMAND);
+ await removeDictionary(graphqlClient, channelDictionaryId, DictionaryType.CHANNEL);
+ await removeDictionary(graphqlClient, parameterDictionaryId, DictionaryType.PARAMETER);
});
it('should load expansion data', async () => {
diff --git a/sequencing-server/test/batchLoaders/expansionSetBatchLoader.spec.ts b/sequencing-server/test/batchLoaders/expansionSetBatchLoader.spec.ts
index 4ef42bc645..fbb6673109 100644
--- a/sequencing-server/test/batchLoaders/expansionSetBatchLoader.spec.ts
+++ b/sequencing-server/test/batchLoaders/expansionSetBatchLoader.spec.ts
@@ -2,21 +2,34 @@ import type { GraphQLClient } from 'graphql-request';
import { expansionSetBatchLoader } from '../../src/lib/batchLoaders/expansionSetBatchLoader.js';
import { removeMissionModel, uploadMissionModel } from '../testUtils/MissionModel.js';
import { insertExpansion, insertExpansionSet, removeExpansion, removeExpansionSet } from '../testUtils/Expansion';
-import { insertCommandDictionary, removeCommandDictionary } from '../testUtils/CommandDictionary';
+import { insertDictionary, removeDictionary } from '../testUtils/Dictionary';
import { getGraphQLClient } from '../testUtils/testUtils.js';
import { insertParcel, removeParcel } from '../testUtils/Parcel';
+import { DictionaryType } from '../../src/types/types';
let graphqlClient: GraphQLClient;
let missionModelId: number;
let expansionId: number;
let expansionSetId: number;
let commandDictionaryId: number;
+let channelDictionaryId: number;
+let paramaterDictionaryId: number;
let parcelId: number;
beforeAll(async () => {
graphqlClient = await getGraphQLClient();
- commandDictionaryId = (await insertCommandDictionary(graphqlClient)).id;
- parcelId = (await insertParcel(graphqlClient, commandDictionaryId, 'expansionSetBatchLoaderTestParcel')).parcelId;
+ commandDictionaryId = (await insertDictionary(graphqlClient, DictionaryType.COMMAND)).id;
+ channelDictionaryId = (await insertDictionary(graphqlClient, DictionaryType.CHANNEL)).id;
+ paramaterDictionaryId = (await insertDictionary(graphqlClient, DictionaryType.PARAMETER)).id;
+ parcelId = (
+ await insertParcel(
+ graphqlClient,
+ commandDictionaryId,
+ channelDictionaryId,
+ paramaterDictionaryId,
+ 'expansionSetBatchLoaderTestParcel',
+ )
+ ).parcelId;
});
beforeAll(async () => {
@@ -38,7 +51,9 @@ afterAll(async () => {
await removeExpansionSet(graphqlClient, expansionSetId);
await removeExpansion(graphqlClient, expansionId);
await removeParcel(graphqlClient, parcelId);
- await removeCommandDictionary(graphqlClient, commandDictionaryId);
+ await removeDictionary(graphqlClient, commandDictionaryId, DictionaryType.COMMAND);
+ await removeDictionary(graphqlClient, channelDictionaryId, DictionaryType.CHANNEL);
+ await removeDictionary(graphqlClient, paramaterDictionaryId, DictionaryType.PARAMETER);
await removeMissionModel(graphqlClient, missionModelId);
await removeMissionModel(graphqlClient, missionModelId);
});
diff --git a/sequencing-server/test/command-dictionary.spec.ts b/sequencing-server/test/command-dictionary.spec.ts
deleted file mode 100644
index ced5df6320..0000000000
--- a/sequencing-server/test/command-dictionary.spec.ts
+++ /dev/null
@@ -1,29 +0,0 @@
-import * as ampcs from '@nasa-jpl/aerie-ampcs';
-import type { GraphQLClient } from 'graphql-request';
-import {
- commandDictionaryString,
- insertCommandDictionary,
- removeCommandDictionary,
-} from './testUtils/CommandDictionary.js';
-import { getGraphQLClient } from './testUtils/testUtils.js';
-
-let graphqlClient: GraphQLClient;
-
-beforeAll(async () => {
- graphqlClient = await getGraphQLClient();
-});
-
-describe('upload command dictionary', () => {
- it('should upload a command dictionary and all of the fields should be populated correctly', async () => {
- // During the test we use a uuid for the mission so there's no conflicting command dictionaries.
- const { id, dictionary_path, mission, parsed_json } = await insertCommandDictionary(graphqlClient);
-
- expect(dictionary_path).toBe(`/usr/src/app/sequencing_file_store/${mission}/command_lib.${mission}.ts`);
-
- expect(parsed_json).toStrictEqual(
- ampcs.parse(commandDictionaryString.replace(/(Banana Nation|1.0.0.0)/g, mission)),
- );
-
- await removeCommandDictionary(graphqlClient, id);
- }, 30000);
-});
diff --git a/sequencing-server/test/command-expansion.spec.ts b/sequencing-server/test/command-expansion.spec.ts
index 80cc39f79b..7d6f810c80 100644
--- a/sequencing-server/test/command-expansion.spec.ts
+++ b/sequencing-server/test/command-expansion.spec.ts
@@ -5,7 +5,7 @@ import {
insertActivityDirective,
removeActivityDirective,
} from './testUtils/ActivityDirective.js';
-import { insertCommandDictionary, removeCommandDictionary } from './testUtils/CommandDictionary.js';
+import { insertDictionary, removeDictionary } from './testUtils/Dictionary';
import {
expand,
getExpandedSequence,
@@ -22,17 +22,30 @@ import { executeSimulation, removeSimulationArtifacts, updateSimulationBounds }
import { getGraphQLClient, waitMs } from './testUtils/testUtils';
import { insertSequence, linkActivityInstance } from './testUtils/Sequence.js';
import { insertParcel, removeParcel } from './testUtils/Parcel';
+import { DictionaryType } from '../src/types/types';
let planId: number;
let graphqlClient: GraphQLClient;
let missionModelId: number;
let commandDictionaryId: number;
+let channelDictionaryId: number;
+let parameterDictionaryId: number;
let parcelId: number;
beforeAll(async () => {
graphqlClient = await getGraphQLClient();
- commandDictionaryId = (await insertCommandDictionary(graphqlClient)).id;
- parcelId = (await insertParcel(graphqlClient, commandDictionaryId, 'expansionTestParcel')).parcelId;
+ commandDictionaryId = (await insertDictionary(graphqlClient, DictionaryType.COMMAND)).id;
+ channelDictionaryId = (await insertDictionary(graphqlClient, DictionaryType.CHANNEL)).id;
+ parameterDictionaryId = (await insertDictionary(graphqlClient, DictionaryType.PARAMETER)).id;
+ parcelId = (
+ await insertParcel(
+ graphqlClient,
+ commandDictionaryId,
+ channelDictionaryId,
+ parameterDictionaryId,
+ 'expansionTestParcel',
+ )
+ ).parcelId;
});
beforeEach(async () => {
@@ -47,7 +60,9 @@ beforeEach(async () => {
afterAll(async () => {
await removeParcel(graphqlClient, parcelId);
- await removeCommandDictionary(graphqlClient, commandDictionaryId);
+ await removeDictionary(graphqlClient, commandDictionaryId, DictionaryType.COMMAND);
+ await removeDictionary(graphqlClient, channelDictionaryId, DictionaryType.CHANNEL);
+ await removeDictionary(graphqlClient, parameterDictionaryId, DictionaryType.PARAMETER);
});
afterEach(async () => {
@@ -493,8 +508,66 @@ describe('expansion', () => {
expect(testProvidedExpansionSetId).toBeNumber();
const testProvidedResp = await getExpansionSet(graphqlClient, testProvidedExpansionSetId);
- expect(testProvidedResp.expansion_set_by_pk.name).toBe(name);
- expect(testProvidedResp.expansion_set_by_pk.description).toBe(description);
+ expect(testProvidedResp).not.toBeNull();
+ expect(testProvidedResp?.name).toBe(name);
+ expect(testProvidedResp?.description).toBe(description);
+
+ const testDefaultExpansionSetId = await insertExpansionSet(graphqlClient, parcelId, missionModelId, [expansionId]);
+ expect(testDefaultExpansionSetId).not.toBeNull();
+ expect(testDefaultExpansionSetId).toBeDefined();
+ expect(testDefaultExpansionSetId).toBeNumber();
+
+ const testDefaultResp = await getExpansionSet(graphqlClient, testDefaultExpansionSetId);
+ expect(testDefaultResp).not.toBeNull();
+ expect(testDefaultResp?.name).toBe('');
+ expect(testDefaultResp?.description).toBe('');
+
+ // Cleanup
+ await removeExpansion(graphqlClient, expansionId);
+ await removeExpansionSet(graphqlClient, testProvidedExpansionSetId);
+ await removeExpansionSet(graphqlClient, testProvidedExpansionSetId);
+ });
+
+ it('should handle optional channel and parameter dictionaries', async () => {
+ // Remove the optional dictionarys
+ await removeDictionary(graphqlClient, channelDictionaryId, DictionaryType.CHANNEL);
+ await removeDictionary(graphqlClient, parameterDictionaryId, DictionaryType.PARAMETER);
+
+ const expansionId = await insertExpansion(
+ graphqlClient,
+ 'GrowBanana',
+ `
+ export default function SingleCommandExpansion(props: {
+ activityInstance: ActivityType,
+ channelDictionary: ChannelDictionary | null,
+ parameterDictionaries : ParameterDictionary[]
+ }): ExpansionReturn {
+ return [
+ A\`2023-091T10:00:00.000\`.ADD_WATER,
+ R\`04:00:00.000\`.GROW_BANANA({ quantity: 10, durationSecs: 7200 })
+ ];
+ }
+ `,
+ parcelId,
+ );
+ const name = 'test name';
+ const description = 'test desc';
+
+ const testProvidedExpansionSetId = await insertExpansionSet(
+ graphqlClient,
+ parcelId,
+ missionModelId,
+ [expansionId],
+ description,
+ name,
+ );
+ expect(testProvidedExpansionSetId).not.toBeNull();
+ expect(testProvidedExpansionSetId).toBeDefined();
+ expect(testProvidedExpansionSetId).toBeNumber();
+
+ const testProvidedResp = await getExpansionSet(graphqlClient, testProvidedExpansionSetId);
+ expect(testProvidedResp?.name).toBe(name);
+ expect(testProvidedResp?.description).toBe(description);
const testDefaultExpansionSetId = await insertExpansionSet(graphqlClient, parcelId, missionModelId, [expansionId]);
expect(testDefaultExpansionSetId).not.toBeNull();
@@ -502,8 +575,8 @@ describe('expansion', () => {
expect(testDefaultExpansionSetId).toBeNumber();
const testDefaultResp = await getExpansionSet(graphqlClient, testDefaultExpansionSetId);
- expect(testDefaultResp.expansion_set_by_pk.name).toBe('');
- expect(testDefaultResp.expansion_set_by_pk.description).toBe('');
+ expect(testDefaultResp?.name).toBe('');
+ expect(testDefaultResp?.description).toBe('');
// Cleanup
await removeExpansion(graphqlClient, expansionId);
diff --git a/sequencing-server/test/command-types.spec.ts b/sequencing-server/test/command-types.spec.ts
index ca4b91552f..ef9e4d1805 100644
--- a/sequencing-server/test/command-types.spec.ts
+++ b/sequencing-server/test/command-types.spec.ts
@@ -1,18 +1,25 @@
import { gql, GraphQLClient } from 'graphql-request';
import { Status } from '../src/common.js';
-import { insertCommandDictionary, removeCommandDictionary } from './testUtils/CommandDictionary.js';
+import { insertDictionary, removeDictionary } from './testUtils/Dictionary';
import { getGraphQLClient } from './testUtils/testUtils.js';
+import { DictionaryType } from '../src/types/types';
let graphqlClient: GraphQLClient;
let commandDictionaryId: number;
+let channelDictionaryId: number;
+let parameterDictionaryId: number;
beforeAll(async () => {
graphqlClient = await getGraphQLClient();
- commandDictionaryId = (await insertCommandDictionary(graphqlClient)).id;
+ commandDictionaryId = (await insertDictionary(graphqlClient, DictionaryType.COMMAND)).id;
+ channelDictionaryId = (await insertDictionary(graphqlClient, DictionaryType.CHANNEL)).id;
+ parameterDictionaryId = (await insertDictionary(graphqlClient, DictionaryType.PARAMETER)).id;
});
afterAll(async () => {
- removeCommandDictionary(graphqlClient, commandDictionaryId);
+ await removeDictionary(graphqlClient, commandDictionaryId, DictionaryType.COMMAND);
+ await removeDictionary(graphqlClient, channelDictionaryId, DictionaryType.CHANNEL);
+ await removeDictionary(graphqlClient, parameterDictionaryId, DictionaryType.PARAMETER);
});
it('should return command types', async () => {
diff --git a/sequencing-server/test/db-state.spec.ts b/sequencing-server/test/db-state.spec.ts
new file mode 100644
index 0000000000..f96f024821
--- /dev/null
+++ b/sequencing-server/test/db-state.spec.ts
@@ -0,0 +1,164 @@
+import type { GraphQLClient } from 'graphql-request';
+import { getDictionary, insertDictionary, removeDictionary } from './testUtils/Dictionary';
+import { getGraphQLClient } from './testUtils/testUtils.js';
+import { DictionaryType } from '../src/types/types';
+import { removeMissionModel, uploadMissionModel } from './testUtils/MissionModel';
+import { getParcel, insertParcel, removeParcel } from './testUtils/Parcel';
+import {
+ getExpansion,
+ getExpansionSet,
+ insertExpansion,
+ insertExpansionSet,
+ removeExpansion,
+ removeExpansionSet,
+} from './testUtils/Expansion';
+let graphqlClient: GraphQLClient;
+let missionModelId: number;
+let commandDictonaryId: number;
+let channelDictionaryId: number;
+let parameterDictionaryId: number;
+let parcelId: number;
+const expansion_rule: string = `export default function MyExpansion(props: {
+ activityInstance: ActivityType,
+ channelDictionary: ChannelDictionary | null
+ parameterDictionaries : ParameterDictionary[]
+ }): ExpansionReturn {
+ const { activityInstance, channelDictionary, parameterDictionaries } = props;
+ return [];
+ }`;
+
+beforeAll(async () => {
+ graphqlClient = await getGraphQLClient();
+ missionModelId = await uploadMissionModel(graphqlClient);
+});
+
+beforeEach(async () => {
+ commandDictonaryId = (await insertDictionary(graphqlClient, DictionaryType.COMMAND)).id;
+ channelDictionaryId = (await insertDictionary(graphqlClient, DictionaryType.CHANNEL)).id;
+ parameterDictionaryId = (await insertDictionary(graphqlClient, DictionaryType.PARAMETER)).id;
+ parcelId = (
+ await insertParcel(graphqlClient, commandDictonaryId, channelDictionaryId, parameterDictionaryId, 'db-parcel-test')
+ ).parcelId;
+}, 10000);
+
+afterEach(async () => {
+ await removeDictionary(graphqlClient, commandDictonaryId, DictionaryType.COMMAND);
+ await removeDictionary(graphqlClient, channelDictionaryId, DictionaryType.CHANNEL);
+ await removeDictionary(graphqlClient, parameterDictionaryId, DictionaryType.PARAMETER);
+ await removeParcel(graphqlClient, parcelId);
+});
+
+afterAll(async () => {
+ await removeMissionModel(graphqlClient, missionModelId);
+});
+
+describe('Sequencing DB State', () => {
+ it('Delete Command Dictionary should remove parcel, and expansion set, but keep expansion rule', async () => {
+ const expansionID = await insertExpansion(graphqlClient, 'BakeBananaBread', expansion_rule, parcelId);
+
+ const setID = await insertExpansionSet(
+ graphqlClient,
+ parcelId,
+ missionModelId,
+ [expansionID],
+ 'db state test',
+ 'db-state set',
+ );
+
+ // Command Dictionary is deleted
+ await removeDictionary(graphqlClient, commandDictonaryId, DictionaryType.COMMAND);
+
+ // Parcel should not exist
+ const parcel = await getParcel(graphqlClient, parcelId);
+ expect(parcel).toBeNull();
+
+ // Expansion Set should not exist
+ const expansionSet = await getExpansionSet(graphqlClient, setID);
+ expect(expansionSet).toBeNull();
+
+ // expansion rule should exist, with no reference to the parcel
+ const expansion = await getExpansion(graphqlClient, expansionID);
+ expect(expansion.parcel_id).toBeNull();
+ expect(expansion.id).toEqual(expansionID);
+
+ // cleanup
+ await removeExpansion(graphqlClient, expansionID);
+ }, 30000);
+
+ it('Delete channel or parameter Dictionary should NOT remove parcel, expansion set, and expansion rule', async () => {
+ const expansionID = await insertExpansion(graphqlClient, 'BakeBananaBread', expansion_rule, parcelId);
+
+ const setID = await insertExpansionSet(
+ graphqlClient,
+ parcelId,
+ missionModelId,
+ [expansionID],
+ 'db state test',
+ 'db-state set',
+ );
+
+ // Remove the channel and parameter dictionary
+ await removeDictionary(graphqlClient, channelDictionaryId, DictionaryType.CHANNEL);
+ await removeDictionary(graphqlClient, parameterDictionaryId, DictionaryType.PARAMETER);
+
+ // Parcel should exist
+ const parcel = await getParcel(graphqlClient, parcelId);
+ expect(parcel?.id).toEqual(parcelId);
+
+ // Expansion Set should exist
+ const expansionSet = await getExpansionSet(graphqlClient, setID);
+ expect(expansionSet?.id).toEqual(setID);
+ expect(expansionSet?.parcel_id).toEqual(parcelId);
+ expect(expansionSet?.mission_model_id).toEqual(missionModelId);
+ expect(expansionSet?.expansion_rules[0]?.id).toEqual(expansionID);
+
+ // expansion rule should exist, with a reference to the parcel
+ const expansion = await getExpansion(graphqlClient, expansionID);
+ expect(expansion.parcel_id).toEqual(parcelId);
+ expect(expansion.id).toEqual(expansionID);
+
+ // cleanup
+ await removeExpansionSet(graphqlClient, setID);
+ await removeExpansion(graphqlClient, expansionID);
+ }, 30000);
+
+ it('Delete Parcel should NOT remove dictionaries, or expansion rule, but remove expansion sets', async () => {
+ const expansionID = await insertExpansion(graphqlClient, 'BakeBananaBread', expansion_rule, parcelId);
+
+ const setID = await insertExpansionSet(
+ graphqlClient,
+ parcelId,
+ missionModelId,
+ [expansionID],
+ 'db state test',
+ 'db-state set',
+ );
+
+ // Remove the channel and parameter dictionary
+ await removeParcel(graphqlClient, parcelId);
+
+ // Parcel should exist
+ const parcel = await getParcel(graphqlClient, parcelId);
+ expect(parcel).toBeNull();
+
+ // Command, Channel, and Parameter Dictionary should exist
+ const commandDictionary = await getDictionary(graphqlClient, commandDictonaryId, DictionaryType.COMMAND);
+ expect(commandDictionary?.id).toEqual(commandDictonaryId);
+ const channelDictionary = await getDictionary(graphqlClient, channelDictionaryId, DictionaryType.CHANNEL);
+ expect(channelDictionary?.id).toEqual(channelDictionaryId);
+ const parameterDictionary = await getDictionary(graphqlClient, parameterDictionaryId, DictionaryType.PARAMETER);
+ expect(parameterDictionary?.id).toEqual(parameterDictionaryId);
+
+ // Expansion Set should not exist
+ const expansionSet = await getExpansionSet(graphqlClient, setID);
+ expect(expansionSet).toBeNull();
+
+ // expansion rule should exist, with no reference to the parcel
+ const expansion = await getExpansion(graphqlClient, expansionID);
+ expect(expansion.parcel_id).toBeNull();
+ expect(expansion.id).toEqual(expansionID);
+
+ // cleanup
+ await removeExpansion(graphqlClient, expansionID);
+ }, 30000);
+});
diff --git a/sequencing-server/test/dictionary.spec.ts b/sequencing-server/test/dictionary.spec.ts
new file mode 100644
index 0000000000..02c93dcd7a
--- /dev/null
+++ b/sequencing-server/test/dictionary.spec.ts
@@ -0,0 +1,61 @@
+import * as ampcs from '@nasa-jpl/aerie-ampcs';
+import type { GraphQLClient } from 'graphql-request';
+import {
+ channelDictionaryString,
+ commandDictionaryString,
+ insertDictionary,
+ parameterDictionaryString,
+ removeDictionary,
+} from './testUtils/Dictionary';
+import { getGraphQLClient } from './testUtils/testUtils.js';
+import { DictionaryType } from '../src/types/types';
+
+let graphqlClient: GraphQLClient;
+
+beforeAll(async () => {
+ graphqlClient = await getGraphQLClient();
+});
+
+describe('upload dictionaries', () => {
+ it('should upload a command dictionary and all of the fields should be populated correctly', async () => {
+ // During the test we use a uuid for the mission so there's no conflicting command dictionaries.
+ const { id, dictionary_path, mission, parsed_json } = await insertDictionary(graphqlClient, DictionaryType.COMMAND);
+
+ expect(dictionary_path).toBe(`/usr/src/app/sequencing_file_store/${mission}/command_lib.${mission}.ts`);
+
+ expect(parsed_json).toStrictEqual(
+ ampcs.parse(commandDictionaryString.replace(/(Banana Nation|1.0.0.0)/g, mission)),
+ );
+
+ await removeDictionary(graphqlClient, id, DictionaryType.COMMAND);
+ }, 30000);
+
+ it('should upload a channel dictionary and all of the fields should be populated correctly', async () => {
+ // During the test we use a uuid for the mission so there's no conflicting command dictionaries.
+ const { id, dictionary_path, mission, parsed_json } = await insertDictionary(graphqlClient, DictionaryType.CHANNEL);
+
+ expect(dictionary_path).toBe(`/usr/src/app/sequencing_file_store/${mission}/channel_lib.${mission}.ts`);
+
+ expect(parsed_json).toEqual(
+ ampcs.parseChannelDictionary(channelDictionaryString.replace(/(Banana Nation|1.0.0.0)/g, mission)),
+ );
+
+ await removeDictionary(graphqlClient, id, DictionaryType.CHANNEL);
+ }, 30000);
+
+ it('should upload a parameter dictionary and all of the fields should be populated correctly', async () => {
+ // During the test we use a uuid for the mission so there's no conflicting command dictionaries.
+ const { id, dictionary_path, mission, parsed_json } = await insertDictionary(
+ graphqlClient,
+ DictionaryType.PARAMETER,
+ );
+
+ expect(dictionary_path).toBe(`/usr/src/app/sequencing_file_store/${mission}/parameter_lib.${mission}.ts`);
+
+ expect(parsed_json).toEqual(
+ ampcs.parseParameterDictionary(parameterDictionaryString.replace(/(Banana Nation|1.0.0.1)/g, mission)),
+ );
+
+ await removeDictionary(graphqlClient, id, DictionaryType.PARAMETER);
+ }, 30000);
+});
diff --git a/sequencing-server/test/sequence-generation.spec.ts b/sequencing-server/test/sequence-generation.spec.ts
index bc572d3551..683819c300 100644
--- a/sequencing-server/test/sequence-generation.spec.ts
+++ b/sequencing-server/test/sequence-generation.spec.ts
@@ -1,12 +1,12 @@
import type { GraphQLClient } from 'graphql-request';
import { TimingTypes } from '../src/lib/codegen/CommandEDSLPreface.js';
-import { FallibleStatus } from '../src/types/types';
+import { DictionaryType, FallibleStatus } from '../src/types/types';
import {
convertActivityDirectiveIdToSimulatedActivityId,
insertActivityDirective,
removeActivityDirective,
} from './testUtils/ActivityDirective.js';
-import { insertCommandDictionary, removeCommandDictionary } from './testUtils/CommandDictionary.js';
+import { insertDictionary, removeDictionary } from './testUtils/Dictionary';
import {
expand,
insertExpansion,
@@ -32,12 +32,24 @@ let planId: number;
let graphqlClient: GraphQLClient;
let missionModelId: number;
let commandDictionaryId: number;
+let channelDictionaryId: number;
+let parameterDictionaryId: number;
let parcelId: number;
beforeAll(async () => {
graphqlClient = await getGraphQLClient();
- commandDictionaryId = (await insertCommandDictionary(graphqlClient)).id;
- parcelId = (await insertParcel(graphqlClient, commandDictionaryId, 'sequenceGenerationTestParcel')).parcelId;
+ commandDictionaryId = (await insertDictionary(graphqlClient, DictionaryType.COMMAND)).id;
+ channelDictionaryId = (await insertDictionary(graphqlClient, DictionaryType.CHANNEL)).id;
+ parameterDictionaryId = (await insertDictionary(graphqlClient, DictionaryType.PARAMETER)).id;
+ parcelId = (
+ await insertParcel(
+ graphqlClient,
+ commandDictionaryId,
+ channelDictionaryId,
+ parameterDictionaryId,
+ 'sequenceGenerationTestParcel',
+ )
+ ).parcelId;
});
beforeEach(async () => {
@@ -52,7 +64,9 @@ beforeEach(async () => {
afterAll(async () => {
await removeParcel(graphqlClient, parcelId);
- await removeCommandDictionary(graphqlClient, commandDictionaryId);
+ await removeDictionary(graphqlClient, commandDictionaryId, DictionaryType.COMMAND);
+ await removeDictionary(graphqlClient, channelDictionaryId, DictionaryType.CHANNEL);
+ await removeDictionary(graphqlClient, parameterDictionaryId, DictionaryType.PARAMETER);
});
afterEach(async () => {
@@ -65,6 +79,7 @@ describe('sequence generation', () => {
let expansionId2: number;
let expansionId3: number;
let expansionId4: number;
+ let expansionId5: number;
beforeEach(async () => {
expansionId1 = await insertExpansion(
@@ -157,6 +172,25 @@ describe('sequence generation', () => {
`,
parcelId,
);
+
+ expansionId5 = await insertExpansion(
+ graphqlClient,
+ 'BananaNap',
+ `
+ export default function MyExpansion(props: {
+ activityInstance: ActivityType,
+ channelDictionary: ChannelDictionary | null
+ parameterDictionaries : ParameterDictionary[]
+ }): ExpansionReturn {
+ const { activityInstance, channelDictionary, parameterDictionaries } = props;
+ return [
+ ...(channelDictionary ? channelDictionary.telemetries.map(t => C.ECHO('Telemetry Name: '+t.name)) : []),
+ ...parameterDictionaries[0].params.map(p => C.ECHO('Parameter Name: '+p.param_name))
+ ];
+ }
+ `,
+ parcelId,
+ );
});
afterEach(async () => {
@@ -1145,7 +1179,7 @@ describe('sequence generation', () => {
it('should work for throwing expansions', async () => {
/** Begin Setup */
// Add throwing expansion
- const expansionId5 = await insertExpansion(
+ const localExpansionId = await insertExpansion(
graphqlClient,
'BiteBanana',
`
@@ -1161,7 +1195,7 @@ describe('sequence generation', () => {
expansionId1,
expansionId2,
expansionId3,
- expansionId5,
+ localExpansionId,
]);
// Create Activity Directives
@@ -1482,14 +1516,14 @@ describe('sequence generation', () => {
removeActivityDirective(graphqlClient, activityId2, planId),
removeActivityDirective(graphqlClient, activityId3, planId),
]);
- await removeExpansion(graphqlClient, expansionId5);
+ await removeExpansion(graphqlClient, localExpansionId);
await removeExpansionSet(graphqlClient, expansionSetId);
/** End Cleanup */
}, 30000);
it('should work for throwing expansions in bulk', async () => {
/** Begin Setup */
- const expansionId5 = await insertExpansion(
+ const localExpansionId = await insertExpansion(
graphqlClient,
'BiteBanana',
`
@@ -1504,7 +1538,7 @@ describe('sequence generation', () => {
expansionId1,
expansionId2,
expansionId3,
- expansionId5,
+ localExpansionId,
]);
// Create Activity Directives
@@ -2126,7 +2160,7 @@ describe('sequence generation', () => {
removeActivityDirective(graphqlClient, activityId7, planId),
removeActivityDirective(graphqlClient, activityId8, planId),
]);
- await removeExpansion(graphqlClient, expansionId5);
+ await removeExpansion(graphqlClient, localExpansionId);
await removeExpansionSet(graphqlClient, expansionSetId);
/** End Cleanup */
}, 30000);
@@ -3677,6 +3711,124 @@ describe('sequence generation', () => {
/** End Cleanup */
}, 30000);
});
+
+ it('Channel and Parameter Dictionary', async () => {
+ /** Begin Setup */
+ // Create Expansion Set
+ const expansionSetId = await insertExpansionSet(graphqlClient, parcelId, missionModelId, [
+ expansionId1,
+ expansionId2,
+ expansionId5,
+ ]);
+
+ // Create Activity Directives
+ const [activityId1, activityId2] = await Promise.all([
+ insertActivityDirective(graphqlClient, planId, 'BiteBanana', '90 minutes'), // non-existent expansion
+
+ insertActivityDirective(graphqlClient, planId, 'BananaNap', '230 minutes'),
+ ]);
+
+ // Simulate Plan
+ const simulationArtifactPk = await executeSimulation(graphqlClient, planId);
+ // Expand Plan to Sequence Fragments
+ const expansionRunPk = await expand(graphqlClient, expansionSetId, simulationArtifactPk.simulationDatasetId);
+ // Create Sequence
+ const [sequencePk1, sequencePk2] = await Promise.all([
+ insertSequence(graphqlClient, {
+ seqId: 'test00000',
+ simulationDatasetId: simulationArtifactPk.simulationDatasetId,
+ }),
+ insertSequence(graphqlClient, {
+ seqId: 'test00001',
+ simulationDatasetId: simulationArtifactPk.simulationDatasetId,
+ }),
+ ]);
+ // Link Activity Instances to Sequence
+ await Promise.all([
+ linkActivityInstance(graphqlClient, sequencePk1, activityId1),
+ linkActivityInstance(graphqlClient, sequencePk2, activityId2),
+ ]);
+
+ // Get the simulated activity ids
+ const [
+ _simulatedActivityId1, // No expansion, so no check required on this one
+ simulatedActivityId2,
+ ] = await Promise.all([
+ convertActivityDirectiveIdToSimulatedActivityId(
+ graphqlClient,
+ simulationArtifactPk.simulationDatasetId,
+ activityId1,
+ ),
+ convertActivityDirectiveIdToSimulatedActivityId(
+ graphqlClient,
+ simulationArtifactPk.simulationDatasetId,
+ activityId2,
+ ),
+ ]);
+
+ /** End Setup */
+
+ // Retrieve seqJson
+ const getSequenceSeqJsonResponse = await getSequenceSeqJsonBulk(graphqlClient, [
+ { seqId: 'test00000', simulationDatasetId: simulationArtifactPk.simulationDatasetId },
+ { seqId: 'test00001', simulationDatasetId: simulationArtifactPk.simulationDatasetId },
+ ]);
+
+ const secondSequence = getSequenceSeqJsonResponse[1]!;
+
+ if (secondSequence.status !== FallibleStatus.SUCCESS) {
+ throw secondSequence.errors;
+ }
+
+ expect(secondSequence.seqJson.id).toBe('test00001');
+ expect(secondSequence.seqJson.metadata).toEqual({
+ planId: planId,
+ simulationDatasetId: simulationArtifactPk.simulationDatasetId,
+ timeSorted: false,
+ });
+ expect(secondSequence.seqJson.steps).toEqual([
+ {
+ args: [
+ {
+ name: 'echo_string',
+ type: 'string',
+ value: 'Telemetry Name: BAKE_STATE',
+ },
+ ],
+ metadata: { simulatedActivityId: simulatedActivityId2 },
+ stem: 'ECHO',
+ time: { type: 'COMMAND_COMPLETE' },
+ type: 'command',
+ },
+ {
+ args: [
+ {
+ name: 'echo_string',
+ type: 'string',
+ value: 'Parameter Name: BANANA_COLOR_RATE',
+ },
+ ],
+ metadata: { simulatedActivityId: simulatedActivityId2 },
+ stem: 'ECHO',
+ time: {
+ type: 'COMMAND_COMPLETE',
+ },
+ type: 'command',
+ },
+ ]);
+
+ /** Begin Cleanup */
+ await Promise.all([removeSequence(graphqlClient, sequencePk1), removeSequence(graphqlClient, sequencePk2)]);
+ await removeExpansionRun(graphqlClient, expansionRunPk);
+ await removeSimulationArtifacts(graphqlClient, simulationArtifactPk);
+ await Promise.all([
+ removeActivityDirective(graphqlClient, activityId1, planId),
+ removeActivityDirective(graphqlClient, activityId2, planId),
+ ]);
+ await removeExpansionSet(graphqlClient, expansionSetId);
+ await removeExpansion(graphqlClient, expansionId5);
+ /** End Cleanup */
+ }, 30000);
});
it('should provide start, end, and computed attributes on activities', async () => {
diff --git a/sequencing-server/test/testUtils/CommandDictionary.ts b/sequencing-server/test/testUtils/CommandDictionary.ts
deleted file mode 100644
index 39893c1167..0000000000
--- a/sequencing-server/test/testUtils/CommandDictionary.ts
+++ /dev/null
@@ -1,64 +0,0 @@
-import fs from 'node:fs';
-import { gql, GraphQLClient } from 'graphql-request';
-import { randomUUID } from 'node:crypto';
-import type { CommandDictionary } from '@nasa-jpl/aerie-ampcs';
-
-export const commandDictionaryString = fs.readFileSync(
- new URL('../../cdict/command_banananation.xml', import.meta.url).pathname,
- 'utf-8',
-);
-
-export async function insertCommandDictionary(graphqlClient: GraphQLClient): Promise<{
- id: number;
- dictionary_path: string;
- mission: string;
- version: string;
- parsed_json: CommandDictionary;
-}> {
- const res = await graphqlClient.request<{
- uploadDictionary: {
- id: number;
- dictionary_path: string;
- mission: string;
- version: string;
- parsed_json: CommandDictionary;
- };
- }>(
- gql`
- mutation PutCommandDictionary($dictionary: String!, $type: String!) {
- uploadDictionary(dictionary: $dictionary, type: $type) {
- id
- dictionary_path
- mission
- version
- parsed_json
- }
- }
- `,
- {
- // Generate a UUID for the command dictionary name and version to avoid conflicts when testing.
- dictionary: commandDictionaryString.replace(/(Banana Nation|1.0.0.0)/g, randomUUID()),
- type: 'COMMAND',
- },
- );
-
- return res.uploadDictionary;
-}
-
-export async function removeCommandDictionary(
- graphqlClient: GraphQLClient,
- commandDictionaryId: number,
-): Promise {
- return graphqlClient.request(
- gql`
- mutation DeleteCommandDictionary($commandDictionaryId: Int!) {
- delete_command_dictionary_by_pk(id: $commandDictionaryId) {
- id
- }
- }
- `,
- {
- commandDictionaryId,
- },
- );
-}
diff --git a/sequencing-server/test/testUtils/Dictionary.ts b/sequencing-server/test/testUtils/Dictionary.ts
new file mode 100644
index 0000000000..08d91f91d2
--- /dev/null
+++ b/sequencing-server/test/testUtils/Dictionary.ts
@@ -0,0 +1,142 @@
+import fs from 'node:fs';
+import { gql, GraphQLClient } from 'graphql-request';
+import { randomUUID } from 'node:crypto';
+import type { CommandDictionary, ChannelDictionary, ParameterDictionary } from '@nasa-jpl/aerie-ampcs';
+import { DictionaryType } from '../../src/types/types';
+
+export const commandDictionaryString = fs.readFileSync(
+ new URL('../../cdict/command_banananation.xml', import.meta.url).pathname,
+ 'utf-8',
+);
+export const channelDictionaryString = fs.readFileSync(
+ new URL('../../cdict/channel_banananation.xml', import.meta.url).pathname,
+ 'utf-8',
+);
+
+export const parameterDictionaryString = fs.readFileSync(
+ new URL('../../cdict/parameter_banananation.xml', import.meta.url).pathname,
+ 'utf-8',
+);
+
+export async function insertDictionary(
+ graphqlClient: GraphQLClient,
+ type: DictionaryType,
+): Promise<{
+ id: number;
+ dictionary_path: string;
+ mission: string;
+ version: string;
+ parsed_json: CommandDictionary | ChannelDictionary | ParameterDictionary;
+}> {
+ let dictonaryString = commandDictionaryString;
+ switch (type) {
+ case DictionaryType.CHANNEL:
+ dictonaryString = channelDictionaryString;
+ break;
+ case DictionaryType.PARAMETER:
+ dictonaryString = parameterDictionaryString;
+ break;
+ }
+ const res = await graphqlClient.request<{
+ uploadDictionary: {
+ id: number;
+ dictionary_path: string;
+ mission: string;
+ version: string;
+ parsed_json: CommandDictionary | ChannelDictionary | ParameterDictionary;
+ };
+ }>(
+ gql`
+ mutation PutDictionary($dictionary: String!, $type: String!) {
+ uploadDictionary(dictionary: $dictionary, type: $type) {
+ id
+ dictionary_path
+ mission
+ version
+ parsed_json
+ }
+ }
+ `,
+ {
+ // Generate a UUID for the command dictionary name and version to avoid conflicts when testing.
+ dictionary: dictonaryString.replace(/(Banana Nation|1.0.0.0|1.0.0.1)/g, randomUUID()),
+ type,
+ },
+ );
+
+ return res.uploadDictionary;
+}
+
+export async function getDictionary(
+ graphqlClient: GraphQLClient,
+ dictionaryId: number,
+ type: DictionaryType,
+): Promise<{
+ id: number;
+ dictionary_path: string;
+ mission: string;
+ version: string;
+ parsed_json: CommandDictionary | ChannelDictionary | ParameterDictionary;
+}> {
+ let dictonaryString = 'command_dictionary_by_pk';
+ switch (type) {
+ case DictionaryType.CHANNEL:
+ dictonaryString = 'channel_dictionary_by_pk';
+ break;
+ case DictionaryType.PARAMETER:
+ dictonaryString = 'parameter_dictionary_by_pk';
+ break;
+ }
+ const res = await graphqlClient.request(
+ gql`
+ query GetDictionary($dictionaryId: Int!) {
+ ${dictonaryString}(id: $dictionaryId) {
+ id
+ dictionary_path
+ mission
+ version
+ parsed_json
+ }
+ }
+ `,
+ {
+ dictionaryId,
+ },
+ );
+ switch (type) {
+ case DictionaryType.COMMAND:
+ return res.command_dictionary_by_pk;
+ case DictionaryType.CHANNEL:
+ return res.channel_dictionary_by_pk;
+ case DictionaryType.PARAMETER:
+ return res.parameter_dictionary_by_pk;
+ }
+}
+
+export async function removeDictionary(
+ graphqlClient: GraphQLClient,
+ dictionaryId: number,
+ type: DictionaryType,
+): Promise {
+ let mutationString: string = 'delete_command_dictionary_by_pk';
+ switch (type) {
+ case DictionaryType.CHANNEL:
+ mutationString = 'delete_channel_dictionary_by_pk';
+ break;
+ case DictionaryType.PARAMETER:
+ mutationString = 'delete_parameter_dictionary_by_pk';
+ break;
+ }
+ return graphqlClient.request(
+ gql`
+ mutation DeleteCommandDictionary($dictionaryId: Int!) {
+ ${mutationString}(id: $dictionaryId) {
+ id
+ }
+ }
+ `,
+ {
+ dictionaryId,
+ },
+ );
+}
diff --git a/sequencing-server/test/testUtils/Expansion.ts b/sequencing-server/test/testUtils/Expansion.ts
index 9e26467fd5..5b43cf6e9e 100644
--- a/sequencing-server/test/testUtils/Expansion.ts
+++ b/sequencing-server/test/testUtils/Expansion.ts
@@ -29,6 +29,35 @@ export async function insertExpansion(
return res.addCommandExpansionTypeScript.id;
}
+export async function getExpansion(
+ graphqlClient: GraphQLClient,
+ expansionId: number,
+): Promise<{
+ id: number;
+ expansion_logic: string;
+ name: string;
+ parcel_id: number;
+}> {
+ const res = await graphqlClient.request<{
+ expansion_rule_by_pk: any;
+ }>(
+ gql`
+ query GetExpansion($expansionId: Int!) {
+ expansion_rule_by_pk(id: $expansionId) {
+ id
+ expansion_logic
+ name
+ parcel_id
+ }
+ }
+ `,
+ {
+ expansionId,
+ },
+ );
+ return res.expansion_rule_by_pk;
+}
+
export async function removeExpansion(graphqlClient: GraphQLClient, expansionId: number): Promise {
return graphqlClient.request(
gql`
@@ -85,13 +114,29 @@ export async function insertExpansionSet(
return res.createExpansionSet.id;
}
-export async function getExpansionSet(graphqlClient: GraphQLClient, expansionSetId: number): Promise {
- return graphqlClient.request(
+export async function getExpansionSet(
+ graphqlClient: GraphQLClient,
+ expansionSetId: number,
+): Promise<{
+ id: number;
+ name: string;
+ description: string;
+ mission_model_id: number;
+ parcel_id: number;
+ expansion_rules: { id: number }[];
+} | null> {
+ const res = await graphqlClient.request(
gql`
- query GetExpansionRule($expansionSetId: Int!) {
+ query GetExpansionSet($expansionSetId: Int!) {
expansion_set_by_pk(id: $expansionSetId) {
+ id
name
description
+ mission_model_id
+ parcel_id
+ expansion_rules {
+ id
+ }
}
}
`,
@@ -99,6 +144,8 @@ export async function getExpansionSet(graphqlClient: GraphQLClient, expansionSet
expansionSetId,
},
);
+
+ return res.expansion_set_by_pk;
}
export async function removeExpansionSet(graphqlClient: GraphQLClient, expansionSetId: number): Promise {
diff --git a/sequencing-server/test/testUtils/Parcel.ts b/sequencing-server/test/testUtils/Parcel.ts
index 8f916f5246..5e1a260e1e 100644
--- a/sequencing-server/test/testUtils/Parcel.ts
+++ b/sequencing-server/test/testUtils/Parcel.ts
@@ -2,7 +2,9 @@ import { gql, GraphQLClient } from 'graphql-request';
export async function insertParcel(
graphqlClient: GraphQLClient,
- dictionaryID: number,
+ commandDictionaryID: number,
+ channelDictionaryID: number,
+ parameterDictionaryID: number,
parcelName: string,
): Promise<{
parcelId: number;
@@ -15,8 +17,20 @@ export async function insertParcel(
};
}>(
gql`
- mutation PutParcel($dictionaryID: Int!, $parcelName: String!) {
- insert_parcel(objects: { command_dictionary_id: $dictionaryID, name: $parcelName }) {
+ mutation PutParcel(
+ $commandDictionaryID: Int!
+ $channelDictionaryID: Int
+ $parameterDictionaryID: Int
+ $parcelName: String!
+ ) {
+ insert_parcel(
+ objects: {
+ command_dictionary_id: $commandDictionaryID
+ channel_dictionary_id: $channelDictionaryID
+ parameter_dictionaries: { data: { parameter_dictionary_id: $parameterDictionaryID } }
+ name: $parcelName
+ }
+ ) {
returning {
id
}
@@ -25,7 +39,9 @@ export async function insertParcel(
`,
{
// Generate a UUID for the command dictionary name and version to avoid conflicts when testing.
- dictionaryID,
+ commandDictionaryID,
+ channelDictionaryID,
+ parameterDictionaryID,
parcelName,
},
);
@@ -33,6 +49,39 @@ export async function insertParcel(
return { parcelId: res.insert_parcel.returning[0]?.id ?? -1 };
}
+export async function getParcel(
+ graphqlClient: GraphQLClient,
+ parcelId: number,
+): Promise<{
+ id: number;
+ name: string;
+} | null> {
+ const res = await graphqlClient.request<{
+ parcel_by_pk: {
+ id: number;
+ name: string;
+ };
+ }>(
+ gql`
+ query GetParcel($parcelId: Int!) {
+ parcel_by_pk(id: $parcelId) {
+ id
+ command_dictionary_id
+ channel_dictionary_id
+ parameter_dictionaries {
+ parameter_dictionary_id
+ }
+ name
+ }
+ }
+ `,
+ {
+ parcelId,
+ },
+ );
+ return res.parcel_by_pk;
+}
+
export async function removeParcel(graphqlClient: GraphQLClient, parcelId: number): Promise {
return graphqlClient.request(
gql`