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
15 changes: 15 additions & 0 deletions .github/workflows/npm-publish.yml
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,21 @@ jobs:
args: dist/cmf-cli.osx-x64.zip application/zip
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Get version from npm package
id: package-version
run: echo "version=$(node -p "require('./npm/package.json').version")" >> $GITHUB_OUTPUT
- name: Upload win-x64 to criticalmanufacturing.io
run: curl -v --user "${{secrets.CRITICALMANUFACTURING_IO_USER}}:${{secrets.CRITICALMANUFACTURING_IO_TOKEN}}" --upload-file dist/cmf-cli.win-x64.zip https://criticalmanufacturing.io/repository/tools/cmf-cli.win-x64.zip
- name: Upload linux-x64 to criticalmanufacturing.io
run: curl -v --user "${{secrets.CRITICALMANUFACTURING_IO_USER}}:${{secrets.CRITICALMANUFACTURING_IO_TOKEN}}" --upload-file dist/cmf-cli.linux-x64.zip https://criticalmanufacturing.io/repository/tools/cmf-cli.linux-x64.zip
- name: Upload osx-x64 to criticalmanufacturing.io
run: curl -v --user "${{secrets.CRITICALMANUFACTURING_IO_USER}}:${{secrets.CRITICALMANUFACTURING_IO_TOKEN}}" --upload-file dist/cmf-cli.osx-x64.zip https://criticalmanufacturing.io/repository/tools/cmf-cli.osx-x64.zip
- name: Upload win-x64 to criticalmanufacturing.com.cn
run: curl -v --user "${{secrets.CRITICALMANUFACTURING_CN_USER}}:${{secrets.CRITICALMANUFACTURING_CN_TOKEN}}" --upload-file dist/cmf-cli.win-x64.zip https://repository.criticalmanufacturing.com.cn/repository/tools/cmf-cli.win-x64.zip
- name: Upload linux-x64 to criticalmanufacturing.com.cn
run: curl -v --user "${{secrets.CRITICALMANUFACTURING_CN_USER}}:${{secrets.CRITICALMANUFACTURING_CN_TOKEN}}" --upload-file dist/cmf-cli.linux-x64.zip https://repository.criticalmanufacturing.com.cn/repository/tools/cmf-cli.linux-x64.zip
- name: Upload osx-x64 to criticalmanufacturing.com.cn
run: curl -v --user "${{secrets.CRITICALMANUFACTURING_CN_USER}}:${{secrets.CRITICALMANUFACTURING_CN_TOKEN}}" --upload-file dist/cmf-cli.osx-x64.zip https://repository.criticalmanufacturing.com.cn/repository/tools/cmf-cli.osx-x64.zip
- run: npm run publish
if: "github.event.release.prerelease"
env:
Expand Down
4 changes: 3 additions & 1 deletion .github/workflows/pr-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ jobs:
6.0.x
8.0.x
- name: Setup node versions
uses: actions/setup-node@v2
uses: actions/setup-node@v4
with:
node-version: '12'
- name: Install dependencies
Expand All @@ -44,3 +44,5 @@ jobs:
run: dotnet build --configuration Release --no-restore
- name: Test
run: dotnet test --no-restore --verbosity normal --filter "TestCategory!=Internal"
- name: Run npm package tests
run: cd npm && npm install --ignore-scripts && npm test
4 changes: 3 additions & 1 deletion .husky/pre-commit
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
#!/bin/sh
. "$(dirname "$0")/_/husky.sh"

dotnet test --filter "(TestCategory!=LongRunning)&(TestCategory!=Node12)"
if git diff --cached --name-only | grep -E '^(cmf-cli|core)/' >/dev/null; then
dotnet test --filter "(TestCategory!=LongRunning)&(TestCategory!=Node12)"
fi
6 changes: 5 additions & 1 deletion npm/.gitignore
Original file line number Diff line number Diff line change
@@ -1 +1,5 @@
dist/
dist/
coverage/
node_modules/
*.log
.DS_Store
128 changes: 128 additions & 0 deletions npm/TESTING.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
# NPM Package Tests

This directory contains unit tests for the CMF CLI npm package installation scripts.

## Test Files

### `postinstall.test.js`
Tests for the postinstall script that handles downloading and installing the CLI binary.

**Test Coverage:**
- **downloadAndExtract function:**
- Successfully downloads and extracts from a URL
- Handles proxy configuration correctly
- Throws appropriate errors on download failure

- **Install with fallback repositories:**
- Tries primary URL (GitHub) first and succeeds
- Falls back to criticalmanufacturing.io on primary failure
- Falls back to criticalmanufacturing.com.cn on secondary failure
- Fails gracefully when all repositories are unavailable
- Uses correct URL format for each platform (win32, linux, darwin)

### `utils.test.js`
Tests for utility functions used across the npm package.

**Test Coverage:**
- **Platform and Architecture Mappings:**
- Validates correct platform mappings (darwin→osx, linux→linux, win32→win)
- Validates correct architecture mappings (ia32→x86, x64→x64)

- **parsePackageJson function:**
- Handles unsupported architectures
- Handles unsupported platforms
- Handles missing package.json file
- Validates package.json structure
- Correctly parses valid configurations for all platforms
- Adds .exe extension for Windows binaries
- Strips leading "v" from version numbers
- Validates all required properties (version, goBinary.name, goBinary.path)

## Running Tests

### Install Dependencies
```bash
cd npm
npm install
```

### Run All Tests
```bash
npm test
```

### Run Tests in Watch Mode
```bash
npm run test:watch
```

### Run Tests with Coverage
```bash
npm run test:coverage
```

Coverage reports will be generated in the `npm/coverage` directory.

### Run Integration Tests
```bash
npm run test:integration
```

Integration tests check actual repository availability without mocking. This is useful for:
- Verifying artifacts were published correctly after a release
- Testing network connectivity to all repository URLs
- Validating fallback repository configuration

You can test specific platforms:
```bash
node integration-test.js linux x64
node integration-test.js win x64
node integration-test.js osx x64
```

## Test Framework

These tests use **Jest**, a popular JavaScript testing framework. Key features:
- Mock all external dependencies (axios, file system, etc.)
- Test multiple platform/architecture combinations
- Verify fallback repository logic
- Ensure error handling works correctly

## Key Test Scenarios

### 1. Fallback Repository Chain
Tests verify that the installation tries repositories in this order:
1. GitHub releases (primary)
2. criticalmanufacturing.io (fallback 1)
3. criticalmanufacturing.com.cn (fallback 2)

### 2. Cross-Platform Support
Tests verify correct behavior on:
- Windows (win32/x64)
- Linux (linux/x64)
- macOS (darwin/x64)

### 3. Error Handling
Tests verify proper error messages for:
- Network failures
- Unsupported platforms
- Invalid package.json
- Missing required properties

## CI/CD Integration

These tests can be integrated into the GitHub Actions workflow by adding a test step before publishing:

```yaml
- name: Run npm package tests
run: cd npm && npm install && npm test
working-directory: .
```

## Extending Tests

To add new tests:
1. Add test cases to the appropriate `*.test.js` file
2. Follow the existing test structure and naming conventions
3. Mock external dependencies appropriately
4. Run tests locally before committing
138 changes: 138 additions & 0 deletions npm/integration-test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
#!/usr/bin/env node

/**
* Integration test script for testing actual downloads from repositories
*
* This script tests the real download functionality by attempting to download
* from each repository URL without mocking. Run this manually when needed.
*
* Usage:
* node integration-test.js [platform] [arch]
*
* Example:
* node integration-test.js linux x64
* node integration-test.js win x64
* node integration-test.js osx x64
*/

"use strict";

const axios = require('axios');
const path = require('path');

const PLATFORM_MAPPING = {
"darwin": "osx",
"linux": "linux",
"win32": "win"
};

const ARCH_MAPPING = {
"ia32": "x86",
"x64": "x64",
};

async function testUrl(url, description) {
console.log(`\n📡 Testing ${description}...`);
console.log(` URL: ${url}`);

try {
const response = await axios({
url: url,
method: 'HEAD', // Just check if the file exists, don't download
timeout: 10000
});

console.log(` ✅ SUCCESS - Status: ${response.status}`);
if (response.headers['content-length']) {
const sizeMB = (parseInt(response.headers['content-length']) / 1024 / 1024).toFixed(2);
console.log(` 📦 Size: ${sizeMB} MB`);
}
return true;
} catch (error) {
if (error.response) {
console.log(` ❌ FAILED - Status: ${error.response.status} ${error.response.statusText}`);
} else if (error.code === 'ECONNABORTED') {
console.log(` ❌ FAILED - Timeout`);
} else {
console.log(` ❌ FAILED - ${error.message}`);
}
return false;
}
}

async function runIntegrationTests() {
console.log('🧪 CMF CLI Repository Integration Tests\n');
console.log('=========================================\n');

// Get platform and arch from args or use current system
const platform = process.argv[2] || process.platform;
const arch = process.argv[3] || process.arch;

// Map to RID format
const mappedPlatform = PLATFORM_MAPPING[platform] || platform;
const mappedArch = ARCH_MAPPING[arch] || arch;

console.log(`Testing for platform: ${platform} (${mappedPlatform})`);
console.log(`Testing for architecture: ${arch} (${mappedArch})`);

// Use a known version for testing (update this to a real version)
const version = '5.7.0';

// Define all URLs to test
const urls = [
{
url: `https://github.com/criticalmanufacturing/cli/releases/download/${version}/cmf-cli.${mappedPlatform}-${mappedArch}.zip`,
description: 'GitHub Releases (Primary)'
},
{
url: `https://criticalmanufacturing.io/repository/tools/cmf-cli.${mappedPlatform}-${mappedArch}.zip`,
description: 'Critical Manufacturing IO (Fallback 1)'
},
{
url: `https://repository.criticalmanufacturing.com.cn/repository/tools/cmf-cli.${mappedPlatform}-${mappedArch}.zip`,
description: 'Critical Manufacturing CN (Fallback 2)'
}
];

// Test each URL
const results = [];
for (const { url, description } of urls) {
const success = await testUrl(url, description);
results.push({ url, description, success });
}

// Summary
console.log('\n\n📊 Test Summary');
console.log('===============\n');

const successCount = results.filter(r => r.success).length;
const totalCount = results.length;

results.forEach(({ description, success }) => {
console.log(`${success ? '✅' : '❌'} ${description}`);
});

console.log(`\n${successCount}/${totalCount} repositories accessible`);

if (successCount === 0) {
console.log('\n⚠️ WARNING: No repositories are accessible!');
console.log('This could indicate:');
console.log(' - Network connectivity issues');
console.log(' - Version does not exist yet');
console.log(' - Artifacts not published');
process.exit(1);
} else if (successCount < totalCount) {
console.log('\n⚠️ Some repositories are not accessible.');
console.log('Users in regions where accessible repos fail may have installation issues.');
process.exit(0);
} else {
console.log('\n✨ All repositories are accessible!');
process.exit(0);
}
}

// Run the tests
runIntegrationTests().catch(error => {
console.error('\n❌ Integration test failed:', error.message);
process.exit(1);
});
22 changes: 22 additions & 0 deletions npm/jest.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
module.exports = {
testEnvironment: 'node',
testMatch: ['**/*.test.js'],
collectCoverageFrom: [
'postinstall.js',
'utils.js',
'!**/node_modules/**',
'!**/dist/**'
],
coverageDirectory: 'coverage',
coverageReporters: ['text', 'lcov', 'html'],
verbose: true,
testTimeout: 10000,
modulePathIgnorePatterns: [
'<rootDir>/dist/',
'<rootDir>/node_modules/'
],
testPathIgnorePatterns: [
'/node_modules/',
'/dist/'
]
};
Loading
Loading