Skip to content
Merged
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
9 changes: 9 additions & 0 deletions docs/DEVELOPMENT.md
Original file line number Diff line number Diff line change
Expand Up @@ -171,6 +171,15 @@ format(input) === expected
format(expected) === expected
```

The same checks run against synthetic CRLF variants of every fixture pair. This protects line-ending stability without storing CRLF fixture files in Git:

```text
format(CRLF input) === CRLF expected
format(CRLF expected) === CRLF expected
```

The runner rejects unpaired or unsupported corpus SQL files so ZIP overlay workflows cannot leave stale fixtures silently ignored. Corpus SQL files must be named as `*.input.sql` or `*.expected.sql` pairs.

When adding a fixture:

- use sanitized SQL that can safely live in the public repository
Expand Down
9 changes: 8 additions & 1 deletion test/corpus/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,16 @@
Corpus fixtures are representative SQL samples that document current formatter behavior.
Each fixture has an `.input.sql` file and a matching `.expected.sql` file.

The corpus runner checks two things for every fixture pair:
The corpus runner checks four things for every fixture pair:

1. formatting the input produces the expected output
2. formatting the expected output leaves it unchanged
3. formatting a synthetic CRLF version of the input produces the CRLF expected output
4. formatting a synthetic CRLF version of the expected output leaves it unchanged

The runner also fails when corpus SQL files are not named as fixture pairs. Use only:

- `*.input.sql`
- `*.expected.sql`

Use sanitized, public-safe SQL only. These fixtures are regression tests, not product roadmap notes.
80 changes: 43 additions & 37 deletions test/runFormatterCorpusTests.js
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,10 @@ function toCrLf(text) {
return text.replace(/\r\n|\r|\n/gu, '\n').replace(/\n/gu, '\r\n');
}

function readFixture(fixturePath) {
return fs.readFileSync(fixturePath, 'utf8');
}

function findCorpusFixturePairingIssues(directory) {
return listSqlFixtureFiles(directory).flatMap((fixturePath) => {
const relativeFixturePath = getRelativeFixturePath(fixturePath);
Expand All @@ -95,51 +99,53 @@ function findCorpusFixturePairingIssues(directory) {
});
}

const inputFixtures = listInputFixtures(corpusRoot);
function listFixturePairs() {
return listInputFixtures(corpusRoot).map((inputPath) => ({
dialect: getDialectForFixture(inputPath),
expectedPath: getExpectedFixturePath(inputPath),
inputPath,
name: getFixtureName(inputPath),
}));
}

function assertCorpusFormatting({ dialect, expected, fixtureName, input, variant }) {
const result = formatSql(input, dialect, defaultOptions);
const idempotentResult = formatSql(expected, dialect, defaultOptions);
const testName = variant ? `${fixtureName} (${variant})` : fixtureName;

assert.equal(result.text, expected, `${testName} did not format to the expected output.`);
assert.equal(idempotentResult.text, expected, `${testName} expected output is not idempotent.`);
}

if (inputFixtures.length === 0) {
const fixturePairs = listFixturePairs();

if (fixturePairs.length === 0) {
throw new Error(`No formatter corpus fixtures found under ${corpusRoot}.`);
}

runTest('formatter corpus fixtures are paired', () => {
assert.deepEqual(findCorpusFixturePairingIssues(corpusRoot), []);
});

for (const inputPath of inputFixtures) {
const expectedPath = getExpectedFixturePath(inputPath);
const fixtureName = getFixtureName(inputPath);

runTest(`formatter corpus: ${fixtureName}`, () => {
const input = fs.readFileSync(inputPath, 'utf8');
const expected = fs.readFileSync(expectedPath, 'utf8');
const dialect = getDialectForFixture(inputPath);
const result = formatSql(input, dialect, defaultOptions);
const idempotentResult = formatSql(expected, dialect, defaultOptions);

assert.equal(result.text, expected, `${fixtureName} did not format to the expected output.`);
assert.equal(
idempotentResult.text,
expected,
`${fixtureName} expected output is not idempotent.`,
);
for (const fixture of fixturePairs) {
runTest(`formatter corpus: ${fixture.name}`, () => {
assertCorpusFormatting({
dialect: fixture.dialect,
expected: readFixture(fixture.expectedPath),
fixtureName: fixture.name,
input: readFixture(fixture.inputPath),
});
});
}

runTest('formatter corpus preserves CRLF line endings', () => {
for (const inputPath of inputFixtures) {
const expectedPath = getExpectedFixturePath(inputPath);
const fixtureName = getFixtureName(inputPath);
const input = toCrLf(fs.readFileSync(inputPath, 'utf8'));
const expected = toCrLf(fs.readFileSync(expectedPath, 'utf8'));
const dialect = getDialectForFixture(inputPath);
const result = formatSql(input, dialect, defaultOptions);
const idempotentResult = formatSql(expected, dialect, defaultOptions);

assert.equal(result.text, expected, `${fixtureName} did not preserve CRLF output.`);
assert.equal(
idempotentResult.text,
expected,
`${fixtureName} expected CRLF output is not idempotent.`,
);
}
});
for (const fixture of fixturePairs) {
runTest(`formatter corpus CRLF: ${fixture.name}`, () => {
assertCorpusFormatting({
dialect: fixture.dialect,
expected: toCrLf(readFixture(fixture.expectedPath)),
fixtureName: fixture.name,
input: toCrLf(readFixture(fixture.inputPath)),
variant: 'CRLF',
});
});
}
Loading