Skip to content
Open
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
32 changes: 17 additions & 15 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,20 +19,22 @@ jobs:
with:
submodules: recursive

# Verify CSP line exists in target TypeScript file
- name: Check CSP configuration in webClientServer.ts
run: |
TARGET_FILE="patched-vscode/src/vs/server/node/webClientServer.ts"
REQUIRED_TEXT="'connect-src \'self\' ws: wss: https://main.vscode-cdn.net http://localhost:* https://localhost:* https://login.microsoftonline.com/ https://update.code.visualstudio.com https://*.vscode-unpkg.net/ https://default.exp-tas.com/vscode/ab https://vscode-sync.trafficmanager.net https://vscode-sync-insiders.trafficmanager.net https://*.gallerycdn.vsassets.io https://marketplace.visualstudio.com https://openvsxorg.blob.core.windows.net https://az764295.vo.msecnd.net https://code.visualstudio.com https://*.gallery.vsassets.io https://*.rel.tunnels.api.visualstudio.com wss://*.rel.tunnels.api.visualstudio.com https://*.servicebus.windows.net/ https://vscode.blob.core.windows.net https://vscode.search.windows.net https://vsmarketplacebadges.dev https://vscode.download.prss.microsoft.com https://download.visualstudio.microsoft.com https://*.vscode-unpkg.net https://open-vsx.org;'"
# Install dependencies
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '20'

if [ ! -f "$TARGET_FILE" ]; then
echo "❌ FAIL: Target file $TARGET_FILE does not exist."
exit 1
fi
# Install quilt for patch management
- name: Install quilt
run: sudo apt-get update && sudo apt-get install -y quilt

if grep -F "$REQUIRED_TEXT" "$TARGET_FILE" > /dev/null; then
echo "✅ PASS: Required CSP text exists."
else
echo "❌ FAIL: Required CSP text NOT found in $TARGET_FILE"
exit 1
fi
# Install system dependencies for native modules
- name: Install system dependencies
run: sudo apt-get install -y libkrb5-dev libx11-dev libxkbfile-dev pkg-config

# Run unit tests
- name: Run patch validation tests
run: |
chmod +x ./scripts/run-unit-tests.sh
./scripts/run-unit-tests.sh
64 changes: 64 additions & 0 deletions scripts/run-unit-tests.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
#!/bin/bash

set -e

echo "INFO: Running SageMaker Code Editor Unit Tests"

# Get project root
PROJ_ROOT=$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)
cd "$PROJ_ROOT"

# Check if Node.js and npx are available
if ! command -v node &> /dev/null || ! command -v npx &> /dev/null; then
echo "ERROR: Node.js and npm are required to run tests"
exit 1
fi

#Install required dependencies
echo "Installing dependencies..."
npm install -g typescript
npm install --save-dev @types/node

# Compile and run each test file
TEST_DIR="tests"
FAILED_TESTS=0
TOTAL_TESTS=0

# First compile all TypeScript files
echo "Compiling TypeScript files..."
if ! npx tsc --project "$TEST_DIR/tsconfig.json" --outDir /tmp/tests; then
echo "ERROR: TypeScript compilation failed"
exit 1
fi

for test_file in "$TEST_DIR"/*.test.ts; do
if [ -f "$test_file" ]; then
TOTAL_TESTS=$((TOTAL_TESTS + 1))
test_name=$(basename "$test_file" .test.ts)

echo "Running $test_name tests..."

# Run the compiled JavaScript
if node "/tmp/tests/$(basename "$test_file" .ts).js"; then
echo "SUCCESS: $test_name tests passed"
else
echo "FAILED: $test_name tests failed"
FAILED_TESTS=$((FAILED_TESTS + 1))
fi
echo ""
fi
done

# Summary
echo "INFO: Test Summary:"
echo "Total test suites: $TOTAL_TESTS"
echo "Failed test suites: $FAILED_TESTS"
echo "Passed test suites: $((TOTAL_TESTS - FAILED_TESTS))"

if [ $FAILED_TESTS -eq 0 ]; then
echo "SUCCESS: All tests passed!"
exit 0
else
echo "FAILED: $FAILED_TESTS test suite(s) failed"
exit 1
fi
60 changes: 60 additions & 0 deletions 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 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(), 'patched-vscode');

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 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(), 'patched-vscode');

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');

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

const content = readFileSync(filePath, '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
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 (!content.includes(openVsxGallery)) {
throw new Error(`Expected open-vsx gallery URL not found in ${filePath}`);
}

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

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

// 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 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(), 'patched-vscode');

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 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(), 'patched-vscode');

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');
});
});
Loading