Skip to content
This repository has been archived by the owner on Nov 13, 2023. It is now read-only.

Fix failure to load schema when there is no profile of that type #916

Merged
merged 6 commits into from
Dec 14, 2022
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,11 @@

All notable changes to the Imperative package will be documented in this file.

## Recent Changes

- BugFix: Fixed ProfileInfo API failing to load schema for v1 profile when schema exists but no profiles of that type exist. [#645](https://github.com/zowe/imperative/issues/645)
- BugFix: Updated return type of `ProfileInfo.getDefaultProfile` method to indicate that it returns null when no profile exists for the specified type.

## `5.7.3`

- Enhancement: Exported `AppSettings` for cli and other apps to use [#840](https://github.com/zowe/imperative/issues/840)
Expand Down
52 changes: 43 additions & 9 deletions packages/config/__tests__/ProfileInfo.OldProfiles.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,12 @@

import * as path from "path";
import { ProfileInfo } from "../src/ProfileInfo";
import { IProfAttrs } from "../src/doc/IProfAttrs";
import { IProfOpts } from "../src/doc/IProfOpts";
import { ProfInfoErr } from "../src/ProfInfoErr";
import { ProfLocType } from "../src/doc/IProfLoc";
import { IProfileSchema, ProfileIO } from "../../profiles";
import { ImperativeError } from "../../error";

const testAppNm = "ProfInfoApp";
const testEnvPrefix = testAppNm.toUpperCase();
Expand Down Expand Up @@ -69,7 +71,7 @@ describe("Old-school ProfileInfo tests", () => {
await profInfo.readProfilesFromDisk();
const profAttrs = profInfo.getDefaultProfile("ThisTypeDoesNotExist");
expect(profAttrs).toBeNull();
expect(warnSpy).toHaveBeenCalledTimes(1);
expect(warnSpy).toHaveBeenCalledTimes(2);
expect(warnSpy).toHaveBeenCalledWith("Found no old-school profile for type 'ThisTypeDoesNotExist'.");
});

Expand Down Expand Up @@ -108,7 +110,7 @@ describe("Old-school ProfileInfo tests", () => {
const profInfo = createNewProfInfo(homeDirPath);
await profInfo.readProfilesFromDisk();
const desiredProfType = "tso";
const profAttrs = profInfo.getDefaultProfile(desiredProfType);
const profAttrs = profInfo.getDefaultProfile(desiredProfType) as IProfAttrs;

expect(profAttrs).not.toBeNull();
expect(profAttrs.isDefaultProfile).toBe(true);
Expand Down Expand Up @@ -222,7 +224,7 @@ describe("Old-school ProfileInfo tests", () => {
it("should find known args in simple service profile", async () => {
const profInfo = createNewProfInfo(homeDirPath);
await profInfo.readProfilesFromDisk();
const profAttrs = profInfo.getDefaultProfile("zosmf");
const profAttrs = profInfo.getDefaultProfile("zosmf") as IProfAttrs;
delete (profInfo as any).mOldSchoolProfileDefaults.base;
const mergedArgs = profInfo.mergeArgsForProfile(profAttrs);

Expand All @@ -246,7 +248,7 @@ describe("Old-school ProfileInfo tests", () => {
it("should find known args in service and base profile", async () => {
const profInfo = createNewProfInfo(homeDirPath);
await profInfo.readProfilesFromDisk();
const profAttrs = profInfo.getDefaultProfile("zosmf");
const profAttrs = profInfo.getDefaultProfile("zosmf") as IProfAttrs;
(profInfo as any).mOldSchoolProfileDefaults.base = "base_apiml";
const mergedArgs = profInfo.mergeArgsForProfile(profAttrs);

Expand Down Expand Up @@ -317,7 +319,7 @@ describe("Old-school ProfileInfo tests", () => {
it("should throw if there are required args missing in service profile", async () => {
const profInfo = createNewProfInfo(homeDirPath);
await profInfo.readProfilesFromDisk();
const profAttrs = profInfo.getDefaultProfile("zosmf");
const profAttrs = profInfo.getDefaultProfile("zosmf") as IProfAttrs;
jest.spyOn(profInfo as any, "loadSchema").mockReturnValue(requiredProfSchema);

let caughtError;
Expand Down Expand Up @@ -357,7 +359,7 @@ describe("Old-school ProfileInfo tests", () => {
it("should throw if schema fails to load", async () => {
const profInfo = createNewProfInfo(homeDirPath);
await profInfo.readProfilesFromDisk();
const profAttrs = profInfo.getDefaultProfile("zosmf");
const profAttrs = profInfo.getDefaultProfile("zosmf") as IProfAttrs;
jest.spyOn(profInfo as any, "loadSchema").mockReturnValueOnce(null);
let caughtError;

Expand All @@ -372,6 +374,23 @@ describe("Old-school ProfileInfo tests", () => {
expect(caughtError.errorCode).toBe(ProfInfoErr.LOAD_SCHEMA_FAILED);
expect(caughtError.message).toContain("Failed to load schema for profile type zosmf");
});

it("should throw if profile attributes are undefined", async () => {
const profInfo = createNewProfInfo(homeDirPath);
await profInfo.readProfilesFromDisk();
const profAttrs = profInfo.getDefaultProfile("missing") as IProfAttrs;
let caughtError;

try {
profInfo.mergeArgsForProfile(profAttrs);
} catch (error) {
expect(error instanceof ImperativeError).toBe(true);
caughtError = error;
}

expect(caughtError).toBeDefined();
expect(caughtError.message).toContain("Profile attributes must be defined");
});
});

describe("mergeArgsForProfileType", () => {
Expand All @@ -398,6 +417,21 @@ describe("Old-school ProfileInfo tests", () => {
});

describe("loadAllSchemas", () => {
it("should load schema for profile type that does not exist", async () => {
const profInfo = createNewProfInfo(homeDirPath);
await profInfo.readProfilesFromDisk();
let caughtError;

try {
(profInfo as any).loadAllSchemas();
} catch (error) {
caughtError = error;
}

expect(caughtError).toBeUndefined();
expect((profInfo as any).mProfileSchemaCache.size).toBe(4);
});

it("should throw when schema file is invalid", async () => {
const profInfo = createNewProfInfo(homeDirPath);
await profInfo.readProfilesFromDisk();
Expand Down Expand Up @@ -459,7 +493,7 @@ describe("Old-school ProfileInfo tests", () => {
it("should load secure args from old school profiles", async () => {
const profInfo = createNewProfInfo(homeDirPath);
await profInfo.readProfilesFromDisk();
const profAttrs = profInfo.getDefaultProfile("zosmf");
const profAttrs = profInfo.getDefaultProfile("zosmf") as IProfAttrs;
const mergedArgs = profInfo.mergeArgsForProfile(profAttrs);

const userArg = mergedArgs.knownArgs.find((arg) => arg.argName === "user");
Expand All @@ -474,7 +508,7 @@ describe("Old-school ProfileInfo tests", () => {
it("should get secure values with mergeArgsForProfile:getSecureVals for old school profiles", async () => {
const profInfo = createNewProfInfo(homeDirPath);
await profInfo.readProfilesFromDisk();
const profAttrs = profInfo.getDefaultProfile("zosmf");
const profAttrs = profInfo.getDefaultProfile("zosmf") as IProfAttrs;
const mergedArgs = profInfo.mergeArgsForProfile(profAttrs, { getSecureVals: true });

const userArg = mergedArgs.knownArgs.find((arg) => arg.argName === "user");
Expand All @@ -496,7 +530,7 @@ describe("Old-school ProfileInfo tests", () => {
it("should return basic osLoc information for a regular v1 profile", async () => {
const profInfo = createNewProfInfo(homeDirPath);
await profInfo.readProfilesFromDisk();
const profAttrs = profInfo.getDefaultProfile("zosmf");
const profAttrs = profInfo.getDefaultProfile("zosmf") as IProfAttrs;
const osLocInfo = profInfo.getOsLocInfo(profAttrs);
expect(osLocInfo).toBeDefined();
expect(osLocInfo.length).toBe(1);
Expand Down
68 changes: 51 additions & 17 deletions packages/config/__tests__/ProfileInfo.TeamConfig.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import { IProfileSchema } from "../../profiles";
import { AbstractSession, SessConstants } from "../../rest";
import { ConfigAutoStore } from "../src/ConfigAutoStore";
import { ImperativeConfig } from "../../utilities/src/ImperativeConfig";
import { ImperativeError } from "../../error";

const testAppNm = "ProfInfoApp";
const testEnvPrefix = testAppNm.toUpperCase();
Expand Down Expand Up @@ -225,11 +226,23 @@ describe("TeamConfig ProfileInfo tests", () => {
expect(caughtError.message).toBe("Failed to initialize secure credential manager");
});

it("should throw exception if readProfilesFromDisk not called", async () => {
const methodNames: (keyof ProfileInfo)[] = [
"updateProperty",
"updateKnownProperty",
"getAllProfiles",
"getDefaultProfile",
"getTeamConfig",
"mergeArgsForProfile",
"mergeArgsForProfileType",
"usingTeamConfig",
"getOsLocInfo",
"loadSecureArg"
];
it.each(methodNames)("should throw exception if readProfilesFromDisk not called before %s", async (methodName) => {
let caughtErr: ProfInfoErr;
const profInfo = createNewProfInfo(teamProjDir);
try {
profInfo.getDefaultProfile("zosmf");
await (profInfo as any)[methodName]();
} catch (err) {
expect(err instanceof ProfInfoErr).toBe(true);
caughtErr = err;
Expand Down Expand Up @@ -277,7 +290,7 @@ describe("TeamConfig ProfileInfo tests", () => {
const profInfo = createNewProfInfo(teamProjDir);
await profInfo.readProfilesFromDisk();
const desiredProfType = "tso";
const profAttrs = profInfo.getDefaultProfile(desiredProfType);
const profAttrs = profInfo.getDefaultProfile(desiredProfType) as IProfAttrs;
zFernand0 marked this conversation as resolved.
Show resolved Hide resolved

expect(profAttrs).not.toBeNull();
expect(profAttrs.isDefaultProfile).toBe(true);
Expand Down Expand Up @@ -439,7 +452,7 @@ describe("TeamConfig ProfileInfo tests", () => {
it("should find known args in simple service profile", async () => {
const profInfo = createNewProfInfo(teamProjDir);
await profInfo.readProfilesFromDisk();
const profAttrs = profInfo.getDefaultProfile("zosmf");
const profAttrs = profInfo.getDefaultProfile("zosmf") as IProfAttrs;
delete profInfo.getTeamConfig().layerActive().properties.defaults.base;
// Since ProjectDir and HomeDir are the same (based on the ZOWE_CLI_HOME),
// we also need to delete the base profile from that layer (even though is't just a copy)
Expand All @@ -465,7 +478,7 @@ describe("TeamConfig ProfileInfo tests", () => {
it("should find known args in nested service profile", async () => {
const profInfo = createNewProfInfo(teamProjDir);
await profInfo.readProfilesFromDisk();
const profAttrs = profInfo.getDefaultProfile("tso");
const profAttrs = profInfo.getDefaultProfile("tso") as IProfAttrs;
delete profInfo.getTeamConfig().layerActive().properties.defaults.base;
// Since ProjectDir and HomeDir are the same (based on the ZOWE_CLI_HOME),
// we also need to delete the base profile from that layer (even though is't just a copy)
Expand Down Expand Up @@ -498,7 +511,7 @@ describe("TeamConfig ProfileInfo tests", () => {
it("should find known args in service and base profile", async () => {
const profInfo = createNewProfInfo(teamProjDir);
await profInfo.readProfilesFromDisk();
const profAttrs = profInfo.getDefaultProfile("zosmf");
const profAttrs = profInfo.getDefaultProfile("zosmf") as IProfAttrs;
const mergedArgs = profInfo.mergeArgsForProfile(profAttrs);

const expectedArgs = [
Expand Down Expand Up @@ -558,7 +571,7 @@ describe("TeamConfig ProfileInfo tests", () => {

const profInfo = createNewProfInfo(teamProjDir, { overrideWithEnv: true });
await profInfo.readProfilesFromDisk();
const profAttrs = profInfo.getDefaultProfile("dummy");
const profAttrs = profInfo.getDefaultProfile("dummy") as IProfAttrs;
delete profInfo.getTeamConfig().layerActive().properties.defaults.base;
// Since ProjectDir and HomeDir are the same (based on the ZOWE_CLI_HOME),
// we also need to delete the base profile from that layer (even though is't just a copy)
Expand Down Expand Up @@ -604,7 +617,7 @@ describe("TeamConfig ProfileInfo tests", () => {
const profInfo = createNewProfInfo(teamProjDir);
await profInfo.readProfilesFromDisk();
profInfo.getTeamConfig().set("profiles.LPAR1.properties.base-path", fakeBasePath);
const profAttrs = profInfo.getDefaultProfile("zosmf");
const profAttrs = profInfo.getDefaultProfile("zosmf") as IProfAttrs;
delete profInfo.getTeamConfig().layerActive().properties.defaults.base;
// Since ProjectDir and HomeDir are the same (based on the ZOWE_CLI_HOME),
// we also need to delete the base profile from that layer (even though is't just a copy)
Expand Down Expand Up @@ -645,8 +658,9 @@ describe("TeamConfig ProfileInfo tests", () => {
expect(caughtError.message).toContain("Failed to find property fake in the profile doesNotExist");
});

it("should throw if profile location type is invalid", () => {
it("should throw if profile location type is invalid", async () => {
const profInfo = createNewProfInfo(teamProjDir);
await profInfo.readProfilesFromDisk();
let caughtError;

try {
Expand All @@ -669,7 +683,7 @@ describe("TeamConfig ProfileInfo tests", () => {
it("should list optional args missing in service profile", async () => {
const profInfo = createNewProfInfo(teamProjDir);
await profInfo.readProfilesFromDisk();
const profAttrs = profInfo.getDefaultProfile("zosmf");
const profAttrs = profInfo.getDefaultProfile("zosmf") as IProfAttrs;
delete profInfo.getTeamConfig().layerActive().properties.defaults.base;
// Since ProjectDir and HomeDir are the same (based on the ZOWE_CLI_HOME),
// we also need to delete the base profile from that layer (even though is't just a copy)
Expand All @@ -694,7 +708,7 @@ describe("TeamConfig ProfileInfo tests", () => {
it("should throw if there are required args missing in service profile", async () => {
const profInfo = createNewProfInfo(teamProjDir);
await profInfo.readProfilesFromDisk();
const profAttrs = profInfo.getDefaultProfile("zosmf");
const profAttrs = profInfo.getDefaultProfile("zosmf") as IProfAttrs;
jest.spyOn(profInfo as any, "loadSchema").mockReturnValueOnce(requiredProfSchema);
let caughtError;

Expand All @@ -713,7 +727,7 @@ describe("TeamConfig ProfileInfo tests", () => {
it("should validate profile for missing args when schema exists", async () => {
const profInfo = createNewProfInfo(teamProjDir);
await profInfo.readProfilesFromDisk();
const profAttrs = profInfo.getDefaultProfile("zosmf");
const profAttrs = profInfo.getDefaultProfile("zosmf") as IProfAttrs;
delete profInfo.getTeamConfig().layerActive().properties.defaults.base;
// Since ProjectDir and HomeDir are the same (based on the ZOWE_CLI_HOME),
// we also need to delete the base profile from that layer (even though is't just a copy)
Expand Down Expand Up @@ -742,7 +756,7 @@ describe("TeamConfig ProfileInfo tests", () => {
it("should throw if schema fails to load", async () => {
const profInfo = createNewProfInfo(teamProjDir);
await profInfo.readProfilesFromDisk();
const profAttrs = profInfo.getDefaultProfile("zosmf");
const profAttrs = profInfo.getDefaultProfile("zosmf") as IProfAttrs;
jest.spyOn(profInfo as any, "loadSchema").mockReturnValueOnce(null);
let caughtError;

Expand Down Expand Up @@ -782,6 +796,22 @@ describe("TeamConfig ProfileInfo tests", () => {
expect(mergedArgs.knownArgs.length).toEqual(3);
});

it("should throw if profile attributes are undefined", async () => {
const profInfo = createNewProfInfo(teamProjDir);
await profInfo.readProfilesFromDisk();
const profAttrs = profInfo.getDefaultProfile("missing") as IProfAttrs;
let caughtError;

try {
profInfo.mergeArgsForProfile(profAttrs);
} catch (error) {
expect(error instanceof ImperativeError).toBe(true);
caughtError = error;
}

expect(caughtError).toBeDefined();
expect(caughtError.message).toContain("Profile attributes must be defined");
});
});

describe("mergeArgsForProfileType", () => {
Expand Down Expand Up @@ -1086,7 +1116,7 @@ describe("TeamConfig ProfileInfo tests", () => {
it("should load secure args from team config", async () => {
const profInfo = createNewProfInfo(teamProjDir);
await profInfo.readProfilesFromDisk();
const profAttrs = profInfo.getDefaultProfile("zosmf");
const profAttrs = profInfo.getDefaultProfile("zosmf") as IProfAttrs;
const mergedArgs = profInfo.mergeArgsForProfile(profAttrs);

const userArg = mergedArgs.knownArgs.find((arg) => arg.argName === "user");
Expand All @@ -1101,7 +1131,7 @@ describe("TeamConfig ProfileInfo tests", () => {
it("should get secure values with mergeArgsForProfile:getSecureVals for team config", async () => {
const profInfo = createNewProfInfo(teamProjDir);
await profInfo.readProfilesFromDisk();
const profAttrs = profInfo.getDefaultProfile("zosmf");
const profAttrs = profInfo.getDefaultProfile("zosmf") as IProfAttrs;
const mergedArgs = profInfo.mergeArgsForProfile(profAttrs, { getSecureVals: true });

const userArg = mergedArgs.knownArgs.find((arg) => arg.argName === "user");
Expand All @@ -1113,6 +1143,8 @@ describe("TeamConfig ProfileInfo tests", () => {

it("should treat secure arg as plain text if loaded from environment variable", async () => {
const profInfo = createNewProfInfo(teamProjDir);
await profInfo.readProfilesFromDisk();

const expectedValue = "insecure";
const actualValue = profInfo.loadSecureArg({
argName: "test",
Expand All @@ -1126,6 +1158,7 @@ describe("TeamConfig ProfileInfo tests", () => {

it("should fail to load secure arg when not found", async () => {
const profInfo = createNewProfInfo(teamProjDir);
await profInfo.readProfilesFromDisk();
let caughtError;

try {
Expand All @@ -1145,8 +1178,9 @@ describe("TeamConfig ProfileInfo tests", () => {
});

describe("getOsLocInfo", () => {
it("should return undefined if no osLoc is present", () => {
it("should return undefined if no osLoc is present", async () => {
const profInfo = createNewProfInfo(teamProjDir);
await profInfo.readProfilesFromDisk();
const prof = { profName: "test", profLoc: { locType: 1 }, profType: "test", isDefaultProfile: false };
expect(profInfo.getOsLocInfo(prof)).toBeUndefined();
expect(profInfo.getOsLocInfo({ ...prof, profLoc: { locType: 1, osLoc: [] } })).toBeUndefined();
Expand All @@ -1155,7 +1189,7 @@ describe("TeamConfig ProfileInfo tests", () => {
it("should return basic osLoc information for a unique profile", async () => {
const profInfo = createNewProfInfo(teamProjDir);
await profInfo.readProfilesFromDisk();
const profAttrs = profInfo.getDefaultProfile("zosmf");
const profAttrs = profInfo.getDefaultProfile("zosmf") as IProfAttrs;
const osLocInfo = profInfo.getOsLocInfo(profAttrs);
const expectedObjs = [
{ name: profAttrs.profName, path: profAttrs.profLoc.osLoc[0], user: false, global: false },
Expand Down