From 27c9e8336469256066b83006370850d3785482c7 Mon Sep 17 00:00:00 2001 From: jace-roell Date: Wed, 22 Jan 2025 14:51:57 -0500 Subject: [PATCH 01/11] init Signed-off-by: jace-roell --- packages/zosfiles/src/methods/copy/Copy.ts | 28 +++++++++++----------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/packages/zosfiles/src/methods/copy/Copy.ts b/packages/zosfiles/src/methods/copy/Copy.ts index 402738f0c..a7cb9656e 100644 --- a/packages/zosfiles/src/methods/copy/Copy.ts +++ b/packages/zosfiles/src/methods/copy/Copy.ts @@ -272,7 +272,7 @@ export class Copy { /* * If the source is a PDS and no member was specified then abort the copy. */ - if((sourceDataSetObj.dsorg == "PO" || sourceDataSetObj.dsorg == "POE") && sourceMember == undefined){ + if((sourceDataSetObj.dsorg == "PO" || sourceDataSetObj.dsorg == "PO-E") && sourceMember == undefined){ throw new ImperativeError({ msg: ZosFilesMessages.datasetCopiedAbortedNoPDS.message }); } } @@ -316,7 +316,7 @@ export class Copy { targetDataSetObj = TargetDsList.apiResponse.items[dsnameIndex]; targetFound = true; - if((targetDataSetObj.dsorg == "PO" || targetDataSetObj.dsorg == "POE") && targetMember == undefined) + if((targetDataSetObj.dsorg == "PO" || targetDataSetObj.dsorg == "PO-E") && targetMember == undefined) { throw new ImperativeError({ msg: ZosFilesMessages.datasetCopiedAbortedTargetNotPDSMember.message }); } @@ -346,11 +346,11 @@ export class Copy { * If this is a PDS but the target is the sequential dataset and does not exist, * create a new sequential dataset with the same parameters as the original PDS. */ - if((createOptions.dsorg == "PO" || createOptions.dsorg == "POE") && targetMember == undefined){ + if((createOptions.dsorg == "PO" || createOptions.dsorg == "PO-E") && targetMember == undefined){ createOptions.dsorg ="PS"; createOptions.dirblk = 0; } - else if(targetMember != undefined && (createOptions.dsorg != "PO" && createOptions.dsorg != "POE")) + else if(targetMember != undefined && (createOptions.dsorg != "PO" && createOptions.dsorg != "PO-E")) { createOptions.dsorg ="PO"; createOptions.dirblk = 1; @@ -419,21 +419,21 @@ export class Copy { storclass: targetOptions.targetStorageClass, mgntclass: targetOptions.targetManagementClass, dataclass: targetOptions.targetDataClass, - dirblk: parseInt(dsInfo.dsorg == "PO" || dsInfo.dsorg == "POE" ? "10" : "0") + dirblk: parseInt(dsInfo.dsorg == "PO" || dsInfo.dsorg == "PO-E" ? "10" : "0") })); } /** - * Private function to convert the ALC value from the format returned by the Get() call in to the format used by the Create() call + * Converts the ALC value from the format returned by the Get() call to the format used by the Create() call. + * @param {string} getValue - The ALC value from the Get() call. + * @returns {string} - The ALC value in the format used by the Create() call. */ - private static convertAlcTozOSMF( zosmfValue: string): string { - /** - * Create dataset only accepts tracks or cylinders as allocation units. - * When the get() call retreives the dataset info, it will convert size - * allocations of the other unit types in to tracks. So we will always - * allocate the new target in tracks. - */ - return "TRK"; + private static convertAlcTozOSMF(getValue: string): string { + const alcMap: Record = { + "TRACK": "TRK", + "CYLINDERS": "CYL" + }; + return alcMap[getValue.toUpperCase()] || "TRK"; } } From c18ac0b0dac2077af99174a14b999b225af56423 Mon Sep 17 00:00:00 2001 From: jace-roell Date: Thu, 23 Jan 2025 12:20:06 -0500 Subject: [PATCH 02/11] dataset list attributes Signed-off-by: jace-roell --- .../__tests__/__system__/methods/copy/Copy.system.test.ts | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/packages/zosfiles/__tests__/__system__/methods/copy/Copy.system.test.ts b/packages/zosfiles/__tests__/__system__/methods/copy/Copy.system.test.ts index bccc37a43..eaec5f583 100644 --- a/packages/zosfiles/__tests__/__system__/methods/copy/Copy.system.test.ts +++ b/packages/zosfiles/__tests__/__system__/methods/copy/Copy.system.test.ts @@ -22,6 +22,7 @@ import { ITestEnvironment } from "../../../../../../__tests__/__src__/environmen import { tmpdir } from "os"; import path = require("path"); import * as fs from "fs"; +import { List } from "@zowe/zos-files-for-zowe-sdk" let REAL_SESSION: Session; let REAL_TARGET_SESSION: Session; @@ -895,6 +896,7 @@ describe("Copy", () => { let error: any; let response: IZosFilesResponse | undefined = undefined; let contents: Buffer; + let listAttributes; const TEST_TARGET_SESSION = REAL_TARGET_SESSION; const toDataset: IDataSet = { dsn: toDataSetName, member: file1 }; const fromOptions: IGetOptions = { @@ -909,11 +911,14 @@ describe("Copy", () => { }; const toDataSetString = `${toDataset.dsn}(${toDataset.member})`; try { + listAttributes = (await List.dataSet(REAL_SESSION, fromDataSetName, {attributes: true})).apiResponse.items; response = await Copy.dataSetCrossLPAR(REAL_SESSION, toDataset, options, fromOptions, TEST_TARGET_SESSION); contents = await Get.dataSet(TEST_TARGET_SESSION, toDataSetString); } catch (err) { error = err; } + + expect(listAttributes[0].spacu).toEqual("CYLINDERS"); expect(response?.success).toBeTruthy(); expect(error).not.toBeDefined(); expect(response?.errorMessage).not.toBeDefined(); From dc3efd1dcd1e4676f34db710a2e631a9ccc3d4ba Mon Sep 17 00:00:00 2001 From: jace-roell Date: Fri, 24 Jan 2025 10:48:26 -0500 Subject: [PATCH 03/11] unit tests, changelog, linting, system tests Signed-off-by: jace-roell --- packages/zosfiles/CHANGELOG.md | 1 + .../methods/copy/Copy.system.test.ts | 78 +++++++++++++- .../__unit__/methods/copy/Copy.unit.test.ts | 100 ++++++++++++++++++ packages/zosfiles/src/methods/copy/Copy.ts | 2 +- 4 files changed, 175 insertions(+), 6 deletions(-) diff --git a/packages/zosfiles/CHANGELOG.md b/packages/zosfiles/CHANGELOG.md index 7ade7108f..a6a8303d8 100644 --- a/packages/zosfiles/CHANGELOG.md +++ b/packages/zosfiles/CHANGELOG.md @@ -4,6 +4,7 @@ All notable changes to the Zowe z/OS files SDK package will be documented in thi ## Recent Changes +- Fixed an issue in the Copy.dataSetCrossLPAR() function where the `spacu` attribute of the copied data set was always set to `TRK`, regardless of the source data set's attributes. [#2412](https://github.com/zowe/zowe-cli/issues/2412) - Enhancement: The `Copy.dataset` function now creates a new data set if the entered target data set does not exist. [#2349](https://github.com/zowe/zowe-cli/issues/2349) - Enhancement: Added the `maxLength` option to List SDK functions (`allMembers`, `dataSetsMatchingPattern`, `membersMatchingPattern`) to specify the maximum number of items to return. [#2409](https://github.com/zowe/zowe-cli/pull/2409) - Enhancement: Added the `start` option to List SDK functions (`allMembers`, `dataSetsMatchingPattern`, `membersMatchingPattern`) to specify the first data set/member name to return in the response. [#2409](https://github.com/zowe/zowe-cli/pull/2409) diff --git a/packages/zosfiles/__tests__/__system__/methods/copy/Copy.system.test.ts b/packages/zosfiles/__tests__/__system__/methods/copy/Copy.system.test.ts index eaec5f583..e80fdd98f 100644 --- a/packages/zosfiles/__tests__/__system__/methods/copy/Copy.system.test.ts +++ b/packages/zosfiles/__tests__/__system__/methods/copy/Copy.system.test.ts @@ -22,7 +22,7 @@ import { ITestEnvironment } from "../../../../../../__tests__/__src__/environmen import { tmpdir } from "os"; import path = require("path"); import * as fs from "fs"; -import { List } from "@zowe/zos-files-for-zowe-sdk" +import { List } from "@zowe/zos-files-for-zowe-sdk"; let REAL_SESSION: Session; let REAL_TARGET_SESSION: Session; @@ -30,6 +30,8 @@ let testEnvironment: ITestEnvironment; let defaultSystem: ITestPropertiesSchema; let defaultTargetSystem: ITestPropertiesSchema; let fromDataSetName: string; +let fromDataSetNameTracks: string; +let fromDataSetNameCylinders: string; let toDataSetName: string; const file1 = "file1"; @@ -45,6 +47,8 @@ describe("Copy", () => { REAL_SESSION = TestEnvironment.createZosmfSession(testEnvironment); REAL_TARGET_SESSION = REAL_SESSION; fromDataSetName = `${defaultSystem.zosmf.user.trim().toUpperCase()}.DATA.ORIGINAL`; + fromDataSetNameTracks = `${defaultSystem.zosmf.user.trim().toUpperCase()}.DATA.TRKORG`; + fromDataSetNameCylinders = `${defaultSystem.zosmf.user.trim().toUpperCase()}.DATA.CYLORG`; toDataSetName = `${defaultSystem.zosmf.user.trim().toUpperCase()}.DATA.COPY`; }); @@ -654,7 +658,7 @@ describe("Copy", () => { responseTimeout: 5, replace: false }; - try { + try { response = await Copy.dataSetCrossLPAR(REAL_SESSION, toDataset, options, fromOptions, TEST_TARGET_SESSION); contents = await Get.dataSet(TEST_TARGET_SESSION, toDataSetName); } catch (err) { @@ -892,7 +896,14 @@ describe("Copy", () => { }); describe("Success cases", () => { - it("should copy the source to the destination data set and allocate the dataset", async() => { + it("should copy the source to the destination data set and allocate the dataset - CYLINDERS", async() => { + try { + await Create.dataSet(REAL_SESSION, CreateDataSetTypeEnum.DATA_SET_SEQUENTIAL, fromDataSetNameCylinders, {alcunit: "CYL"}); + await Upload.fileToDataset(REAL_SESSION, fileLocation, fromDataSetNameCylinders); + } catch (err) { + Imperative.console.info(`Error: ${inspect(err)}`); + } + let error: any; let response: IZosFilesResponse | undefined = undefined; let contents: Buffer; @@ -905,13 +916,13 @@ describe("Copy", () => { record: false }; const options: ICrossLparCopyDatasetOptions = { - "from-dataset": { dsn: fromDataSetName }, + "from-dataset": { dsn: fromDataSetNameCylinders }, responseTimeout: 5, replace: false }; const toDataSetString = `${toDataset.dsn}(${toDataset.member})`; try { - listAttributes = (await List.dataSet(REAL_SESSION, fromDataSetName, {attributes: true})).apiResponse.items; + listAttributes = (await List.dataSet(REAL_SESSION, fromDataSetNameCylinders, {attributes: true})).apiResponse.items; response = await Copy.dataSetCrossLPAR(REAL_SESSION, toDataset, options, fromOptions, TEST_TARGET_SESSION); contents = await Get.dataSet(TEST_TARGET_SESSION, toDataSetString); } catch (err) { @@ -924,6 +935,63 @@ describe("Copy", () => { expect(response?.errorMessage).not.toBeDefined(); expect(response?.commandResponse).toContain("Data set copied successfully"); expect(contents.toString().trim()).toBe(readFileSync(fileLocation).toString()); + + try { + await Delete.dataSet(REAL_SESSION, fromDataSetNameCylinders); + await Delete.dataSet(REAL_SESSION, toDataSetName); + } catch (err) { + Imperative.console.info(`Error: ${inspect(err)}`); + } + }); + + it("should copy the source to the destination data set and allocate the dataset - TRACKS", async() => { + try { + await Create.dataSet(REAL_SESSION, CreateDataSetTypeEnum.DATA_SET_SEQUENTIAL, fromDataSetNameTracks, {alcunit: "TRK"}); + await Upload.fileToDataset(REAL_SESSION, fileLocation, fromDataSetNameTracks); + } catch (err) { + Imperative.console.info(`Error: ${inspect(err)}`); + } + + let error: any; + let response: IZosFilesResponse | undefined = undefined; + let contents: Buffer; + let listAttributes; + const TEST_TARGET_SESSION = REAL_TARGET_SESSION; + + // Append "1" such that it is not an existing data set and thus will reach generateDatasetOptions() within Copy.ts + const toDataset: IDataSet = { dsn: toDataSetName, member: file1 }; + const fromOptions: IGetOptions = { + binary: false, + encoding: undefined, + record: false + }; + const options: ICrossLparCopyDatasetOptions = { + "from-dataset": { dsn: fromDataSetNameTracks }, + responseTimeout: 5, + replace: false + }; + const toDataSetString = `${toDataset.dsn}(${toDataset.member})`; + try { + listAttributes = (await List.dataSet(REAL_SESSION, fromDataSetNameTracks, {attributes: true})).apiResponse.items; + response = await Copy.dataSetCrossLPAR(REAL_SESSION, toDataset, options, fromOptions, TEST_TARGET_SESSION); + contents = await Get.dataSet(TEST_TARGET_SESSION, toDataSetString); + } catch (err) { + error = err; + } + + expect(listAttributes[0].spacu).toEqual("TRACKS"); + expect(response?.success).toBeTruthy(); + expect(error).not.toBeDefined(); + expect(response?.errorMessage).not.toBeDefined(); + expect(response?.commandResponse).toContain("Data set copied successfully"); + expect(contents.toString().trim()).toBe(readFileSync(fileLocation).toString()); + + try { + await Delete.dataSet(REAL_SESSION, fromDataSetNameTracks); + await Delete.dataSet(REAL_SESSION, toDataSetName); + } catch (err) { + Imperative.console.info(`Error: ${inspect(err)}`); + } }); it("should overwrite the destination data set member", async() => { diff --git a/packages/zosfiles/__tests__/__unit__/methods/copy/Copy.unit.test.ts b/packages/zosfiles/__tests__/__unit__/methods/copy/Copy.unit.test.ts index e6de696ef..50b5aa007 100644 --- a/packages/zosfiles/__tests__/__unit__/methods/copy/Copy.unit.test.ts +++ b/packages/zosfiles/__tests__/__unit__/methods/copy/Copy.unit.test.ts @@ -807,6 +807,7 @@ describe("Copy", () => { const listAllMembersSpy = jest.spyOn(List, "allMembers"); const createDatasetSpy = jest.spyOn(Create, "dataSet"); const uploadDatasetSpy = jest.spyOn(Upload, "bufferToDataSet"); + const psDataSetName = "TEST.PS.DATA.SET"; const memberName = "mem1"; const poDataSetName = "TEST.PO.DATA.SET"; @@ -826,6 +827,12 @@ describe("Copy", () => { spacu: "TRK" }; + const dataSetPOCYL = { + dsname: poDataSetName, + dsorg: "PO", + spacu: "CYL" + }; + beforeEach(() => { getDatasetSpy.mockClear(); listDatasetSpy.mockClear(); @@ -959,6 +966,99 @@ describe("Copy", () => { expect(uploadDatasetSpy).toHaveBeenCalledTimes(1); }); + it("should send a request - TRK and validate spacu", async () => { + let response; + let caughtError; + + listDatasetSpy.mockImplementation(async (): Promise => { + return { + apiResponse: { + returnedRows: 1, + items: [dataSetPO], + } + }; + }); + + listAllMembersSpy.mockImplementation(async (): Promise => { + return { + apiResponse: { + returnedRows: 1 + } + }; + }); + + try { + response = await Copy.dataSetCrossLPAR( + dummySession, + { dsn: poDataSetName, member: memberName }, + { "from-dataset": { dsn: poDataSetName, member: memberName }, replace: true }, + {}, + dummySession + ); + } catch (e) { + caughtError = e; + } + + // Assertions + expect(response).toEqual({ + success: true, + commandResponse: ZosFilesMessages.datasetCopiedSuccessfully.message + }); + + expect(listDatasetSpy).toHaveBeenCalledTimes(2); + expect(listAllMembersSpy).toHaveBeenCalledTimes(1); + expect(listAllMembersSpy.mock.calls[0][2].start).toBe(memberName); + expect(getDatasetSpy).toHaveBeenCalledTimes(1); + expect(uploadDatasetSpy).toHaveBeenCalledTimes(1); + expect(dataSetPO.spacu).toBe("TRK"); + }); + + it("should send a request - CYL and validate spacu", async () => { + let response; + let caughtError; + + listDatasetSpy.mockImplementation(async (): Promise => { + return { + apiResponse: { + returnedRows: 1, + items: [dataSetPOCYL], + } + }; + }); + + listAllMembersSpy.mockImplementation(async (): Promise => { + return { + apiResponse: { + returnedRows: 1 + } + }; + }); + + try { + response = await Copy.dataSetCrossLPAR( + dummySession, + { dsn: poDataSetName, member: memberName }, + { "from-dataset": { dsn: poDataSetName, member: memberName }, replace: true }, + {}, + dummySession + ); + } catch (e) { + caughtError = e; + } + + expect(response).toEqual({ + success: true, + commandResponse: ZosFilesMessages.datasetCopiedSuccessfully.message + }); + + expect(listDatasetSpy).toHaveBeenCalledTimes(2); + expect(listAllMembersSpy).toHaveBeenCalledTimes(1); + expect(listAllMembersSpy.mock.calls[0][2].start).toBe(memberName); + expect(getDatasetSpy).toHaveBeenCalledTimes(1); + expect(uploadDatasetSpy).toHaveBeenCalledTimes(1); + expect(dataSetPOCYL.spacu).toBe("CYL"); + }); + describe("Sequential > Member", () => { it("should send a request", async () => { let response; diff --git a/packages/zosfiles/src/methods/copy/Copy.ts b/packages/zosfiles/src/methods/copy/Copy.ts index bf9a256d7..185531b5d 100644 --- a/packages/zosfiles/src/methods/copy/Copy.ts +++ b/packages/zosfiles/src/methods/copy/Copy.ts @@ -430,7 +430,7 @@ export class Copy { */ private static convertAlcTozOSMF(getValue: string): string { const alcMap: Record = { - "TRACK": "TRK", + "TRACKS": "TRK", "CYLINDERS": "CYL" }; return alcMap[getValue.toUpperCase()] || "TRK"; From f2ea88230224c9f0859e0dcde6b8b96af1a5bfc7 Mon Sep 17 00:00:00 2001 From: jace-roell Date: Fri, 24 Jan 2025 11:08:10 -0500 Subject: [PATCH 04/11] unused var Signed-off-by: jace-roell --- .../__tests__/__unit__/methods/copy/Copy.unit.test.ts | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/packages/zosfiles/__tests__/__unit__/methods/copy/Copy.unit.test.ts b/packages/zosfiles/__tests__/__unit__/methods/copy/Copy.unit.test.ts index 50b5aa007..ad87455bd 100644 --- a/packages/zosfiles/__tests__/__unit__/methods/copy/Copy.unit.test.ts +++ b/packages/zosfiles/__tests__/__unit__/methods/copy/Copy.unit.test.ts @@ -850,7 +850,6 @@ describe("Copy", () => { describe("Sequential > Sequential", () => { it("should send a request", async () => { let response; - let caughtError; listDatasetSpy.mockImplementation(async (): Promise => { return { @@ -872,9 +871,7 @@ describe("Copy", () => { { }, dummySession ); - } catch (e) { - caughtError = e; - } + } catch{} expect(response).toEqual({ success: true, @@ -912,9 +909,7 @@ describe("Copy", () => { { }, dummySession ); - } catch (e) { - caughtError = e; - } + } catch{} expect(listDatasetSpy).toHaveBeenCalledTimes(2); expect(getDatasetSpy).toHaveBeenCalledTimes(1); From 1f123d86924350b4f69617656222f104738de6e0 Mon Sep 17 00:00:00 2001 From: jace-roell Date: Fri, 24 Jan 2025 11:24:16 -0500 Subject: [PATCH 05/11] linting Signed-off-by: jace-roell --- .../methods/copy/Copy.system.test.ts | 2 +- .../__unit__/methods/copy/Copy.unit.test.ts | 38 ++++++++----------- 2 files changed, 16 insertions(+), 24 deletions(-) diff --git a/packages/zosfiles/__tests__/__system__/methods/copy/Copy.system.test.ts b/packages/zosfiles/__tests__/__system__/methods/copy/Copy.system.test.ts index e80fdd98f..27652eea7 100644 --- a/packages/zosfiles/__tests__/__system__/methods/copy/Copy.system.test.ts +++ b/packages/zosfiles/__tests__/__system__/methods/copy/Copy.system.test.ts @@ -658,7 +658,7 @@ describe("Copy", () => { responseTimeout: 5, replace: false }; - try { + try { response = await Copy.dataSetCrossLPAR(REAL_SESSION, toDataset, options, fromOptions, TEST_TARGET_SESSION); contents = await Get.dataSet(TEST_TARGET_SESSION, toDataSetName); } catch (err) { diff --git a/packages/zosfiles/__tests__/__unit__/methods/copy/Copy.unit.test.ts b/packages/zosfiles/__tests__/__unit__/methods/copy/Copy.unit.test.ts index ad87455bd..0c557eaf0 100644 --- a/packages/zosfiles/__tests__/__unit__/methods/copy/Copy.unit.test.ts +++ b/packages/zosfiles/__tests__/__unit__/methods/copy/Copy.unit.test.ts @@ -849,8 +849,6 @@ describe("Copy", () => { describe("Success Scenarios", () => { describe("Sequential > Sequential", () => { it("should send a request", async () => { - let response; - listDatasetSpy.mockImplementation(async (): Promise => { return { apiResponse: { @@ -863,15 +861,14 @@ describe("Copy", () => { return Buffer.from("123456789abcd"); }); - try { - response = await Copy.dataSetCrossLPAR( - dummySession, - { dsn: psDataSetName }, - { "from-dataset": { dsn: dataSetPS.dsname }, replace: true}, - { }, - dummySession - ); - } catch{} + + const response = await Copy.dataSetCrossLPAR( + dummySession, + { dsn: psDataSetName }, + { "from-dataset": { dsn: dataSetPS.dsname }, replace: true}, + { }, + dummySession + ); expect(response).toEqual({ success: true, @@ -886,9 +883,6 @@ describe("Copy", () => { describe("Sequential > Sequential - no replace", () => { it("should send a request", async () => { - let response; - let caughtError; - listDatasetSpy.mockImplementation(async (): Promise => { return { apiResponse: { @@ -901,15 +895,13 @@ describe("Copy", () => { return Buffer.from("123456789abcd"); }); - try { - response = await Copy.dataSetCrossLPAR( - dummySession, - { dsn: psDataSetName }, - { "from-dataset": { dsn: dataSetPS.dsname }, replace: false}, - { }, - dummySession - ); - } catch{} + const response = await Copy.dataSetCrossLPAR( + dummySession, + { dsn: psDataSetName }, + { "from-dataset": { dsn: dataSetPS.dsname }, replace: false}, + { }, + dummySession + ); expect(listDatasetSpy).toHaveBeenCalledTimes(2); expect(getDatasetSpy).toHaveBeenCalledTimes(1); From d53ba7bb106bb96f37fa388a5c325e0b219a3c70 Mon Sep 17 00:00:00 2001 From: jace-roell Date: Fri, 24 Jan 2025 11:38:04 -0500 Subject: [PATCH 06/11] linting Signed-off-by: jace-roell --- .../__unit__/methods/copy/Copy.unit.test.ts | 63 +++++++------------ 1 file changed, 24 insertions(+), 39 deletions(-) diff --git a/packages/zosfiles/__tests__/__unit__/methods/copy/Copy.unit.test.ts b/packages/zosfiles/__tests__/__unit__/methods/copy/Copy.unit.test.ts index 0c557eaf0..590d8ab3a 100644 --- a/packages/zosfiles/__tests__/__unit__/methods/copy/Copy.unit.test.ts +++ b/packages/zosfiles/__tests__/__unit__/methods/copy/Copy.unit.test.ts @@ -895,7 +895,7 @@ describe("Copy", () => { return Buffer.from("123456789abcd"); }); - const response = await Copy.dataSetCrossLPAR( + await Copy.dataSetCrossLPAR( dummySession, { dsn: psDataSetName }, { "from-dataset": { dsn: dataSetPS.dsname }, replace: false}, @@ -955,7 +955,6 @@ describe("Copy", () => { it("should send a request - TRK and validate spacu", async () => { let response; - let caughtError; listDatasetSpy.mockImplementation(async (): Promise => { return { @@ -974,17 +973,12 @@ describe("Copy", () => { }; }); - try { - response = await Copy.dataSetCrossLPAR( - dummySession, - { dsn: poDataSetName, member: memberName }, - { "from-dataset": { dsn: poDataSetName, member: memberName }, replace: true }, - {}, - dummySession - ); - } catch (e) { - caughtError = e; - } + await Copy.dataSetCrossLPAR( + dummySession, + { dsn: poDataSetName, member: memberName }, + { "from-dataset": { dsn: poDataSetName, member: memberName }, replace: true }, + {}, + dummySession); // Assertions expect(response).toEqual({ @@ -1001,9 +995,6 @@ describe("Copy", () => { }); it("should send a request - CYL and validate spacu", async () => { - let response; - let caughtError; - listDatasetSpy.mockImplementation(async (): Promise => { return { apiResponse: { @@ -1021,17 +1012,15 @@ describe("Copy", () => { }; }); - try { - response = await Copy.dataSetCrossLPAR( - dummySession, - { dsn: poDataSetName, member: memberName }, - { "from-dataset": { dsn: poDataSetName, member: memberName }, replace: true }, - {}, - dummySession - ); - } catch (e) { - caughtError = e; - } + + const response = await Copy.dataSetCrossLPAR( + dummySession, + { dsn: poDataSetName, member: memberName }, + { "from-dataset": { dsn: poDataSetName, member: memberName }, replace: true }, + {}, + dummySession + ); + expect(response).toEqual({ success: true, @@ -1048,7 +1037,6 @@ describe("Copy", () => { describe("Sequential > Member", () => { it("should send a request", async () => { - let response; let caughtError; listDatasetSpy.mockReturnValueOnce({ @@ -1070,17 +1058,14 @@ describe("Copy", () => { }; }); - try { - response = await Copy.dataSetCrossLPAR( - dummySession, - { dsn: poDataSetName, member: memberName }, - { "from-dataset": { dsn: psDataSetName }, replace: true}, - { }, - dummySession - ); - } catch (e) { - caughtError = e; - } + const response = await Copy.dataSetCrossLPAR( + dummySession, + { dsn: poDataSetName, member: memberName }, + { "from-dataset": { dsn: psDataSetName }, replace: true}, + { }, + dummySession + ); + expect(response).toEqual({ success: true, From ee5c0b778bd032f2a398c724f1d1733f98800d31 Mon Sep 17 00:00:00 2001 From: jace-roell Date: Fri, 24 Jan 2025 11:56:29 -0500 Subject: [PATCH 07/11] fix test Signed-off-by: jace-roell --- .../__unit__/methods/copy/Copy.unit.test.ts | 24 +++++++++++-------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/packages/zosfiles/__tests__/__unit__/methods/copy/Copy.unit.test.ts b/packages/zosfiles/__tests__/__unit__/methods/copy/Copy.unit.test.ts index 590d8ab3a..ea3ef2049 100644 --- a/packages/zosfiles/__tests__/__unit__/methods/copy/Copy.unit.test.ts +++ b/packages/zosfiles/__tests__/__unit__/methods/copy/Copy.unit.test.ts @@ -883,6 +883,7 @@ describe("Copy", () => { describe("Sequential > Sequential - no replace", () => { it("should send a request", async () => { + let caughtError; listDatasetSpy.mockImplementation(async (): Promise => { return { apiResponse: { @@ -895,14 +896,19 @@ describe("Copy", () => { return Buffer.from("123456789abcd"); }); - await Copy.dataSetCrossLPAR( - dummySession, - { dsn: psDataSetName }, - { "from-dataset": { dsn: dataSetPS.dsname }, replace: false}, - { }, - dummySession - ); + try { + await Copy.dataSetCrossLPAR( + dummySession, + { dsn: psDataSetName }, + { "from-dataset": { dsn: dataSetPS.dsname }, replace: false}, + { }, + dummySession + ); + } catch (e) { + caughtError = e; + } + expect(caughtError).toBeDefined(); expect(listDatasetSpy).toHaveBeenCalledTimes(2); expect(getDatasetSpy).toHaveBeenCalledTimes(1); expect(uploadDatasetSpy).toHaveBeenCalledTimes(0); @@ -954,8 +960,6 @@ describe("Copy", () => { }); it("should send a request - TRK and validate spacu", async () => { - let response; - listDatasetSpy.mockImplementation(async (): Promise => { return { apiResponse: { @@ -973,7 +977,7 @@ describe("Copy", () => { }; }); - await Copy.dataSetCrossLPAR( + const response = await Copy.dataSetCrossLPAR( dummySession, { dsn: poDataSetName, member: memberName }, { "from-dataset": { dsn: poDataSetName, member: memberName }, replace: true }, From f63bab611c4eca1c76cbd736815b467ba776775b Mon Sep 17 00:00:00 2001 From: jace-roell Date: Wed, 29 Jan 2025 14:38:41 -0500 Subject: [PATCH 08/11] changelog Signed-off-by: jace-roell --- packages/zosfiles/CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/zosfiles/CHANGELOG.md b/packages/zosfiles/CHANGELOG.md index a6a8303d8..10dc1cfd1 100644 --- a/packages/zosfiles/CHANGELOG.md +++ b/packages/zosfiles/CHANGELOG.md @@ -4,7 +4,7 @@ All notable changes to the Zowe z/OS files SDK package will be documented in thi ## Recent Changes -- Fixed an issue in the Copy.dataSetCrossLPAR() function where the `spacu` attribute of the copied data set was always set to `TRK`, regardless of the source data set's attributes. [#2412](https://github.com/zowe/zowe-cli/issues/2412) +- Fixed an issue in the `Copy.dataSetCrossLPAR()` function where the `spacu` attribute of the copied data set was always set to `TRK`, regardless of the source data set's attributes. [#2412](https://github.com/zowe/zowe-cli/issues/2412) - Enhancement: The `Copy.dataset` function now creates a new data set if the entered target data set does not exist. [#2349](https://github.com/zowe/zowe-cli/issues/2349) - Enhancement: Added the `maxLength` option to List SDK functions (`allMembers`, `dataSetsMatchingPattern`, `membersMatchingPattern`) to specify the maximum number of items to return. [#2409](https://github.com/zowe/zowe-cli/pull/2409) - Enhancement: Added the `start` option to List SDK functions (`allMembers`, `dataSetsMatchingPattern`, `membersMatchingPattern`) to specify the first data set/member name to return in the response. [#2409](https://github.com/zowe/zowe-cli/pull/2409) From 9c95fa5460b8670f641cd6d3c96cd553bd8d8b26 Mon Sep 17 00:00:00 2001 From: jace-roell Date: Wed, 29 Jan 2025 14:44:13 -0500 Subject: [PATCH 09/11] changelog Signed-off-by: jace-roell --- packages/zosfiles/CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/packages/zosfiles/CHANGELOG.md b/packages/zosfiles/CHANGELOG.md index ab7a443b3..9027ce8ea 100644 --- a/packages/zosfiles/CHANGELOG.md +++ b/packages/zosfiles/CHANGELOG.md @@ -2,6 +2,10 @@ All notable changes to the Zowe z/OS files SDK package will be documented in this file. +## Recent Changes + +- Fixed an issue in the `Copy.dataSetCrossLPAR()` function where the `spacu` attribute of the copied data set was always set to `TRK`, regardless of the source data set's attributes. [#2412](https://github.com/zowe/zowe-cli/issues/2412) + ## `8.12.0` - Fixed an issue in the `Copy.dataSetCrossLPAR()` function where the `spacu` attribute of the copied data set was always set to `TRK`, regardless of the source data set's attributes. [#2412](https://github.com/zowe/zowe-cli/issues/2412) From 02c3786f7ee5bd10192f5686ade5ad1539731836 Mon Sep 17 00:00:00 2001 From: jace-roell Date: Wed, 29 Jan 2025 16:33:16 -0500 Subject: [PATCH 10/11] changelog, startsWith(), comments Signed-off-by: jace-roell --- packages/zosfiles/CHANGELOG.md | 1 - packages/zosfiles/src/methods/copy/Copy.ts | 8 +++++++- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/packages/zosfiles/CHANGELOG.md b/packages/zosfiles/CHANGELOG.md index 9027ce8ea..186ef4334 100644 --- a/packages/zosfiles/CHANGELOG.md +++ b/packages/zosfiles/CHANGELOG.md @@ -8,7 +8,6 @@ All notable changes to the Zowe z/OS files SDK package will be documented in thi ## `8.12.0` -- Fixed an issue in the `Copy.dataSetCrossLPAR()` function where the `spacu` attribute of the copied data set was always set to `TRK`, regardless of the source data set's attributes. [#2412](https://github.com/zowe/zowe-cli/issues/2412) - Enhancement: The `Copy.dataset` function now creates a new data set if the entered target data set does not exist. [#2349](https://github.com/zowe/zowe-cli/issues/2349) - Enhancement: Added the `maxLength` option to List SDK functions (`allMembers`, `dataSetsMatchingPattern`, `membersMatchingPattern`) to specify the maximum number of items to return. [#2409](https://github.com/zowe/zowe-cli/pull/2409) - Enhancement: Added the `start` option to List SDK functions (`allMembers`, `dataSetsMatchingPattern`, `membersMatchingPattern`) to specify the first data set/member name to return in the response. [#2409](https://github.com/zowe/zowe-cli/pull/2409) diff --git a/packages/zosfiles/src/methods/copy/Copy.ts b/packages/zosfiles/src/methods/copy/Copy.ts index 185531b5d..cb7b56ef5 100644 --- a/packages/zosfiles/src/methods/copy/Copy.ts +++ b/packages/zosfiles/src/methods/copy/Copy.ts @@ -272,7 +272,7 @@ export class Copy { /* * If the source is a PDS and no member was specified then abort the copy. */ - if((sourceDataSetObj.dsorg == "PO" || sourceDataSetObj.dsorg == "PO-E") && sourceMember == undefined){ + if((sourceDataSetObj.dsorg.startsWith("PO") || sourceDataSetObj.dsorg.startsWith("PO-E")) && sourceMember == undefined){ throw new ImperativeError({ msg: ZosFilesMessages.datasetCopiedAbortedNoPDS.message }); } } @@ -429,6 +429,12 @@ export class Copy { * @returns {string} - The ALC value in the format used by the Create() call. */ private static convertAlcTozOSMF(getValue: string): string { + /** + * Create dataset only accepts tracks or cylinders as allocation units. + * When the get() call retreives the dataset info, it will convert size + * allocations of the other unit types in to tracks. So we will always + * allocate the new target in tracks. + */ const alcMap: Record = { "TRACKS": "TRK", "CYLINDERS": "CYL" From 2d40140bd234405a8d8049fffa8d2a473df6ba35 Mon Sep 17 00:00:00 2001 From: jace-roell Date: Wed, 29 Jan 2025 16:47:46 -0500 Subject: [PATCH 11/11] startsWith(PO) and removed PO-E case Signed-off-by: jace-roell --- packages/zosfiles/src/methods/copy/Copy.ts | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/packages/zosfiles/src/methods/copy/Copy.ts b/packages/zosfiles/src/methods/copy/Copy.ts index cb7b56ef5..7e5d91d90 100644 --- a/packages/zosfiles/src/methods/copy/Copy.ts +++ b/packages/zosfiles/src/methods/copy/Copy.ts @@ -272,7 +272,7 @@ export class Copy { /* * If the source is a PDS and no member was specified then abort the copy. */ - if((sourceDataSetObj.dsorg.startsWith("PO") || sourceDataSetObj.dsorg.startsWith("PO-E")) && sourceMember == undefined){ + if(sourceDataSetObj.dsorg.startsWith("PO") && sourceMember == undefined){ throw new ImperativeError({ msg: ZosFilesMessages.datasetCopiedAbortedNoPDS.message }); } } @@ -316,7 +316,7 @@ export class Copy { targetDataSetObj = TargetDsList.apiResponse.items[dsnameIndex]; targetFound = true; - if((targetDataSetObj.dsorg == "PO" || targetDataSetObj.dsorg == "PO-E") && targetMember == undefined) + if(targetDataSetObj.dsorg.startsWith("PO") && targetMember == undefined) { throw new ImperativeError({ msg: ZosFilesMessages.datasetCopiedAbortedTargetNotPDSMember.message }); } @@ -346,11 +346,11 @@ export class Copy { * If this is a PDS but the target is the sequential dataset and does not exist, * create a new sequential dataset with the same parameters as the original PDS. */ - if((createOptions.dsorg == "PO" || createOptions.dsorg == "PO-E") && targetMember == undefined){ + if(createOptions.dsorg.startsWith("PO") && targetMember == undefined){ createOptions.dsorg ="PS"; createOptions.dirblk = 0; } - else if(targetMember != undefined && (createOptions.dsorg != "PO" && createOptions.dsorg != "PO-E")) + else if(targetMember != undefined && !createOptions.dsorg.startsWith("PO")) { createOptions.dsorg ="PO"; createOptions.dirblk = 1; @@ -419,7 +419,7 @@ export class Copy { storclass: targetOptions.targetStorageClass, mgntclass: targetOptions.targetManagementClass, dataclass: targetOptions.targetDataClass, - dirblk: parseInt(dsInfo.dsorg == "PO" || dsInfo.dsorg == "PO-E" ? "10" : "0") + dirblk: parseInt(dsInfo.dsorg.startsWith("PO") ? "10" : "0") })); }