This is an automated email from the ASF dual-hosted git repository. machristie pushed a commit to branch develop in repository https://gitbox.apache.org/repos/asf/airavata-django-portal.git
commit fcf01b900a24e4c02826587cb38e6bf48f67f9e0 Author: Marcus Christie <[email protected]> AuthorDate: Fri Jul 30 12:59:30 2021 -0400 AIRAVATA-3491 Unit tests for ExperimentUtils.js --- .../js/models/ApplicationInterfaceDefinition.js | 1 + .../js/utils/ExperimentUtils.js | 6 +- .../tests/utils/ExperimentUtils.test.js | 534 +++++++++++++++++++++ 3 files changed, 540 insertions(+), 1 deletion(-) diff --git a/django_airavata/apps/api/static/django_airavata_api/js/models/ApplicationInterfaceDefinition.js b/django_airavata/apps/api/static/django_airavata_api/js/models/ApplicationInterfaceDefinition.js index ee919d4..6ed2eed 100644 --- a/django_airavata/apps/api/static/django_airavata_api/js/models/ApplicationInterfaceDefinition.js +++ b/django_airavata/apps/api/static/django_airavata_api/js/models/ApplicationInterfaceDefinition.js @@ -26,6 +26,7 @@ const FIELDS = [ name: "applicationOutputs", type: OutputDataObjectType, list: true, + default: BaseModel.defaultNewInstance(Array), }, { name: "archiveWorkingDirectory", diff --git a/django_airavata/apps/api/static/django_airavata_api/js/utils/ExperimentUtils.js b/django_airavata/apps/api/static/django_airavata_api/js/utils/ExperimentUtils.js index 613e868..2e6cea0 100644 --- a/django_airavata/apps/api/static/django_airavata_api/js/utils/ExperimentUtils.js +++ b/django_airavata/apps/api/static/django_airavata_api/js/utils/ExperimentUtils.js @@ -79,7 +79,7 @@ const loadApplicationInterfaceByName = async function (applicationName) { ); if (!applicationInterface) { throw new Error( - `Could not find application with interface named ${applicationName}` + `Could not find application interface named ${applicationName}` ); } return applicationInterface; @@ -146,6 +146,10 @@ const loadWorkspacePreferences = async function () { return await services.WorkspacePreferencesService.get(); }; +export { + createExperiment, +}; + export default { createExperiment, }; diff --git a/django_airavata/apps/api/static/django_airavata_api/tests/utils/ExperimentUtils.test.js b/django_airavata/apps/api/static/django_airavata_api/tests/utils/ExperimentUtils.test.js new file mode 100644 index 0000000..02ecde7 --- /dev/null +++ b/django_airavata/apps/api/static/django_airavata_api/tests/utils/ExperimentUtils.test.js @@ -0,0 +1,534 @@ +import { services } from "../../js/index"; +import ApplicationInterfaceDefinition from "../../js/models/ApplicationInterfaceDefinition"; +import GroupResourceProfile from "../../js/models/GroupResourceProfile"; +import ApplicationDeploymentDescription from "../../js/models/ApplicationDeploymentDescription"; +import BatchQueue from "../../js/models/BatchQueue"; +import { createExperiment } from "../../js/utils/ExperimentUtils"; + +// Mock out 'index' so that RESTful service calls can be mocked +jest.mock("../../js/index"); + +test("error thrown when no applicationName given", async () => { + try { + expect.assertions(2); + await createExperiment(); + } catch (e) { + expect(e).toBeInstanceOf(Error); + expect(e.message).toEqual("applicationName is required"); + } +}); + +test("error thrown with applicationName doesn't match any interfaces", async () => { + services.ApplicationInterfaceService.list.mockResolvedValue([ + new ApplicationInterfaceDefinition({ + applicationName: "Foo", + applicationModules: ["Foo_module1"], + }), + new ApplicationInterfaceDefinition({ + applicationName: "Bar", + applicationModules: ["bar_module1"], + }), + ]); + try { + expect.assertions(2); + await createExperiment({ applicationName: "test" }); + } catch (e) { + expect(e).toBeInstanceOf(Error); + expect(e.message).toEqual( + "Could not find application interface named test" + ); + } +}); + +test("error thrown when no computeResourceName given", async () => { + services.ApplicationInterfaceService.list.mockResolvedValue([ + new ApplicationInterfaceDefinition({ + applicationName: "test", + applicationModules: ["test_module1"], + }), + new ApplicationInterfaceDefinition({ + applicationName: "Bar", + applicationModules: ["bar_module1"], + }), + ]); + try { + expect.assertions(2); + await createExperiment({ applicationName: "test" }); + } catch (e) { + expect(e).toBeInstanceOf(Error); + expect(e.message).toEqual("computeResourceName is required"); + } +}); + +test("error thrown when computeResourceName doesn't match any compute resources", async () => { + services.ApplicationInterfaceService.list.mockResolvedValue([ + new ApplicationInterfaceDefinition({ + applicationName: "test", + applicationModules: ["test_module1"], + }), + new ApplicationInterfaceDefinition({ + applicationName: "Bar", + applicationModules: ["bar_module1"], + }), + ]); + services.ComputeResourceService.names.mockResolvedValue({ + "compute1.resource.org_id1": "compute1.resource.org", + "compute2.resource.org_id2": "compute2.resource.org", + }); + try { + expect.assertions(2); + await createExperiment({ + applicationName: "test", + computeResourceName: "nonexistent.compute.resource.org", + }); + } catch (e) { + expect(e).toBeInstanceOf(Error); + expect(e.message).toEqual( + "Could not find compute resource with name nonexistent.compute.resource.org" + ); + } +}); + +test("error thrown when no GRP found for compute resource", async () => { + services.ApplicationInterfaceService.list.mockResolvedValue([ + new ApplicationInterfaceDefinition({ + applicationName: "test", + applicationModules: ["test_module1"], + }), + new ApplicationInterfaceDefinition({ + applicationName: "Bar", + applicationModules: ["bar_module1"], + }), + ]); + services.ComputeResourceService.names.mockResolvedValue({ + "compute1.resource.org_id1": "compute1.resource.org", + "compute2.resource.org_id2": "compute2.resource.org", + }); + // Have mock GRP response be an empty list + services.GroupResourceProfileService.list.mockResolvedValue([]); + try { + expect.assertions(2); + await createExperiment({ + applicationName: "test", + computeResourceName: "compute1.resource.org", + }); + } catch (e) { + expect(e).toBeInstanceOf(Error); + expect(e.message).toEqual( + "Couldn't find a group resource profile for compute resource compute1.resource.org_id1" + ); + } +}); + +test("error thrown when no deployment found for compute resource", async () => { + services.ApplicationInterfaceService.list.mockResolvedValue([ + new ApplicationInterfaceDefinition({ + applicationName: "test", + applicationModules: ["test_module1"], + }), + new ApplicationInterfaceDefinition({ + applicationName: "Bar", + applicationModules: ["bar_module1"], + }), + ]); + services.ComputeResourceService.names.mockResolvedValue({ + "compute1.resource.org_id1": "compute1.resource.org", + "compute2.resource.org_id2": "compute2.resource.org", + }); + services.GroupResourceProfileService.list.mockResolvedValue([ + new GroupResourceProfile({ + groupResourceProfileId: "groupResourceProfileId1", + computePreferences: [ + { + computeResourceId: "compute1.resource.org_id1", + }, + ], + }), + ]); + // Just return an empty list + services.ApplicationDeploymentService.list.mockResolvedValue([]); + try { + expect.assertions(3); + await createExperiment({ + applicationName: "test", + computeResourceName: "compute1.resource.org", + }); + } catch (e) { + expect(services.ApplicationDeploymentService.list).toHaveBeenCalledWith({ + appModuleId: "test_module1", + groupResourceProfileId: "groupResourceProfileId1", + }); + expect(e).toBeInstanceOf(Error); + expect(e.message).toEqual( + "Couldn't find a deployment for compute resource compute1.resource.org_id1" + ); + } +}); + +test("verify that default queue values are used in computationalResourceScheduling", async () => { + services.ApplicationInterfaceService.list.mockResolvedValue([ + new ApplicationInterfaceDefinition({ + applicationName: "test", + applicationModules: ["test_module1"], + }), + new ApplicationInterfaceDefinition({ + applicationName: "Bar", + applicationModules: ["bar_module1"], + }), + ]); + services.ComputeResourceService.names.mockResolvedValue({ + "compute1.resource.org_id1": "compute1.resource.org", + "compute2.resource.org_id2": "compute2.resource.org", + }); + services.GroupResourceProfileService.list.mockResolvedValue([ + new GroupResourceProfile({ + groupResourceProfileId: "groupResourceProfileId1", + computePreferences: [ + { + computeResourceId: "compute1.resource.org_id1", + }, + ], + }), + ]); + services.ApplicationDeploymentService.list.mockResolvedValue([ + new ApplicationDeploymentDescription({ + appDeploymentId: "appDeploymentId1", + computeHostId: "compute1.resource.org_id1", + }), + ]); + services.ApplicationDeploymentService.getQueues.mockResolvedValue([ + new BatchQueue({ + queueName: "queue1", + isDefaultQueue: false, + defaultCPUCount: 10, + defaultNodeCount: 11, + defaultWalltime: 12, + }), + new BatchQueue({ + queueName: "queue2", + isDefaultQueue: true, + defaultCPUCount: 20, + defaultNodeCount: 21, + defaultWalltime: 22, + }), + ]); + services.WorkspacePreferencesService.get.mockResolvedValue({ + most_recent_project_id: "project1", + }); + const experiment = await createExperiment({ + applicationName: "test", + computeResourceName: "compute1.resource.org", + }); + expect( + experiment.userConfigurationData.computationalResourceScheduling + .resourceHostId + ).toBe("compute1.resource.org_id1"); + expect( + experiment.userConfigurationData.computationalResourceScheduling + .totalCPUCount + ).toBe(20); + expect( + experiment.userConfigurationData.computationalResourceScheduling.nodeCount + ).toBe(21); + expect( + experiment.userConfigurationData.computationalResourceScheduling + .wallTimeLimit + ).toBe(22); + expect( + experiment.userConfigurationData.computationalResourceScheduling.queueName + ).toBe("queue2"); +}); + +test("verify that experiment name is the given name", async () => { + services.ApplicationInterfaceService.list.mockResolvedValue([ + new ApplicationInterfaceDefinition({ + applicationName: "test", + applicationModules: ["test_module1"], + }), + ]); + services.ComputeResourceService.names.mockResolvedValue({ + "compute1.resource.org_id1": "compute1.resource.org", + }); + services.GroupResourceProfileService.list.mockResolvedValue([ + new GroupResourceProfile({ + groupResourceProfileId: "groupResourceProfileId1", + computePreferences: [ + { + computeResourceId: "compute1.resource.org_id1", + }, + ], + }), + ]); + services.ApplicationDeploymentService.list.mockResolvedValue([ + new ApplicationDeploymentDescription({ + appDeploymentId: "appDeploymentId1", + computeHostId: "compute1.resource.org_id1", + }), + ]); + services.ApplicationDeploymentService.getQueues.mockResolvedValue([ + new BatchQueue({ + queueName: "queue2", + isDefaultQueue: true, + defaultCPUCount: 20, + defaultNodeCount: 21, + defaultWalltime: 22, + }), + ]); + services.WorkspacePreferencesService.get.mockResolvedValue({ + most_recent_project_id: "project1", + }); + const experiment = await createExperiment({ + experimentName: "My Experiment", + applicationName: "test", + computeResourceName: "compute1.resource.org", + }); + expect(experiment.experimentName).toBe("My Experiment"); +}); + +test("verify that if no experiment name is given, name is based on experiment name", async () => { + services.ApplicationInterfaceService.list.mockResolvedValue([ + new ApplicationInterfaceDefinition({ + applicationName: "test", + applicationModules: ["test_module1"], + }), + ]); + services.ComputeResourceService.names.mockResolvedValue({ + "compute1.resource.org_id1": "compute1.resource.org", + }); + services.GroupResourceProfileService.list.mockResolvedValue([ + new GroupResourceProfile({ + groupResourceProfileId: "groupResourceProfileId1", + computePreferences: [ + { + computeResourceId: "compute1.resource.org_id1", + }, + ], + }), + ]); + services.ApplicationDeploymentService.list.mockResolvedValue([ + new ApplicationDeploymentDescription({ + appDeploymentId: "appDeploymentId1", + computeHostId: "compute1.resource.org_id1", + }), + ]); + services.ApplicationDeploymentService.getQueues.mockResolvedValue([ + new BatchQueue({ + queueName: "queue2", + isDefaultQueue: true, + defaultCPUCount: 20, + defaultNodeCount: 21, + defaultWalltime: 22, + }), + ]); + services.WorkspacePreferencesService.get.mockResolvedValue({ + most_recent_project_id: "project1", + }); + const experiment = await createExperiment({ + applicationName: "test", + computeResourceName: "compute1.resource.org", + }); + // Date string doesn't include seconds, so it should match exactly + const dateString = new Date().toLocaleString([], { + dateStyle: "medium", + timeStyle: "short", + }); + expect(experiment.experimentName).toBe(`test on ${dateString}`); +}); + +test("verify that application inputs and outputs are cloned on experiment", async () => { + services.ApplicationInterfaceService.list.mockResolvedValue([ + new ApplicationInterfaceDefinition({ + applicationName: "test", + applicationModules: ["test_module1"], + applicationInputs: [ + { + name: "appInput1", + }, + { + name: "appInput2", + }, + ], + applicationOutputs: [ + { + name: "appOutput1", + }, + { + name: "appOutput2", + }, + ], + }), + ]); + services.ComputeResourceService.names.mockResolvedValue({ + "compute1.resource.org_id1": "compute1.resource.org", + }); + services.GroupResourceProfileService.list.mockResolvedValue([ + new GroupResourceProfile({ + groupResourceProfileId: "groupResourceProfileId1", + computePreferences: [ + { + computeResourceId: "compute1.resource.org_id1", + }, + ], + }), + ]); + services.ApplicationDeploymentService.list.mockResolvedValue([ + new ApplicationDeploymentDescription({ + appDeploymentId: "appDeploymentId1", + computeHostId: "compute1.resource.org_id1", + }), + ]); + services.ApplicationDeploymentService.getQueues.mockResolvedValue([ + new BatchQueue({ + queueName: "queue2", + isDefaultQueue: true, + defaultCPUCount: 20, + defaultNodeCount: 21, + defaultWalltime: 22, + }), + ]); + services.WorkspacePreferencesService.get.mockResolvedValue({ + most_recent_project_id: "project1", + }); + const experiment = await createExperiment({ + applicationName: "test", + computeResourceName: "compute1.resource.org", + }); + expect( + experiment.experimentInputs.find((i) => i.name === "appInput1") + ).toBeDefined(); + expect( + experiment.experimentInputs.find((i) => i.name === "appInput2") + ).toBeDefined(); + expect(experiment.experimentInputs.length).toBe(2); + expect( + experiment.experimentOutputs.find((i) => i.name === "appOutput1") + ).toBeDefined(); + expect( + experiment.experimentOutputs.find((i) => i.name === "appOutput2") + ).toBeDefined(); + expect(experiment.experimentOutputs.length).toBe(2); +}); + +test("verify that projectId is copied from preferences", async () => { + services.ApplicationInterfaceService.list.mockResolvedValue([ + new ApplicationInterfaceDefinition({ + applicationName: "test", + applicationModules: ["test_module1"], + }), + ]); + services.ComputeResourceService.names.mockResolvedValue({ + "compute1.resource.org_id1": "compute1.resource.org", + }); + services.GroupResourceProfileService.list.mockResolvedValue([ + new GroupResourceProfile({ + groupResourceProfileId: "groupResourceProfileId1", + computePreferences: [ + { + computeResourceId: "compute1.resource.org_id1", + }, + ], + }), + ]); + services.ApplicationDeploymentService.list.mockResolvedValue([ + new ApplicationDeploymentDescription({ + appDeploymentId: "appDeploymentId1", + computeHostId: "compute1.resource.org_id1", + }), + ]); + services.ApplicationDeploymentService.getQueues.mockResolvedValue([ + new BatchQueue({ + queueName: "queue2", + isDefaultQueue: true, + defaultCPUCount: 20, + defaultNodeCount: 21, + defaultWalltime: 22, + }), + ]); + services.WorkspacePreferencesService.get.mockResolvedValue({ + most_recent_project_id: "project1", + }); + const experiment = await createExperiment({ + applicationName: "test", + computeResourceName: "compute1.resource.org", + }); + expect(experiment.projectId).toBe("project1"); +}); + +test("verify that given input values are copied to experiment", async () => { + services.ApplicationInterfaceService.list.mockResolvedValue([ + new ApplicationInterfaceDefinition({ + applicationName: "test", + applicationModules: ["test_module1"], + applicationInputs: [ + { + name: "appInput1", + value: "default1", + }, + { + name: "appInput2", + }, + ], + }), + ]); + services.ComputeResourceService.names.mockResolvedValue({ + "compute1.resource.org_id1": "compute1.resource.org", + }); + services.GroupResourceProfileService.list.mockResolvedValue([ + new GroupResourceProfile({ + groupResourceProfileId: "groupResourceProfileId1", + computePreferences: [ + { + computeResourceId: "compute1.resource.org_id1", + }, + ], + }), + ]); + services.ApplicationDeploymentService.list.mockResolvedValue([ + new ApplicationDeploymentDescription({ + appDeploymentId: "appDeploymentId1", + computeHostId: "compute1.resource.org_id1", + }), + ]); + services.ApplicationDeploymentService.getQueues.mockResolvedValue([ + new BatchQueue({ + queueName: "queue2", + isDefaultQueue: true, + defaultCPUCount: 20, + defaultNodeCount: 21, + defaultWalltime: 22, + }), + ]); + services.WorkspacePreferencesService.get.mockResolvedValue({ + most_recent_project_id: "project1", + }); + const experiment = await createExperiment({ + applicationName: "test", + computeResourceName: "compute1.resource.org", + experimentInputs: { + appInput1: "value1", + appInput2: "value2", + }, + }); + expect( + experiment.experimentInputs.find((i) => i.name === "appInput1").value + ).toBe("value1"); + expect( + experiment.experimentInputs.find((i) => i.name === "appInput2").value + ).toBe("value2"); + + // Don't pass appInput1 and take the default value instead + const experiment2 = await createExperiment({ + applicationName: "test", + computeResourceName: "compute1.resource.org", + experimentInputs: { + // "appInput1": "value1", + appInput2: "value2", + }, + }); + expect( + experiment2.experimentInputs.find((i) => i.name === "appInput1").value + ).toBe("default1"); + expect( + experiment2.experimentInputs.find((i) => i.name === "appInput2").value + ).toBe("value2"); +});
