Compare commits

..
Author SHA1 Message Date
TabishB 4a723999c4 fix: prefer native realpath for canonical paths 2026-04-14 17:24:04 +10:00
TabishB 8945ed21ca fix: canonicalize workflow artifact paths 2026-04-14 17:01:41 +10:00
2 changed files with 13 additions and 17 deletions
+5 -6
View File
@@ -3,14 +3,11 @@ import { promises as fs } from 'fs';
import path from 'path';
import os from 'os';
import { runCLI } from '../helpers/run-cli.js';
import { FileSystemUtils } from '../../src/utils/file-system.js';
describe('artifact-workflow CLI commands', () => {
let tempDir: string;
let changesDir: string;
const canonical = (targetPath: string): string => FileSystemUtils.canonicalizeExistingPath(targetPath);
beforeEach(async () => {
tempDir = await fs.mkdtemp(path.join(os.tmpdir(), 'openspec-artifact-workflow-'));
changesDir = path.join(tempDir, 'openspec', 'changes');
@@ -414,8 +411,10 @@ describe('artifact-workflow CLI commands', () => {
expect(result.stderr).toBe('');
const json = JSON.parse(result.stdout);
const expectedProposalPath = canonical(path.join(changesDir, 'json-apply', 'proposal.md'));
const expectedSpecPath = canonical(path.join(changesDir, 'json-apply', 'specs', 'test-spec.md'));
const expectedProposalPath = await fs.realpath(path.join(changesDir, 'json-apply', 'proposal.md'));
const expectedSpecPath = await fs.realpath(
path.join(changesDir, 'json-apply', 'specs', 'test-spec.md')
);
expect(json.changeName).toBe('json-apply');
expect(json.schemaName).toBe('spec-driven');
expect(json.state).toBe('ready');
@@ -473,7 +472,7 @@ apply:
);
expect(applyResult.exitCode).toBe(0);
const applyJson = JSON.parse(applyResult.stdout);
const resolvedSpecPath = canonical(specPath);
const resolvedSpecPath = await fs.realpath(specPath);
expect(applyJson.state).toBe('ready');
expect(applyJson.missingArtifacts).toBeUndefined();
expect(applyJson.contextFiles).toEqual({
+8 -11
View File
@@ -2,14 +2,11 @@ import { describe, it, expect, beforeEach, afterEach } from 'vitest';
import * as fs from 'node:fs';
import * as path from 'node:path';
import * as os from 'node:os';
import { FileSystemUtils } from '../../../src/utils/file-system.js';
import { artifactOutputExists, resolveArtifactOutputs } from '../../../src/core/artifact-graph/outputs.js';
describe('artifact-graph/outputs', () => {
let tempDir: string;
const canonical = (targetPath: string): string => FileSystemUtils.canonicalizeExistingPath(targetPath);
beforeEach(() => {
tempDir = path.join(os.tmpdir(), `openspec-outputs-test-${Date.now()}`);
fs.mkdirSync(tempDir, { recursive: true });
@@ -23,7 +20,7 @@ describe('artifact-graph/outputs', () => {
const filePath = path.join(tempDir, 'proposal.md');
fs.writeFileSync(filePath, 'content');
expect(resolveArtifactOutputs(tempDir, 'proposal.md')).toEqual([canonical(filePath)]);
expect(resolveArtifactOutputs(tempDir, 'proposal.md')).toEqual([fs.realpathSync(filePath)]);
expect(artifactOutputExists(tempDir, 'proposal.md')).toBe(true);
});
@@ -41,7 +38,7 @@ describe('artifact-graph/outputs', () => {
fs.mkdirSync(nestedDir, { recursive: true });
fs.writeFileSync(filePath, 'content');
expect(resolveArtifactOutputs(tempDir, 'specs/*/spec.md')).toEqual([canonical(filePath)]);
expect(resolveArtifactOutputs(tempDir, 'specs/*/spec.md')).toEqual([fs.realpathSync(filePath)]);
expect(artifactOutputExists(tempDir, 'specs/*/spec.md')).toBe(true);
});
@@ -53,7 +50,7 @@ describe('artifact-graph/outputs', () => {
fs.writeFileSync(matching, 'content');
fs.writeFileSync(nonMatching, 'content');
expect(resolveArtifactOutputs(tempDir, 'specs/foo*.md')).toEqual([canonical(matching)]);
expect(resolveArtifactOutputs(tempDir, 'specs/foo*.md')).toEqual([fs.realpathSync(matching)]);
});
it('supports question-mark glob patterns', () => {
@@ -63,7 +60,7 @@ describe('artifact-graph/outputs', () => {
fs.writeFileSync(matching, 'content');
fs.writeFileSync(path.join(specsDir, 'a10.md'), 'content');
expect(resolveArtifactOutputs(tempDir, 'specs/a?.md')).toEqual([canonical(matching)]);
expect(resolveArtifactOutputs(tempDir, 'specs/a?.md')).toEqual([fs.realpathSync(matching)]);
});
it('supports character class glob patterns', () => {
@@ -76,8 +73,8 @@ describe('artifact-graph/outputs', () => {
fs.writeFileSync(path.join(specsDir, 'c.md'), 'content');
expect(resolveArtifactOutputs(tempDir, 'specs/[ab].md')).toEqual([
canonical(aPath),
canonical(bPath),
fs.realpathSync(aPath),
fs.realpathSync(bPath),
]);
});
@@ -95,10 +92,10 @@ describe('artifact-graph/outputs', () => {
fs.symlinkSync(realChangeDir, aliasChangeDir, process.platform === 'win32' ? 'junction' : 'dir');
expect(resolveArtifactOutputs(aliasChangeDir, 'proposal.md')).toEqual([
canonical(proposalPath),
fs.realpathSync(proposalPath),
]);
expect(resolveArtifactOutputs(aliasChangeDir, 'specs/*/spec.md')).toEqual([
canonical(specPath),
fs.realpathSync(specPath),
]);
});