@@ -2,6 +2,7 @@ import 'dart:convert';
22import 'dart:typed_data' ;
33
44import 'package:code_scout/code_scout.dart' ;
5+ import 'package:code_scout/src/csx_interface/log_buffer.dart' ;
56import 'package:code_scout/src/db/db_dispatcher.dart' ;
67import 'package:flutter_test/flutter_test.dart' ;
78import 'package:sqflite_common_ffi/sqflite_ffi.dart' ;
@@ -82,6 +83,14 @@ void main() {
8283 group ('rows' , () {
8384 setUp (() => DatabaseRegistry .i.register ('shop.db' , CodeScoutSqflite (db)));
8485
86+ test ('rows carries writable from the registration, not from the caller' , () async {
87+ // The dashboard draws edit affordances off this. It must come from what
88+ // the app agreed to, never from anything the browser said about itself.
89+ final reply = await DatabaseDispatcher .handle (
90+ 'rows' , {'db' : 'shop.db' , 'namespace' : 'flags' });
91+ expect (reply['writable' ], isFalse);
92+ });
93+
8594 test ('a page comes back with columns, rows and handles' , () async {
8695 final reply = await DatabaseDispatcher .handle (
8796 'rows' , {'db' : 'shop.db' , 'namespace' : 'flags' });
@@ -148,6 +157,47 @@ void main() {
148157 expect ((await db.rawQuery ('SELECT enabled FROM flags' )).first['enabled' ], 0 );
149158 });
150159
160+ test ('a successful write is logged on the device' , () async {
161+ // A read leaves no trace and should not. A write changed the device, so
162+ // it goes through the ordinary pipeline and lands in the session
163+ // timeline — the answer to "why is this device's data odd" weeks later.
164+ DatabaseRegistry .i.register ('shop.db' , CodeScoutSqflite (db), writable: true );
165+ LogBuffer .i.clear ();
166+
167+ await DatabaseDispatcher .handle ('update' , {
168+ 'db' : 'shop.db' ,
169+ 'namespace' : 'flags' ,
170+ 'column' : 'enabled' ,
171+ 'handle' : await rowid (),
172+ 'value' : '1' ,
173+ 'was' : 0 ,
174+ 'by' : 'ada@example.com' ,
175+ });
176+
177+ final audit = LogBuffer .i.entries.where ((e) => e.level == LogLevel .system);
178+ expect (audit, hasLength (1 ), reason: 'the write left no trace' );
179+ expect (audit.single.message, contains ('flags.enabled' ));
180+ expect (audit.single.message, contains ('ada@example.com' ));
181+ });
182+
183+ test ('a refused write is not logged as a change' , () async {
184+ // Nothing changed, so nothing happened worth recording. An audit line
185+ // for a refusal would read as "the dashboard edited this" when it did not.
186+ DatabaseRegistry .i.register ('shop.db' , CodeScoutSqflite (db));
187+ LogBuffer .i.clear ();
188+
189+ await DatabaseDispatcher .handle ('update' , {
190+ 'db' : 'shop.db' ,
191+ 'namespace' : 'flags' ,
192+ 'column' : 'enabled' ,
193+ 'handle' : await rowid (),
194+ 'value' : '1' ,
195+ 'was' : 0 ,
196+ });
197+
198+ expect (LogBuffer .i.entries.where ((e) => e.level == LogLevel .system), isEmpty);
199+ });
200+
151201 test ('a conflict carries the current value back' , () async {
152202 DatabaseRegistry .i.register ('shop.db' , CodeScoutSqflite (db), writable: true );
153203 await db.rawUpdate ('UPDATE flags SET enabled = 9' );
0 commit comments