Skip to content

Commit 3d20fb2

Browse files
authored
E2E test: facilities for controlling workbench tests based on version (#13727)
## Summary Add version-gated test for DATABRICKS_HOST environment variable in Workbench managed credentials test. ## Details The `DATABRICKS_HOST` environment variable is only reliably available in Workbench versions 2026.05 and later. This PR adds conditional test logic to only verify this variable when running against compatible versions. **Changes:** - Add `WorkbenchVersion` utility class for parsing and comparing Workbench versions from Docker containers - Update managed credentials test to conditionally check `DATABRICKS_HOST` only when version > 2026.04 **Implementation:** The utility fetches the version from the container by running `rstudio-server version`, parses the output (e.g., `2026.05.0+218.pro2 Workbench...`), and provides semantic version comparison methods. **Usage:** ```typescript const workbenchVersion = await WorkbenchVersion.fetchFromContainer(); if (workbenchVersion.isGreaterThan('2026.04')) { // Check DATABRICKS_HOST } ``` ### Validation Steps @:workbench-stable
1 parent 513b849 commit 3d20fb2

2 files changed

Lines changed: 123 additions & 4 deletions

File tree

test/e2e/tests/workbench/managed-credentials/managed-credentials-variables.test.ts

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
*--------------------------------------------------------------------------------------------*/
55

66
import { test, tags } from '../../_test.setup';
7+
import { WorkbenchVersion } from '../../../utils/workbench-version';
78

89
test.use({
910
suiteId: __filename
@@ -13,14 +14,17 @@ test.describe('Managed Credentials - Environment Variables', {
1314
tag: [tags.WORKBENCH]
1415
}, () => {
1516
test('R - Verify managed credentials environment variables are set', async function ({ app, r }) {
17+
const workbenchVersion = await WorkbenchVersion.fetchFromContainer();
1618

1719
// Verify SNOWFLAKE_ACCOUNT environment variable
1820
await app.workbench.console.executeCode('R', 'Sys.getenv("SNOWFLAKE_ACCOUNT")');
1921
await app.workbench.console.waitForConsoleContents(process.env.SNOWFLAKE_ACCOUNT!);
2022

21-
// Verify DATABRICKS_HOST environment variable
22-
await app.workbench.console.executeCode('R', 'Sys.getenv("DATABRICKS_HOST")');
23-
const databricksHost = new URL(process.env.DATABRICKS_URL!).host;
24-
await app.workbench.console.waitForConsoleContents(databricksHost);
23+
// Verify DATABRICKS_HOST environment variable (only available in 2026.04+)
24+
if (workbenchVersion.isGreaterThan('2026.04')) {
25+
await app.workbench.console.executeCode('R', 'Sys.getenv("DATABRICKS_HOST")');
26+
const databricksHost = new URL(process.env.DATABRICKS_URL!).host;
27+
await app.workbench.console.waitForConsoleContents(databricksHost);
28+
}
2529
});
2630
});
Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
/*---------------------------------------------------------------------------------------------
2+
* Copyright (C) 2026 Posit Software, PBC. All rights reserved.
3+
* Licensed under the Elastic License 2.0. See LICENSE.txt for license information.
4+
*--------------------------------------------------------------------------------------------*/
5+
6+
import { runDockerCommand } from '../fixtures/test-setup/docker-utils';
7+
8+
/**
9+
* Represents a Workbench version in YYYY.MM.PATCH format
10+
*/
11+
export class WorkbenchVersion {
12+
constructor(
13+
public readonly year: number,
14+
public readonly month: number,
15+
public readonly patch: number
16+
) { }
17+
18+
/**
19+
* Parse a version string in format "YYYY.MM.PATCH+BUILD.TYPE"
20+
* Example: "2026.05.0+218.pro2" -> WorkbenchVersion(2026, 5, 0)
21+
*/
22+
static parse(versionString: string): WorkbenchVersion {
23+
// Extract just the version part before the + sign
24+
const versionPart = versionString.split('+')[0].trim();
25+
const parts = versionPart.split('.');
26+
27+
if (parts.length < 2) {
28+
throw new Error(`Invalid version format: ${versionString}. Expected format: YYYY.MM.PATCH`);
29+
}
30+
31+
const year = parseInt(parts[0], 10);
32+
const month = parseInt(parts[1], 10);
33+
const patch = parts.length >= 3 ? parseInt(parts[2], 10) : 0;
34+
35+
if (isNaN(year) || isNaN(month) || isNaN(patch)) {
36+
throw new Error(`Invalid version numbers in: ${versionString}`);
37+
}
38+
39+
return new WorkbenchVersion(year, month, patch);
40+
}
41+
42+
/**
43+
* Fetch the Workbench version from the Docker container
44+
*/
45+
static async fetchFromContainer(containerName: string = 'test'): Promise<WorkbenchVersion> {
46+
const result = await runDockerCommand(
47+
`docker exec ${containerName} rstudio-server version`,
48+
'Get Workbench version'
49+
);
50+
51+
// Parse output like: "2026.05.0+218.pro2 Workbench (Golden Wattle) for Ubuntu Jammy"
52+
const firstLine = result.stdout.trim().split('\n')[0];
53+
const versionMatch = firstLine.match(/^([\d.]+\+[\w.]+)/);
54+
55+
if (!versionMatch) {
56+
throw new Error(`Could not parse version from: ${firstLine}`);
57+
}
58+
59+
return WorkbenchVersion.parse(versionMatch[1]);
60+
}
61+
62+
/**
63+
* Check if this version is greater than another version
64+
*/
65+
isGreaterThan(other: WorkbenchVersion | string): boolean {
66+
const otherVersion = typeof other === 'string' ? WorkbenchVersion.parse(other) : other;
67+
68+
if (this.year !== otherVersion.year) {
69+
return this.year > otherVersion.year;
70+
}
71+
if (this.month !== otherVersion.month) {
72+
return this.month > otherVersion.month;
73+
}
74+
return this.patch > otherVersion.patch;
75+
}
76+
77+
/**
78+
* Check if this version is greater than or equal to another version
79+
*/
80+
isGreaterThanOrEqualTo(other: WorkbenchVersion | string): boolean {
81+
return this.equals(other) || this.isGreaterThan(other);
82+
}
83+
84+
/**
85+
* Check if this version is less than another version
86+
*/
87+
isLessThan(other: WorkbenchVersion | string): boolean {
88+
const otherVersion = typeof other === 'string' ? WorkbenchVersion.parse(other) : other;
89+
return !this.isGreaterThanOrEqualTo(otherVersion);
90+
}
91+
92+
/**
93+
* Check if this version is less than or equal to another version
94+
*/
95+
isLessThanOrEqualTo(other: WorkbenchVersion | string): boolean {
96+
return this.equals(other) || this.isLessThan(other);
97+
}
98+
99+
/**
100+
* Check if this version equals another version
101+
*/
102+
equals(other: WorkbenchVersion | string): boolean {
103+
const otherVersion = typeof other === 'string' ? WorkbenchVersion.parse(other) : other;
104+
return this.year === otherVersion.year &&
105+
this.month === otherVersion.month &&
106+
this.patch === otherVersion.patch;
107+
}
108+
109+
/**
110+
* Return string representation in YYYY.MM.PATCH format
111+
*/
112+
toString(): string {
113+
return `${this.year}.${this.month.toString().padStart(2, '0')}.${this.patch}`;
114+
}
115+
}

0 commit comments

Comments
 (0)