@red-hat-developer-hub/e2e-test-utils 1.1.24 → 1.1.26
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/deployment/orchestrator/install-orchestrator.sh +10 -6
- package/dist/playwright/helpers/ui-helper.d.ts +7 -0
- package/dist/playwright/helpers/ui-helper.d.ts.map +1 -1
- package/dist/playwright/helpers/ui-helper.js +15 -0
- package/dist/utils/tests/helpers.d.ts +26 -0
- package/dist/utils/tests/helpers.d.ts.map +1 -0
- package/dist/utils/tests/helpers.js +84 -0
- package/dist/utils/tests/plugin-metadata.fixtures.test.d.ts +2 -0
- package/dist/utils/tests/plugin-metadata.fixtures.test.d.ts.map +1 -0
- package/dist/utils/tests/plugin-metadata.fixtures.test.js +563 -0
- package/dist/utils/tests/plugin-metadata.nightly.test.d.ts +2 -0
- package/dist/utils/tests/plugin-metadata.nightly.test.d.ts.map +1 -0
- package/dist/utils/tests/plugin-metadata.nightly.test.js +178 -0
- package/dist/utils/tests/plugin-metadata.pr.test.d.ts +2 -0
- package/dist/utils/tests/plugin-metadata.pr.test.d.ts.map +1 -0
- package/dist/utils/tests/plugin-metadata.pr.test.js +296 -0
- package/dist/utils/tests/plugin-metadata.test.d.ts.map +1 -0
- package/dist/utils/tests/plugin-metadata.test.js +156 -0
- package/package.json +1 -1
- package/dist/utils/plugin-metadata.test.d.ts.map +0 -1
- package/dist/utils/plugin-metadata.test.js +0 -39
- /package/dist/utils/{plugin-metadata.test.d.ts → tests/plugin-metadata.test.d.ts} +0 -0
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Nightly mode tests — isNightlyJob detection and nightly plugin resolution.
|
|
3
|
+
*/
|
|
4
|
+
import { describe, it, beforeEach, afterEach } from "node:test";
|
|
5
|
+
import assert from "node:assert";
|
|
6
|
+
import fs from "fs-extra";
|
|
7
|
+
import { isNightlyJob, processPluginsForDeployment, } from "../plugin-metadata.js";
|
|
8
|
+
import { withCleanEnv, createMetadataFixture } from "./helpers.js";
|
|
9
|
+
// ── isNightlyJob ─────────────────────────────────────────────────────────────
|
|
10
|
+
describe("isNightlyJob", () => {
|
|
11
|
+
const env = withCleanEnv();
|
|
12
|
+
beforeEach(() => env.save());
|
|
13
|
+
afterEach(() => env.restore());
|
|
14
|
+
it("returns false with no env vars set", () => {
|
|
15
|
+
delete process.env.E2E_NIGHTLY_MODE;
|
|
16
|
+
delete process.env.JOB_NAME;
|
|
17
|
+
delete process.env.GIT_PR_NUMBER;
|
|
18
|
+
assert.strictEqual(isNightlyJob(), false);
|
|
19
|
+
});
|
|
20
|
+
it("returns true when E2E_NIGHTLY_MODE is 'true'", () => {
|
|
21
|
+
delete process.env.GIT_PR_NUMBER;
|
|
22
|
+
process.env.E2E_NIGHTLY_MODE = "true";
|
|
23
|
+
assert.strictEqual(isNightlyJob(), true);
|
|
24
|
+
});
|
|
25
|
+
it("returns true when E2E_NIGHTLY_MODE is '1'", () => {
|
|
26
|
+
delete process.env.GIT_PR_NUMBER;
|
|
27
|
+
process.env.E2E_NIGHTLY_MODE = "1";
|
|
28
|
+
assert.strictEqual(isNightlyJob(), true);
|
|
29
|
+
});
|
|
30
|
+
it("returns false when E2E_NIGHTLY_MODE is 'false' (strict check)", () => {
|
|
31
|
+
delete process.env.GIT_PR_NUMBER;
|
|
32
|
+
process.env.E2E_NIGHTLY_MODE = "false";
|
|
33
|
+
assert.strictEqual(isNightlyJob(), false, "'false' string must not trigger nightly mode");
|
|
34
|
+
});
|
|
35
|
+
it("returns false when E2E_NIGHTLY_MODE is empty string", () => {
|
|
36
|
+
delete process.env.GIT_PR_NUMBER;
|
|
37
|
+
process.env.E2E_NIGHTLY_MODE = "";
|
|
38
|
+
assert.strictEqual(isNightlyJob(), false, "empty string must not trigger nightly mode");
|
|
39
|
+
});
|
|
40
|
+
it("returns true when JOB_NAME contains 'periodic-'", () => {
|
|
41
|
+
delete process.env.GIT_PR_NUMBER;
|
|
42
|
+
delete process.env.E2E_NIGHTLY_MODE;
|
|
43
|
+
process.env.JOB_NAME = "periodic-ci-overlay-e2e-nightly";
|
|
44
|
+
assert.strictEqual(isNightlyJob(), true);
|
|
45
|
+
});
|
|
46
|
+
it("returns false when JOB_NAME contains 'periodic' without trailing dash", () => {
|
|
47
|
+
delete process.env.GIT_PR_NUMBER;
|
|
48
|
+
delete process.env.E2E_NIGHTLY_MODE;
|
|
49
|
+
process.env.JOB_NAME = "run-periodically";
|
|
50
|
+
assert.strictEqual(isNightlyJob(), false, "'periodic' without dash must not trigger nightly mode");
|
|
51
|
+
});
|
|
52
|
+
it("returns false when GIT_PR_NUMBER is set (PR takes precedence)", () => {
|
|
53
|
+
process.env.GIT_PR_NUMBER = "42";
|
|
54
|
+
process.env.E2E_NIGHTLY_MODE = "true";
|
|
55
|
+
assert.strictEqual(isNightlyJob(), false, "GIT_PR_NUMBER must take precedence over nightly mode");
|
|
56
|
+
});
|
|
57
|
+
it("returns false when GIT_PR_NUMBER is set even with periodic JOB_NAME", () => {
|
|
58
|
+
process.env.GIT_PR_NUMBER = "42";
|
|
59
|
+
process.env.JOB_NAME = "periodic-ci-overlay-e2e-nightly";
|
|
60
|
+
assert.strictEqual(isNightlyJob(), false, "GIT_PR_NUMBER must take precedence over periodic job detection");
|
|
61
|
+
});
|
|
62
|
+
});
|
|
63
|
+
// ── Nightly resolution scenarios ─────────────────────────────────────────────
|
|
64
|
+
describe("processPluginsForDeployment — nightly mode", () => {
|
|
65
|
+
const env = withCleanEnv();
|
|
66
|
+
beforeEach(() => {
|
|
67
|
+
env.save();
|
|
68
|
+
delete process.env.GIT_PR_NUMBER;
|
|
69
|
+
process.env.E2E_NIGHTLY_MODE = "true";
|
|
70
|
+
});
|
|
71
|
+
afterEach(() => env.restore());
|
|
72
|
+
it("skips metadata injection in nightly mode", async () => {
|
|
73
|
+
const metadataDir = await createMetadataFixture([
|
|
74
|
+
{
|
|
75
|
+
name: "backstage-community-plugin-tech-radar",
|
|
76
|
+
packageName: "@backstage-community/plugin-tech-radar",
|
|
77
|
+
dynamicArtifact: "./dynamic-plugins/dist/backstage-community-plugin-tech-radar",
|
|
78
|
+
appConfigExamples: {
|
|
79
|
+
techRadar: { url: "http://default.example.com" },
|
|
80
|
+
},
|
|
81
|
+
},
|
|
82
|
+
]);
|
|
83
|
+
try {
|
|
84
|
+
const config = {
|
|
85
|
+
plugins: [
|
|
86
|
+
{
|
|
87
|
+
package: "./dynamic-plugins/dist/backstage-community-plugin-tech-radar",
|
|
88
|
+
disabled: false,
|
|
89
|
+
},
|
|
90
|
+
],
|
|
91
|
+
};
|
|
92
|
+
const result = await processPluginsForDeployment(config, metadataDir);
|
|
93
|
+
assert.strictEqual(result.plugins[0].pluginConfig, undefined, "nightly mode must NOT inject metadata pluginConfig");
|
|
94
|
+
}
|
|
95
|
+
finally {
|
|
96
|
+
await fs.remove(metadataDir);
|
|
97
|
+
}
|
|
98
|
+
});
|
|
99
|
+
it("preserves user-provided pluginConfig in nightly mode", async () => {
|
|
100
|
+
const metadataDir = await createMetadataFixture([
|
|
101
|
+
{
|
|
102
|
+
name: "backstage-community-plugin-tech-radar",
|
|
103
|
+
packageName: "@backstage-community/plugin-tech-radar",
|
|
104
|
+
dynamicArtifact: "./dynamic-plugins/dist/backstage-community-plugin-tech-radar",
|
|
105
|
+
appConfigExamples: {
|
|
106
|
+
techRadar: { url: "http://metadata.example.com" },
|
|
107
|
+
},
|
|
108
|
+
},
|
|
109
|
+
]);
|
|
110
|
+
try {
|
|
111
|
+
const userPluginConfig = {
|
|
112
|
+
techRadar: { url: "http://user.example.com" },
|
|
113
|
+
};
|
|
114
|
+
const config = {
|
|
115
|
+
plugins: [
|
|
116
|
+
{
|
|
117
|
+
package: "./dynamic-plugins/dist/backstage-community-plugin-tech-radar",
|
|
118
|
+
disabled: false,
|
|
119
|
+
pluginConfig: userPluginConfig,
|
|
120
|
+
},
|
|
121
|
+
],
|
|
122
|
+
};
|
|
123
|
+
const result = await processPluginsForDeployment(config, metadataDir);
|
|
124
|
+
assert.deepStrictEqual(result.plugins[0].pluginConfig, userPluginConfig, "nightly mode must preserve user pluginConfig exactly as-is");
|
|
125
|
+
}
|
|
126
|
+
finally {
|
|
127
|
+
await fs.remove(metadataDir);
|
|
128
|
+
}
|
|
129
|
+
});
|
|
130
|
+
it("resolves OCI plugin to metadata dynamicArtifact in nightly", async () => {
|
|
131
|
+
const metadataDir = await createMetadataFixture([
|
|
132
|
+
{
|
|
133
|
+
name: "backstage-community-plugin-tekton",
|
|
134
|
+
packageName: "@backstage-community/plugin-tekton",
|
|
135
|
+
dynamicArtifact: "oci://ghcr.io/redhat-developer/rhdh-plugin-export-overlays/backstage-community-plugin-tekton:bs_1.45.3__3.33.3!backstage-community-plugin-tekton",
|
|
136
|
+
},
|
|
137
|
+
]);
|
|
138
|
+
try {
|
|
139
|
+
const config = {
|
|
140
|
+
plugins: [
|
|
141
|
+
{
|
|
142
|
+
package: "oci://ghcr.io/redhat-developer/rhdh-plugin-export-overlays/backstage-community-plugin-tekton:old_stale_tag!backstage-community-plugin-tekton",
|
|
143
|
+
disabled: false,
|
|
144
|
+
},
|
|
145
|
+
],
|
|
146
|
+
};
|
|
147
|
+
const result = await processPluginsForDeployment(config, metadataDir);
|
|
148
|
+
assert.ok(result.plugins[0].package.includes("bs_1.45.3__3.33.3"), "nightly must resolve to metadata dynamicArtifact (latest published version)");
|
|
149
|
+
}
|
|
150
|
+
finally {
|
|
151
|
+
await fs.remove(metadataDir);
|
|
152
|
+
}
|
|
153
|
+
});
|
|
154
|
+
it("keeps local path plugins unchanged in nightly", async () => {
|
|
155
|
+
const metadataDir = await createMetadataFixture([
|
|
156
|
+
{
|
|
157
|
+
name: "red-hat-developer-hub-backstage-plugin-quickstart",
|
|
158
|
+
packageName: "@red-hat-developer-hub/backstage-plugin-quickstart",
|
|
159
|
+
dynamicArtifact: "./dynamic-plugins/dist/red-hat-developer-hub-backstage-plugin-quickstart",
|
|
160
|
+
},
|
|
161
|
+
]);
|
|
162
|
+
try {
|
|
163
|
+
const config = {
|
|
164
|
+
plugins: [
|
|
165
|
+
{
|
|
166
|
+
package: "./dynamic-plugins/dist/red-hat-developer-hub-backstage-plugin-quickstart",
|
|
167
|
+
disabled: false,
|
|
168
|
+
},
|
|
169
|
+
],
|
|
170
|
+
};
|
|
171
|
+
const result = await processPluginsForDeployment(config, metadataDir);
|
|
172
|
+
assert.strictEqual(result.plugins[0].package, "./dynamic-plugins/dist/red-hat-developer-hub-backstage-plugin-quickstart", "local path plugins must not be converted to OCI in nightly");
|
|
173
|
+
}
|
|
174
|
+
finally {
|
|
175
|
+
await fs.remove(metadataDir);
|
|
176
|
+
}
|
|
177
|
+
});
|
|
178
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"plugin-metadata.pr.test.d.ts","sourceRoot":"","sources":["../../../src/utils/tests/plugin-metadata.pr.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,296 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* PR mode tests — metadata injection, OCI resolution, skip injection, precedence.
|
|
3
|
+
*/
|
|
4
|
+
import { describe, it, beforeEach, afterEach } from "node:test";
|
|
5
|
+
import assert from "node:assert";
|
|
6
|
+
import fs from "fs-extra";
|
|
7
|
+
import { isNightlyJob, processPluginsForDeployment, } from "../plugin-metadata.js";
|
|
8
|
+
import { withCleanEnv, createMetadataFixture, createWorkspaceFixture, } from "./helpers.js";
|
|
9
|
+
describe("processPluginsForDeployment — PR mode", () => {
|
|
10
|
+
const env = withCleanEnv();
|
|
11
|
+
beforeEach(() => {
|
|
12
|
+
env.save();
|
|
13
|
+
delete process.env.E2E_NIGHTLY_MODE;
|
|
14
|
+
delete process.env.JOB_NAME;
|
|
15
|
+
delete process.env.GIT_PR_NUMBER;
|
|
16
|
+
});
|
|
17
|
+
afterEach(() => env.restore());
|
|
18
|
+
it("injects appConfigExamples from metadata as base pluginConfig", async () => {
|
|
19
|
+
const metadataDir = await createMetadataFixture([
|
|
20
|
+
{
|
|
21
|
+
name: "backstage-community-plugin-tech-radar",
|
|
22
|
+
packageName: "@backstage-community/plugin-tech-radar",
|
|
23
|
+
dynamicArtifact: "./dynamic-plugins/dist/backstage-community-plugin-tech-radar",
|
|
24
|
+
appConfigExamples: {
|
|
25
|
+
techRadar: { url: "http://default.example.com" },
|
|
26
|
+
},
|
|
27
|
+
},
|
|
28
|
+
]);
|
|
29
|
+
try {
|
|
30
|
+
const config = {
|
|
31
|
+
plugins: [
|
|
32
|
+
{
|
|
33
|
+
package: "./dynamic-plugins/dist/backstage-community-plugin-tech-radar",
|
|
34
|
+
disabled: false,
|
|
35
|
+
},
|
|
36
|
+
],
|
|
37
|
+
};
|
|
38
|
+
const result = await processPluginsForDeployment(config, metadataDir);
|
|
39
|
+
assert.deepStrictEqual(result.plugins[0].pluginConfig, { techRadar: { url: "http://default.example.com" } }, "metadata appConfigExamples must be injected as pluginConfig in PR mode");
|
|
40
|
+
}
|
|
41
|
+
finally {
|
|
42
|
+
await fs.remove(metadataDir);
|
|
43
|
+
}
|
|
44
|
+
});
|
|
45
|
+
it("user pluginConfig overrides metadata appConfigExamples", async () => {
|
|
46
|
+
const metadataDir = await createMetadataFixture([
|
|
47
|
+
{
|
|
48
|
+
name: "backstage-community-plugin-tech-radar",
|
|
49
|
+
packageName: "@backstage-community/plugin-tech-radar",
|
|
50
|
+
dynamicArtifact: "./dynamic-plugins/dist/backstage-community-plugin-tech-radar",
|
|
51
|
+
appConfigExamples: {
|
|
52
|
+
techRadar: { url: "http://default.example.com" },
|
|
53
|
+
},
|
|
54
|
+
},
|
|
55
|
+
]);
|
|
56
|
+
try {
|
|
57
|
+
const config = {
|
|
58
|
+
plugins: [
|
|
59
|
+
{
|
|
60
|
+
package: "./dynamic-plugins/dist/backstage-community-plugin-tech-radar",
|
|
61
|
+
disabled: false,
|
|
62
|
+
pluginConfig: {
|
|
63
|
+
techRadar: { url: "http://custom.example.com" },
|
|
64
|
+
},
|
|
65
|
+
},
|
|
66
|
+
],
|
|
67
|
+
};
|
|
68
|
+
const result = await processPluginsForDeployment(config, metadataDir);
|
|
69
|
+
assert.strictEqual(result.plugins[0].pluginConfig
|
|
70
|
+
.techRadar &&
|
|
71
|
+
result.plugins[0].pluginConfig
|
|
72
|
+
.techRadar.url, "http://custom.example.com", "user pluginConfig must override metadata defaults");
|
|
73
|
+
}
|
|
74
|
+
finally {
|
|
75
|
+
await fs.remove(metadataDir);
|
|
76
|
+
}
|
|
77
|
+
});
|
|
78
|
+
it("resolves OCI plugin to metadata dynamicArtifact when no PR number", async () => {
|
|
79
|
+
const metadataDir = await createMetadataFixture([
|
|
80
|
+
{
|
|
81
|
+
name: "backstage-community-plugin-tekton",
|
|
82
|
+
packageName: "@backstage-community/plugin-tekton",
|
|
83
|
+
dynamicArtifact: "oci://ghcr.io/redhat-developer/rhdh-plugin-export-overlays/backstage-community-plugin-tekton:bs_1.45.3__3.33.3!backstage-community-plugin-tekton",
|
|
84
|
+
},
|
|
85
|
+
]);
|
|
86
|
+
try {
|
|
87
|
+
const config = {
|
|
88
|
+
plugins: [
|
|
89
|
+
{
|
|
90
|
+
package: "oci://ghcr.io/redhat-developer/rhdh-plugin-export-overlays/backstage-community-plugin-tekton:old_tag!backstage-community-plugin-tekton",
|
|
91
|
+
disabled: false,
|
|
92
|
+
},
|
|
93
|
+
],
|
|
94
|
+
};
|
|
95
|
+
const result = await processPluginsForDeployment(config, metadataDir);
|
|
96
|
+
assert.strictEqual(result.plugins[0].package, "oci://ghcr.io/redhat-developer/rhdh-plugin-export-overlays/backstage-community-plugin-tekton:bs_1.45.3__3.33.3!backstage-community-plugin-tekton", "OCI plugin must be resolved to metadata dynamicArtifact");
|
|
97
|
+
}
|
|
98
|
+
finally {
|
|
99
|
+
await fs.remove(metadataDir);
|
|
100
|
+
}
|
|
101
|
+
});
|
|
102
|
+
it("keeps local path plugins unchanged", async () => {
|
|
103
|
+
const metadataDir = await createMetadataFixture([
|
|
104
|
+
{
|
|
105
|
+
name: "backstage-community-plugin-tech-radar",
|
|
106
|
+
packageName: "@backstage-community/plugin-tech-radar",
|
|
107
|
+
dynamicArtifact: "./dynamic-plugins/dist/backstage-community-plugin-tech-radar",
|
|
108
|
+
},
|
|
109
|
+
]);
|
|
110
|
+
try {
|
|
111
|
+
const config = {
|
|
112
|
+
plugins: [
|
|
113
|
+
{
|
|
114
|
+
package: "./dynamic-plugins/dist/backstage-community-plugin-tech-radar",
|
|
115
|
+
disabled: false,
|
|
116
|
+
},
|
|
117
|
+
],
|
|
118
|
+
};
|
|
119
|
+
const result = await processPluginsForDeployment(config, metadataDir);
|
|
120
|
+
assert.strictEqual(result.plugins[0].package, "./dynamic-plugins/dist/backstage-community-plugin-tech-radar", "local path plugins must stay unchanged");
|
|
121
|
+
}
|
|
122
|
+
finally {
|
|
123
|
+
await fs.remove(metadataDir);
|
|
124
|
+
}
|
|
125
|
+
});
|
|
126
|
+
it("keeps plugins without metadata unchanged", async () => {
|
|
127
|
+
const metadataDir = await createMetadataFixture([]);
|
|
128
|
+
try {
|
|
129
|
+
const keycloakPackage = "./dynamic-plugins/dist/backstage-community-plugin-catalog-backend-module-keycloak-dynamic";
|
|
130
|
+
const config = {
|
|
131
|
+
plugins: [{ package: keycloakPackage, disabled: false }],
|
|
132
|
+
};
|
|
133
|
+
const result = await processPluginsForDeployment(config, metadataDir);
|
|
134
|
+
assert.strictEqual(result.plugins[0].package, keycloakPackage, "plugins not in workspace metadata must stay unchanged");
|
|
135
|
+
}
|
|
136
|
+
finally {
|
|
137
|
+
await fs.remove(metadataDir);
|
|
138
|
+
}
|
|
139
|
+
});
|
|
140
|
+
it("resolves OCI plugin from different registry using metadata ref", async () => {
|
|
141
|
+
const metadataDir = await createMetadataFixture([
|
|
142
|
+
{
|
|
143
|
+
name: "backstage-plugin-events-backend-module-github",
|
|
144
|
+
packageName: "@backstage/plugin-events-backend-module-github",
|
|
145
|
+
dynamicArtifact: "oci://quay.io/rhdh/backstage-plugin-events-backend-module-github@sha256:abc123",
|
|
146
|
+
},
|
|
147
|
+
]);
|
|
148
|
+
try {
|
|
149
|
+
const config = {
|
|
150
|
+
plugins: [
|
|
151
|
+
{
|
|
152
|
+
package: "oci://ghcr.io/redhat-developer/rhdh-plugin-export-overlays/backstage-plugin-events-backend-module-github:bs_1.45.3__0.4.6",
|
|
153
|
+
disabled: false,
|
|
154
|
+
},
|
|
155
|
+
],
|
|
156
|
+
};
|
|
157
|
+
const result = await processPluginsForDeployment(config, metadataDir);
|
|
158
|
+
assert.ok(result.plugins[0].package.startsWith("oci://quay.io/rhdh/"), "must use the actual registry from metadata, not hardcoded ghcr.io");
|
|
159
|
+
assert.strictEqual(result.plugins[0].package, "oci://quay.io/rhdh/backstage-plugin-events-backend-module-github@sha256:abc123", "must use metadata dynamicArtifact exactly");
|
|
160
|
+
}
|
|
161
|
+
finally {
|
|
162
|
+
await fs.remove(metadataDir);
|
|
163
|
+
}
|
|
164
|
+
});
|
|
165
|
+
it("skips injection when RHDH_SKIP_PLUGIN_METADATA_INJECTION is 'true'", async () => {
|
|
166
|
+
process.env.RHDH_SKIP_PLUGIN_METADATA_INJECTION = "true";
|
|
167
|
+
const metadataDir = await createMetadataFixture([
|
|
168
|
+
{
|
|
169
|
+
name: "backstage-community-plugin-tech-radar",
|
|
170
|
+
packageName: "@backstage-community/plugin-tech-radar",
|
|
171
|
+
dynamicArtifact: "./dynamic-plugins/dist/backstage-community-plugin-tech-radar",
|
|
172
|
+
appConfigExamples: {
|
|
173
|
+
techRadar: { url: "http://default.example.com" },
|
|
174
|
+
},
|
|
175
|
+
},
|
|
176
|
+
]);
|
|
177
|
+
try {
|
|
178
|
+
const config = {
|
|
179
|
+
plugins: [
|
|
180
|
+
{
|
|
181
|
+
package: "./dynamic-plugins/dist/backstage-community-plugin-tech-radar",
|
|
182
|
+
disabled: false,
|
|
183
|
+
},
|
|
184
|
+
],
|
|
185
|
+
};
|
|
186
|
+
const result = await processPluginsForDeployment(config, metadataDir);
|
|
187
|
+
assert.strictEqual(result.plugins[0].pluginConfig, undefined, "pluginConfig must not be injected when RHDH_SKIP_PLUGIN_METADATA_INJECTION=true");
|
|
188
|
+
}
|
|
189
|
+
finally {
|
|
190
|
+
await fs.remove(metadataDir);
|
|
191
|
+
}
|
|
192
|
+
});
|
|
193
|
+
it("does not skip injection when RHDH_SKIP_PLUGIN_METADATA_INJECTION is 'false'", async () => {
|
|
194
|
+
process.env.RHDH_SKIP_PLUGIN_METADATA_INJECTION = "false";
|
|
195
|
+
const metadataDir = await createMetadataFixture([
|
|
196
|
+
{
|
|
197
|
+
name: "backstage-community-plugin-tech-radar",
|
|
198
|
+
packageName: "@backstage-community/plugin-tech-radar",
|
|
199
|
+
dynamicArtifact: "./dynamic-plugins/dist/backstage-community-plugin-tech-radar",
|
|
200
|
+
appConfigExamples: { techRadar: { url: "http://example.com" } },
|
|
201
|
+
},
|
|
202
|
+
]);
|
|
203
|
+
try {
|
|
204
|
+
const config = {
|
|
205
|
+
plugins: [
|
|
206
|
+
{
|
|
207
|
+
package: "./dynamic-plugins/dist/backstage-community-plugin-tech-radar",
|
|
208
|
+
disabled: false,
|
|
209
|
+
},
|
|
210
|
+
],
|
|
211
|
+
};
|
|
212
|
+
const result = await processPluginsForDeployment(config, metadataDir);
|
|
213
|
+
assert.ok(result.plugins[0].pluginConfig, "pluginConfig must be injected when RHDH_SKIP_PLUGIN_METADATA_INJECTION='false' (strict check)");
|
|
214
|
+
}
|
|
215
|
+
finally {
|
|
216
|
+
await fs.remove(metadataDir);
|
|
217
|
+
}
|
|
218
|
+
});
|
|
219
|
+
it("resolves mixed plugin types correctly in a single config", async () => {
|
|
220
|
+
const metadataDir = await createMetadataFixture([
|
|
221
|
+
{
|
|
222
|
+
name: "backstage-community-plugin-tekton",
|
|
223
|
+
packageName: "@backstage-community/plugin-tekton",
|
|
224
|
+
dynamicArtifact: "oci://ghcr.io/redhat-developer/rhdh-plugin-export-overlays/backstage-community-plugin-tekton:bs_1.45.3__3.33.3!backstage-community-plugin-tekton",
|
|
225
|
+
},
|
|
226
|
+
{
|
|
227
|
+
name: "backstage-community-plugin-tech-radar",
|
|
228
|
+
packageName: "@backstage-community/plugin-tech-radar",
|
|
229
|
+
dynamicArtifact: "./dynamic-plugins/dist/backstage-community-plugin-tech-radar",
|
|
230
|
+
},
|
|
231
|
+
]);
|
|
232
|
+
try {
|
|
233
|
+
const config = {
|
|
234
|
+
plugins: [
|
|
235
|
+
{
|
|
236
|
+
package: "oci://ghcr.io/redhat-developer/rhdh-plugin-export-overlays/backstage-community-plugin-tekton:old_tag!backstage-community-plugin-tekton",
|
|
237
|
+
disabled: false,
|
|
238
|
+
},
|
|
239
|
+
{
|
|
240
|
+
package: "./dynamic-plugins/dist/backstage-community-plugin-tech-radar",
|
|
241
|
+
disabled: false,
|
|
242
|
+
},
|
|
243
|
+
{
|
|
244
|
+
package: "./dynamic-plugins/dist/backstage-community-plugin-catalog-backend-module-keycloak-dynamic",
|
|
245
|
+
disabled: false,
|
|
246
|
+
},
|
|
247
|
+
],
|
|
248
|
+
};
|
|
249
|
+
const result = await processPluginsForDeployment(config, metadataDir);
|
|
250
|
+
const plugins = result.plugins;
|
|
251
|
+
assert.ok(plugins[0].package.includes("bs_1.45.3__3.33.3"), "OCI plugin with metadata must resolve to metadata dynamicArtifact");
|
|
252
|
+
assert.strictEqual(plugins[1].package, "./dynamic-plugins/dist/backstage-community-plugin-tech-radar", "local path plugin with metadata must stay unchanged");
|
|
253
|
+
assert.strictEqual(plugins[2].package, "./dynamic-plugins/dist/backstage-community-plugin-catalog-backend-module-keycloak-dynamic", "plugin without metadata must stay unchanged");
|
|
254
|
+
}
|
|
255
|
+
finally {
|
|
256
|
+
await fs.remove(metadataDir);
|
|
257
|
+
}
|
|
258
|
+
});
|
|
259
|
+
// ── PR vs nightly precedence ────────────────────────────────────────────
|
|
260
|
+
describe("PR vs nightly precedence", () => {
|
|
261
|
+
it("isNightlyJob returns false when both GIT_PR_NUMBER and E2E_NIGHTLY_MODE are set", () => {
|
|
262
|
+
process.env.GIT_PR_NUMBER = "42";
|
|
263
|
+
process.env.E2E_NIGHTLY_MODE = "true";
|
|
264
|
+
assert.strictEqual(isNightlyJob(), false, "GIT_PR_NUMBER must make isNightlyJob return false");
|
|
265
|
+
});
|
|
266
|
+
it("injects metadata config when GIT_PR_NUMBER is set (PR mode despite nightly env)", async () => {
|
|
267
|
+
process.env.GIT_PR_NUMBER = "42";
|
|
268
|
+
process.env.E2E_NIGHTLY_MODE = "true";
|
|
269
|
+
const { wsDir, metadataDir } = await createWorkspaceFixture([
|
|
270
|
+
{
|
|
271
|
+
name: "backstage-community-plugin-tech-radar",
|
|
272
|
+
packageName: "@backstage-community/plugin-tech-radar",
|
|
273
|
+
dynamicArtifact: "./dynamic-plugins/dist/backstage-community-plugin-tech-radar",
|
|
274
|
+
appConfigExamples: {
|
|
275
|
+
techRadar: { url: "http://default.example.com" },
|
|
276
|
+
},
|
|
277
|
+
},
|
|
278
|
+
]);
|
|
279
|
+
try {
|
|
280
|
+
const config = {
|
|
281
|
+
plugins: [
|
|
282
|
+
{
|
|
283
|
+
package: "./dynamic-plugins/dist/backstage-community-plugin-tech-radar",
|
|
284
|
+
disabled: false,
|
|
285
|
+
},
|
|
286
|
+
],
|
|
287
|
+
};
|
|
288
|
+
const result = await processPluginsForDeployment(config, metadataDir);
|
|
289
|
+
assert.ok(result.plugins[0].pluginConfig, "metadata injection must happen when GIT_PR_NUMBER is set (PR takes precedence over nightly)");
|
|
290
|
+
}
|
|
291
|
+
finally {
|
|
292
|
+
await fs.remove(wsDir);
|
|
293
|
+
}
|
|
294
|
+
});
|
|
295
|
+
});
|
|
296
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"plugin-metadata.test.d.ts","sourceRoot":"","sources":["../../../src/utils/tests/plugin-metadata.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pure utility function tests — no env vars, no file system fixtures.
|
|
3
|
+
* Tests: extractPluginName, getNormalizedPluginMergeKey, disablePluginWrappers, generatePluginsFromMetadata
|
|
4
|
+
*/
|
|
5
|
+
import { describe, it } from "node:test";
|
|
6
|
+
import assert from "node:assert";
|
|
7
|
+
import fs from "fs-extra";
|
|
8
|
+
import { extractPluginName, getNormalizedPluginMergeKey, disablePluginWrappers, generatePluginsFromMetadata, } from "../plugin-metadata.js";
|
|
9
|
+
import { createMetadataFixture } from "./helpers.js";
|
|
10
|
+
// ── extractPluginName ────────────────────────────────────────────────────────
|
|
11
|
+
describe("extractPluginName", () => {
|
|
12
|
+
it("extracts name from OCI URL with tag and alias", () => {
|
|
13
|
+
assert.strictEqual(extractPluginName("oci://ghcr.io/org/repo/backstage-community-plugin-keycloak:pr_1__1.0!alias"), "backstage-community-plugin-keycloak");
|
|
14
|
+
});
|
|
15
|
+
it("extracts name from OCI URL with digest and alias", () => {
|
|
16
|
+
assert.strictEqual(extractPluginName("oci://quay.io/rhdh/backstage-plugin-events-backend-module-github@sha256:abc123!backstage-plugin-events-backend-module-github"), "backstage-plugin-events-backend-module-github");
|
|
17
|
+
});
|
|
18
|
+
it("extracts name from OCI URL with tag only (no alias)", () => {
|
|
19
|
+
assert.strictEqual(extractPluginName("oci://ghcr.io/redhat-developer/rhdh-plugin-export-overlays/backstage-community-plugin-tekton:bs_1.45.3__3.33.3"), "backstage-community-plugin-tekton");
|
|
20
|
+
});
|
|
21
|
+
it("extracts name from local path", () => {
|
|
22
|
+
assert.strictEqual(extractPluginName("./dynamic-plugins/dist/backstage-community-plugin-keycloak-dynamic"), "backstage-community-plugin-keycloak-dynamic");
|
|
23
|
+
});
|
|
24
|
+
it("extracts name from OCI URL with digest but no alias", () => {
|
|
25
|
+
assert.strictEqual(extractPluginName("oci://quay.io/rhdh/backstage-plugin-events-backend-module-github@sha256:c1d17d47aaa"), "backstage-plugin-events-backend-module-github");
|
|
26
|
+
});
|
|
27
|
+
it("uses alias when alias differs from image name (redhat-resource-optimization pattern)", () => {
|
|
28
|
+
assert.strictEqual(extractPluginName("oci://quay.io/redhat-resource-optimization/dynamic-plugins:1.3.2!red-hat-developer-hub-plugin-redhat-resource-optimization"), "dynamic-plugins", "when alias is present, extractPluginName strips alias first and extracts from OCI path");
|
|
29
|
+
});
|
|
30
|
+
it("extracts name from npm package reference", () => {
|
|
31
|
+
assert.strictEqual(extractPluginName("@red-hat-developer-hub/backstage-plugin-global-header-test@0.0.2"), "backstage-plugin-global-header-test", "must extract plugin name from npm @scope/name@version format");
|
|
32
|
+
});
|
|
33
|
+
});
|
|
34
|
+
// ── getNormalizedPluginMergeKey ───────────────────────────────────────────────
|
|
35
|
+
describe("getNormalizedPluginMergeKey", () => {
|
|
36
|
+
it("returns same key for OCI and local -dynamic variant of the same plugin", () => {
|
|
37
|
+
const oci = getNormalizedPluginMergeKey({
|
|
38
|
+
package: "oci://ghcr.io/redhat-developer/rhdh-plugin-export-overlays/backstage-community-plugin-catalog-backend-module-keycloak:pr_1980__3.16.0!backstage-community-plugin-catalog-backend-module-keycloak",
|
|
39
|
+
});
|
|
40
|
+
const local = getNormalizedPluginMergeKey({
|
|
41
|
+
package: "./dynamic-plugins/dist/backstage-community-plugin-catalog-backend-module-keycloak-dynamic",
|
|
42
|
+
});
|
|
43
|
+
assert.strictEqual(oci, local, "same logical plugin has same merge key");
|
|
44
|
+
assert.strictEqual(oci, "backstage-community-plugin-catalog-backend-module-keycloak");
|
|
45
|
+
});
|
|
46
|
+
it("returns different keys for different plugins", () => {
|
|
47
|
+
const keycloak = getNormalizedPluginMergeKey({
|
|
48
|
+
package: "./dynamic-plugins/dist/backstage-community-plugin-catalog-backend-module-keycloak-dynamic",
|
|
49
|
+
});
|
|
50
|
+
const techRadar = getNormalizedPluginMergeKey({
|
|
51
|
+
package: "./dynamic-plugins/dist/backstage-community-plugin-tech-radar-dynamic",
|
|
52
|
+
});
|
|
53
|
+
assert.notStrictEqual(keycloak, techRadar);
|
|
54
|
+
});
|
|
55
|
+
it("returns empty string for missing or empty package", () => {
|
|
56
|
+
assert.strictEqual(getNormalizedPluginMergeKey({}), "");
|
|
57
|
+
assert.strictEqual(getNormalizedPluginMergeKey({ package: "" }), "");
|
|
58
|
+
assert.strictEqual(getNormalizedPluginMergeKey({ package: undefined }), "");
|
|
59
|
+
});
|
|
60
|
+
});
|
|
61
|
+
// ── disablePluginWrappers ────────────────────────────────────────────────────
|
|
62
|
+
describe("disablePluginWrappers", () => {
|
|
63
|
+
it("returns empty plugins array for empty input", () => {
|
|
64
|
+
const result = disablePluginWrappers([]);
|
|
65
|
+
assert.deepStrictEqual(result, { plugins: [] });
|
|
66
|
+
});
|
|
67
|
+
it("creates disabled entries with correct local path format", () => {
|
|
68
|
+
const result = disablePluginWrappers([
|
|
69
|
+
"backstage-community-plugin-tech-radar",
|
|
70
|
+
"backstage-plugin-kubernetes",
|
|
71
|
+
]);
|
|
72
|
+
assert.strictEqual(result.plugins.length, 2);
|
|
73
|
+
assert.deepStrictEqual(result.plugins[0], {
|
|
74
|
+
package: "./dynamic-plugins/dist/backstage-community-plugin-tech-radar",
|
|
75
|
+
disabled: true,
|
|
76
|
+
});
|
|
77
|
+
assert.deepStrictEqual(result.plugins[1], {
|
|
78
|
+
package: "./dynamic-plugins/dist/backstage-plugin-kubernetes",
|
|
79
|
+
disabled: true,
|
|
80
|
+
});
|
|
81
|
+
});
|
|
82
|
+
});
|
|
83
|
+
// ── generatePluginsFromMetadata ──────────────────────────────────────────────
|
|
84
|
+
describe("generatePluginsFromMetadata", () => {
|
|
85
|
+
it("generates entries from metadata with package set to dynamicArtifact", async () => {
|
|
86
|
+
const metadataDir = await createMetadataFixture([
|
|
87
|
+
{
|
|
88
|
+
name: "backstage-community-plugin-tech-radar",
|
|
89
|
+
packageName: "@backstage-community/plugin-tech-radar",
|
|
90
|
+
dynamicArtifact: "./dynamic-plugins/dist/backstage-community-plugin-tech-radar",
|
|
91
|
+
appConfigExamples: { techRadar: { url: "http://example.com" } },
|
|
92
|
+
},
|
|
93
|
+
]);
|
|
94
|
+
try {
|
|
95
|
+
const result = await generatePluginsFromMetadata(metadataDir);
|
|
96
|
+
assert.strictEqual(result.plugins.length, 1);
|
|
97
|
+
assert.strictEqual(result.plugins[0].package, "./dynamic-plugins/dist/backstage-community-plugin-tech-radar", "package must be the dynamicArtifact from metadata");
|
|
98
|
+
assert.strictEqual(result.plugins[0].disabled, false);
|
|
99
|
+
assert.strictEqual(result.plugins[0].pluginConfig, undefined, "generatePluginsFromMetadata must NOT include pluginConfig");
|
|
100
|
+
}
|
|
101
|
+
finally {
|
|
102
|
+
await fs.remove(metadataDir);
|
|
103
|
+
}
|
|
104
|
+
});
|
|
105
|
+
it("generates entries for OCI-referenced plugins", async () => {
|
|
106
|
+
const metadataDir = await createMetadataFixture([
|
|
107
|
+
{
|
|
108
|
+
name: "backstage-community-plugin-tekton",
|
|
109
|
+
packageName: "@backstage-community/plugin-tekton",
|
|
110
|
+
dynamicArtifact: "oci://ghcr.io/redhat-developer/rhdh-plugin-export-overlays/backstage-community-plugin-tekton:bs_1.45.3__3.33.3!backstage-community-plugin-tekton",
|
|
111
|
+
},
|
|
112
|
+
]);
|
|
113
|
+
try {
|
|
114
|
+
const result = await generatePluginsFromMetadata(metadataDir);
|
|
115
|
+
assert.strictEqual(result.plugins.length, 1);
|
|
116
|
+
assert.ok(result.plugins[0].package.startsWith("oci://"), "OCI artifact must be preserved as package");
|
|
117
|
+
}
|
|
118
|
+
finally {
|
|
119
|
+
await fs.remove(metadataDir);
|
|
120
|
+
}
|
|
121
|
+
});
|
|
122
|
+
it("generates entries for mixed local and OCI artifacts", async () => {
|
|
123
|
+
const metadataDir = await createMetadataFixture([
|
|
124
|
+
{
|
|
125
|
+
name: "backstage-community-plugin-tech-radar",
|
|
126
|
+
packageName: "@backstage-community/plugin-tech-radar",
|
|
127
|
+
dynamicArtifact: "./dynamic-plugins/dist/backstage-community-plugin-tech-radar",
|
|
128
|
+
},
|
|
129
|
+
{
|
|
130
|
+
name: "backstage-community-plugin-tekton",
|
|
131
|
+
packageName: "@backstage-community/plugin-tekton",
|
|
132
|
+
dynamicArtifact: "oci://ghcr.io/redhat-developer/rhdh-plugin-export-overlays/backstage-community-plugin-tekton:bs_1.45.3__3.33.3!backstage-community-plugin-tekton",
|
|
133
|
+
},
|
|
134
|
+
{
|
|
135
|
+
name: "backstage-plugin-events-backend-module-github",
|
|
136
|
+
packageName: "@backstage/plugin-events-backend-module-github",
|
|
137
|
+
dynamicArtifact: "oci://quay.io/rhdh/backstage-plugin-events-backend-module-github@sha256:abc123",
|
|
138
|
+
},
|
|
139
|
+
]);
|
|
140
|
+
try {
|
|
141
|
+
const result = await generatePluginsFromMetadata(metadataDir);
|
|
142
|
+
assert.strictEqual(result.plugins.length, 3, "must generate 3 entries");
|
|
143
|
+
const packages = result.plugins.map((p) => p.package).sort();
|
|
144
|
+
assert.ok(packages.some((p) => p.startsWith("./")), "must include local path artifact");
|
|
145
|
+
assert.ok(packages.some((p) => p.startsWith("oci://ghcr.io/")), "must include ghcr.io OCI");
|
|
146
|
+
assert.ok(packages.some((p) => p.startsWith("oci://quay.io/")), "must include quay.io OCI");
|
|
147
|
+
for (const plugin of result.plugins) {
|
|
148
|
+
assert.strictEqual(plugin.disabled, false);
|
|
149
|
+
assert.strictEqual(plugin.pluginConfig, undefined);
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
finally {
|
|
153
|
+
await fs.remove(metadataDir);
|
|
154
|
+
}
|
|
155
|
+
});
|
|
156
|
+
});
|
package/package.json
CHANGED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"plugin-metadata.test.d.ts","sourceRoot":"","sources":["../../src/utils/plugin-metadata.test.ts"],"names":[],"mappings":""}
|