Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .github/CODEOWNERS
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,4 @@
/.github/ @aws/aws-code-editor
/patches/sagemaker.series @aws/aws-code-editor @aws/sagemaker-code-editor
/patches/sagemaker/ @aws/aws-code-editor @aws/sagemaker-code-editor
/sagemaker-tests/ @aws/aws-code-editor @aws/sagemaker-code-editor
5 changes: 4 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,7 @@ build-artifacts
build-private
code-editor-src
vscode-reh-web-*
code-editor-src-*
code-editor-src-*
node_modules
package-lock.json
package.json
Comment on lines +11 to +12
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this necessary? What prompted adding this? Usually package.json files are not ignored

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When running ./scripts/run-sagemaker-unit-tests.sh the package.json is generated, and for now we don't really need to store it, since the only dependency is @types/node, and the tests themselves are not a proper single project (the test files are just compiled one by one and run individually). If the unit tests are expanded in the future then we could add a proper package.json.

60 changes: 60 additions & 0 deletions sagemaker-tests/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
# SageMaker Code Editor Unit Tests

This directory contains TypeScript unit tests that validate all patches applied to the VSCode codebase.

## Test Structure

Each patch file in `patches/series` has a corresponding test file:

- `sagemaker-extension.test.ts` - Validates sagemaker-extension.diff patch
- `disable-online-services.test.ts` - Validates disable-online-services.diff patch
- `disable-telemetry.test.ts` - Validates disable-telemetry.diff patch
- `update-csp.test.ts` - Validates update-csp.diff patch
- `webview.test.ts` - Validates webview.diff patch
- `local-storage.test.ts` - Validates local-storage.diff patch
- `sagemaker-integration.test.ts` - Validates sagemaker-integration.diff patch
- `license.test.ts` - Validates license.diff patch
- `base-path-compatibility.test.ts` - Validates base-path-compatibility.diff patch
- `sagemaker-idle-extension.test.ts` - Validates sagemaker-idle-extension.patch
- `terminal-crash-mitigation.test.ts` - Validates terminal-crash-mitigation.patch
- `sagemaker-open-notebook-extension.test.ts` - Validates sagemaker-open-notebook-extension.patch
- `sagemaker-ui-dark-theme.test.ts` - Validates sagemaker-ui-dark-theme.patch
- `sagemaker-ui-post-startup.test.ts` - Validates sagemaker-ui-post-startup.patch
- `sagemaker-extension-smus-support.test.ts` - Validates sagemaker-extension-smus-support.patch
- `post-startup-notifications.test.ts` - Validates post-startup-notifications.patch
- `sagemaker-extensions-sync.test.ts` - Validates sagemaker-extensions-sync.patch
- `custom-extensions-marketplace.test.ts` - Validates custom-extensions-marketplace.diff patch
- `signature-verification.test.ts` - Validates signature-verification.diff patch
- `display-language.test.ts` - Validates display-language.patch

**Total: 20 test files covering all patches in the series**

## Running Tests

### Locally
```bash
./scripts/run-unit-tests.sh
```

### In CI
Tests run automatically on every push via GitHub Actions in `.github/workflows/ci.yml`

## Test Framework

Tests use a simple Node.js-based framework defined in `test-framework.ts` with:
- `describe()` - Test suite grouping
- `test()` - Individual test cases

## What Tests Validate

Tests check that:
1. Patches are properly applied to `patched-vscode/` directory
2. Expected code modifications exist in target files
3. New files/directories are created where needed
4. Configuration changes are present

## Requirements

- Node.js 20+
- TypeScript compiler (npx tsc)
- Patches must be applied (script handles this automatically)
37 changes: 37 additions & 0 deletions sagemaker-tests/base-path-compatibility.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import { readFileSync, existsSync } from 'fs';
import { join } from 'path';
import './test-framework';

const PATCHED_VSCODE_DIR = join(process.cwd(), 'code-editor-src');

describe('base-path-compatibility.diff validation', () => {
test('serverEnvironmentService.ts should have base-path option added', () => {
const filePath = join(PATCHED_VSCODE_DIR, 'src/vs/server/node/serverEnvironmentService.ts');

if (!existsSync(filePath)) {
throw new Error(`File not found: ${filePath}`);
}

const content = readFileSync(filePath, 'utf8');

// Check for base-path option in serverOptions
const basePathOption = "'base-path': { type: 'string' },";
if (!content.includes(basePathOption)) {
throw new Error(`Expected base-path option not found in ${filePath}`);
}

// Check for base-path in ServerParsedArgs interface
const basePathArg = "'base-path'?: string,";
if (!content.includes(basePathArg)) {
throw new Error(`Expected base-path argument type not found in ${filePath}`);
}

// Check for constructor modification
const constructorLogic = "if (args['base-path']) {\n\t\t\targs['server-base-path'] = args['base-path'];\n\t\t}";
if (!content.includes(constructorLogic)) {
throw new Error(`Expected constructor base-path mapping not found in ${filePath}`);
}

console.log('PASS: Base path compatibility modifications found in serverEnvironmentService.ts');
});
});
61 changes: 61 additions & 0 deletions sagemaker-tests/custom-extensions-marketplace.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
import { readFileSync, existsSync } from 'fs';
import { join } from 'path';
import './test-framework';

const PATCHED_VSCODE_DIR = join(process.cwd(), 'code-editor-src');

describe('custom-extensions-marketplace.diff validation', () => {
test('product.ts should have custom extensions gallery logic', () => {
const filePath = join(PATCHED_VSCODE_DIR, 'src/vs/platform/product/common/product.ts');
const productPath = join(PATCHED_VSCODE_DIR, 'product.json');
if (!existsSync(filePath)) {
throw new Error(`File not found: ${filePath}`);
}

const content = readFileSync(filePath, 'utf8');
const productContent = readFileSync(productPath, 'utf8');
// Check for custom extensions gallery environment variable check
const customGalleryCheck = "if (env['EXTENSIONS_GALLERY']) {";
if (!content.includes(customGalleryCheck)) {
throw new Error(`Expected custom extensions gallery check not found in ${filePath}`);
}

// Check for custom gallery parsing log
const customGalleryLog = "console.log(`Custom extensions gallery detected. Parsing...`);";
if (!content.includes(customGalleryLog)) {
throw new Error(`Expected custom gallery log not found in ${filePath}`);
}

// Check for default gallery log - not needed for Code Editor, reference patches/web-server/marketplace.diff and patches/common/integration.diff
// const defaultGalleryLog = "console.log(`Using default extensions gallery.`);";
// if (!content.includes(defaultGalleryLog)) {
// throw new Error(`Expected default gallery log not found in ${filePath}`);
// }

// Check for open-vsx gallery configuration
const openVsxGallery = '"serviceUrl": "https://open-vsx.org/vscode/gallery",';
if (!productContent.includes(openVsxGallery)) {
throw new Error(`Expected open-vsx gallery URL not found in ${productPath}`);
}

// Check for item URL
const itemUrl = '"itemUrl": "https://open-vsx.org/vscode/item",';
if (!productContent.includes(itemUrl)) {
throw new Error(`Expected open-vsx item URL not found in ${productPath}`);
}

// Check for resource URL template
const resourceUrl = '"resourceUrlTemplate": "https://open-vsx.org/vscode/unpkg/{publisher}/{name}/{version}/{path}",';
if (!productContent.includes(resourceUrl)) {
throw new Error(`Expected open-vsx resource URL template not found in ${productPath}`);
}

// Check for gallery logging
const galleryLogging = "console.log(JSON.stringify(product.extensionsGallery, null, 2));";
if (!content.includes(galleryLogging)) {
throw new Error(`Expected gallery logging not found in ${filePath}`);
}

console.log('PASS: Custom extensions marketplace logic found in product.ts');
});
});
31 changes: 31 additions & 0 deletions sagemaker-tests/disable-online-services.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import { readFileSync, existsSync } from 'fs';
import { join } from 'path';
import './test-framework';

const PATCHED_VSCODE_DIR = join(process.cwd(), 'code-editor-src');

describe('disable-online-services.diff validation', () => {
test('update.config.contribution.ts should disable automatic updates', () => {
const filePath = join(PATCHED_VSCODE_DIR, 'src/vs/platform/update/common/update.config.contribution.ts');

if (!existsSync(filePath)) {
throw new Error(`File not found: ${filePath}`);
}

const content = readFileSync(filePath, 'utf8');

// Check update mode is set to none
const updateModeDefault = "default: 'none',";
if (!content.includes(updateModeDefault)) {
throw new Error(`Expected update mode 'none' not found in ${filePath}`);
}

// Check release notes are disabled
const releaseNotesDefault = "default: false,";
if (!content.includes(releaseNotesDefault)) {
throw new Error(`Expected release notes disabled not found in ${filePath}`);
}

console.log('PASS: Online services disabled in update configuration');
});
});
73 changes: 73 additions & 0 deletions sagemaker-tests/disable-telemetry.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
import { readFileSync, existsSync } from 'fs';
import { join } from 'path';
import './test-framework';

const PATCHED_VSCODE_DIR = join(process.cwd(), 'code-editor-src');

describe('disable-telemetry.diff validation', () => {
test('telemetryService.ts should have telemetry disabled by default', () => {
const filePath = join(PATCHED_VSCODE_DIR, 'src/vs/platform/telemetry/common/telemetryService.ts');

if (!existsSync(filePath)) {
throw new Error(`File not found: ${filePath}`);
}

const content = readFileSync(filePath, 'utf8');

// Check that enum only contains OFF
const enumLine = "'enum': [TelemetryConfiguration.OFF],";
if (!content.includes(enumLine)) {
throw new Error(`Expected telemetry enum restriction not found in ${filePath}`);
}

// Check that default is OFF
const defaultLine = "'default': TelemetryConfiguration.OFF,";
if (!content.includes(defaultLine)) {
throw new Error(`Expected telemetry default OFF not found in ${filePath}`);
}

console.log('PASS: Telemetry disabled by default in telemetryService.ts');
});

test('desktop.contribution.ts should have crash reporter disabled', () => {
const filePath = join(PATCHED_VSCODE_DIR, 'src/vs/workbench/electron-sandbox/desktop.contribution.ts');

if (!existsSync(filePath)) {
throw new Error(`File not found: ${filePath}`);
}

const content = readFileSync(filePath, 'utf8');

// Check crash reporter is disabled
const crashReporterDisabled = "'default': false,";
if (!content.includes(crashReporterDisabled)) {
throw new Error(`Expected crash reporter disabled not found in ${filePath}`);
}

console.log('PASS: Crash reporter disabled in desktop.contribution.ts');
});

test('1dsAppender.ts should have Microsoft endpoints blocked', () => {
const filePath = join(PATCHED_VSCODE_DIR, 'src/vs/platform/telemetry/common/1dsAppender.ts');

if (!existsSync(filePath)) {
throw new Error(`File not found: ${filePath}`);
}

const content = readFileSync(filePath, 'utf8');

// Check endpoints are redirected to 0.0.0.0
const blockedEndpoint = "const endpointUrl = 'https://0.0.0.0/OneCollector/1.0';";
const blockedHealthEndpoint = "const endpointHealthUrl = 'https://0.0.0.0/ping';";

if (!content.includes(blockedEndpoint)) {
throw new Error(`Expected blocked endpoint not found in ${filePath}`);
}

if (!content.includes(blockedHealthEndpoint)) {
throw new Error(`Expected blocked health endpoint not found in ${filePath}`);
}

console.log('PASS: Microsoft telemetry endpoints blocked in 1dsAppender.ts');
});
});
72 changes: 72 additions & 0 deletions sagemaker-tests/display-language.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
import { readFileSync, existsSync } from 'fs';
import { join } from 'path';
import './test-framework';

const PATCHED_VSCODE_DIR = join(process.cwd(), 'code-editor-src');

describe('display-language.diff validation', () => {
test('remoteLanguagePacks.ts should have locale functions', () => {
const filePath = join(PATCHED_VSCODE_DIR, 'src/vs/server/node/remoteLanguagePacks.ts');

if (!existsSync(filePath)) {
throw new Error(`File not found: ${filePath}`);
}

const content = readFileSync(filePath, 'utf8');

if (!content.includes('export const getLocaleFromConfig') ||
!content.includes('export async function getBrowserNLSConfiguration')) {
throw new Error(`Failed to find locale functions in ${filePath}`);
}

console.log('PASS: Locale functions found in remoteLanguagePacks.ts');
});

test('webClientServer.ts should use locale from args', () => {
const filePath = join(PATCHED_VSCODE_DIR, 'src/vs/server/node/webClientServer.ts');

if (!existsSync(filePath)) {
throw new Error(`File not found: ${filePath}`);
}

const content = readFileSync(filePath, 'utf8');

if (!content.includes('this._environmentService.args.locale')) {
throw new Error(`Failed to find locale from args in ${filePath}`);
}

console.log('PASS: Locale from args found in webClientServer.ts');
});

test('serverEnvironmentService.ts should have locale option', () => {
const filePath = join(PATCHED_VSCODE_DIR, 'src/vs/server/node/serverEnvironmentService.ts');

if (!existsSync(filePath)) {
throw new Error(`File not found: ${filePath}`);
}

const content = readFileSync(filePath, 'utf8');

if (!content.includes("'locale': { type: 'string' }")) {
throw new Error(`Failed to find locale option in ${filePath}`);
}

console.log('PASS: Locale option found in serverEnvironmentService.ts');
});

test('languagePacks.ts should use remote service', () => {
const filePath = join(PATCHED_VSCODE_DIR, 'src/vs/platform/languagePacks/browser/languagePacks.ts');

if (!existsSync(filePath)) {
throw new Error(`File not found: ${filePath}`);
}

const content = readFileSync(filePath, 'utf8');

if (!content.includes('ProxyChannel.toService<ILanguagePackService>')) {
throw new Error(`Failed to find ProxyChannel usage in ${filePath}`);
}

console.log('PASS: Remote language pack service found in languagePacks.ts');
});
});
6 changes: 6 additions & 0 deletions sagemaker-tests/globals.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
declare global {
function describe(name: string, fn: () => void): void;
function test(name: string, fn: () => void): void;
}

export {};
Loading