|
1 | 1 | import { readFileSync } from 'fs'; |
2 | 2 | import { fileURLToPath } from 'url'; |
3 | 3 | import { dirname, join } from 'path'; |
| 4 | +import https from 'https'; |
4 | 5 |
|
5 | 6 | const __filename = fileURLToPath(import.meta.url); |
6 | 7 | const __dirname = dirname(__filename); |
@@ -357,6 +358,138 @@ describe('Sstart Download Process Tests', () => { |
357 | 358 | }); |
358 | 359 | }); |
359 | 360 |
|
| 361 | + describe('Actual Download Validation', () => { |
| 362 | + let actionYmlContent; |
| 363 | + let renovateConfig; |
| 364 | + |
| 365 | + beforeAll(() => { |
| 366 | + // Read actual files |
| 367 | + const projectRoot = join(__dirname, '..'); |
| 368 | + actionYmlContent = readFileSync(join(projectRoot, 'action.yml'), 'utf8'); |
| 369 | + renovateConfig = JSON.parse( |
| 370 | + readFileSync(join(projectRoot, 'renovate.json'), 'utf8') |
| 371 | + ); |
| 372 | + }); |
| 373 | + |
| 374 | + // Helper function to check if a URL exists (HEAD request) |
| 375 | + function checkUrlExists(url) { |
| 376 | + return new Promise((resolve, reject) => { |
| 377 | + const urlObj = new URL(url); |
| 378 | + const options = { |
| 379 | + hostname: urlObj.hostname, |
| 380 | + port: urlObj.port || 443, |
| 381 | + path: urlObj.pathname + urlObj.search, |
| 382 | + method: 'HEAD', |
| 383 | + timeout: 5000, // 5 second timeout |
| 384 | + }; |
| 385 | + |
| 386 | + const req = https.request(options, (res) => { |
| 387 | + // Follow redirects |
| 388 | + if (res.statusCode === 301 || res.statusCode === 302 || res.statusCode === 307 || res.statusCode === 308) { |
| 389 | + const location = res.headers.location; |
| 390 | + if (location) { |
| 391 | + return checkUrlExists(location).then(resolve).catch(reject); |
| 392 | + } |
| 393 | + } |
| 394 | + resolve(res.statusCode === 200); |
| 395 | + res.destroy(); // Close the response |
| 396 | + }); |
| 397 | + |
| 398 | + req.on('error', (error) => { |
| 399 | + reject(error); |
| 400 | + }); |
| 401 | + |
| 402 | + req.on('timeout', () => { |
| 403 | + req.destroy(); |
| 404 | + reject(new Error('Request timeout')); |
| 405 | + }); |
| 406 | + |
| 407 | + req.end(); |
| 408 | + }); |
| 409 | + } |
| 410 | + |
| 411 | + it('should validate that current version in action.yml can be downloaded', async () => { |
| 412 | + // Extract current version from action.yml using Renovate's regex pattern |
| 413 | + const manager = renovateConfig.regexManagers[0]; |
| 414 | + const pattern = new RegExp(manager.matchStrings[0]); |
| 415 | + const match = actionYmlContent.match(pattern); |
| 416 | + |
| 417 | + expect(match).not.toBeNull(); |
| 418 | + const currentVersion = match.groups.currentValue; |
| 419 | + |
| 420 | + // Build download URL for current platform |
| 421 | + const url = buildDownloadUrl(currentVersion, process.platform, process.arch); |
| 422 | + |
| 423 | + // Verify the URL actually exists (downloadable) |
| 424 | + const exists = await checkUrlExists(url); |
| 425 | + expect(exists).toBe(true); |
| 426 | + }, 10000); // 10 second timeout for network request |
| 427 | + |
| 428 | + it('should validate download URL for all supported platforms with current version', async () => { |
| 429 | + // Extract current version from action.yml |
| 430 | + const manager = renovateConfig.regexManagers[0]; |
| 431 | + const pattern = new RegExp(manager.matchStrings[0]); |
| 432 | + const match = actionYmlContent.match(pattern); |
| 433 | + const currentVersion = match.groups.currentValue; |
| 434 | + |
| 435 | + const platforms = [ |
| 436 | + { platform: 'darwin', arch: 'x64' }, |
| 437 | + { platform: 'darwin', arch: 'arm64' }, |
| 438 | + { platform: 'linux', arch: 'x64' }, |
| 439 | + { platform: 'linux', arch: 'arm64' }, |
| 440 | + ]; |
| 441 | + |
| 442 | + // Test each platform (but skip if not the current platform to avoid unnecessary network calls) |
| 443 | + for (const { platform, arch } of platforms) { |
| 444 | + const url = buildDownloadUrl(currentVersion, platform, arch); |
| 445 | + |
| 446 | + // Only test the current platform, others are just URL validation |
| 447 | + if (platform === process.platform && arch === process.arch) { |
| 448 | + const exists = await checkUrlExists(url); |
| 449 | + expect(exists).toBe(true); |
| 450 | + } else { |
| 451 | + // For other platforms, just verify URL format is correct |
| 452 | + expect(url).toContain(`/v${currentVersion}/`); |
| 453 | + expect(url).toContain('github.com/dirathea/sstart/releases/download'); |
| 454 | + } |
| 455 | + } |
| 456 | + }, 15000); |
| 457 | + |
| 458 | + it('should validate that Renovate-updated versions are actually downloadable', async () => { |
| 459 | + // This test simulates what happens when Renovate updates the version |
| 460 | + // It validates that the new version can actually be downloaded |
| 461 | + |
| 462 | + // Get current version from action.yml |
| 463 | + const manager = renovateConfig.regexManagers[0]; |
| 464 | + const pattern = new RegExp(manager.matchStrings[0]); |
| 465 | + const match = actionYmlContent.match(pattern); |
| 466 | + const currentVersion = match.groups.currentValue; |
| 467 | + |
| 468 | + // Build URL with current version and verify it's downloadable |
| 469 | + const currentUrl = buildDownloadUrl(currentVersion, process.platform, process.arch); |
| 470 | + const currentExists = await checkUrlExists(currentUrl); |
| 471 | + expect(currentExists).toBe(true); |
| 472 | + |
| 473 | + // Note: We can't test future versions that don't exist yet, |
| 474 | + // but this ensures the current version works and validates the download mechanism |
| 475 | + }, 10000); |
| 476 | + |
| 477 | + it('should fail validation if version does not exist in GitHub releases', async () => { |
| 478 | + // Test with a version that definitely doesn't exist |
| 479 | + const nonExistentVersion = '999.999.999'; |
| 480 | + const url = buildDownloadUrl(nonExistentVersion, process.platform, process.arch); |
| 481 | + |
| 482 | + // This should fail (return false or throw) |
| 483 | + try { |
| 484 | + const exists = await checkUrlExists(url); |
| 485 | + expect(exists).toBe(false); |
| 486 | + } catch (error) { |
| 487 | + // It's also acceptable if it throws an error |
| 488 | + expect(error).toBeDefined(); |
| 489 | + } |
| 490 | + }, 10000); |
| 491 | + }); |
| 492 | + |
360 | 493 | afterAll(() => { |
361 | 494 | // Restore original values if needed |
362 | 495 | Object.defineProperty(process, 'platform', { value: originalPlatform, writable: true }); |
|
0 commit comments