Skip to content
6 changes: 5 additions & 1 deletion sonar-project.properties
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,10 @@ sonar.qualitygate.wait=true
sonar.qualitygate.timeout=1000
sonar.sources=src,server
sonar.tests=test
sonar.test.inclusions=test/**/*.ts
sonar.test.exclusions=**/*.js,**/node_modules/**
sonar.javascript.lcov.reportPaths=lcov.info
sonar.coverage.exclusions=server/src/utils/parserUtils.ts,src/ipc/**,src/models/**,src/extension.ts,src/classes/**,src/commands/installTools.ts,src/utils/cpUtils.ts
sonar.typescript.lcov.reportPaths=lcov.info
sonar.coverage.exclusions=server/src/utils/parserUtils.ts,src/ipc/**,src/models/**,src/extension.ts,src/classes/**,src/commands/installTools.ts,src/utils/cpUtils.ts,test/**,**/*.test.ts,**/*.spec.ts
sonar.cpd.exclusions=src/services/completionProvider.ts,src/extension.ts
sonar.sourceEncoding=UTF-8
17 changes: 15 additions & 2 deletions src/utils/connLabel.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,10 @@ export function getWorkspaceLabels() {
.get<Labels[]>("kdb.connectionLabels");
ext.connLabelList.length = 0;
if (existingConnLbls && existingConnLbls.length > 0) {
existingConnLbls.forEach((label: Labels) => {
const sortedLabels = existingConnLbls.sort((a, b) =>
a.name.localeCompare(b.name),
);
sortedLabels.forEach((label: Labels) => {
ext.connLabelList.push(label);
});
}
Expand All @@ -46,7 +49,10 @@ export function createNewLabel(name: string, colorName: string) {
name: name,
color: color,
};

ext.connLabelList.push(newLbl);
ext.connLabelList.sort((a, b) => a.name.localeCompare(b.name));

workspace
.getConfiguration()
.update("kdb.connectionLabels", ext.connLabelList, true);
Expand All @@ -70,7 +76,14 @@ export function getWorkspaceLabelsConnMap() {
ext.labelConnMapList.length = 0;
if (existingLabelConnMaps && existingLabelConnMaps.length > 0) {
existingLabelConnMaps.forEach((labelConnMap: ConnectionLabel) => {
ext.labelConnMapList.push(labelConnMap);
const sortedLabelConnMap: ConnectionLabel = {
labelName: labelConnMap.labelName,
connections: labelConnMap.connections.sort((a, b) =>
a.localeCompare(b),
),
};

ext.labelConnMapList.push(sortedLabelConnMap);
});
}
}
Expand Down
29 changes: 25 additions & 4 deletions src/utils/core.ts
Original file line number Diff line number Diff line change
Expand Up @@ -205,7 +205,15 @@ export async function getWorkspaceFolder(
}

export function getServers(): Server | undefined {
return workspace.getConfiguration().get("kdb.servers");
const servers = workspace.getConfiguration().get<Server>("kdb.servers");

return servers
? Object.fromEntries(
Object.entries(servers).sort(([, a], [, b]) =>
a.serverAlias.localeCompare(b.serverAlias),
),
)
: servers;
}

// TODO: Remove this on 1.9.0 release
Expand Down Expand Up @@ -292,9 +300,22 @@ export function getInsights(): Insights | undefined {
"kdb.insightsEnterpriseConnections",
);

return insights && Object.keys(insights).length > 0
? insights
: configuration.get("kdb.insights");
const insightsList: Insights | undefined =
insights && Object.keys(insights).length > 0
? insights
: configuration.get("kdb.insights");

if (!insightsList || Object.keys(insightsList).length === 0) {
return undefined;
}

return insightsList
? Object.fromEntries(
Object.entries(insightsList).sort(([, a], [, b]) =>
a.alias.localeCompare(b.alias),
),
)
: insightsList;
}

export async function updateServers(servers: Server): Promise<void> {
Expand Down
145 changes: 140 additions & 5 deletions test/coverage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,15 @@ import * as path from "path";
const REPO_ROOT = path.join(__dirname, "../..");

export function instrument() {
const instrumenter = createInstrumenter();
const instrumenter = createInstrumenter({
coverageVariable: "__coverage__",
preserveComments: true,
compact: false,
esModules: true,
autoWrap: true,
produceSourceMap: true,
});

const files = rreaddir(path.resolve(REPO_ROOT, "out"));

for (let i = 0; i < files.length; i++) {
Expand All @@ -46,11 +54,18 @@ export function instrument() {
// missing source map - map remains undefined
}

const sourceCode = fs.readFileSync(inputPath).toString();

let sourceFilePath = files[i];

sourceFilePath = sourceFilePath.replace(/\.js$/, ".ts");

const instrumentedCode = instrumenter.instrumentSync(
fs.readFileSync(inputPath).toString(),
inputPath,
sourceCode,
sourceFilePath,
map,
);

safeWriteFile(outputPath, instrumentedCode);
}
}
Expand All @@ -65,7 +80,6 @@ export function createReport(): void {

const coverageMap = createCoverageMap(global.__coverage__);

// Use the simpler approach that we know works
const context = createContext({
dir: path.join(REPO_ROOT, `coverage-reports`),
coverageMap: coverageMap,
Expand All @@ -82,13 +96,134 @@ export function createReport(): void {
reports.forEach((report) => {
try {
report.execute(context);
console.log(
`✅ Generated ${report.constructor.name} report successfully`,
);
} catch (err) {
console.error(
`Failed to generate ${report.constructor.name} report:`,
`Failed to generate ${report.constructor.name} report:`,
err,
);
}
});

const summary = coverageMap.getCoverageSummary();
console.log("\n=== COVERAGE SUMMARY ===");
console.log(
`Lines: ${summary.lines.pct}% (${summary.lines.covered}/${summary.lines.total})`,
);
console.log(
`Functions: ${summary.functions.pct}% (${summary.functions.covered}/${summary.functions.total})`,
);
}

export function fixLcovPaths(): void {
const lcovPath = path.join(REPO_ROOT, "coverage-reports", "lcov.info");

if (!fs.existsSync(lcovPath)) {
console.warn("❌ lcov.info file not found");
return;
}

let content = fs.readFileSync(lcovPath, "utf8");

// console.log("\n=== FIXING LCOV PATHS ===");

// const originalSfLines = content.match(/^SF:.*$/gm);
// if (originalSfLines) {
// console.log("Original SF lines (first 5):");
// originalSfLines.slice(0, 5).forEach((line) => console.log(` ${line}`));
// }

content = content.replace(/\\/g, "/");

const repoRootEscaped = REPO_ROOT.replace(/[/\\]/g, "[/\\\\]");
content = content.replace(
new RegExp(`^SF:.*${repoRootEscaped}[/\\\\](.*)$`, "gm"),
"SF:$1",
);

content = content.replace(/^SF:.*\/out\/(.*?)\.js$/gm, "SF:$1.ts");

content = content.replace(/^SF:(src|server)\/(.*?)\.js$/gm, "SF:$1/$2.ts");

content = content.replace(/^SF:(.*?)\.js$/gm, "SF:$1.ts");

content = content.replace(/^SF:(src)\/\1\//gm, "SF:$1/");
content = content.replace(/^SF:(server)\/\1\//gm, "SF:$1/");

fs.writeFileSync(lcovPath, content);

// const fixedSfLines = content.match(/^SF:.*$/gm);
// if (fixedSfLines) {
// console.log("Fixed SF lines (first 5):");
// fixedSfLines.slice(0, 5).forEach((line) => console.log(` ${line}`));
// }

console.log("✅ Fixed lcov.info paths");
}

export function debugLcov(): void {
const lcovPath = path.join(REPO_ROOT, "coverage-reports", "lcov.info");

if (!fs.existsSync(lcovPath)) {
console.warn("❌ lcov.info file not found");
return;
}

const content = fs.readFileSync(lcovPath, "utf8");

console.log("\n=== LCOV DEBUG ===");

const sfLines = content.match(/^SF:.*$/gm);
if (sfLines) {
console.log(`Found ${sfLines.length} source files:`);
sfLines.forEach((line) => console.log(` ${line}`));
} else {
console.log("❌ No SF (Source File) lines found in lcov.info");
}
}

export function generateCoverageReport(): void {
const coverageReportsDir = path.join(REPO_ROOT, "coverage-reports");

const global = new Function("return this")();
if (!global.__coverage__ || Object.keys(global.__coverage__).length === 0) {
console.warn("❌ No coverage data available to generate report");
console.warn("This might be because:");
console.warn("1. Tests are not running with instrumented code");
console.warn("2. No tests are actually executing the source code");
console.warn("3. The coverage variable is not being set correctly");
return;
}

if (!fs.existsSync(coverageReportsDir)) {
console.log(`Creating coverage reports directory: ${coverageReportsDir}`);
fs.mkdirSync(coverageReportsDir, { recursive: true });
}

createReport();

const lcovPath = path.join(coverageReportsDir, "lcov.info");
if (!fs.existsSync(lcovPath)) {
console.error(`❌ lcov.info was not created at ${lcovPath}`);
return;
}

console.log(`✅ lcov.info created at ${lcovPath}`);
const stats = fs.statSync(lcovPath);
console.log(`File size: ${stats.size} bytes`);

fixLcovPaths();

if (fs.existsSync(lcovPath)) {
const finalStats = fs.statSync(lcovPath);
console.log(`✅ Final lcov.info size: ${finalStats.size} bytes`);
} else {
console.error(`❌ lcov.info missing after processing!`);
}

console.log("✅ Coverage report generation completed!");
}

function copyFile(inputPath: string, outputPath: string): void {
Expand Down
20 changes: 14 additions & 6 deletions test/runTest.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,18 +21,26 @@ async function main() {
const extensionDevelopmentPath = path.join(__dirname, "../../");

let extensionTestsPath = path.join(__dirname, "./suite/index");
if (process.argv.indexOf("--coverage") >= 0) {
// generate instrumented files at out-cov
instrument();

// load the instrumented files
const hasCoverageFlag = process.argv.indexOf("--coverage") >= 0;

if (hasCoverageFlag) {
try {
instrument();
console.log("✅ Code instrumentation completed");
} catch (error) {
console.error("❌ Code instrumentation failed:", error);
throw error;
}

extensionTestsPath = path.join(
__dirname,
"../../out-cov/test/suite/index"
"../../out-cov/test/suite/index",
);

// signal that the coverage data should be gathered
process.env["GENERATE_COVERAGE"] = "1";
} else {
console.log("⚠️ Coverage mode disabled, running normal tests");
}

await runTests({
Expand Down
14 changes: 12 additions & 2 deletions test/suite/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import { glob } from "glob";
import Mocha from "mocha";
import * as path from "path";

import { createReport } from "../coverage";
import { generateCoverageReport } from "../coverage";

export async function run(): Promise<void> {
const options: Mocha.MochaOptions = {
Expand Down Expand Up @@ -48,7 +48,17 @@ export async function run(): Promise<void> {
});
}).then(() => {
if (process.env["GENERATE_COVERAGE"]) {
createReport();
try {
generateCoverageReport();
console.log("✅ Coverage generation completed successfully");
} catch (error) {
console.error("❌ Coverage generation failed:", error);
throw error;
}
} else {
console.log(
"❌ GENERATE_COVERAGE not set, skipping coverage generation",
);
}
});
} catch (err) {
Expand Down
Loading