249 lines
8.6 KiB
Dart
249 lines
8.6 KiB
Dart
// SPDX-FileCopyrightText: 2026 FocusFlow contributors
|
|
// SPDX-License-Identifier: AGPL-3.0-only
|
|
|
|
/// Tests FocusFlow runtime config and file logging behavior.
|
|
library;
|
|
|
|
import 'dart:convert';
|
|
import 'dart:io';
|
|
|
|
import 'package:flutter_test/flutter_test.dart';
|
|
import 'package:focus_flow_flutter/app/runtime/focus_flow_file_logger.dart';
|
|
import 'package:focus_flow_flutter/app/runtime/focus_flow_runtime_config.dart';
|
|
import 'package:scheduler_core/scheduler_core.dart' as scheduler_core;
|
|
|
|
/// Runs runtime config tests.
|
|
void main() {
|
|
tearDown(scheduler_core.logger.disable);
|
|
|
|
group('FocusFlowRuntimeConfig', () {
|
|
test('missing config file leaves optional settings unset', () async {
|
|
final directory = await Directory.systemTemp.createTemp(
|
|
'focus-flow-config-test-',
|
|
);
|
|
addTearDown(() => directory.delete(recursive: true));
|
|
|
|
final config = await FocusFlowRuntimeConfig.load(
|
|
path: '${directory.path}${Platform.pathSeparator}missing.json',
|
|
);
|
|
|
|
expect(config.logLevel, isNull);
|
|
expect(config.logFileLocation, isNull);
|
|
expect(config.enablesFileLogging, isFalse);
|
|
});
|
|
|
|
test('loads supported settings from a config file', () async {
|
|
final directory = await Directory.systemTemp.createTemp(
|
|
'focus-flow-config-file-test-',
|
|
);
|
|
addTearDown(() => directory.delete(recursive: true));
|
|
final configFile = File(
|
|
'${directory.path}${Platform.pathSeparator}config.json',
|
|
);
|
|
await configFile.writeAsString(
|
|
jsonEncode({'LogLevel': 'finest', 'LogfileLocation': directory.path}),
|
|
);
|
|
|
|
final config = await FocusFlowRuntimeConfig.load(path: configFile.path);
|
|
|
|
expect(config.logLevel, FocusFlowLogLevel.finest);
|
|
expect(config.logFileLocation, directory.path);
|
|
expect(config.enablesFileLogging, isTrue);
|
|
});
|
|
|
|
test('parses every supported log level', () {
|
|
expect(FocusFlowLogLevel.tryParse('finest'), FocusFlowLogLevel.finest);
|
|
expect(FocusFlowLogLevel.tryParse('finer'), FocusFlowLogLevel.finer);
|
|
expect(FocusFlowLogLevel.tryParse('fine'), FocusFlowLogLevel.fine);
|
|
expect(FocusFlowLogLevel.tryParse('debug'), FocusFlowLogLevel.debug);
|
|
expect(FocusFlowLogLevel.tryParse('info'), FocusFlowLogLevel.info);
|
|
expect(FocusFlowLogLevel.tryParse('warn'), FocusFlowLogLevel.warn);
|
|
expect(FocusFlowLogLevel.tryParse('error'), FocusFlowLogLevel.error);
|
|
});
|
|
|
|
test('loads supported logging settings from JSON', () {
|
|
final config = FocusFlowRuntimeConfig.fromJson({
|
|
'LogLevel': 'debug',
|
|
'LogfileLocation': '/tmp/focus-flow-logs',
|
|
});
|
|
|
|
expect(config.logLevel, FocusFlowLogLevel.debug);
|
|
expect(config.logFileLocation, '/tmp/focus-flow-logs');
|
|
expect(config.enablesFileLogging, isTrue);
|
|
});
|
|
|
|
test('ignores missing and unsupported optional settings', () {
|
|
final config = FocusFlowRuntimeConfig.fromJson({
|
|
'LogLevel': 'verbose',
|
|
'LogfileLocation': ' ',
|
|
});
|
|
|
|
expect(config.logLevel, isNull);
|
|
expect(config.logFileLocation, isNull);
|
|
expect(config.enablesFileLogging, isFalse);
|
|
});
|
|
});
|
|
|
|
group('FocusFlowFileLogger', () {
|
|
test('does nothing unless both logging settings are set', () async {
|
|
final logger = await FocusFlowFileLogger.open(
|
|
const FocusFlowRuntimeConfig(logLevel: FocusFlowLogLevel.debug),
|
|
);
|
|
|
|
expect(logger.isEnabled, isFalse);
|
|
await logger.info('ignored');
|
|
});
|
|
|
|
test('writes messages at or above the configured level', () async {
|
|
final directory = await Directory.systemTemp.createTemp(
|
|
'focus-flow-log-test-',
|
|
);
|
|
addTearDown(() => directory.delete(recursive: true));
|
|
final logger = await FocusFlowFileLogger.open(
|
|
FocusFlowRuntimeConfig(
|
|
logLevel: FocusFlowLogLevel.warn,
|
|
logFileLocation: directory.path,
|
|
),
|
|
now: () => DateTime.utc(2026, 7, 3, 12),
|
|
);
|
|
|
|
await logger.info('not written');
|
|
await logger.warn('written warning');
|
|
await logger.error('written error', error: 'boom');
|
|
|
|
final logFile = File(
|
|
'${directory.path}${Platform.pathSeparator}focus_flow.log',
|
|
);
|
|
final contents = await logFile.readAsString();
|
|
expect(contents, isNot(contains('not written')));
|
|
expect(contents, contains('2026-07-03T12:00:00.000Z WARN'));
|
|
expect(contents, contains('written warning'));
|
|
expect(contents, contains('ERROR written error | error=boom'));
|
|
});
|
|
|
|
test('skips lazy message work below the configured level', () async {
|
|
final directory = await Directory.systemTemp.createTemp(
|
|
'focus-flow-lazy-skip-test-',
|
|
);
|
|
addTearDown(() => directory.delete(recursive: true));
|
|
final logger = await FocusFlowFileLogger.open(
|
|
FocusFlowRuntimeConfig(
|
|
logLevel: FocusFlowLogLevel.info,
|
|
logFileLocation: directory.path,
|
|
),
|
|
);
|
|
var evaluated = false;
|
|
|
|
await logger.debug(() {
|
|
evaluated = true;
|
|
return 'expensive debug state';
|
|
});
|
|
|
|
expect(evaluated, isFalse);
|
|
expect(
|
|
File(
|
|
'${directory.path}${Platform.pathSeparator}focus_flow.log',
|
|
).existsSync(),
|
|
isFalse,
|
|
);
|
|
});
|
|
|
|
test('evaluates lazy messages once the level is enabled', () async {
|
|
final directory = await Directory.systemTemp.createTemp(
|
|
'focus-flow-lazy-write-test-',
|
|
);
|
|
addTearDown(() => directory.delete(recursive: true));
|
|
final logger = await FocusFlowFileLogger.open(
|
|
FocusFlowRuntimeConfig(
|
|
logLevel: FocusFlowLogLevel.debug,
|
|
logFileLocation: directory.path,
|
|
),
|
|
now: () => DateTime.utc(2026, 7, 3, 12),
|
|
);
|
|
var evaluations = 0;
|
|
|
|
await logger.debug(() {
|
|
evaluations += 1;
|
|
return 'computed debug state';
|
|
});
|
|
|
|
expect(evaluations, 1);
|
|
final contents = await File(
|
|
'${directory.path}${Platform.pathSeparator}focus_flow.log',
|
|
).readAsString();
|
|
expect(contents, contains('DEBUG computed debug state'));
|
|
});
|
|
|
|
test(
|
|
'finest captures caller information for every written level',
|
|
() async {
|
|
final directory = await Directory.systemTemp.createTemp(
|
|
'focus-flow-finest-caller-test-',
|
|
);
|
|
addTearDown(() => directory.delete(recursive: true));
|
|
final logger = await FocusFlowFileLogger.open(
|
|
FocusFlowRuntimeConfig(
|
|
logLevel: FocusFlowLogLevel.finest,
|
|
logFileLocation: directory.path,
|
|
),
|
|
now: () => DateTime.utc(2026, 7, 3, 12),
|
|
);
|
|
|
|
await logger.info('high level message');
|
|
|
|
final contents = await File(
|
|
'${directory.path}${Platform.pathSeparator}focus_flow.log',
|
|
).readAsString();
|
|
expect(contents, contains('INFO caller='));
|
|
expect(contents, contains('runtime_config_test.dart'));
|
|
expect(contents, contains('high level message'));
|
|
},
|
|
);
|
|
|
|
test('fine captures caller information for warnings and errors', () async {
|
|
final directory = await Directory.systemTemp.createTemp(
|
|
'focus-flow-fine-caller-test-',
|
|
);
|
|
addTearDown(() => directory.delete(recursive: true));
|
|
final logger = await FocusFlowFileLogger.open(
|
|
FocusFlowRuntimeConfig(
|
|
logLevel: FocusFlowLogLevel.fine,
|
|
logFileLocation: directory.path,
|
|
),
|
|
now: () => DateTime.utc(2026, 7, 3, 12),
|
|
);
|
|
|
|
await logger.warn('handled warning');
|
|
|
|
final contents = await File(
|
|
'${directory.path}${Platform.pathSeparator}focus_flow.log',
|
|
).readAsString();
|
|
expect(contents, contains('WARN caller='));
|
|
expect(contents, contains('runtime_config_test.dart'));
|
|
expect(contents, contains('handled warning'));
|
|
});
|
|
|
|
test('info does not capture caller information for warnings', () async {
|
|
final directory = await Directory.systemTemp.createTemp(
|
|
'focus-flow-info-caller-test-',
|
|
);
|
|
addTearDown(() => directory.delete(recursive: true));
|
|
final logger = await FocusFlowFileLogger.open(
|
|
FocusFlowRuntimeConfig(
|
|
logLevel: FocusFlowLogLevel.info,
|
|
logFileLocation: directory.path,
|
|
),
|
|
now: () => DateTime.utc(2026, 7, 3, 12),
|
|
);
|
|
|
|
await logger.warn('minimal warning');
|
|
|
|
final contents = await File(
|
|
'${directory.path}${Platform.pathSeparator}focus_flow.log',
|
|
).readAsString();
|
|
expect(contents, contains('WARN minimal warning'));
|
|
expect(contents, isNot(contains('caller=')));
|
|
expect(contents, isNot(contains('runtime_config_test.dart')));
|
|
});
|
|
});
|
|
}
|