From 2faad3096a4829a553ffbb1ba8981d8bcfb59200 Mon Sep 17 00:00:00 2001 From: Matthias Osswald Date: Fri, 1 Sep 2023 11:08:21 +0200 Subject: [PATCH] [INTERNAL] Add integration tests --- lib/ui5Framework/AbstractResolver.js | 12 +- .../graph/helpers/ui5Framework.integration.js | 1 + .../Openui5Resolver.integration.js | 201 ++++++++++++++++++ ...Sapui5MavenSnapshotResolver.integration.js | 158 ++++++++++++++ .../Sapui5Resolver.integration.js | 201 ++++++++++++++++++ 5 files changed, 567 insertions(+), 6 deletions(-) create mode 100644 test/lib/ui5framework/Openui5Resolver.integration.js create mode 100644 test/lib/ui5framework/Sapui5MavenSnapshotResolver.integration.js create mode 100644 test/lib/ui5framework/Sapui5Resolver.integration.js diff --git a/lib/ui5Framework/AbstractResolver.js b/lib/ui5Framework/AbstractResolver.js index dfd01de3c..6d9b8166f 100644 --- a/lib/ui5Framework/AbstractResolver.js +++ b/lib/ui5Framework/AbstractResolver.js @@ -269,16 +269,16 @@ class AbstractResolver { const allTags = await this.fetchAllTags({ui5HomeDir, cwd}); - if (!allTags && (version === "latest" || version === "latest-snapshot")) { + if (!allTags) { // Resolver doesn't support tags (e.g. Sapui5MavenSnapshotResolver) // Only latest and latest-snapshot are supported which both resolve // to the latest available version. // See "isSnapshotVersionOrRange" for -snapshot handling - return "*"; - } - - if (!allTags) { - return null; + if ((version === "latest" || version === "latest-snapshot")) { + return "*"; + } else { + return null; + } } if (!allTags[version]) { diff --git a/test/lib/graph/helpers/ui5Framework.integration.js b/test/lib/graph/helpers/ui5Framework.integration.js index 3eec2a802..e92d40823 100644 --- a/test/lib/graph/helpers/ui5Framework.integration.js +++ b/test/lib/graph/helpers/ui5Framework.integration.js @@ -150,6 +150,7 @@ test.afterEach.always((t) => { esmock.purge(t.context.Registry); esmock.purge(t.context.Installer); esmock.purge(t.context.AbstractResolver); + esmock.purge(t.context.Openui5Resolver); esmock.purge(t.context.Sapui5Resolver); esmock.purge(t.context.Application); esmock.purge(t.context.Library); diff --git a/test/lib/ui5framework/Openui5Resolver.integration.js b/test/lib/ui5framework/Openui5Resolver.integration.js new file mode 100644 index 000000000..c62f3537d --- /dev/null +++ b/test/lib/ui5framework/Openui5Resolver.integration.js @@ -0,0 +1,201 @@ +import test from "ava"; +import sinonGlobal from "sinon"; +import esmock from "esmock"; +import path from "node:path"; +import {fileURLToPath} from "node:url"; + +const __dirname = path.dirname(fileURLToPath(import.meta.url)); + +// Use path within project as mocking base directory to reduce chance of side effects +// in case mocks/stubs do not work and real fs is used +const fakeBaseDir = path.join(__dirname, "fake-tmp"); + +test.beforeEach(async (t) => { + const sinon = t.context.sinon = sinonGlobal.createSandbox(); + + t.context.logStub = { + info: sinon.stub(), + verbose: sinon.stub(), + silly: sinon.stub(), + warn: sinon.stub(), + error: sinon.stub(), + isLevelEnabled: sinon.stub().returns(false), + _getLogger: sinon.stub() + }; + const ui5Logger = { + getLogger: sinon.stub().returns(t.context.logStub) + }; + + t.context.pacote = { + packument: sinon.stub().callsFake(async (...args) => { + throw new Error(`pacote.packument stub called with unknown args: ${args}`); + }) + }; + + t.context.NpmcliConfig = sinon.stub().returns({ + load: sinon.stub().resolves(), + flat: { + registry: "https://registry.fake" + } + }); + + t.context.Registry = await esmock.p("../../../lib/ui5Framework/npm/Registry.js", { + "@ui5/logger": ui5Logger, + "pacote": t.context.pacote, + "@npmcli/config": { + "default": t.context.NpmcliConfig + } + }); + + const AbstractInstaller = await esmock.p("../../../lib/ui5Framework/AbstractInstaller.js", { + "@ui5/logger": ui5Logger, + "../../../lib/utils/fs.js": { + mkdirp: sinon.stub().resolves() + }, + "lockfile": { + lock: sinon.stub().yieldsAsync(), + unlock: sinon.stub().yieldsAsync() + } + }); + + t.context.Installer = await esmock.p("../../../lib/ui5Framework/npm/Installer.js", { + "@ui5/logger": ui5Logger, + "graceful-fs": { + rename: sinon.stub().yieldsAsync(), + }, + "../../../lib/utils/fs.js": { + mkdirp: sinon.stub().resolves() + }, + "../../../lib/ui5Framework/npm/Registry.js": t.context.Registry, + "../../../lib/ui5Framework/AbstractInstaller.js": AbstractInstaller + }); + + t.context.AbstractResolver = await esmock.p("../../../lib/ui5Framework/AbstractResolver.js", { + "@ui5/logger": ui5Logger, + "node:os": { + homedir: sinon.stub().returns(path.join(fakeBaseDir, "homedir")) + }, + }); + + t.context.Openui5Resolver = await esmock.p("../../../lib/ui5Framework/Openui5Resolver.js", { + "@ui5/logger": ui5Logger, + "node:os": { + homedir: sinon.stub().returns(path.join(fakeBaseDir, "homedir")) + }, + "../../../lib/ui5Framework/AbstractResolver.js": t.context.AbstractResolver, + "../../../lib/ui5Framework/npm/Installer.js": t.context.Installer + }); +}); + +test.afterEach.always((t) => { + t.context.sinon.restore(); + esmock.purge(t.context.Registry); + esmock.purge(t.context.Installer); + esmock.purge(t.context.AbstractResolver); + esmock.purge(t.context.Openui5Resolver); +}); + +test.serial("resolveVersion", async (t) => { + const {Openui5Resolver, pacote, logStub, NpmcliConfig} = t.context; + + pacote.packument + .withArgs("@openui5/sap.ui.core") + .resolves({ + "versions": { + "1.120.1": "", + "1.120.0": "", + "1.119.0": "", + "1.118.0": "", + "2.0.0-rc.1": "", + "1.123.4-SNAPSHOT": "" + }, + "dist-tags": { + // NOTE: latest does not correspond to highest version in order to verify + // that this tag is used instead of picking the highest version + "latest": "1.120.0", + + "next": "2.0.0-rc.1", + + // NOTE: Tag ends with "-snapshot" in order to verify that the special handling + // of that + "not-a-snapshot": "1.118.0" + } + }); + + const defaultCwd = process.cwd(); + const defaultUi5HomeDir = path.join(fakeBaseDir, "homedir", ".ui5"); + + // Generic testing without and with options argument + const optionsArguments = [ + undefined, + { + cwd: path.join(fakeBaseDir, "custom-cwd"), + ui5HomeDir: path.join(fakeBaseDir, "custom-homedir", ".ui5") + } + ]; + for (const options of optionsArguments) { + // Reset calls to be able to check them per for-loop run + NpmcliConfig.resetHistory(); + pacote.packument.resetHistory(); + + // Ranges + t.is(await Openui5Resolver.resolveVersion("1", options), "1.120.1"); + t.is(await Openui5Resolver.resolveVersion("1.120", options), "1.120.1"); + t.is(await Openui5Resolver.resolveVersion("1.x", options), "1.120.1"); + t.is(await Openui5Resolver.resolveVersion("1.x.x", options), "1.120.1"); + t.is(await Openui5Resolver.resolveVersion("^1", options), "1.120.1"); + t.is(await Openui5Resolver.resolveVersion("*", options), "1.120.1"); + + // Tags + t.is(await Openui5Resolver.resolveVersion("latest", options), "1.120.0"); + t.is(await Openui5Resolver.resolveVersion("next", options), "2.0.0-rc.1"); + t.is(await Openui5Resolver.resolveVersion("not-a-snapshot", options), "1.118.0"); + + // Exact versions + t.is(await Openui5Resolver.resolveVersion("1.118.0", options), "1.118.0"); + t.is(await Openui5Resolver.resolveVersion("2.0.0-rc.1", options), "2.0.0-rc.1"); + t.is(await Openui5Resolver.resolveVersion("1.123.4-SNAPSHOT", options), "1.123.4-SNAPSHOT"); + + // SNAPSHOT ranges + t.is(await Openui5Resolver.resolveVersion("1-SNAPSHOT", options), "1.123.4-SNAPSHOT"); + t.is(await Openui5Resolver.resolveVersion("1.123-SNAPSHOT", options), "1.123.4-SNAPSHOT"); + + // Error cases + await t.throwsAsync(Openui5Resolver.resolveVersion("", options), { + message: `Framework version specifier "" is incorrect or not supported` + }); + await t.throwsAsync(Openui5Resolver.resolveVersion("tag-does-not-exist", options), { + message: `Could not resolve framework version via tag 'tag-does-not-exist'. ` + + `Make sure the tag is available in the configured registry.` + }); + await t.throwsAsync(Openui5Resolver.resolveVersion("invalid-tag-%20", options), { + message: `Framework version specifier "invalid-tag-%20" is incorrect or not supported` + }); + + await t.throwsAsync(Openui5Resolver.resolveVersion("1.999.9", options), { + message: `Could not resolve framework version 1.999.9. ` + + `Make sure the version is valid and available in the configured registry.` + }); + await t.throwsAsync(Openui5Resolver.resolveVersion("1.0.0", options), { + message: `Could not resolve framework version 1.0.0. ` + + `Note that OpenUI5 framework libraries can only be consumed by the UI5 Tooling ` + + `starting with OpenUI5 v1.52.5` + }); + await t.throwsAsync(Openui5Resolver.resolveVersion("^999", options), { + message: `Could not resolve framework version ^999. ` + + `Make sure the version is valid and available in the configured registry.` + }); + + // Check whether options have been passed as expected + t.true(NpmcliConfig.alwaysCalledWithNew()); + t.true(NpmcliConfig.alwaysCalledWithMatch(sinonGlobal.match({ + cwd: options?.cwd ?? defaultCwd + }))); + t.true(pacote.packument.alwaysCalledWithMatch("@openui5/sap.ui.core", { + cache: path.join(options?.ui5HomeDir ?? defaultUi5HomeDir, "framework", "cacache") + })); + } + + t.is(logStub.warn.callCount, 0); + t.is(logStub.error.callCount, 0); +}); diff --git a/test/lib/ui5framework/Sapui5MavenSnapshotResolver.integration.js b/test/lib/ui5framework/Sapui5MavenSnapshotResolver.integration.js new file mode 100644 index 000000000..61317ed0c --- /dev/null +++ b/test/lib/ui5framework/Sapui5MavenSnapshotResolver.integration.js @@ -0,0 +1,158 @@ +import test from "ava"; +import sinonGlobal from "sinon"; +import esmock from "esmock"; +import path from "node:path"; +import {fileURLToPath} from "node:url"; + +const __dirname = path.dirname(fileURLToPath(import.meta.url)); + +// Use path within project as mocking base directory to reduce chance of side effects +// in case mocks/stubs do not work and real fs is used +const fakeBaseDir = path.join(__dirname, "fake-tmp"); + +test.beforeEach(async (t) => { + const sinon = t.context.sinon = sinonGlobal.createSandbox(); + + process.env.UI5_MAVEN_SNAPSHOT_ENDPOINT_URL = "_SNAPSHOT_URL_"; + + t.context.logStub = { + info: sinon.stub(), + verbose: sinon.stub(), + silly: sinon.stub(), + warn: sinon.stub(), + error: sinon.stub(), + isLevelEnabled: sinon.stub().returns(false), + _getLogger: sinon.stub() + }; + const ui5Logger = { + getLogger: sinon.stub().returns(t.context.logStub) + }; + + t.context.makeFetchHappen = sinon.stub(); + + t.context.gracefulFs = { + stat: sinon.stub().yieldsAsync(), + readFile: sinon.stub().yieldsAsync(), + writeFile: sinon.stub().yieldsAsync(), + rename: sinon.stub().yieldsAsync(), + rm: sinon.stub().yieldsAsync(), + createWriteStream: sinon.stub() + }; + + t.context.Registry = await esmock.p("../../../lib/ui5Framework/maven/Registry.js", { + "@ui5/logger": ui5Logger, + "graceful-fs": t.context.gracefulFs, + "make-fetch-happen": t.context.makeFetchHappen, + }); + + const AbstractInstaller = await esmock.p("../../../lib/ui5Framework/AbstractInstaller.js", { + "@ui5/logger": ui5Logger, + "../../../lib/utils/fs.js": { + mkdirp: sinon.stub().resolves() + }, + "lockfile": { + lock: sinon.stub().yieldsAsync(), + unlock: sinon.stub().yieldsAsync() + } + }); + + t.context.Installer = await esmock.p("../../../lib/ui5Framework/maven/Installer.js", { + "@ui5/logger": ui5Logger, + "graceful-fs": t.context.gracefulFs, + "../../../lib/utils/fs.js": { + mkdirp: sinon.stub().resolves() + }, + "../../../lib/ui5Framework/maven/Registry.js": t.context.Registry, + "../../../lib/ui5Framework/AbstractInstaller.js": AbstractInstaller + }); + + t.context.AbstractResolver = await esmock.p("../../../lib/ui5Framework/AbstractResolver.js", { + "@ui5/logger": ui5Logger, + "node:os": { + homedir: sinon.stub().returns(path.join(fakeBaseDir, "homedir")) + }, + }); + + t.context.Sapui5MavenSnapshotResolver = await esmock.p("../../../lib/ui5Framework/Sapui5MavenSnapshotResolver.js", { + "@ui5/logger": ui5Logger, + "node:os": { + homedir: sinon.stub().returns(path.join(fakeBaseDir, "homedir")) + }, + "../../../lib/ui5Framework/AbstractResolver.js": t.context.AbstractResolver, + "../../../lib/ui5Framework/maven/Installer.js": t.context.Installer + }); +}); + +test.afterEach.always((t) => { + t.context.sinon.restore(); + esmock.purge(t.context.Registry); + esmock.purge(t.context.Installer); + esmock.purge(t.context.AbstractResolver); + esmock.purge(t.context.Sapui5MavenSnapshotResolver); + delete process.env.UI5_MAVEN_SNAPSHOT_ENDPOINT_URL; +}); + +test.serial("resolveVersion", async (t) => { + const {Sapui5MavenSnapshotResolver, makeFetchHappen, logStub, sinon} = t.context; + + makeFetchHappen.withArgs("_SNAPSHOT_URL_/com/sap/ui5/dist/sapui5-sdk-dist/maven-metadata.xml") + .resolves({ + ok: true, + buffer: sinon.stub().resolves(` + + + + + 1.120.1 + 2.0.0-rc.1 + 1.120.1-SNAPSHOT + 1.123.4-SNAPSHOT + 2.0.0-SNAPSHOT + 2.0.1-SNAPSHOT + 2.1.2-SNAPSHOT + + + + `) + }); + + + // Exact SNAPSHOT versions + t.is(await Sapui5MavenSnapshotResolver.resolveVersion("1.123.4-SNAPSHOT"), "1.123.4-SNAPSHOT"); + t.is(await Sapui5MavenSnapshotResolver.resolveVersion("2.0.1-SNAPSHOT"), "2.0.1-SNAPSHOT"); + + // latest-snapshot + t.is(await Sapui5MavenSnapshotResolver.resolveVersion("latest-snapshot"), "2.1.2-SNAPSHOT"); + + // SNAPSHOT ranges + t.is(await Sapui5MavenSnapshotResolver.resolveVersion("1-SNAPSHOT"), "1.123.4-SNAPSHOT"); + t.is(await Sapui5MavenSnapshotResolver.resolveVersion("2-SNAPSHOT"), "2.1.2-SNAPSHOT"); + t.is(await Sapui5MavenSnapshotResolver.resolveVersion("1.123-SNAPSHOT"), "1.123.4-SNAPSHOT"); + + // Error cases + await t.throwsAsync(Sapui5MavenSnapshotResolver.resolveVersion(""), { + message: `Framework version specifier "" is incorrect or not supported` + }); + await t.throwsAsync(Sapui5MavenSnapshotResolver.resolveVersion("tag-does-not-exist"), { + message: `Framework version specifier "tag-does-not-exist" is incorrect or not supported` + }); + await t.throwsAsync(Sapui5MavenSnapshotResolver.resolveVersion("invalid-tag-%20"), { + message: `Framework version specifier "invalid-tag-%20" is incorrect or not supported` + }); + + await t.throwsAsync(Sapui5MavenSnapshotResolver.resolveVersion("1.999.9"), { + message: `Could not resolve framework version 1.999.9. ` + + `Make sure the version is valid and available in the configured registry.` + }); + await t.throwsAsync(Sapui5MavenSnapshotResolver.resolveVersion("1.0.0-SNAPSHOT"), { + message: `Could not resolve framework version 1.0.0-SNAPSHOT. ` + + `Make sure the version is valid and available in the configured registry.` + }); + await t.throwsAsync(Sapui5MavenSnapshotResolver.resolveVersion("3-SNAPSHOT"), { + message: `Could not resolve framework version 3-SNAPSHOT. ` + + `Make sure the version is valid and available in the configured registry.` + }); + + t.is(logStub.warn.callCount, 0); + t.is(logStub.error.callCount, 0); +}); diff --git a/test/lib/ui5framework/Sapui5Resolver.integration.js b/test/lib/ui5framework/Sapui5Resolver.integration.js new file mode 100644 index 000000000..511496775 --- /dev/null +++ b/test/lib/ui5framework/Sapui5Resolver.integration.js @@ -0,0 +1,201 @@ +import test from "ava"; +import sinonGlobal from "sinon"; +import esmock from "esmock"; +import path from "node:path"; +import {fileURLToPath} from "node:url"; + +const __dirname = path.dirname(fileURLToPath(import.meta.url)); + +// Use path within project as mocking base directory to reduce chance of side effects +// in case mocks/stubs do not work and real fs is used +const fakeBaseDir = path.join(__dirname, "fake-tmp"); + +test.beforeEach(async (t) => { + const sinon = t.context.sinon = sinonGlobal.createSandbox(); + + t.context.logStub = { + info: sinon.stub(), + verbose: sinon.stub(), + silly: sinon.stub(), + warn: sinon.stub(), + error: sinon.stub(), + isLevelEnabled: sinon.stub().returns(false), + _getLogger: sinon.stub() + }; + const ui5Logger = { + getLogger: sinon.stub().returns(t.context.logStub) + }; + + t.context.pacote = { + packument: sinon.stub().callsFake(async (...args) => { + throw new Error(`pacote.packument stub called with unknown args: ${args}`); + }) + }; + + t.context.NpmcliConfig = sinon.stub().returns({ + load: sinon.stub().resolves(), + flat: { + registry: "https://registry.fake" + } + }); + + t.context.Registry = await esmock.p("../../../lib/ui5Framework/npm/Registry.js", { + "@ui5/logger": ui5Logger, + "pacote": t.context.pacote, + "@npmcli/config": { + "default": t.context.NpmcliConfig + } + }); + + const AbstractInstaller = await esmock.p("../../../lib/ui5Framework/AbstractInstaller.js", { + "@ui5/logger": ui5Logger, + "../../../lib/utils/fs.js": { + mkdirp: sinon.stub().resolves() + }, + "lockfile": { + lock: sinon.stub().yieldsAsync(), + unlock: sinon.stub().yieldsAsync() + } + }); + + t.context.Installer = await esmock.p("../../../lib/ui5Framework/npm/Installer.js", { + "@ui5/logger": ui5Logger, + "graceful-fs": { + rename: sinon.stub().yieldsAsync(), + }, + "../../../lib/utils/fs.js": { + mkdirp: sinon.stub().resolves() + }, + "../../../lib/ui5Framework/npm/Registry.js": t.context.Registry, + "../../../lib/ui5Framework/AbstractInstaller.js": AbstractInstaller + }); + + t.context.AbstractResolver = await esmock.p("../../../lib/ui5Framework/AbstractResolver.js", { + "@ui5/logger": ui5Logger, + "node:os": { + homedir: sinon.stub().returns(path.join(fakeBaseDir, "homedir")) + }, + }); + + t.context.Sapui5Resolver = await esmock.p("../../../lib/ui5Framework/Sapui5Resolver.js", { + "@ui5/logger": ui5Logger, + "node:os": { + homedir: sinon.stub().returns(path.join(fakeBaseDir, "homedir")) + }, + "../../../lib/ui5Framework/AbstractResolver.js": t.context.AbstractResolver, + "../../../lib/ui5Framework/npm/Installer.js": t.context.Installer + }); +}); + +test.afterEach.always((t) => { + t.context.sinon.restore(); + esmock.purge(t.context.Registry); + esmock.purge(t.context.Installer); + esmock.purge(t.context.AbstractResolver); + esmock.purge(t.context.Sapui5Resolver); +}); + +test.serial("resolveVersion", async (t) => { + const {Sapui5Resolver, pacote, logStub, NpmcliConfig} = t.context; + + pacote.packument + .withArgs("@sapui5/distribution-metadata") + .resolves({ + "versions": { + "1.120.1": "", + "1.120.0": "", + "1.119.0": "", + "1.118.0": "", + "2.0.0-rc.1": "", + "1.123.4-SNAPSHOT": "" + }, + "dist-tags": { + // NOTE: latest does not correspond to highest version in order to verify + // that this tag is used instead of picking the highest version + "latest": "1.120.0", + + "next": "2.0.0-rc.1", + + // NOTE: Tag ends with "-snapshot" in order to verify that the special handling + // of that + "not-a-snapshot": "1.118.0" + } + }); + + const defaultCwd = process.cwd(); + const defaultUi5HomeDir = path.join(fakeBaseDir, "homedir", ".ui5"); + + // Generic testing without and with options argument + const optionsArguments = [ + undefined, + { + cwd: path.join(fakeBaseDir, "custom-cwd"), + ui5HomeDir: path.join(fakeBaseDir, "custom-homedir", ".ui5") + } + ]; + for (const options of optionsArguments) { + // Reset calls to be able to check them per for-loop run + NpmcliConfig.resetHistory(); + pacote.packument.resetHistory(); + + // Ranges + t.is(await Sapui5Resolver.resolveVersion("1", options), "1.120.1"); + t.is(await Sapui5Resolver.resolveVersion("1.120", options), "1.120.1"); + t.is(await Sapui5Resolver.resolveVersion("1.x", options), "1.120.1"); + t.is(await Sapui5Resolver.resolveVersion("1.x.x", options), "1.120.1"); + t.is(await Sapui5Resolver.resolveVersion("^1", options), "1.120.1"); + t.is(await Sapui5Resolver.resolveVersion("*", options), "1.120.1"); + + // Tags + t.is(await Sapui5Resolver.resolveVersion("latest", options), "1.120.0"); + t.is(await Sapui5Resolver.resolveVersion("next", options), "2.0.0-rc.1"); + t.is(await Sapui5Resolver.resolveVersion("not-a-snapshot", options), "1.118.0"); + + // Exact versions + t.is(await Sapui5Resolver.resolveVersion("1.118.0", options), "1.118.0"); + t.is(await Sapui5Resolver.resolveVersion("2.0.0-rc.1", options), "2.0.0-rc.1"); + t.is(await Sapui5Resolver.resolveVersion("1.123.4-SNAPSHOT", options), "1.123.4-SNAPSHOT"); + + // SNAPSHOT ranges + t.is(await Sapui5Resolver.resolveVersion("1-SNAPSHOT", options), "1.123.4-SNAPSHOT"); + t.is(await Sapui5Resolver.resolveVersion("1.123-SNAPSHOT", options), "1.123.4-SNAPSHOT"); + + // Error cases + await t.throwsAsync(Sapui5Resolver.resolveVersion("", options), { + message: `Framework version specifier "" is incorrect or not supported` + }); + await t.throwsAsync(Sapui5Resolver.resolveVersion("tag-does-not-exist", options), { + message: `Could not resolve framework version via tag 'tag-does-not-exist'. ` + + `Make sure the tag is available in the configured registry.` + }); + await t.throwsAsync(Sapui5Resolver.resolveVersion("invalid-tag-%20", options), { + message: `Framework version specifier "invalid-tag-%20" is incorrect or not supported` + }); + + await t.throwsAsync(Sapui5Resolver.resolveVersion("1.999.9", options), { + message: `Could not resolve framework version 1.999.9. ` + + `Make sure the version is valid and available in the configured registry.` + }); + await t.throwsAsync(Sapui5Resolver.resolveVersion("1.0.0", options), { + message: `Could not resolve framework version 1.0.0. ` + + `Note that SAPUI5 framework libraries can only be consumed by the UI5 Tooling ` + + `starting with SAPUI5 v1.76.0` + }); + await t.throwsAsync(Sapui5Resolver.resolveVersion("^999", options), { + message: `Could not resolve framework version ^999. ` + + `Make sure the version is valid and available in the configured registry.` + }); + + // Check whether options have been passed as expected + t.true(NpmcliConfig.alwaysCalledWithNew()); + t.true(NpmcliConfig.alwaysCalledWithMatch(sinonGlobal.match({ + cwd: options?.cwd ?? defaultCwd + }))); + t.true(pacote.packument.alwaysCalledWithMatch("@openui5/sap.ui.core", { + cache: path.join(options?.ui5HomeDir ?? defaultUi5HomeDir, "framework", "cacache") + })); + } + + t.is(logStub.warn.callCount, 0); + t.is(logStub.error.callCount, 0); +});