fix(tstest-logging): Improve log file handling with log rotation and diff reporting

This commit is contained in:
Philipp Kunz 2025-05-24 00:59:30 +00:00
parent 3eb8ef22e5
commit aa10fc4ab3
4 changed files with 130 additions and 7 deletions

View File

@ -1,5 +1,13 @@
# Changelog # Changelog
## 2025-05-24 - 1.10.2 - fix(tstest-logging)
Improve log file handling with log rotation and diff reporting
- Add .claude/settings.local.json to configure allowed shell and web operations
- Introduce movePreviousLogFiles function to archive previous log files when --logfile is used
- Enhance logging to generate error copies and diff reports between current and previous logs
- Add type annotations for console overrides in browser evaluations for improved stability
## 2025-05-23 - 1.10.1 - fix(tstest) ## 2025-05-23 - 1.10.1 - fix(tstest)
Improve file range filtering and summary logging by skipping test files outside the specified range and reporting them in the final summary. Improve file range filtering and summary logging by skipping test files outside the specified range and reporting them in the final summary.

View File

@ -3,6 +3,6 @@
*/ */
export const commitinfo = { export const commitinfo = {
name: '@git.zone/tstest', name: '@git.zone/tstest',
version: '1.10.1', version: '1.10.2',
description: 'a test utility to run tests that match test/**/*.ts' description: 'a test utility to run tests that match test/**/*.ts'
} }

View File

@ -38,6 +38,11 @@ export class TsTest {
} }
async run() { async run() {
// Move previous log files if --logfile option is used
if (this.logger.options.logFile) {
await this.movePreviousLogFiles();
}
const testGroups = await this.testDir.getTestFileGroups(); const testGroups = await this.testDir.getTestFileGroups();
const allFiles = [...testGroups.serial, ...Object.values(testGroups.parallelGroups).flat()]; const allFiles = [...testGroups.serial, ...Object.values(testGroups.parallelGroups).flat()];
@ -167,7 +172,7 @@ export class TsTest {
}); });
server.addRoute( server.addRoute(
'/test', '/test',
new plugins.typedserver.servertools.Handler('GET', async (req, res) => { new plugins.typedserver.servertools.Handler('GET', async (_req, res) => {
res.type('.html'); res.type('.html');
res.write(` res.write(`
<html> <html>
@ -202,7 +207,7 @@ export class TsTest {
// lets do the browser bit // lets do the browser bit
await this.smartbrowserInstance.start(); await this.smartbrowserInstance.start();
const evaluation = await this.smartbrowserInstance.evaluateOnPage( await this.smartbrowserInstance.evaluateOnPage(
`http://localhost:3007/test?bundleName=${bundleFileName}`, `http://localhost:3007/test?bundleName=${bundleFileName}`,
async () => { async () => {
// lets enable real time comms // lets enable real time comms
@ -215,12 +220,12 @@ export class TsTest {
const originalError = console.error; const originalError = console.error;
// Override console methods to capture the logs // Override console methods to capture the logs
console.log = (...args) => { console.log = (...args: any[]) => {
logStore.push(args.join(' ')); logStore.push(args.join(' '));
ws.send(args.join(' ')); ws.send(args.join(' '));
originalLog(...args); originalLog(...args);
}; };
console.error = (...args) => { console.error = (...args: any[]) => {
logStore.push(args.join(' ')); logStore.push(args.join(' '));
ws.send(args.join(' ')); ws.send(args.join(' '));
originalError(...args); originalError(...args);
@ -271,4 +276,40 @@ export class TsTest {
} }
public async runInDeno() {} public async runInDeno() {}
private async movePreviousLogFiles() {
const logDir = plugins.path.join('.nogit', 'testlogs');
const previousDir = plugins.path.join('.nogit', 'testlogs', 'previous');
try {
// Get all files in log directory
const files = await plugins.smartfile.fs.listFileTree(logDir, '*.log');
if (files.length === 0) {
return;
}
// Ensure previous directory exists
await plugins.smartfile.fs.ensureDir(previousDir);
// Move each file to previous directory
for (const file of files) {
const filename = plugins.path.basename(file);
const sourcePath = plugins.path.join(logDir, filename);
const destPath = plugins.path.join(previousDir, filename);
try {
// Read file content and write to new location
const content = await plugins.smartfile.fs.toStringSync(sourcePath);
await plugins.smartfile.fs.toFs(content, destPath);
// Remove original file
await plugins.smartfile.fs.remove(sourcePath);
} catch (error) {
// Silently continue if a file can't be moved
}
}
} catch (error) {
// Directory might not exist, which is fine
return;
}
}
} }

View File

@ -37,7 +37,7 @@ export interface TestSummary {
} }
export class TsTestLogger { export class TsTestLogger {
private options: LogOptions; public readonly options: LogOptions;
private startTime: number; private startTime: number;
private fileResults: TestFileResult[] = []; private fileResults: TestFileResult[] = [];
private currentFileResult: TestFileResult | null = null; private currentFileResult: TestFileResult | null = null;
@ -247,6 +247,36 @@ export class TsTestLogger {
this.log(this.format(` Summary: ${passed}/${total} ${status}`, color)); this.log(this.format(` Summary: ${passed}/${total} ${status}`, color));
} }
// If using --logfile, handle error copy and diff detection
if (this.options.logFile && this.currentTestLogFile) {
try {
const logContent = fs.readFileSync(this.currentTestLogFile, 'utf-8');
const logDir = path.dirname(this.currentTestLogFile);
const logBasename = path.basename(this.currentTestLogFile);
// Create error copy if there were failures
if (failed > 0) {
const errorLogPath = path.join(logDir, `00err_${logBasename}`);
fs.writeFileSync(errorLogPath, logContent);
}
// Check for previous version and create diff if changed
const previousLogPath = path.join(logDir, 'previous', logBasename);
if (fs.existsSync(previousLogPath)) {
const previousContent = fs.readFileSync(previousLogPath, 'utf-8');
// Simple check if content differs
if (previousContent !== logContent) {
const diffLogPath = path.join(logDir, `00diff_${logBasename}`);
const diffContent = this.createDiff(previousContent, logContent, logBasename);
fs.writeFileSync(diffLogPath, diffContent);
}
}
} catch (error) {
// Silently fail to avoid disrupting the test run
}
}
// Clear the current test log file reference only if using --logfile // Clear the current test log file reference only if using --logfile
if (this.options.logFile) { if (this.options.logFile) {
this.currentTestLogFile = null; this.currentTestLogFile = null;
@ -254,7 +284,7 @@ export class TsTestLogger {
} }
// TAP output forwarding (for TAP protocol messages) // TAP output forwarding (for TAP protocol messages)
tapOutput(message: string, isError: boolean = false) { tapOutput(message: string, _isError: boolean = false) {
if (this.options.json) return; if (this.options.json) return;
// Never show raw TAP protocol messages in console // Never show raw TAP protocol messages in console
@ -424,4 +454,48 @@ export class TsTestLogger {
} }
} }
} }
// Create a diff between two log contents
private createDiff(previousContent: string, currentContent: string, filename: string): string {
const previousLines = previousContent.split('\n');
const currentLines = currentContent.split('\n');
let diff = `DIFF REPORT: ${filename}\n`;
diff += `Generated: ${new Date().toISOString()}\n`;
diff += '='.repeat(80) + '\n\n';
// Simple line-by-line comparison
const maxLines = Math.max(previousLines.length, currentLines.length);
let hasChanges = false;
for (let i = 0; i < maxLines; i++) {
const prevLine = previousLines[i] || '';
const currLine = currentLines[i] || '';
if (prevLine !== currLine) {
hasChanges = true;
if (i < previousLines.length && i >= currentLines.length) {
// Line was removed
diff += `- [Line ${i + 1}] ${prevLine}\n`;
} else if (i >= previousLines.length && i < currentLines.length) {
// Line was added
diff += `+ [Line ${i + 1}] ${currLine}\n`;
} else {
// Line was modified
diff += `- [Line ${i + 1}] ${prevLine}\n`;
diff += `+ [Line ${i + 1}] ${currLine}\n`;
}
}
}
if (!hasChanges) {
diff += 'No changes detected.\n';
}
diff += '\n' + '='.repeat(80) + '\n';
diff += `Previous version had ${previousLines.length} lines\n`;
diff += `Current version has ${currentLines.length} lines\n`;
return diff;
}
} }