Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
61 commits
Select commit Hold shift + click to select a range
10d70bd
chore: bump vite in @dbml/parse and add vite to workspace root
huydo862003 Nov 17, 2025
6cb5ef0
chore: add coverage npm script and ignore coverage/
huydo862003 Nov 17, 2025
7266d01
chore: remove vitest in @dbml/parse
huydo862003 Nov 17, 2025
2b32950
chore: setup coverage
huydo862003 Nov 17, 2025
34d2941
chore: add coverage test to pr template
huydo862003 Nov 17, 2025
11ff080
chore: add coverage to all packages and setup coverage report workflow
huydo862003 Nov 17, 2025
41aa724
chore: remove upload artifact from coverage workflow
huydo862003 Nov 17, 2025
c0cd2df
test(dbml-parse): move example-based tests to a dedicated folder
huydo862003 Nov 17, 2025
103c3de
test(dbml-parse): move tests to __tests__ for consistency
huydo862003 Nov 17, 2025
5817665
fix(dbml-connector): properly close mssql client
huydo862003 Nov 17, 2025
1bc3a71
test: refactor jest setup and migrate test to typescript
huydo862003 Nov 17, 2025
b1368db
chore(dbml-core): add tsparser to lint
huydo862003 Nov 17, 2025
857ce12
test: add lexer property-based tests
huydo862003 Nov 17, 2025
00be264
test(dbml-core): prune some source files from coverage
huydo862003 Nov 17, 2025
3e689ee
chore: fix coverage workflow
huydo862003 Nov 17, 2025
b12694e
test(dbml-parse): fix wrong interpreter check test
huydo862003 Nov 17, 2025
9b61f66
test(dbml-parse): extract out arbitraries
huydo862003 Nov 17, 2025
89fa956
chore: merge coverage and unit test workflow
huydo862003 Nov 19, 2025
867c9b0
test(dbml-parse): fix token arbitraries
huydo862003 Nov 19, 2025
343146a
chore: remove wrong comment in collect coverage report
huydo862003 Nov 19, 2025
6d7e478
test(dbml-parse): basic parser property tests
huydo862003 Nov 19, 2025
267832d
fix(dbml-parse): missing cases in markInvalid
huydo862003 Nov 19, 2025
781a2b1
fix(dbml-parse): more precisely check for failed variable extraction …
huydo862003 Nov 19, 2025
ff50c43
fix(dbml-parse): more precisely check for failed variable extraction …
huydo862003 Nov 19, 2025
a1f7f0f
fix(dbml-parse): allow nested call expression and array expression in…
huydo862003 Nov 19, 2025
00a5228
test(dbml-parse): debug parser props tests
huydo862003 Nov 19, 2025
77c9086
fix(dbml-parse): include op in partial injection
huydo862003 Nov 19, 2025
441df11
fix(dbml-parse): remove redundat assignment of e.partialNode
huydo862003 Nov 19, 2025
547fa41
fix(dbml-parse): use isInvalidToken instead of checking token.isInvalid
huydo862003 Nov 19, 2025
c1d6eb5
fix(dbml-parse): wrong collection of invalid tokens in parser
huydo862003 Nov 19, 2025
b744449
fix(dbml-core): wrongly placed oracle test cases
huydo862003 Nov 19, 2025
2ee97b7
fix(dbml-parse): properly interpret complex types that mixes multiple…
huydo862003 Nov 19, 2025
fafbf6b
test(dbml-parse): fix flattenTokens
huydo862003 Nov 19, 2025
9b7c33e
fix(dbml-parse): do not mark EOF as invalid
huydo862003 Nov 19, 2025
9c8fbc0
test(dbml-parse): add one roundtrip prop for parser
huydo862003 Nov 19, 2025
5808d3a
test(dbml-parse): use dbmlSchemaArbitrary instead of smallSchemaArbit…
huydo862003 Nov 19, 2025
1a5d2be
test(dbml-parse): add basic services tests
huydo862003 Nov 19, 2025
f7d930d
test(dbml-parse): refactor tests
huydo862003 Nov 20, 2025
3fd863a
fix: remove io-ts
huydo862003 Nov 20, 2025
c5fe561
test(dbml-parse): remove useless service util tests
huydo862003 Nov 20, 2025
522d4ee
chore: set timeout for github action workflow
huydo862003 Nov 20, 2025
a1ad791
chore: lower timeout to 10 min
huydo862003 Nov 20, 2025
a3a4f8d
test(dbml-parse): remove useless service fuzz tests
huydo862003 Nov 20, 2025
50e137e
test(dbml-parse): add interpreter fuzzer tests and improve all exampl…
huydo862003 Nov 20, 2025
554ce39
fix(dbml-parse): add quote when suggesting empty strings
huydo862003 Nov 20, 2025
b0449a5
test(dbml-parse): add service props tests
huydo862003 Nov 20, 2025
d75a941
test(dbml-parse): fix type errors
huydo862003 Nov 20, 2025
4591508
test: reorganize tests
huydo862003 Jan 8, 2026
54cf515
test: improve arbitraries
huydo862003 Jan 9, 2026
85ad1a2
fix: make the as keyword case-insensitive & improve test arbitrary
huydo862003 Jan 9, 2026
a47ed58
chore: increase test timeout to 20 minutes
huydo862003 Jan 9, 2026
cd7839c
test: split example and snapshot
huydo862003 Jan 9, 2026
8ea6bfa
test: add more service test
huydo862003 Jan 9, 2026
9dd7200
fix: missing handling of partial injection ops
huydo862003 Jan 9, 2026
30698e2
fix: type errors
huydo862003 Jan 9, 2026
fa20907
fix: move compiler tests to __test__
huydo862003 Jan 9, 2026
78ab6c0
fix: avoid connection leaks in pg and mysql connectors
huydo862003 Jan 9, 2026
c565f5c
test: add complex data type dbml definition
huydo862003 Jan 9, 2026
7949b95
test: improve example tests
huydo862003 Jan 12, 2026
66cbca7
chore: reduce lint timeout
huydo862003 Jan 12, 2026
69cf679
chore: more precise typings for @dbml/core tests
huydo862003 Jan 12, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
5 changes: 3 additions & 2 deletions .github/PULL_REQUEST_TEMPLATE.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,8 @@
Please check directly on the box once each of these are done

- [ ] Documentation (if necessary)
- [ ] Lint checks
- [ ] Tests (integration test/unit test)
- [ ] Lint Checks Passed
- [ ] Unit Tests Passed
- [ ] Coverage Tests Passed
- [ ] Integration Tests Passed
- [ ] Code Review
1 change: 1 addition & 0 deletions .github/workflows/lint.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ on:
jobs:
lint:
runs-on: ubuntu-22.04
timeout-minutes: 5
strategy:
matrix:
node-version: [22.x]
Expand Down
224 changes: 224 additions & 0 deletions .github/workflows/scripts/collect-coverage.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,224 @@
#!/usr/bin/env node

const fs = require('fs');
const path = require('path');

const PACKAGES = ['dbml-cli', 'dbml-connector', 'dbml-core', 'dbml-parse'];
const COVERAGE_THRESHOLD = 80; // Warning threshold for coverage

function readCoverageSummary(packageName) {
const summaryPath = path.join(__dirname, '../../../packages', packageName, 'coverage', 'coverage-summary.json');

if (!fs.existsSync(summaryPath)) {
console.error(`Warning: No coverage summary found for ${packageName}`);
return null;
}

const summary = JSON.parse(fs.readFileSync(summaryPath, 'utf8'));
return summary;
}

function formatPercentage(value) {
return value.toFixed(2) + '%';
}

function getIcon(percentage) {
if (percentage >= 80) return '✅';
if (percentage >= 50) return '⚠️';
return '❌';
}

function getFilesWithLowCoverage(coverageData, packageName, threshold = 80) {
const files = [];

for (const [filePath, coverage] of Object.entries(coverageData)) {
if (filePath === 'total') continue;

// Check if any metric is below threshold
const linePct = coverage.lines.pct;
const stmtPct = coverage.statements.pct;
const funcPct = coverage.functions.pct;
const branchPct = coverage.branches.pct;

if (linePct < threshold || stmtPct < threshold || funcPct < threshold || branchPct < threshold) {
// Get relative path from package directory
const packageDir = path.join('packages', packageName);
const relativePath = path.relative(path.join(process.cwd(), packageDir), filePath);

files.push({
path: relativePath,
lines: linePct,
statements: stmtPct,
functions: funcPct,
branches: branchPct,
});
}
}

// Sort by lowest line coverage first
files.sort((a, b) => a.lines - b.lines);

return files;
}

function generateMarkdownReport(coverageData, commitSha) {
let markdown = `## Coverage Report\n\n`;
markdown += `**Commit:** ${commitSha}\n\n`;

// Add artifact link if running in GitHub Actions
if (process.env.GITHUB_RUN_ID && process.env.GITHUB_REPOSITORY) {
const artifactUrl = `https://github.com/${process.env.GITHUB_REPOSITORY}/actions/runs/${process.env.GITHUB_RUN_ID}`;
markdown += `📊 [View detailed HTML coverage reports](${artifactUrl})\n\n`;
}

let overallLines = { total: 0, covered: 0 };
let overallBranches = { total: 0, covered: 0 };
let overallFunctions = { total: 0, covered: 0 };
let overallStatements = { total: 0, covered: 0 };

// Calculate overall coverage
for (const pkg of coverageData) {
if (!pkg.coverageData || !pkg.coverageData.total) continue;

overallLines.total += pkg.coverageData.total.lines.total;
overallLines.covered += pkg.coverageData.total.lines.covered;
overallBranches.total += pkg.coverageData.total.branches.total;
overallBranches.covered += pkg.coverageData.total.branches.covered;
overallFunctions.total += pkg.coverageData.total.functions.total;
overallFunctions.covered += pkg.coverageData.total.functions.covered;
overallStatements.total += pkg.coverageData.total.statements.total;
overallStatements.covered += pkg.coverageData.total.statements.covered;
}

const overallLinePct = (overallLines.covered / overallLines.total) * 100;
const overallBranchPct = (overallBranches.covered / overallBranches.total) * 100;
const overallFunctionPct = (overallFunctions.covered / overallFunctions.total) * 100;
const overallStatementPct = (overallStatements.covered / overallStatements.total) * 100;

markdown += `### Overall Coverage\n\n`;
markdown += `| Metric | Coverage |\n`;
markdown += `|--------|----------|\n`;
markdown += `| Lines | ${getIcon(overallLinePct)} ${formatPercentage(overallLinePct)} (${overallLines.covered}/${overallLines.total}) |\n`;
markdown += `| Statements | ${getIcon(overallStatementPct)} ${formatPercentage(overallStatementPct)} (${overallStatements.covered}/${overallStatements.total}) |\n`;
markdown += `| Functions | ${getIcon(overallFunctionPct)} ${formatPercentage(overallFunctionPct)} (${overallFunctions.covered}/${overallFunctions.total}) |\n`;
markdown += `| Branches | ${getIcon(overallBranchPct)} ${formatPercentage(overallBranchPct)} (${overallBranches.covered}/${overallBranches.total}) |\n\n`;

markdown += `### Package Coverage\n\n`;
markdown += `| Package | Lines | Statements | Functions | Branches |\n`;
markdown += `|---------|-------|------------|-----------|----------|\n`;

for (const pkg of coverageData) {
if (!pkg.coverageData || !pkg.coverageData.total) {
markdown += `| @dbml/${pkg.name} | N/A | N/A | N/A | N/A |\n`;
continue;
}

const linePct = pkg.coverageData.total.lines.pct;
const stmtPct = pkg.coverageData.total.statements.pct;
const funcPct = pkg.coverageData.total.functions.pct;
const branchPct = pkg.coverageData.total.branches.pct;

markdown += `| @dbml/${pkg.name} | ${getIcon(linePct)} ${formatPercentage(linePct)} | ${getIcon(stmtPct)} ${formatPercentage(stmtPct)} | ${getIcon(funcPct)} ${formatPercentage(funcPct)} | ${getIcon(branchPct)} ${formatPercentage(branchPct)} |\n`;
}

markdown += `\n`;

// Add warnings for packages below threshold
const lowCoveragePackages = coverageData.filter(pkg =>
pkg.coverageData && pkg.coverageData.total && pkg.coverageData.total.lines.pct < COVERAGE_THRESHOLD
);

if (lowCoveragePackages.length > 0) {
markdown += `### ⚠️ Coverage Warnings\n\n`;
markdown += `The following packages have coverage below ${COVERAGE_THRESHOLD}%:\n\n`;
for (const pkg of lowCoveragePackages) {
markdown += `- **@dbml/${pkg.name}**: ${formatPercentage(pkg.coverageData.total.lines.pct)} line coverage\n`;
}
markdown += `\n`;
}

// Add file-level low coverage reporting
markdown += `### Files with Coverage Below ${COVERAGE_THRESHOLD}%\n\n`;

let hasLowCoverageFiles = false;

for (const pkg of coverageData) {
if (!pkg.coverageData) continue;

const lowCoverageFiles = getFilesWithLowCoverage(pkg.coverageData, pkg.name, COVERAGE_THRESHOLD);

if (lowCoverageFiles.length > 0) {
hasLowCoverageFiles = true;

markdown += `#### @dbml/${pkg.name}\n\n`;
markdown += '<details>\n';
markdown += `<summary>${lowCoverageFiles.length} file(s) below ${COVERAGE_THRESHOLD}% coverage</summary>\n\n`;
markdown += '| File | Lines | Statements | Functions | Branches |\n';
markdown += '|------|-------|------------|-----------|----------|\n';

for (const file of lowCoverageFiles) {
markdown += `| \`${file.path}\` | ${formatPercentage(file.lines)} | ${formatPercentage(file.statements)} | ${formatPercentage(file.functions)} | ${formatPercentage(file.branches)} |\n`;
}

markdown += '\n</details>\n\n';
}
}

if (!hasLowCoverageFiles) {
markdown += `All files have coverage at or above ${COVERAGE_THRESHOLD}%! 🎉\n\n`;
}

// Set GitHub Actions outputs
if (process.env.GITHUB_OUTPUT) {
const output = [
`overall-lines=${overallLinePct.toFixed(2)}`,
`overall-branches=${overallBranchPct.toFixed(2)}`,
`overall-functions=${overallFunctionPct.toFixed(2)}`,
`overall-statements=${overallStatementPct.toFixed(2)}`,
].join('\n');

fs.appendFileSync(process.env.GITHUB_OUTPUT, output + '\n');
}

return markdown;
}

function main() {
// Get commit SHA from environment or git
const commitSha = process.env.GITHUB_SHA ||
require('child_process').execSync('git rev-parse HEAD', { encoding: 'utf8' }).trim();

console.log('Collecting coverage data from packages...');

const coverageData = PACKAGES.map(packageName => {
console.log(`- Reading coverage for ${packageName}...`);
const coverageData = readCoverageSummary(packageName);
return {
name: packageName,
coverageData
};
});

console.log('\nGenerating coverage report...');
const markdown = generateMarkdownReport(coverageData, commitSha.substring(0, 8));

// Write markdown report
const reportPath = path.join(__dirname, '../../../coverage-report.md');
fs.writeFileSync(reportPath, markdown);
console.log(`\nCoverage report written to: ${reportPath}`);

// Output to console
console.log('\n' + markdown);

const criticallyLowCoverage = coverageData.some(pkg =>
pkg.coverageData && pkg.coverageData.total && pkg.coverageData.total.lines.pct < 60
);

if (criticallyLowCoverage) {
console.error('\n❌ Critical: One or more packages have coverage below 60%');
}
}

if (require.main === module) {
main();
}
22 changes: 19 additions & 3 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
name: Run unit test
name: Run unit test and collect coverage
on:
push:
branches: [master]
Expand All @@ -12,6 +12,7 @@ on:
jobs:
build:
runs-on: ubuntu-22.04
timeout-minutes: 20
strategy:
matrix:
node-version: [22.x]
Expand Down Expand Up @@ -96,5 +97,20 @@ jobs:
run: yarn install --frozen-lockfile
- name: Build packages
run: yarn build
- name: Run unit tests
run: yarn test
- name: Run unit test and coverage
run: yarn coverage
- name: Generate coverage report
id: coverage
run: node .github/workflows/scripts/collect-coverage.js
- name: Upload coverage HTML reports
uses: actions/upload-artifact@v4
with:
name: coverage-reports
path: |
packages/*/coverage
- name: Comment PR with coverage report
uses: marocchino/sticky-pull-request-comment@v2
if: github.event_name == 'pull_request'
with:
recreate: true
path: coverage-report.md
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -24,3 +24,6 @@ packages/dbml-core/benchmarks/*
tsconfig.tsbuildinfo

packages/dbml-connector/keys

# coverage
coverage/
12 changes: 6 additions & 6 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,17 +11,21 @@
"build": "yarn clean-build && npx lerna run build",
"clean-build": "rm -rf ./packages/dbml-cli/lib ./packages/dbml-core/lib ./packages/dbml-parse/dist ./packages/dbml-connector/dist",
"lint": "npx lerna run lint",
"lint:fix": "npx lerna run lint:fix"
"lint:fix": "npx lerna run lint:fix",
"coverage": "npx lerna run coverage"
},
"devDependencies": {
"@babel/core": "^7.21.4",
"@babel/plugin-transform-runtime": "^7.21.4",
"@babel/preset-env": "^7.21.4",
"@glen/jest-raw-loader": "^2.0.0",
"@vitest/coverage-v8": "^4.0.9",
"babel-jest": "^29.5.0",
"fast-check": "^4.3.0",
"jest": "^29.5.0",
"lerna": "^7.1.4",
"lerna-changelog": "^2.2.0"
"lerna-changelog": "^2.2.0",
"vitest": "^4.0.9"
},
"resolutions": {
"**/tmp": "0.2.5"
Expand All @@ -32,10 +36,6 @@
"url": "https://github.com/holistics/dbml"
},
"jest": {
"setupFiles": [
"./packages/dbml-cli/jestHelpers.js",
"./packages/dbml-core/jestHelpers.js"
],
"setupFilesAfterEnv": [
"<rootDir>/test/testSetupFile.js"
],
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,12 @@ import fs from 'fs';
import util from 'util';
import childProcess from 'child_process';
import stripAnsi from 'strip-ansi';
import { scanDirNames } from './testHelpers';

const exec = util.promisify(childProcess.exec);

describe('@dbml/cli', () => {
const runTest = async (dirName, binFile) => {
const runTest = async (dirName: string, binFile: string) => {
process.chdir(dirName);
const args = [path.join(__dirname, binFile)];
const optsRaw = fs.readFileSync(path.join(dirName, './options.json'), 'utf-8');
Expand Down
8 changes: 8 additions & 0 deletions packages/dbml-cli/__tests__/testHelpers.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import fs from 'fs';
import path from 'path';

export function scanDirNames (dirname: string, subpath: string): string[] {
const dirPath = path.join(dirname, subpath);
const dirs = fs.readdirSync(dirPath);
return dirs;
};
7 changes: 4 additions & 3 deletions packages/dbml-cli/jest.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,13 @@ import { type Config } from 'jest';

const config: Config = {
testMatch: ["**/?(*.)+(spec|test).?([mc])[jt]s?(x)"],
setupFiles: [
"./jestHelpers.js",
],
preset: 'ts-jest',
transform: {
"^.+\\.js$": "babel-jest",
},
collectCoverage: true,
coverageReporters: ["json", "json-summary", "html", "text"],
coverageDirectory: "coverage",
};

export default config;
8 changes: 0 additions & 8 deletions packages/dbml-cli/jestHelpers.js

This file was deleted.

4 changes: 3 additions & 1 deletion packages/dbml-cli/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@
"main": "lib/index.js",
"license": "Apache-2.0",
"scripts": {
"test": "jest",
"test": "jest --coverage=false",
"coverage": "jest --coverage",
"build": "babel src --out-dir lib --copy-files",
"prepublish": "npm run build",
"lint": "eslint .",
Expand Down Expand Up @@ -55,6 +56,7 @@
"eslint-config-airbnb-base": "^15.0.0",
"eslint-plugin-jest": "^29.0.1",
"jest": "^29.5.0",
"ts-jest": "^29.4.5",
"typescript": "^5.9.3",
"typescript-eslint": "^8.46.3"
},
Expand Down
4 changes: 2 additions & 2 deletions packages/dbml-connector/__tests__/connector.test.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import fs from 'fs';
import path from 'path';
import { scanDirNames } from '../jestHelpers.ts';
import { connector } from '../src/index.ts';
import { scanDirNames } from './testHelpers';
import { connector } from '../src/index';

const sortKeys = (obj: any): any => {
if (Array.isArray(obj)) {
Expand Down
Loading
Loading