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
Original file line number Diff line number Diff line change
Expand Up @@ -316,4 +316,44 @@ describe('copyConfigKeyEntry', () => {
await expect(fileExists(joinPath(outDir, 'tools.json'))).resolves.toBe(true)
})
})

describe('value guard', () => {
test('throws when value is an empty string', async () => {
await inTemporaryDirectory(async (tmpDir) => {
const outDir = joinPath(tmpDir, 'out')
await mkdir(outDir)
const context = makeContext({assets: ''})
const promise = copyConfigKeyEntry({key: 'assets', baseDir: tmpDir, outputDir: outDir, context})
await expect(promise).rejects.toThrow(AbortError)
await expect(promise).rejects.toThrow(`'assets' can't be empty.`)
})
})

test('throws when value is whitespace-only', async () => {
await inTemporaryDirectory(async (tmpDir) => {
const outDir = joinPath(tmpDir, 'out')
await mkdir(outDir)
const context = makeContext({assets: ' '})
const promise = copyConfigKeyEntry({key: 'assets', baseDir: tmpDir, outputDir: outDir, context})
await expect(promise).rejects.toThrow(AbortError)
await expect(promise).rejects.toThrow(`'assets' can't be empty.`)
})
})

test('throws with the field name only, not the full configKey, when the key is nested', async () => {
await inTemporaryDirectory(async (tmpDir) => {
const outDir = joinPath(tmpDir, 'out')
await mkdir(outDir)
const context = makeContext({extension_points: [{assets: ''}]})
const promise = copyConfigKeyEntry({
key: 'extension_points[].assets',
baseDir: tmpDir,
outputDir: outDir,
context,
})
await expect(promise).rejects.toThrow(AbortError)
await expect(promise).rejects.toThrow(`'assets' can't be empty.`)
})
})
})
})
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,13 @@ export async function copyConfigKeyEntry(config: {
// should only be copied once; the pathMap entry is reused for all references.
const uniquePaths = [...new Set(paths)]

const fieldName = key.split('.').pop()?.replace(/\[\]$/, '') ?? key
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should do the check earlier when we get the value so we don't have to do the extra unnecessary work of setting up the paths etc.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To keep things simple, let's just add the check here to scope this validation to the single string value block - this will fulfill our current use case.

In the case where an asset key can have an array, I think we should review what the expected outcome should be for arrays before forcing an error if only one of the paths are empty string - and it's out of scope for us ATM so we can defer

for (const sourcePath of uniquePaths) {
if (sourcePath.trim() === '') {
throw new AbortError(`'${fieldName}' can't be empty.`)
}
}

// Process sequentially to avoid filesystem race conditions on shared output paths.
const pathMap = new Map<string, string | string[]>()
let filesCopied = 0
Expand Down
Loading