feat(audit): free-text search, JSONL export of the view, labeled dev reset
Some checks failed
Security / Security check (push) Failing after 1s

- search field over flow/step/module/error/detail/project/id backs
  the list and the export ('current view' semantics); match logic
  is a top-level function with unit tests
- export writes one JSON object per line via the save dialog; the
  CLI stays the canonical WORM-grade export
- the bare trash icon on the audit toolbar read as 'delete
  evidence' (security-auditor finding) — the dev-only reset now
  sits in a labeled overflow menu next to the export action

Signed-off-by: flemming-it <stefan.a.flemming@googlemail.com>
This commit is contained in:
flemming-it 2026-07-18 00:08:51 +02:00
parent 0e53572589
commit 5aa69104e7
7 changed files with 273 additions and 7 deletions

View file

@ -0,0 +1,49 @@
// Free-text audit search the match function behind the audit
// page's search field and JSONL export ("current view" semantics).
import 'package:flutter_test/flutter_test.dart';
import 'package:chain_studio/data/hub.dart';
import 'package:chain_studio/pages/audit.dart';
AuditEvent _event({
String type = 'step.completed',
String? flow,
String? step,
String? module,
String? error,
String? detail,
String project = '',
}) {
return AuditEvent(
eventId: 'evt-1',
timestamp: DateTime.utc(2026, 7, 18),
type: type,
flowName: flow,
stepId: step,
moduleName: module,
error: error,
detail: detail,
project: project,
);
}
void main() {
test('matches on every displayed field, case-insensitive', () {
expect(matchesAuditQuery(_event(flow: 'Rechnungslauf'), 'rechnung'), isTrue);
expect(matchesAuditQuery(_event(step: 'extract'), 'extract'), isTrue);
expect(matchesAuditQuery(_event(module: 'debug.echo'), 'echo'), isTrue);
expect(matchesAuditQuery(_event(error: 'Timeout nach 30s'), 'timeout'),
isTrue);
expect(matchesAuditQuery(_event(detail: '{"anchor":"tsa"}'), 'anchor'),
isTrue);
expect(matchesAuditQuery(_event(project: 'stromnetz'), 'strom'), isTrue);
expect(matchesAuditQuery(_event(), 'evt-1'), isTrue);
expect(matchesAuditQuery(_event(), 'step.'), isTrue);
});
test('rejects non-matching queries and null fields', () {
expect(matchesAuditQuery(_event(), 'nichtvorhanden'), isFalse);
expect(matchesAuditQuery(_event(flow: null, error: null), 'flow'), isFalse);
});
}