Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

copy dsclp command resulting in spacu attribute always being TRACK #2419

Merged
merged 13 commits into from
Jan 29, 2025
4 changes: 4 additions & 0 deletions packages/zosfiles/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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`

- 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)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,13 +22,16 @@ 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;
let testEnvironment: ITestEnvironment<ITestPropertiesSchema>;
let defaultSystem: ITestPropertiesSchema;
let defaultTargetSystem: ITestPropertiesSchema;
let fromDataSetName: string;
let fromDataSetNameTracks: string;
let fromDataSetNameCylinders: string;
let toDataSetName: string;

const file1 = "file1";
Expand All @@ -44,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`;
});

Expand Down Expand Up @@ -891,10 +896,18 @@ 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;
let listAttributes;
const TEST_TARGET_SESSION = REAL_TARGET_SESSION;
const toDataset: IDataSet = { dsn: toDataSetName, member: file1 };
const fromOptions: IGetOptions = {
Expand All @@ -903,22 +916,82 @@ 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, 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) {
error = err;
}

expect(listAttributes[0].spacu).toEqual("CYLINDERS");
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, 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() => {
Expand Down
134 changes: 105 additions & 29 deletions packages/zosfiles/__tests__/__unit__/methods/copy/Copy.unit.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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";
Expand All @@ -826,6 +827,12 @@ describe("Copy", () => {
spacu: "TRK"
};

const dataSetPOCYL = {
dsname: poDataSetName,
dsorg: "PO",
spacu: "CYL"
};

beforeEach(() => {
getDatasetSpy.mockClear();
listDatasetSpy.mockClear();
Expand All @@ -842,9 +849,6 @@ describe("Copy", () => {
describe("Success Scenarios", () => {
describe("Sequential > Sequential", () => {
it("should send a request", async () => {
let response;
let caughtError;

listDatasetSpy.mockImplementation(async (): Promise<any> => {
return {
apiResponse: {
Expand All @@ -857,17 +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 (e) {
caughtError = e;
}

const response = await Copy.dataSetCrossLPAR(
dummySession,
{ dsn: psDataSetName },
{ "from-dataset": { dsn: dataSetPS.dsname }, replace: true},
{ },
dummySession
);

expect(response).toEqual({
success: true,
Expand All @@ -882,9 +883,7 @@ describe("Copy", () => {

describe("Sequential > Sequential - no replace", () => {
it("should send a request", async () => {
let response;
let caughtError;

listDatasetSpy.mockImplementation(async (): Promise<any> => {
return {
apiResponse: {
Expand All @@ -898,7 +897,7 @@ describe("Copy", () => {
});

try {
response = await Copy.dataSetCrossLPAR(
await Copy.dataSetCrossLPAR(
dummySession,
{ dsn: psDataSetName },
{ "from-dataset": { dsn: dataSetPS.dsname }, replace: false},
Expand All @@ -909,6 +908,7 @@ describe("Copy", () => {
caughtError = e;
}

expect(caughtError).toBeDefined();
expect(listDatasetSpy).toHaveBeenCalledTimes(2);
expect(getDatasetSpy).toHaveBeenCalledTimes(1);
expect(uploadDatasetSpy).toHaveBeenCalledTimes(0);
Expand Down Expand Up @@ -959,9 +959,88 @@ describe("Copy", () => {
expect(uploadDatasetSpy).toHaveBeenCalledTimes(1);
});

it("should send a request - TRK and validate spacu", async () => {
listDatasetSpy.mockImplementation(async (): Promise<any> => {
return {
apiResponse: {
returnedRows: 1,
items: [dataSetPO],
}
};
});

listAllMembersSpy.mockImplementation(async (): Promise<any> => {
return {
apiResponse: {
returnedRows: 1
}
};
});

const response = await Copy.dataSetCrossLPAR(
dummySession,
{ dsn: poDataSetName, member: memberName },
{ "from-dataset": { dsn: poDataSetName, member: memberName }, replace: true },
{},
dummySession);

// 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 () => {
listDatasetSpy.mockImplementation(async (): Promise<any> => {
return {
apiResponse: {
returnedRows: 1,
items: [dataSetPOCYL],
}
};
});

listAllMembersSpy.mockImplementation(async (): Promise<any> => {
return {
apiResponse: {
returnedRows: 1
}
};
});


const response = await Copy.dataSetCrossLPAR(
dummySession,
{ dsn: poDataSetName, member: memberName },
{ "from-dataset": { dsn: poDataSetName, member: memberName }, replace: true },
{},
dummySession
);


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;
let caughtError;

listDatasetSpy.mockReturnValueOnce({
Expand All @@ -983,17 +1062,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,
Expand Down
Loading
Loading