#!/usr/bin/env npx tsx /* Tests for the sync-back.ts script */ import * as assert from "node:assert/strict"; import * as fs from "node:fs"; import * as os from "node:os"; import * as path from "node:path"; import { afterEach, beforeEach, describe, it } from "node:test"; import { scanGeneratedWorkflows, updateSyncTs, updateTemplateFiles, } from "./sync-back"; let testDir: string; let workflowDir: string; let checksDir: string; let syncTsPath: string; beforeEach(() => { /** Set up temporary directories and files for testing */ testDir = fs.mkdtempSync(path.join(os.tmpdir(), "sync-back-test-")); workflowDir = path.join(testDir, ".github", "workflows"); checksDir = path.join(testDir, "pr-checks", "checks"); fs.mkdirSync(workflowDir, { recursive: true }); fs.mkdirSync(checksDir, { recursive: true }); // Create sync.ts file path syncTsPath = path.join(testDir, "pr-checks", "sync.ts"); }); afterEach(() => { /** Clean up temporary directories */ fs.rmSync(testDir, { recursive: true, force: true }); }); describe("scanGeneratedWorkflows", async () => { await it("basic workflow scanning", () => { /** Test basic workflow scanning functionality */ const workflowContent = ` name: Test Workflow jobs: test: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: actions/setup-node@v5 - uses: actions/setup-go@v6 `; fs.writeFileSync(path.join(workflowDir, "__test.yml"), workflowContent); const result = scanGeneratedWorkflows(workflowDir); assert.equal(result["actions/checkout"], "v4"); assert.equal(result["actions/setup-node"], "v5"); assert.equal(result["actions/setup-go"], "v6"); }); await it("scanning workflows with version comments", () => { /** Test scanning workflows with version comments */ const workflowContent = ` name: Test Workflow jobs: test: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: ruby/setup-ruby@44511735964dcb71245e7e55f72539531f7bc0eb # v1.257.0 - uses: actions/setup-python@v6 # Latest Python `; fs.writeFileSync(path.join(workflowDir, "__test.yml"), workflowContent); const result = scanGeneratedWorkflows(workflowDir); assert.equal(result["actions/checkout"], "v4"); assert.equal( result["ruby/setup-ruby"], "44511735964dcb71245e7e55f72539531f7bc0eb # v1.257.0", ); assert.equal(result["actions/setup-python"], "v6 # Latest Python"); }); await it("ignores local actions", () => { /** Test that local actions (starting with ./) are ignored */ const workflowContent = ` name: Test Workflow jobs: test: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: ./.github/actions/local-action - uses: ./another-local-action@v1 `; fs.writeFileSync(path.join(workflowDir, "__test.yml"), workflowContent); const result = scanGeneratedWorkflows(workflowDir); assert.equal(result["actions/checkout"], "v4"); assert.equal("./.github/actions/local-action" in result, false); assert.equal("./another-local-action" in result, false); }); }); describe("updateSyncTs", async () => { await it("updates sync.ts file", () => { /** Test updating sync.ts file */ const syncTsContent = ` const steps = [ { uses: "actions/setup-node@v4", with: { "node-version": "16" }, }, { uses: "actions/setup-go@v5", with: { "go-version": "1.19" }, }, ]; `; fs.writeFileSync(syncTsPath, syncTsContent); const actionVersions = { "actions/setup-node": "v5", "actions/setup-go": "v6", }; const result = updateSyncTs(syncTsPath, actionVersions); assert.equal(result, true); const updatedContent = fs.readFileSync(syncTsPath, "utf8"); assert.ok(updatedContent.includes('uses: "actions/setup-node@v5"')); assert.ok(updatedContent.includes('uses: "actions/setup-go@v6"')); }); await it("strips comments from versions", () => { /** Test updating sync.ts file when versions have comments */ const syncTsContent = ` const steps = [ { uses: "actions/setup-node@v4", with: { "node-version": "16" }, }, ]; `; fs.writeFileSync(syncTsPath, syncTsContent); const actionVersions = { "actions/setup-node": "v5 # Latest version", }; const result = updateSyncTs(syncTsPath, actionVersions); assert.equal(result, true); const updatedContent = fs.readFileSync(syncTsPath, "utf8"); // sync.ts should get the version without comment assert.ok(updatedContent.includes('uses: "actions/setup-node@v5"')); assert.ok(!updatedContent.includes("# Latest version")); }); await it("returns false when no changes are needed", () => { /** Test that updateSyncTs returns false when no changes are needed */ const syncTsContent = ` const steps = [ { uses: "actions/setup-node@v5", with: { "node-version": "16" }, }, ]; `; fs.writeFileSync(syncTsPath, syncTsContent); const actionVersions = { "actions/setup-node": "v5", }; const result = updateSyncTs(syncTsPath, actionVersions); assert.equal(result, false); }); }); describe("updateTemplateFiles", async () => { await it("updates template files", () => { /** Test updating template files */ const templateContent = ` name: Test Template steps: - uses: actions/checkout@v3 - uses: actions/setup-node@v4 with: node-version: 16 `; const templatePath = path.join(checksDir, "test.yml"); fs.writeFileSync(templatePath, templateContent); const actionVersions = { "actions/checkout": "v4", "actions/setup-node": "v5 # Latest", }; const result = updateTemplateFiles(checksDir, actionVersions); assert.equal(result.length, 1); assert.ok(result.includes(templatePath)); const updatedContent = fs.readFileSync(templatePath, "utf8"); assert.ok(updatedContent.includes("uses: actions/checkout@v4")); assert.ok(updatedContent.includes("uses: actions/setup-node@v5 # Latest")); }); await it("preserves version comments", () => { /** Test that updating template files preserves version comments */ const templateContent = ` name: Test Template steps: - uses: ruby/setup-ruby@44511735964dcb71245e7e55f72539531f7bc0eb # v1.256.0 `; const templatePath = path.join(checksDir, "test.yml"); fs.writeFileSync(templatePath, templateContent); const actionVersions = { "ruby/setup-ruby": "55511735964dcb71245e7e55f72539531f7bc0eb # v1.257.0", }; const result = updateTemplateFiles(checksDir, actionVersions); assert.equal(result.length, 1); const updatedContent = fs.readFileSync(templatePath, "utf8"); assert.ok( updatedContent.includes( "uses: ruby/setup-ruby@55511735964dcb71245e7e55f72539531f7bc0eb # v1.257.0", ), ); }); });