Files
plezy/test/utils/app_logger_test.dart
T

98 lines
3.6 KiB
Dart

import 'package:flutter_test/flutter_test.dart';
import 'package:logger/logger.dart';
import 'package:plezy/utils/app_logger.dart';
import 'package:plezy/utils/log_redaction_manager.dart';
void main() {
late MemoryAwareLogPrinter printer;
setUp(() {
MemoryLogOutput.clearLogs();
LogRedactionManager.clearTrackedValues();
printer = MemoryAwareLogPrinter(SimplePrinter());
});
tearDown(() {
MemoryLogOutput.clearLogs();
LogRedactionManager.clearTrackedValues();
});
test('redacts message, error, and stack trace before storage and rendering', () {
const messageSecret = 'message.value-_/+~==';
const errorSecret = 'error.value-_/+~==';
const stackSecret = 'stack.value-_/+~==';
const passwordSecret = 'password.value-_/+~==';
final stackTrace = StackTrace.fromString(
'#0 connect (Authorization: Bearer $stackSecret)\n'
'#1 retry (package:plezy/connect.dart:12:4)',
);
final renderedLines = printer.log(
LogEvent(
Level.error,
'connect operation Authorization: Bearer $messageSecret status=pending',
error:
'request failed Authorization=Basic $errorSecret '
'{"password":"$passwordSecret","status":401}',
stackTrace: stackTrace,
),
);
final rendered = renderedLines.join('\n');
final stored = MemoryLogOutput.getLogs().single;
final storedText = '${stored.message}\n${stored.error}\n${stored.stackTrace}';
for (final secret in [messageSecret, errorSecret, passwordSecret]) {
expect(rendered, isNot(contains(secret)));
}
for (final secret in [messageSecret, errorSecret, passwordSecret, stackSecret]) {
expect(storedText, isNot(contains(secret)));
}
expect(rendered, contains('connect operation'));
expect(rendered, contains('status=pending'));
expect(rendered, contains('request failed'));
expect(rendered, contains('"status":401'));
expect(storedText, contains('connect operation'));
expect(storedText, contains('"status":401'));
expect(storedText, contains('#1 retry'));
expect(storedText, contains('#0 connect'));
});
test('applies registered literal redaction to every logger field', () {
const registeredToken = 'registered-token-sentinel';
const registeredError = 'registered-error-sentinel';
const registeredStack = 'registered-stack-sentinel';
LogRedactionManager.registerToken(registeredToken);
LogRedactionManager.registerCustomValue(registeredError);
LogRedactionManager.registerCustomValue(registeredStack);
final rendered = MemoryAwareLogPrinter(_FieldRenderingPrinter())
.log(
LogEvent(
Level.warning,
'operation=refresh credential=$registeredToken status=starting',
error: 'category=remote detail=$registeredError status=failed',
stackTrace: StackTrace.fromString('#0 refresh $registeredStack\n#1 caller preserved'),
),
)
.join('\n');
final stored = MemoryLogOutput.getLogs().single;
final storedText = '${stored.message}\n${stored.error}\n${stored.stackTrace}';
for (final secret in [registeredToken, registeredError, registeredStack]) {
expect(rendered, isNot(contains(secret)));
expect(storedText, isNot(contains(secret)));
}
expect(rendered, contains('operation=refresh'));
expect(rendered, contains('status=failed'));
expect(rendered, contains('#1 caller preserved'));
});
}
class _FieldRenderingPrinter extends LogPrinter {
@override
List<String> log(LogEvent event) {
return ['${event.message}\n${event.error}\n${event.stackTrace}'];
}
}