From 7f70ccaf7dfc405f177e6b1a0d4100af6dcf8ede Mon Sep 17 00:00:00 2001 From: "b.mucolli" Date: Mon, 15 Dec 2025 16:08:11 +0100 Subject: [PATCH 01/18] TA-4767: Append protocol to CELONIS_URL environment variable --- src/core/profile/profile.service.ts | 6 +- tests/core/profile/profile.service.spec.ts | 199 +++++++++++++++++++++ 2 files changed, 204 insertions(+), 1 deletion(-) create mode 100644 tests/core/profile/profile.service.spec.ts diff --git a/src/core/profile/profile.service.ts b/src/core/profile/profile.service.ts index 6ecde3be..69485add 100644 --- a/src/core/profile/profile.service.ts +++ b/src/core/profile/profile.service.ts @@ -309,7 +309,11 @@ export class ProfileService { return; } - process.env.TEAM_URL = process.env.CELONIS_URL; + let celonisUrl = process.env.CELONIS_URL; + if (!celonisUrl.startsWith("http://") && !celonisUrl.startsWith("https://")) { + celonisUrl = `https://${celonisUrl}`; + } + process.env.TEAM_URL = celonisUrl; if (process.env.CELONIS_API_TOKEN) { process.env.API_TOKEN = process.env.CELONIS_API_TOKEN; diff --git a/tests/core/profile/profile.service.spec.ts b/tests/core/profile/profile.service.spec.ts new file mode 100644 index 00000000..269db047 --- /dev/null +++ b/tests/core/profile/profile.service.spec.ts @@ -0,0 +1,199 @@ +import { ProfileService } from "../../../src/core/profile/profile.service"; + +describe("ProfileService - mapCelonisEnvProfile", () => { + let profileService: ProfileService; + let originalCelonisUrl: string | undefined; + let originalCelonisApiToken: string | undefined; + let originalTeamUrl: string | undefined; + let originalApiToken: string | undefined; + + beforeEach(() => { + profileService = new ProfileService(); + originalCelonisUrl = process.env.CELONIS_URL; + originalCelonisApiToken = process.env.CELONIS_API_TOKEN; + originalTeamUrl = process.env.TEAM_URL; + originalApiToken = process.env.API_TOKEN; + }); + + afterEach(() => { + if (originalCelonisUrl !== undefined) { + process.env.CELONIS_URL = originalCelonisUrl; + } else { + delete process.env.CELONIS_URL; + } + + if (originalCelonisApiToken !== undefined) { + process.env.CELONIS_API_TOKEN = originalCelonisApiToken; + } else { + delete process.env.CELONIS_API_TOKEN; + } + + if (originalTeamUrl !== undefined) { + process.env.TEAM_URL = originalTeamUrl; + } else { + delete process.env.TEAM_URL; + } + + if (originalApiToken !== undefined) { + process.env.API_TOKEN = originalApiToken; + } else { + delete process.env.API_TOKEN; + } + }); + + describe("when CELONIS_URL is not set", () => { + it("should return early and not modify environment variables", () => { + delete process.env.CELONIS_URL; + delete process.env.TEAM_URL; + delete process.env.API_TOKEN; + + (profileService as any).mapCelonisEnvProfile(); + + expect(process.env.TEAM_URL).toBeUndefined(); + expect(process.env.API_TOKEN).toBeUndefined(); + }); + }); + + describe("when CELONIS_URL is set", () => { + it("should set TEAM_URL to CELONIS_URL when it already starts with https://", () => { + process.env.CELONIS_URL = "https://example.celonis.cloud"; + delete process.env.TEAM_URL; + + (profileService as any).mapCelonisEnvProfile(); + + expect(process.env.TEAM_URL).toBe("https://example.celonis.cloud"); + }); + + it("should prepend https:// to CELONIS_URL when it does not start with https://", () => { + process.env.CELONIS_URL = "example.celonis.cloud"; + delete process.env.TEAM_URL; + + (profileService as any).mapCelonisEnvProfile(); + + expect(process.env.TEAM_URL).toBe("https://example.celonis.cloud"); + }); + + it("should leave CELONIS_URL unchanged when it starts with http://", () => { + process.env.CELONIS_URL = "http://example.celonis.cloud"; + delete process.env.TEAM_URL; + + (profileService as any).mapCelonisEnvProfile(); + + expect(process.env.TEAM_URL).toBe("http://example.celonis.cloud"); + }); + + it("should handle CELONIS_URL with path and prepend https://", () => { + process.env.CELONIS_URL = "example.celonis.cloud/path/to/resource"; + delete process.env.TEAM_URL; + + (profileService as any).mapCelonisEnvProfile(); + + expect(process.env.TEAM_URL).toBe("https://example.celonis.cloud/path/to/resource"); + }); + + it("should handle CELONIS_URL with port and prepend https://", () => { + process.env.CELONIS_URL = "example.celonis.cloud:8080"; + delete process.env.TEAM_URL; + + (profileService as any).mapCelonisEnvProfile(); + + expect(process.env.TEAM_URL).toBe("https://example.celonis.cloud:8080"); + }); + }); + + describe("when CELONIS_API_TOKEN is set", () => { + it("should set API_TOKEN to CELONIS_API_TOKEN when CELONIS_URL starts with https://", () => { + process.env.CELONIS_URL = "https://example.celonis.cloud"; + process.env.CELONIS_API_TOKEN = "test-api-token"; + delete process.env.API_TOKEN; + + (profileService as any).mapCelonisEnvProfile(); + + expect(process.env.API_TOKEN).toBe("test-api-token"); + expect(process.env.TEAM_URL).toBe("https://example.celonis.cloud"); + }); + + it("should set API_TOKEN to CELONIS_API_TOKEN when CELONIS_URL does not start with https://", () => { + process.env.CELONIS_URL = "example.celonis.cloud"; + process.env.CELONIS_API_TOKEN = "test-api-token"; + delete process.env.API_TOKEN; + + (profileService as any).mapCelonisEnvProfile(); + + expect(process.env.API_TOKEN).toBe("test-api-token"); + expect(process.env.TEAM_URL).toBe("https://example.celonis.cloud"); + }); + + it("should set API_TOKEN to CELONIS_API_TOKEN when CELONIS_URL starts with http://", () => { + process.env.CELONIS_URL = "http://example.celonis.cloud"; + process.env.CELONIS_API_TOKEN = "test-api-token"; + delete process.env.API_TOKEN; + + (profileService as any).mapCelonisEnvProfile(); + + expect(process.env.API_TOKEN).toBe("test-api-token"); + expect(process.env.TEAM_URL).toBe("http://example.celonis.cloud"); + }); + }); + + describe("when CELONIS_API_TOKEN is not set", () => { + it("should delete API_TOKEN when CELONIS_URL starts with https://", () => { + process.env.CELONIS_URL = "https://example.celonis.cloud"; + process.env.API_TOKEN = "existing-token"; + delete process.env.CELONIS_API_TOKEN; + + (profileService as any).mapCelonisEnvProfile(); + + expect(process.env.API_TOKEN).toBeUndefined(); + expect(process.env.TEAM_URL).toBe("https://example.celonis.cloud"); + }); + + it("should delete API_TOKEN when CELONIS_URL does not start with https://", () => { + process.env.CELONIS_URL = "example.celonis.cloud"; + process.env.API_TOKEN = "existing-token"; + delete process.env.CELONIS_API_TOKEN; + + (profileService as any).mapCelonisEnvProfile(); + + expect(process.env.API_TOKEN).toBeUndefined(); + expect(process.env.TEAM_URL).toBe("https://example.celonis.cloud"); + }); + + it("should delete API_TOKEN when CELONIS_URL starts with http://", () => { + process.env.CELONIS_URL = "http://example.celonis.cloud"; + process.env.API_TOKEN = "existing-token"; + delete process.env.CELONIS_API_TOKEN; + + (profileService as any).mapCelonisEnvProfile(); + + expect(process.env.API_TOKEN).toBeUndefined(); + expect(process.env.TEAM_URL).toBe("http://example.celonis.cloud"); + }); + }); + + describe("combined scenarios", () => { + it("should handle URL without https:// and set API_TOKEN when both are provided", () => { + process.env.CELONIS_URL = "example.celonis.cloud"; + process.env.CELONIS_API_TOKEN = "my-token-123"; + delete process.env.TEAM_URL; + delete process.env.API_TOKEN; + + (profileService as any).mapCelonisEnvProfile(); + + expect(process.env.TEAM_URL).toBe("https://example.celonis.cloud"); + expect(process.env.API_TOKEN).toBe("my-token-123"); + }); + + it("should handle URL with https:// and delete API_TOKEN when token is not provided", () => { + process.env.CELONIS_URL = "https://example.celonis.cloud"; + process.env.API_TOKEN = "old-token"; + delete process.env.CELONIS_API_TOKEN; + + (profileService as any).mapCelonisEnvProfile(); + + expect(process.env.TEAM_URL).toBe("https://example.celonis.cloud"); + expect(process.env.API_TOKEN).toBeUndefined(); + }); + }); +}); + From 79b93700f32ddc14a07b09c549464bc65595cf50 Mon Sep 17 00:00:00 2001 From: "b.mucolli" Date: Mon, 15 Dec 2025 16:39:56 +0100 Subject: [PATCH 02/18] TA-4767: Push debug logs --- src/core/command/cli-context.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/core/command/cli-context.ts b/src/core/command/cli-context.ts index 1aafd622..1d23dc8e 100644 --- a/src/core/command/cli-context.ts +++ b/src/core/command/cli-context.ts @@ -76,6 +76,8 @@ export class Context { } try { this.gitProfile = await this.gitProfileService.findProfile(gitProfileName); + console.log("gitProfile: " + this.gitProfile); + console.log("gitProfileName: " + this.gitProfileName); this.gitProfileName = gitProfileName; this.log.debug(`Using Git profile ${gitProfileName}`); } catch (err) { From 4a6530484f04c6ad8aaa17d1287fe0ee775c91d9 Mon Sep 17 00:00:00 2001 From: "b.mucolli" Date: Mon, 15 Dec 2025 16:41:09 +0100 Subject: [PATCH 03/18] TA-4767: Use log --- src/core/command/cli-context.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/core/command/cli-context.ts b/src/core/command/cli-context.ts index 1d23dc8e..74dd0e40 100644 --- a/src/core/command/cli-context.ts +++ b/src/core/command/cli-context.ts @@ -76,8 +76,8 @@ export class Context { } try { this.gitProfile = await this.gitProfileService.findProfile(gitProfileName); - console.log("gitProfile: " + this.gitProfile); - console.log("gitProfileName: " + this.gitProfileName); + this.log.debug("gitProfile: " + this.gitProfile); + this.log.debug("gitProfileName: " + this.gitProfileName); this.gitProfileName = gitProfileName; this.log.debug(`Using Git profile ${gitProfileName}`); } catch (err) { From 462bf56b77a3b59a258b292323c0bb487240e293 Mon Sep 17 00:00:00 2001 From: "b.mucolli" Date: Tue, 16 Dec 2025 10:03:56 +0100 Subject: [PATCH 04/18] TA-4767: Add another debug log --- src/core/profile/profile.service.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/core/profile/profile.service.ts b/src/core/profile/profile.service.ts index 69485add..afd29df3 100644 --- a/src/core/profile/profile.service.ts +++ b/src/core/profile/profile.service.ts @@ -21,6 +21,7 @@ export interface Config { } export class ProfileService { + private log; private profileContainerPath = path.resolve(homedir, ".celonis-content-cli-profiles"); private configContainer = path.resolve(this.profileContainerPath, "config.json"); @@ -313,6 +314,7 @@ export class ProfileService { if (!celonisUrl.startsWith("http://") && !celonisUrl.startsWith("https://")) { celonisUrl = `https://${celonisUrl}`; } + this.log.debug("url: " + celonisUrl); process.env.TEAM_URL = celonisUrl; if (process.env.CELONIS_API_TOKEN) { From c628c422b4f2a84bf538488ca15d36d1ca599159 Mon Sep 17 00:00:00 2001 From: "b.mucolli" Date: Tue, 16 Dec 2025 10:07:28 +0100 Subject: [PATCH 05/18] TA-4767: Add another debug log --- src/core/command/cli-context.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/core/command/cli-context.ts b/src/core/command/cli-context.ts index 74dd0e40..087f8cdc 100644 --- a/src/core/command/cli-context.ts +++ b/src/core/command/cli-context.ts @@ -56,6 +56,7 @@ export class Context { } } try { + this.log.debug("Another log"); this.profile = await this.profileService.findProfile(profileName); this.profileName = profileName; this.log.debug(`Using profile ${profileName}`); From 970b1a1b19bb63dd9598ffe19ea251a825399855 Mon Sep 17 00:00:00 2001 From: "b.mucolli" Date: Tue, 16 Dec 2025 10:09:32 +0100 Subject: [PATCH 06/18] TA-4767: Add another debug log --- src/core/profile/profile.service.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/core/profile/profile.service.ts b/src/core/profile/profile.service.ts index afd29df3..2beaa140 100644 --- a/src/core/profile/profile.service.ts +++ b/src/core/profile/profile.service.ts @@ -307,6 +307,7 @@ export class ProfileService { private mapCelonisEnvProfile(): void { if (!process.env.CELONIS_URL) { + this.log.debug("no CELONIS_URL env var"); return; } From d2f521beffe0542f3d2bd93ce0fa1e44c0ca948e Mon Sep 17 00:00:00 2001 From: "b.mucolli" Date: Tue, 16 Dec 2025 10:10:28 +0100 Subject: [PATCH 07/18] TA-4767: Add another debug log --- src/core/profile/profile.service.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/core/profile/profile.service.ts b/src/core/profile/profile.service.ts index 2beaa140..95c0b1bc 100644 --- a/src/core/profile/profile.service.ts +++ b/src/core/profile/profile.service.ts @@ -21,7 +21,8 @@ export interface Config { } export class ProfileService { - private log; + // @ts-ignore + private log: logger; private profileContainerPath = path.resolve(homedir, ".celonis-content-cli-profiles"); private configContainer = path.resolve(this.profileContainerPath, "config.json"); From 0b2c7113d18b8ea740023d3dba4ee0484377c69a Mon Sep 17 00:00:00 2001 From: "b.mucolli" Date: Tue, 16 Dec 2025 10:19:14 +0100 Subject: [PATCH 08/18] TA-4767: Fix added logger --- src/core/profile/profile.service.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/core/profile/profile.service.ts b/src/core/profile/profile.service.ts index 95c0b1bc..720f0780 100644 --- a/src/core/profile/profile.service.ts +++ b/src/core/profile/profile.service.ts @@ -21,8 +21,7 @@ export interface Config { } export class ProfileService { - // @ts-ignore - private log: logger; + private log = logger; private profileContainerPath = path.resolve(homedir, ".celonis-content-cli-profiles"); private configContainer = path.resolve(this.profileContainerPath, "config.json"); From 47dd6816f7b3802e7f76b28395a6511f7cc8540a Mon Sep 17 00:00:00 2001 From: "b.mucolli" Date: Tue, 16 Dec 2025 10:25:15 +0100 Subject: [PATCH 09/18] TA-4767: Add more logs --- src/core/profile/profile.service.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/core/profile/profile.service.ts b/src/core/profile/profile.service.ts index 720f0780..be1d42ba 100644 --- a/src/core/profile/profile.service.ts +++ b/src/core/profile/profile.service.ts @@ -82,6 +82,9 @@ export class ProfileService { private async buildProfileFromEnvVariables(): Promise { const profileVariables = this.getProfileEnvVariables(); + this.log.debug("building profile token: " + profileVariables.apiToken) + this.log.debug("building profile url: " + profileVariables.teamUrl) + const profile: Profile = { name: profileVariables.teamUrl, team: profileVariables.teamUrl, From 636ff26126ac49d86b7150e78f2efc12f751e75c Mon Sep 17 00:00:00 2001 From: "b.mucolli" Date: Tue, 16 Dec 2025 10:35:29 +0100 Subject: [PATCH 10/18] TA-4767: Add more logs --- src/core/command/cli-context.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/core/command/cli-context.ts b/src/core/command/cli-context.ts index 087f8cdc..14d801e0 100644 --- a/src/core/command/cli-context.ts +++ b/src/core/command/cli-context.ts @@ -59,6 +59,9 @@ export class Context { this.log.debug("Another log"); this.profile = await this.profileService.findProfile(profileName); this.profileName = profileName; + this.log.debug("Profile name: " + this.profile.name) + this.log.debug("Profile url: " + this.profile.apiToken) + this.log.debug("Profile url " + this.profile.team) this.log.debug(`Using profile ${profileName}`); } catch (err) { this.log.debug(err); From 85c1a29a24a851e2af759b942e909076e6b4355c Mon Sep 17 00:00:00 2001 From: "b.mucolli" Date: Tue, 16 Dec 2025 16:17:37 +0100 Subject: [PATCH 11/18] TA-4767: Add more logs --- src/core/profile/profile.service.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/core/profile/profile.service.ts b/src/core/profile/profile.service.ts index be1d42ba..a5c3142f 100644 --- a/src/core/profile/profile.service.ts +++ b/src/core/profile/profile.service.ts @@ -27,7 +27,7 @@ export class ProfileService { public async findProfile(profileName: string): Promise { return new Promise((resolve, reject) => { - this.mapCelonisEnvProfile(); + // this.mapCelonisEnvProfile(); this.checkIfMissingProfile(profileName, reject); try { if (process.env.TEAM_URL && process.env.API_TOKEN) { @@ -303,6 +303,8 @@ export class ProfileService { } private checkIfMissingProfile(profileName: string, reject: any): void { + this.log.debug("teamUrl: " + process.env.TEAM_URL); + this.log.debug("apiToken: " + process.env.API_TOKEN); if (!profileName && (!process.env.TEAM_URL || !process.env.API_TOKEN)) { reject("Profile not found"); } From e3b0a87c97fa6d929b0917b58e5dd74368206880 Mon Sep 17 00:00:00 2001 From: "b.mucolli" Date: Tue, 16 Dec 2025 16:38:11 +0100 Subject: [PATCH 12/18] TA-4767: Change logic to give priority to profile --- src/core/profile/profile.service.ts | 27 +++++++++++++++++++-------- 1 file changed, 19 insertions(+), 8 deletions(-) diff --git a/src/core/profile/profile.service.ts b/src/core/profile/profile.service.ts index a5c3142f..ee3fd770 100644 --- a/src/core/profile/profile.service.ts +++ b/src/core/profile/profile.service.ts @@ -27,12 +27,8 @@ export class ProfileService { public async findProfile(profileName: string): Promise { return new Promise((resolve, reject) => { - // this.mapCelonisEnvProfile(); - this.checkIfMissingProfile(profileName, reject); try { - if (process.env.TEAM_URL && process.env.API_TOKEN) { - resolve(this.buildProfileFromEnvVariables()); - } else { + if (!this.checkIfMissingProfile(profileName)) { const file = fs.readFileSync( path.resolve(this.profileContainerPath, this.constructProfileFileName(profileName)), { encoding: "utf-8" } @@ -40,7 +36,22 @@ export class ProfileService { const profile : Profile = JSON.parse(file); this.refreshProfile(profile) .then(() => resolve(profile)); + } else { + this.mapCelonisEnvProfile(); + resolve(this.buildProfileFromEnvVariables()); } + + // if (process.env.TEAM_URL && process.env.API_TOKEN) { + // resolve(this.buildProfileFromEnvVariables()); + // } else { + // const file = fs.readFileSync( + // path.resolve(this.profileContainerPath, this.constructProfileFileName(profileName)), + // { encoding: "utf-8" } + // ); + // const profile : Profile = JSON.parse(file); + // this.refreshProfile(profile) + // .then(() => resolve(profile)); + // } } catch (e) { reject(`The profile ${profileName} couldn't be resolved.`); } @@ -302,11 +313,11 @@ export class ProfileService { }) } - private checkIfMissingProfile(profileName: string, reject: any): void { + private checkIfMissingProfile(profileName: string): boolean { this.log.debug("teamUrl: " + process.env.TEAM_URL); this.log.debug("apiToken: " + process.env.API_TOKEN); - if (!profileName && (!process.env.TEAM_URL || !process.env.API_TOKEN)) { - reject("Profile not found"); + if (!profileName) { + return true; } } From daa6f4b73330a108bcfc15d87fdf543249c0fbeb Mon Sep 17 00:00:00 2001 From: "b.mucolli" Date: Tue, 16 Dec 2025 16:47:28 +0100 Subject: [PATCH 13/18] TA-4767: Change logic to give more priority to team_url and api_token than celonis_url and celonis_api_token --- src/core/profile/profile.service.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/core/profile/profile.service.ts b/src/core/profile/profile.service.ts index ee3fd770..62935f8b 100644 --- a/src/core/profile/profile.service.ts +++ b/src/core/profile/profile.service.ts @@ -36,6 +36,8 @@ export class ProfileService { const profile : Profile = JSON.parse(file); this.refreshProfile(profile) .then(() => resolve(profile)); + } else if (process.env.TEAM_URL && process.env.API_TOKEN) { + resolve(this.buildProfileFromEnvVariables()); } else { this.mapCelonisEnvProfile(); resolve(this.buildProfileFromEnvVariables()); From 77e496ee16ca8293d2c56a6c9dc5ca08dc48d00d Mon Sep 17 00:00:00 2001 From: "b.mucolli" Date: Tue, 16 Dec 2025 17:27:42 +0100 Subject: [PATCH 14/18] TA-4767: Add more logs --- src/core/profile/profile.service.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/core/profile/profile.service.ts b/src/core/profile/profile.service.ts index 62935f8b..642c04bc 100644 --- a/src/core/profile/profile.service.ts +++ b/src/core/profile/profile.service.ts @@ -272,6 +272,9 @@ export class ProfileService { } private getProfileEnvVariables(): any { + this.log.debug("inside getProfileEnvVariables"); + this.log.debug("TEAM_URL: " + process.env.TEAM_URL); + this.log.debug("API_TOKEN: " + process.env.API_TOKEN); return { teamUrl: this.getBaseTeamUrl(process.env.TEAM_URL), apiToken: process.env.API_TOKEN, From 3bd8d0d849795db2553bebe327a3d2c2cc5cd13c Mon Sep 17 00:00:00 2001 From: "b.mucolli" Date: Tue, 16 Dec 2025 17:32:29 +0100 Subject: [PATCH 15/18] TA-4767: Add more looogs --- src/core/profile/profile.service.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/core/profile/profile.service.ts b/src/core/profile/profile.service.ts index 642c04bc..c1273678 100644 --- a/src/core/profile/profile.service.ts +++ b/src/core/profile/profile.service.ts @@ -29,6 +29,7 @@ export class ProfileService { return new Promise((resolve, reject) => { try { if (!this.checkIfMissingProfile(profileName)) { + this.log.debug("has profile") const file = fs.readFileSync( path.resolve(this.profileContainerPath, this.constructProfileFileName(profileName)), { encoding: "utf-8" } @@ -37,8 +38,10 @@ export class ProfileService { this.refreshProfile(profile) .then(() => resolve(profile)); } else if (process.env.TEAM_URL && process.env.API_TOKEN) { + this.log.debug("has TEAM_URL and API_TOKEN") resolve(this.buildProfileFromEnvVariables()); } else { + this.log.debug("doesn't have TEAM_URL and API_TOKEN") this.mapCelonisEnvProfile(); resolve(this.buildProfileFromEnvVariables()); } From 9d76d3f04794ba5b6ac39ab24be571161833eddc Mon Sep 17 00:00:00 2001 From: "b.mucolli" Date: Tue, 16 Dec 2025 17:50:10 +0100 Subject: [PATCH 16/18] TA-4767: Remove all logs and add tests --- src/core/command/cli-context.ts | 4 - src/core/profile/profile.service.ts | 26 --- tests/core/profile/profile.service.spec.ts | 224 +++++++++++++++++++++ 3 files changed, 224 insertions(+), 30 deletions(-) diff --git a/src/core/command/cli-context.ts b/src/core/command/cli-context.ts index 14d801e0..74dd0e40 100644 --- a/src/core/command/cli-context.ts +++ b/src/core/command/cli-context.ts @@ -56,12 +56,8 @@ export class Context { } } try { - this.log.debug("Another log"); this.profile = await this.profileService.findProfile(profileName); this.profileName = profileName; - this.log.debug("Profile name: " + this.profile.name) - this.log.debug("Profile url: " + this.profile.apiToken) - this.log.debug("Profile url " + this.profile.team) this.log.debug(`Using profile ${profileName}`); } catch (err) { this.log.debug(err); diff --git a/src/core/profile/profile.service.ts b/src/core/profile/profile.service.ts index c1273678..2f5656b9 100644 --- a/src/core/profile/profile.service.ts +++ b/src/core/profile/profile.service.ts @@ -21,7 +21,6 @@ export interface Config { } export class ProfileService { - private log = logger; private profileContainerPath = path.resolve(homedir, ".celonis-content-cli-profiles"); private configContainer = path.resolve(this.profileContainerPath, "config.json"); @@ -29,7 +28,6 @@ export class ProfileService { return new Promise((resolve, reject) => { try { if (!this.checkIfMissingProfile(profileName)) { - this.log.debug("has profile") const file = fs.readFileSync( path.resolve(this.profileContainerPath, this.constructProfileFileName(profileName)), { encoding: "utf-8" } @@ -38,25 +36,11 @@ export class ProfileService { this.refreshProfile(profile) .then(() => resolve(profile)); } else if (process.env.TEAM_URL && process.env.API_TOKEN) { - this.log.debug("has TEAM_URL and API_TOKEN") resolve(this.buildProfileFromEnvVariables()); } else { - this.log.debug("doesn't have TEAM_URL and API_TOKEN") this.mapCelonisEnvProfile(); resolve(this.buildProfileFromEnvVariables()); } - - // if (process.env.TEAM_URL && process.env.API_TOKEN) { - // resolve(this.buildProfileFromEnvVariables()); - // } else { - // const file = fs.readFileSync( - // path.resolve(this.profileContainerPath, this.constructProfileFileName(profileName)), - // { encoding: "utf-8" } - // ); - // const profile : Profile = JSON.parse(file); - // this.refreshProfile(profile) - // .then(() => resolve(profile)); - // } } catch (e) { reject(`The profile ${profileName} couldn't be resolved.`); } @@ -98,9 +82,6 @@ export class ProfileService { private async buildProfileFromEnvVariables(): Promise { const profileVariables = this.getProfileEnvVariables(); - this.log.debug("building profile token: " + profileVariables.apiToken) - this.log.debug("building profile url: " + profileVariables.teamUrl) - const profile: Profile = { name: profileVariables.teamUrl, team: profileVariables.teamUrl, @@ -275,9 +256,6 @@ export class ProfileService { } private getProfileEnvVariables(): any { - this.log.debug("inside getProfileEnvVariables"); - this.log.debug("TEAM_URL: " + process.env.TEAM_URL); - this.log.debug("API_TOKEN: " + process.env.API_TOKEN); return { teamUrl: this.getBaseTeamUrl(process.env.TEAM_URL), apiToken: process.env.API_TOKEN, @@ -322,8 +300,6 @@ export class ProfileService { } private checkIfMissingProfile(profileName: string): boolean { - this.log.debug("teamUrl: " + process.env.TEAM_URL); - this.log.debug("apiToken: " + process.env.API_TOKEN); if (!profileName) { return true; } @@ -331,7 +307,6 @@ export class ProfileService { private mapCelonisEnvProfile(): void { if (!process.env.CELONIS_URL) { - this.log.debug("no CELONIS_URL env var"); return; } @@ -339,7 +314,6 @@ export class ProfileService { if (!celonisUrl.startsWith("http://") && !celonisUrl.startsWith("https://")) { celonisUrl = `https://${celonisUrl}`; } - this.log.debug("url: " + celonisUrl); process.env.TEAM_URL = celonisUrl; if (process.env.CELONIS_API_TOKEN) { diff --git a/tests/core/profile/profile.service.spec.ts b/tests/core/profile/profile.service.spec.ts index 269db047..5094f96b 100644 --- a/tests/core/profile/profile.service.spec.ts +++ b/tests/core/profile/profile.service.spec.ts @@ -1,3 +1,13 @@ +import * as fs from "fs"; +import * as path from "path"; +import * as os from "os"; +import { ProfileValidator } from "../../../src/core/profile/profile.validator"; +import { Profile, ProfileType, AuthenticationType } from "../../../src/core/profile/profile.interface"; + +jest.mock("os", () => ({ + homedir: jest.fn(() => "/mock/home") +})); + import { ProfileService } from "../../../src/core/profile/profile.service"; describe("ProfileService - mapCelonisEnvProfile", () => { @@ -197,3 +207,217 @@ describe("ProfileService - mapCelonisEnvProfile", () => { }); }); +describe("ProfileService - findProfile", () => { + let profileService: ProfileService; + let originalCelonisUrl: string | undefined; + let originalCelonisApiToken: string | undefined; + let originalTeamUrl: string | undefined; + let originalApiToken: string | undefined; + const mockHomedir = "/mock/home"; + const mockProfilePath = path.resolve(mockHomedir, ".celonis-content-cli-profiles"); + + beforeEach(() => { + (os.homedir as jest.Mock).mockReturnValue(mockHomedir); + profileService = new ProfileService(); + originalCelonisUrl = process.env.CELONIS_URL; + originalCelonisApiToken = process.env.CELONIS_API_TOKEN; + originalTeamUrl = process.env.TEAM_URL; + originalApiToken = process.env.API_TOKEN; + + jest.spyOn(ProfileValidator, "validateProfile").mockResolvedValue(AuthenticationType.BEARER); + }); + + afterEach(() => { + jest.clearAllMocks(); + + if (originalCelonisUrl !== undefined) { + process.env.CELONIS_URL = originalCelonisUrl; + } else { + delete process.env.CELONIS_URL; + } + + if (originalCelonisApiToken !== undefined) { + process.env.CELONIS_API_TOKEN = originalCelonisApiToken; + } else { + delete process.env.CELONIS_API_TOKEN; + } + + if (originalTeamUrl !== undefined) { + process.env.TEAM_URL = originalTeamUrl; + } else { + delete process.env.TEAM_URL; + } + + if (originalApiToken !== undefined) { + process.env.API_TOKEN = originalApiToken; + } else { + delete process.env.API_TOKEN; + } + }); + + describe("when profileName is provided and profile file exists", () => { + it("should load profile from file and ignore environment variables", async () => { + const profileName = "test-profile"; + const mockProfile: Profile = { + name: profileName, + team: "https://example.celonis.cloud", + apiToken: "profile-token", + authenticationType: AuthenticationType.BEARER, + type: ProfileType.KEY + }; + + const profileFilePath = path.resolve(mockProfilePath, `${profileName}.json`); + (fs.readFileSync as jest.Mock).mockReturnValue(JSON.stringify(mockProfile)); + + process.env.TEAM_URL = "https://env.celonis.cloud"; + process.env.API_TOKEN = "env-token"; + + jest.spyOn(profileService, "refreshProfile").mockResolvedValue(undefined); + + const result = await profileService.findProfile(profileName); + + expect(fs.readFileSync).toHaveBeenCalledWith(profileFilePath, { encoding: "utf-8" }); + expect(result).toEqual(mockProfile); + expect(profileService.refreshProfile).toHaveBeenCalledWith(mockProfile); + }); + + it("should call refreshProfile before resolving", async () => { + const profileName = "test-profile"; + const mockProfile: Profile = { + name: profileName, + team: "https://example.celonis.cloud", + apiToken: "profile-token", + authenticationType: AuthenticationType.BEARER, + type: ProfileType.KEY + }; + + const profileFilePath = path.resolve(mockProfilePath, `${profileName}.json`); + (fs.readFileSync as jest.Mock).mockReturnValue(JSON.stringify(mockProfile)); + + const refreshProfileSpy = jest.spyOn(profileService, "refreshProfile").mockResolvedValue(undefined); + + await profileService.findProfile(profileName); + + expect(refreshProfileSpy).toHaveBeenCalledWith(mockProfile); + }); + }); + + describe("when profileName is not provided", () => { + it("should use TEAM_URL and API_TOKEN from environment when both are set", async () => { + delete process.env.CELONIS_URL; + process.env.TEAM_URL = "https://env.celonis.cloud"; + process.env.API_TOKEN = "env-token"; + + const result = await profileService.findProfile(""); + + expect(result.name).toBe("https://env.celonis.cloud"); + expect(result.team).toBe("https://env.celonis.cloud"); + expect(result.apiToken).toBe("env-token"); + expect(result.type).toBe(ProfileType.KEY); + expect(ProfileValidator.validateProfile).toHaveBeenCalled(); + }); + + it("should map CELONIS_URL to TEAM_URL when TEAM_URL is not set", async () => { + process.env.CELONIS_URL = "https://celonis.celonis.cloud"; + process.env.CELONIS_API_TOKEN = "celonis-token"; + delete process.env.TEAM_URL; + delete process.env.API_TOKEN; + + const result = await profileService.findProfile(""); + + expect(process.env.TEAM_URL).toBe("https://celonis.celonis.cloud"); + expect(process.env.API_TOKEN).toBe("celonis-token"); + expect(result.name).toBe("https://celonis.celonis.cloud"); + expect(result.team).toBe("https://celonis.celonis.cloud"); + expect(result.apiToken).toBe("celonis-token"); + }); + + it("should map CELONIS_URL without https:// and prepend it", async () => { + process.env.CELONIS_URL = "celonis.celonis.cloud"; + process.env.CELONIS_API_TOKEN = "celonis-token"; + delete process.env.TEAM_URL; + delete process.env.API_TOKEN; + + const result = await profileService.findProfile(""); + + expect(process.env.TEAM_URL).toBe("https://celonis.celonis.cloud"); + expect(result.team).toBe("https://celonis.celonis.cloud"); + }); + + it("should delete API_TOKEN when CELONIS_API_TOKEN is not set", async () => { + process.env.CELONIS_URL = "https://celonis.celonis.cloud"; + process.env.API_TOKEN = "old-token"; + delete process.env.CELONIS_API_TOKEN; + delete process.env.TEAM_URL; + + await profileService.findProfile(""); + + expect(process.env.API_TOKEN).toBeUndefined(); + }); + }); + + describe("when profileName is provided but profile file does not exist", () => { + it("should reject with error message", async () => { + const profileName = "non-existent-profile"; + (fs.readFileSync as jest.Mock).mockImplementation(() => { + throw new Error("File not found"); + }); + + await expect(profileService.findProfile(profileName)).rejects.toBe( + `The profile ${profileName} couldn't be resolved.` + ); + }); + }); + + describe("priority of profile vs environment variables", () => { + it("should prioritize profile file over environment variables when both exist", async () => { + const profileName = "test-profile"; + const mockProfile: Profile = { + name: profileName, + team: "https://profile.celonis.cloud", + apiToken: "profile-token", + authenticationType: AuthenticationType.BEARER, + type: ProfileType.KEY + }; + + const profileFilePath = path.resolve(mockProfilePath, `${profileName}.json`); + (fs.readFileSync as jest.Mock).mockReturnValue(JSON.stringify(mockProfile)); + + process.env.TEAM_URL = "https://env.celonis.cloud"; + process.env.API_TOKEN = "env-token"; + process.env.CELONIS_URL = "https://celonis.celonis.cloud"; + process.env.CELONIS_API_TOKEN = "celonis-token"; + + jest.spyOn(profileService, "refreshProfile").mockResolvedValue(undefined); + + const result = await profileService.findProfile(profileName); + + expect(result.team).toBe("https://profile.celonis.cloud"); + expect(result.apiToken).toBe("profile-token"); + expect(fs.readFileSync).toHaveBeenCalledWith(profileFilePath, { encoding: "utf-8" }); + }); + }); + + describe("error handling", () => { + it("should reject when profile file read fails", async () => { + const profileName = "test-profile"; + (fs.readFileSync as jest.Mock).mockImplementation(() => { + throw new Error("Read error"); + }); + + await expect(profileService.findProfile(profileName)).rejects.toBe( + `The profile ${profileName} couldn't be resolved.` + ); + }); + + it("should reject when profile JSON is invalid", async () => { + const profileName = "test-profile"; + (fs.readFileSync as jest.Mock).mockReturnValue("invalid json"); + + await expect(profileService.findProfile(profileName)).rejects.toBe( + `The profile ${profileName} couldn't be resolved.` + ); + }); + }); +}); + From 069e9b00a81f4bbf145f83fce7cc34412a8868ca Mon Sep 17 00:00:00 2001 From: "b.mucolli" Date: Tue, 16 Dec 2025 17:54:56 +0100 Subject: [PATCH 17/18] TA-4767: Remove some other logs --- src/core/command/cli-context.ts | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/core/command/cli-context.ts b/src/core/command/cli-context.ts index 74dd0e40..1aafd622 100644 --- a/src/core/command/cli-context.ts +++ b/src/core/command/cli-context.ts @@ -76,8 +76,6 @@ export class Context { } try { this.gitProfile = await this.gitProfileService.findProfile(gitProfileName); - this.log.debug("gitProfile: " + this.gitProfile); - this.log.debug("gitProfileName: " + this.gitProfileName); this.gitProfileName = gitProfileName; this.log.debug(`Using Git profile ${gitProfileName}`); } catch (err) { From ffc0d1707434a6d8a51860ded140de8263e242a0 Mon Sep 17 00:00:00 2001 From: "b.mucolli" Date: Wed, 17 Dec 2025 13:27:04 +0100 Subject: [PATCH 18/18] TA-4767: Call reject when missing profile and all env vars --- src/core/profile/profile.service.ts | 8 ++- tests/core/profile/profile.service.spec.ts | 61 ++++++++++++++++++---- 2 files changed, 55 insertions(+), 14 deletions(-) diff --git a/src/core/profile/profile.service.ts b/src/core/profile/profile.service.ts index 2f5656b9..dbd3ebc3 100644 --- a/src/core/profile/profile.service.ts +++ b/src/core/profile/profile.service.ts @@ -37,9 +37,11 @@ export class ProfileService { .then(() => resolve(profile)); } else if (process.env.TEAM_URL && process.env.API_TOKEN) { resolve(this.buildProfileFromEnvVariables()); - } else { + } else if (process.env.CELONIS_URL && process.env.CELONIS_API_TOKEN) { this.mapCelonisEnvProfile(); resolve(this.buildProfileFromEnvVariables()); + } else { + reject(`The profile ${profileName} couldn't be resolved due to missing environment variables.`); } } catch (e) { reject(`The profile ${profileName} couldn't be resolved.`); @@ -306,10 +308,6 @@ export class ProfileService { } private mapCelonisEnvProfile(): void { - if (!process.env.CELONIS_URL) { - return; - } - let celonisUrl = process.env.CELONIS_URL; if (!celonisUrl.startsWith("http://") && !celonisUrl.startsWith("https://")) { celonisUrl = `https://${celonisUrl}`; diff --git a/tests/core/profile/profile.service.spec.ts b/tests/core/profile/profile.service.spec.ts index 5094f96b..e4b24488 100644 --- a/tests/core/profile/profile.service.spec.ts +++ b/tests/core/profile/profile.service.spec.ts @@ -52,15 +52,14 @@ describe("ProfileService - mapCelonisEnvProfile", () => { }); describe("when CELONIS_URL is not set", () => { - it("should return early and not modify environment variables", () => { + it("should throw an error when trying to call startsWith on undefined", () => { delete process.env.CELONIS_URL; delete process.env.TEAM_URL; delete process.env.API_TOKEN; - (profileService as any).mapCelonisEnvProfile(); - - expect(process.env.TEAM_URL).toBeUndefined(); - expect(process.env.API_TOKEN).toBeUndefined(); + expect(() => { + (profileService as any).mapCelonisEnvProfile(); + }).toThrow(TypeError); }); }); @@ -317,14 +316,17 @@ describe("ProfileService - findProfile", () => { expect(ProfileValidator.validateProfile).toHaveBeenCalled(); }); - it("should map CELONIS_URL to TEAM_URL when TEAM_URL is not set", async () => { + it("should use CELONIS_URL and CELONIS_API_TOKEN when TEAM_URL and API_TOKEN are not set", async () => { process.env.CELONIS_URL = "https://celonis.celonis.cloud"; process.env.CELONIS_API_TOKEN = "celonis-token"; delete process.env.TEAM_URL; delete process.env.API_TOKEN; + const mapCelonisEnvProfileSpy = jest.spyOn(profileService as any, "mapCelonisEnvProfile"); + const result = await profileService.findProfile(""); + expect(mapCelonisEnvProfileSpy).toHaveBeenCalled(); expect(process.env.TEAM_URL).toBe("https://celonis.celonis.cloud"); expect(process.env.API_TOKEN).toBe("celonis-token"); expect(result.name).toBe("https://celonis.celonis.cloud"); @@ -344,15 +346,56 @@ describe("ProfileService - findProfile", () => { expect(result.team).toBe("https://celonis.celonis.cloud"); }); - it("should delete API_TOKEN when CELONIS_API_TOKEN is not set", async () => { + it("should reject when CELONIS_API_TOKEN is not set but CELONIS_URL is set", async () => { process.env.CELONIS_URL = "https://celonis.celonis.cloud"; process.env.API_TOKEN = "old-token"; delete process.env.CELONIS_API_TOKEN; delete process.env.TEAM_URL; - await profileService.findProfile(""); + const profileName = ""; - expect(process.env.API_TOKEN).toBeUndefined(); + await expect(profileService.findProfile(profileName)).rejects.toBe( + `The profile ${profileName} couldn't be resolved due to missing environment variables.` + ); + }); + + it("should reject when no environment variables are set", async () => { + delete process.env.TEAM_URL; + delete process.env.API_TOKEN; + delete process.env.CELONIS_URL; + delete process.env.CELONIS_API_TOKEN; + + const profileName = ""; + + await expect(profileService.findProfile(profileName)).rejects.toBe( + `The profile ${profileName} couldn't be resolved due to missing environment variables.` + ); + }); + + it("should reject when only CELONIS_URL is set without CELONIS_API_TOKEN", async () => { + process.env.CELONIS_URL = "https://celonis.celonis.cloud"; + delete process.env.CELONIS_API_TOKEN; + delete process.env.TEAM_URL; + delete process.env.API_TOKEN; + + const profileName = ""; + + await expect(profileService.findProfile(profileName)).rejects.toBe( + `The profile ${profileName} couldn't be resolved due to missing environment variables.` + ); + }); + + it("should reject when only CELONIS_API_TOKEN is set without CELONIS_URL", async () => { + process.env.CELONIS_API_TOKEN = "celonis-token"; + delete process.env.CELONIS_URL; + delete process.env.TEAM_URL; + delete process.env.API_TOKEN; + + const profileName = ""; + + await expect(profileService.findProfile(profileName)).rejects.toBe( + `The profile ${profileName} couldn't be resolved due to missing environment variables.` + ); }); });