fix(ci): Update CI workflows, build scripts, and export configuration

This commit is contained in:
2025-05-12 10:03:22 +00:00
parent e853b2d5e4
commit 9b63777a76
50 changed files with 7188 additions and 1823 deletions

9
test/test.context.ts Normal file
View File

@ -0,0 +1,9 @@
import { expect, tap } from '@push.rocks/tapbundle';
import * as smartlogContext from '../ts_context/index.js';
tap.test('should correctly export strings from context module', async () => {
expect(typeof smartlogContext.standardExport).toEqual('string');
expect(smartlogContext.standardExport).toEqual('Hi there! :) This is an exported string');
});
export default tap.start();

View File

@ -0,0 +1,19 @@
import { expect, tap } from '@push.rocks/tapbundle';
import { SmartlogDestinationClickhouse } from '../ts_destination_clickhouse/index.js';
import * as smartclickhouse from '@push.rocks/smartclickhouse';
// Test we can create a destination instance
tap.test('should create a ClickHouse destination instance', async () => {
// Use mock configuration
const clickhouseOptions: smartclickhouse.IClickhouseConstructorOptions = {
url: 'defult:@localhost:8123',
database: 'test_logs'
};
// Verify we can create an instance
// We won't start it to avoid making real connections
const clickhouseDestination = new SmartlogDestinationClickhouse(clickhouseOptions);
expect(clickhouseDestination).toBeTruthy();
});
export default tap.start();

View File

@ -0,0 +1,117 @@
import { expect, tap } from '@push.rocks/tapbundle';
import { SmartlogDestinationDevtools } from '../ts_destination_devtools/index.js';
import * as smartlogInterfaces from '../ts_interfaces/index.js';
let testDestination: SmartlogDestinationDevtools;
let originalConsoleLog: any;
let consoleLogCalls: any[] = [];
// Helper to create log package
const createMockLogPackage = (level: smartlogInterfaces.TLogLevel, message: string): smartlogInterfaces.ILogPackage => {
return {
timestamp: Date.now(),
type: 'log',
level,
message,
context: {
environment: 'test',
runtime: 'chrome'
},
correlation: {
id: '123',
type: 'none'
}
};
};
// Tests
tap.test('should setup test environment', async () => {
// Save original console.log
originalConsoleLog = console.log;
// Replace with mock
console.log = (...args: any[]) => {
consoleLogCalls.push(args);
// Don't call original to avoid polluting test output
};
});
tap.test('should create a devtools destination instance', async () => {
testDestination = new SmartlogDestinationDevtools();
expect(testDestination).toBeTruthy();
});
tap.test('should log error level messages with appropriate styling', async () => {
consoleLogCalls = [];
const logPackage = createMockLogPackage('error', 'Test error message');
await testDestination.handleLog(logPackage);
expect(consoleLogCalls.length).toEqual(1);
expect(consoleLogCalls[0][0]).toContain('Error:');
expect(consoleLogCalls[0][1]).toContain('background:#000000;color:#800000;');
expect(consoleLogCalls[0][2]).toContain('Test error message');
});
tap.test('should log info level messages with appropriate styling', async () => {
consoleLogCalls = [];
const logPackage = createMockLogPackage('info', 'Test info message');
await testDestination.handleLog(logPackage);
expect(consoleLogCalls.length).toEqual(1);
expect(consoleLogCalls[0][0]).toContain('Info:');
expect(consoleLogCalls[0][1]).toContain('background:#EC407A;color:#ffffff;');
expect(consoleLogCalls[0][2]).toContain('Test info message');
});
tap.test('should log ok level messages with appropriate styling', async () => {
consoleLogCalls = [];
const logPackage = createMockLogPackage('ok', 'Test ok message');
await testDestination.handleLog(logPackage);
expect(consoleLogCalls.length).toEqual(1);
expect(consoleLogCalls[0][0]).toContain('OK:');
expect(consoleLogCalls[0][2]).toContain('Test ok message');
});
tap.test('should log success level messages with appropriate styling', async () => {
consoleLogCalls = [];
const logPackage = createMockLogPackage('success', 'Test success message');
await testDestination.handleLog(logPackage);
expect(consoleLogCalls.length).toEqual(1);
expect(consoleLogCalls[0][0]).toContain('Success:');
expect(consoleLogCalls[0][2]).toContain('Test success message');
});
tap.test('should log warn level messages with appropriate styling', async () => {
consoleLogCalls = [];
const logPackage = createMockLogPackage('warn', 'Test warning message');
await testDestination.handleLog(logPackage);
expect(consoleLogCalls.length).toEqual(1);
expect(consoleLogCalls[0][0]).toContain('Warn:');
expect(consoleLogCalls[0][2]).toContain('Test warning message');
});
tap.test('should log note level messages with appropriate styling', async () => {
consoleLogCalls = [];
const logPackage = createMockLogPackage('note', 'Test note message');
await testDestination.handleLog(logPackage);
expect(consoleLogCalls.length).toEqual(1);
expect(consoleLogCalls[0][0]).toContain('Note:');
expect(consoleLogCalls[0][2]).toContain('Test note message');
});
tap.test('should clean up test environment', async () => {
// Restore the original console.log
console.log = originalConsoleLog;
});
export default tap.start();

View File

@ -0,0 +1,10 @@
import { expect, tap } from '@push.rocks/tapbundle';
import { SmartlogDestinationDevtools } from '../ts_destination_devtools/index.js';
// Test we can create a destination instance
tap.test('should create a DevTools destination instance', async () => {
const devtoolsDestination = new SmartlogDestinationDevtools();
expect(devtoolsDestination).toBeTruthy();
});
export default tap.start();

View File

@ -0,0 +1,87 @@
import { expect, tap } from '@push.rocks/tapbundle';
import { SmartlogDestinationFile } from '../ts_destination_file/index.js';
import * as fs from 'fs';
import * as path from 'path';
import * as os from 'os';
let testLogDir: string;
let testLogFile: string;
let testDestination: SmartlogDestinationFile;
// Setup and teardown helpers
const createTempLogDir = () => {
const tempDir = fs.mkdtempSync(path.join(os.tmpdir(), 'smartlog-test-'));
return tempDir;
};
const removeTempDir = (dirPath: string) => {
if (fs.existsSync(dirPath)) {
const files = fs.readdirSync(dirPath);
for (const file of files) {
fs.unlinkSync(path.join(dirPath, file));
}
fs.rmdirSync(dirPath);
}
};
// Tests
tap.test('should prepare test environment', async () => {
testLogDir = createTempLogDir();
testLogFile = path.join(testLogDir, 'test.log');
expect(fs.existsSync(testLogDir)).toBeTrue();
});
tap.test('should create a file destination instance with a valid path', async () => {
testDestination = new SmartlogDestinationFile(testLogFile);
expect(testDestination).toBeTruthy();
expect(fs.existsSync(testLogFile)).toBeTrue();
});
tap.test('should throw error when file path is not absolute', async () => {
let errorThrown = false;
try {
new SmartlogDestinationFile('relative/path/file.log');
} catch (error) {
errorThrown = true;
expect(error.message).toContain('filePath needs to be absolute');
}
expect(errorThrown).toBeTrue();
});
tap.test('should write log messages to file', async () => {
const testMessage = 'Test log message';
await testDestination.handleLog({
timestamp: Date.now(),
type: 'log',
level: 'info',
message: testMessage,
context: {
environment: 'test',
runtime: 'node'
},
correlation: {
id: '123',
type: 'none'
}
});
// Give file system a moment to write
await new Promise(resolve => setTimeout(resolve, 50));
const fileContent = fs.readFileSync(testLogFile, 'utf8');
expect(fileContent).toContain(testMessage);
});
tap.test('should clean up test resources', async () => {
// Close file handle before cleanup
testDestination.fileWriteStream.end();
// Small delay to ensure file is properly closed
await new Promise(resolve => setTimeout(resolve, 100));
removeTempDir(testLogDir);
expect(fs.existsSync(testLogDir)).toBeFalse();
});
export default tap.start();

View File

@ -0,0 +1,96 @@
import { expect, tap } from '@push.rocks/tapbundle';
import { DestinationLocal } from '../ts_destination_local/index.js';
import * as smartlogInterfaces from '../ts_interfaces/index.js';
let testDestination: DestinationLocal;
// Mock log package
const createMockLogPackage = (level: smartlogInterfaces.TLogLevel, message: string): smartlogInterfaces.ILogPackage => {
return {
timestamp: Date.now(),
type: 'log',
level,
message,
context: {
environment: 'test',
runtime: 'node'
},
correlation: {
id: '123',
type: 'none'
}
};
};
// Tests
tap.test('should create a local destination instance', async () => {
testDestination = new DestinationLocal();
expect(testDestination).toBeTruthy();
});
tap.test('should handle logs with different levels', async () => {
// Testing with a spy would be ideal, but since we don't have a mocking framework,
// we'll just verify the method runs without errors for different log levels
// Test info level
const logPackageInfo = createMockLogPackage('info', 'Info message');
await testDestination.handleLog(logPackageInfo);
// Test error level
const logPackageError = createMockLogPackage('error', 'Error message');
await testDestination.handleLog(logPackageError);
// Test warn level
const logPackageWarn = createMockLogPackage('warn', 'Warning message');
await testDestination.handleLog(logPackageWarn);
// Test silly level
const logPackageSilly = createMockLogPackage('silly', 'Silly message');
await testDestination.handleLog(logPackageSilly);
});
tap.test('should handle reduced logging', async () => {
testDestination = new DestinationLocal();
// Note: In a real test environment with a mocking framework,
// we would capture console output and verify it's only written
// according to the expected behavior. Here we just ensure
// the methods execute without errors.
// First call with message
testDestination.logReduced('Test message');
// Same message immediately after
testDestination.logReduced('Test message');
// Different message
testDestination.logReduced('Different message');
});
tap.test('should handle repeated logging with repeatEveryTimesArg', async () => {
testDestination = new DestinationLocal();
// First call with message
testDestination.logReduced('Repeated with count', 3);
// Second call
testDestination.logReduced('Repeated with count', 3);
// Third call
testDestination.logReduced('Repeated with count', 3);
// Fourth call (3rd repetition)
testDestination.logReduced('Repeated with count', 3);
});
tap.test('should create new line(s)', async () => {
testDestination = new DestinationLocal();
// Default 1 line
testDestination.newLine();
// Multiple lines
testDestination.newLine(3);
});
export default tap.start();

View File

@ -0,0 +1,72 @@
import { expect, tap } from '@push.rocks/tapbundle';
import { SmartlogDestinationReceiver } from '../ts_destination_receiver/index.js';
import { Smartlog } from '../ts/index.js';
import * as smartlogInterfaces from '../ts_interfaces/index.js';
let testDestination: SmartlogDestinationReceiver;
let testSmartlog: Smartlog;
// Mock log package
const createMockLogPackage = (level: smartlogInterfaces.TLogLevel, message: string): smartlogInterfaces.ILogPackage => {
return {
timestamp: Date.now(),
type: 'log',
level,
message,
context: {
environment: 'test',
runtime: 'node'
},
correlation: {
id: '123',
type: 'none'
}
};
};
// Tests
tap.test('should create a Smartlog instance', async () => {
testSmartlog = new Smartlog({
logContext: {
environment: 'test',
runtime: 'node',
zone: 'test-zone',
company: 'Test Company',
companyunit: 'Test Unit',
containerName: 'test-container',
},
});
expect(testSmartlog).toBeTruthy();
});
tap.test('should create a destination receiver instance with valid options', async () => {
testDestination = new SmartlogDestinationReceiver({
passphrase: 'test-passphrase',
receiverEndpoint: 'https://example.com/logs',
});
expect(testDestination).toBeTruthy();
});
tap.test('should attempt to send logs to the receiver endpoint', async () => {
// Create a mock version of the webrequest.postJson method to avoid actual HTTP calls
const originalPostJson = testDestination['webrequest'].postJson;
testDestination['webrequest'].postJson = async () => {
return {
body: { status: 'ok' },
statusCode: 200
};
};
try {
const logPackage = createMockLogPackage('info', 'Test receiver message');
const result = await testDestination.handleLog(logPackage);
expect(result).toEqual({ status: 'ok' });
} finally {
// Restore the original method
testDestination['webrequest'].postJson = originalPostJson;
}
});
export default tap.start();

115
test/test.receiver.node.ts Normal file
View File

@ -0,0 +1,115 @@
import { expect, tap } from '@push.rocks/tapbundle';
import { SmartlogReceiver } from '../ts_receiver/index.js';
import { Smartlog } from '../ts/index.js';
import * as smartlogInterfaces from '../ts_interfaces/index.js';
import * as smarthash from '@push.rocks/smarthash';
let testSmartlog: Smartlog;
let testReceiver: SmartlogReceiver;
const testPassphrase = 'test-secret-passphrase';
// Helper to create authenticated log package
const createAuthenticatedLogPackage = (
level: smartlogInterfaces.TLogLevel,
message: string
): smartlogInterfaces.ILogPackageAuthenticated => {
const logPackage: smartlogInterfaces.ILogPackage = {
timestamp: Date.now(),
type: 'log',
level,
message,
context: {
environment: 'test',
runtime: 'node'
},
correlation: {
id: '123',
type: 'none'
}
};
return {
auth: smarthash.sha256FromStringSync(testPassphrase),
logPackage
};
};
// Tests
tap.test('should create a Smartlog instance for receiver', async () => {
testSmartlog = new Smartlog({
logContext: {
environment: 'test',
runtime: 'node',
zone: 'test-zone',
company: 'Test Company',
companyunit: 'Test Unit',
containerName: 'test-container',
},
});
expect(testSmartlog).toBeTruthy();
});
tap.test('should create a SmartlogReceiver instance', async () => {
// Create a validator function that always returns true
const validatorFunction = async () => true;
testReceiver = new SmartlogReceiver({
smartlogInstance: testSmartlog,
passphrase: testPassphrase,
validatorFunction
});
expect(testReceiver).toBeTruthy();
expect(testReceiver.passphrase).toEqual(testPassphrase);
});
tap.test('should handle authenticated log with correct passphrase', async () => {
const authLogPackage = createAuthenticatedLogPackage('info', 'Test authenticated message');
const result = await testReceiver.handleAuthenticatedLog(authLogPackage);
expect(result).toBeTruthy();
expect(result.status).toEqual('ok');
});
tap.test('should reject authenticated log with incorrect passphrase', async () => {
const logPackage: smartlogInterfaces.ILogPackage = {
timestamp: Date.now(),
type: 'log',
level: 'info',
message: 'Test unauthorized message',
context: {
environment: 'test',
runtime: 'node'
},
correlation: {
id: '123',
type: 'none'
}
};
const badAuthPackage = {
auth: 'incorrect-hash',
logPackage
};
const result = await testReceiver.handleAuthenticatedLog(badAuthPackage);
expect(result).toBeTruthy();
expect(result.status).toEqual('error');
});
tap.test('should handle many authenticated logs', async () => {
const authLogPackage1 = createAuthenticatedLogPackage('info', 'Test batch message 1');
const authLogPackage2 = createAuthenticatedLogPackage('warn', 'Test batch message 2');
const authLogPackage3 = createAuthenticatedLogPackage('error', 'Test batch message 3');
const authLogPackages = [authLogPackage1, authLogPackage2, authLogPackage3];
await testReceiver.handleManyAuthenticatedLogs(authLogPackages);
// No assertions needed as we're just testing it doesn't throw errors
});
export default tap.start();

View File

@ -0,0 +1,75 @@
import { expect, tap } from '@push.rocks/tapbundle';
import { SmartlogSourceOra } from '../ts_source_ora/index.js';
let testSourceOra: SmartlogSourceOra;
tap.test('should create a SmartlogSourceOra instance', async () => {
testSourceOra = new SmartlogSourceOra();
expect(testSourceOra).toBeTruthy();
expect(testSourceOra.started).toBeFalse();
});
tap.test('should set text and start spinner', async () => {
const testText = 'Testing ora spinner';
testSourceOra.text(testText);
expect(testSourceOra.started).toBeTrue();
expect(testSourceOra.oraInstance.text).toEqual(testText);
});
tap.test('should update text', async () => {
const newText = 'Updated text';
testSourceOra.text(newText);
expect(testSourceOra.oraInstance.text).toEqual(newText);
expect(testSourceOra.started).toBeTrue();
});
tap.test('should stop spinner', async () => {
testSourceOra.stop();
// We can't easily test the visual state, but we can verify it doesn't throw errors
});
tap.test('should finish with success', async () => {
testSourceOra = new SmartlogSourceOra();
testSourceOra.text('Starting again');
const successText = 'Operation successful';
testSourceOra.finishSuccess(successText);
expect(testSourceOra.started).toBeFalse();
});
tap.test('should finish with failure', async () => {
testSourceOra = new SmartlogSourceOra();
testSourceOra.text('Starting again');
const failText = 'Operation failed';
testSourceOra.finishFail(failText);
expect(testSourceOra.started).toBeFalse();
});
tap.test('should handle success and next', async () => {
testSourceOra = new SmartlogSourceOra();
testSourceOra.text('Starting again');
const nextText = 'Next operation';
testSourceOra.successAndNext(nextText);
expect(testSourceOra.started).toBeTrue();
expect(testSourceOra.oraInstance.text).toEqual(nextText);
});
tap.test('should handle fail and next', async () => {
testSourceOra = new SmartlogSourceOra();
testSourceOra.text('Starting again');
const nextText = 'Next operation after failure';
testSourceOra.failAndNext(nextText);
expect(testSourceOra.started).toBeTrue();
expect(testSourceOra.oraInstance.text).toEqual(nextText);
});
export default tap.start();