VHS-Karten integriert, Bildzoom ergänzt und Chat-IDs korrigiert
Unterschriftenerfassung dauerhaft auf Deutsch anzeigen, lokale VHS-Buildkonfiguration und Regressionstests ergänzen sowie Version auf 357.0.0+357 anheben.
This commit is contained in:
1 parent
c0b667c4ee
commit
5e7babf265
26 files changed
+752
-93
No files matched your search
@@ -0,0 +1,86 @@
|
||||
import 'package:flat_buffers/flat_buffers.dart' as fb;
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:objectbox/internal.dart' show EntityDefinition;
|
||||
import 'package:stadtbote/model/chat_message.dart';
|
||||
import 'package:stadtbote/objectbox.g.dart';
|
||||
|
||||
void main() {
|
||||
group('ChatMessage IDs', () {
|
||||
test('initialisiert ausgehende Nachrichten vollständig', () {
|
||||
final message = _outgoingMessage();
|
||||
|
||||
expect(message.id, 0);
|
||||
expect(message.toJson(), {
|
||||
'id': 42,
|
||||
'time': 1791366832,
|
||||
'from': 'courier-123',
|
||||
'subj': '',
|
||||
'text': 'Auftrag erledigt',
|
||||
'read': true,
|
||||
});
|
||||
});
|
||||
|
||||
test('serialisiert ausgehende Nachrichten für ObjectBox ohne Fehler', () {
|
||||
final message = _outgoingMessage();
|
||||
final definition = getObjectBoxModel().bindings[ChatMessage]!
|
||||
as EntityDefinition<ChatMessage>;
|
||||
final builder = fb.Builder();
|
||||
|
||||
expect(definition.objectToFB(message, builder), 0);
|
||||
|
||||
final buffer = fb.BufferContext.fromBytes(builder.buffer);
|
||||
final root = buffer.derefObject(0);
|
||||
const intReader = fb.Int64Reader();
|
||||
const stringReader = fb.StringReader();
|
||||
|
||||
expect(intReader.vTableGet(buffer, root, 4, -1), 0);
|
||||
expect(intReader.vTableGet(buffer, root, 6, -1), 42);
|
||||
expect(intReader.vTableGet(buffer, root, 8, -1), 1791366832);
|
||||
expect(stringReader.vTableGet(buffer, root, 10, ''), 'courier-123');
|
||||
expect(stringReader.vTableGet(buffer, root, 12, 'missing'), '');
|
||||
expect(stringReader.vTableGet(buffer, root, 14, ''), 'Auftrag erledigt');
|
||||
expect(const fb.BoolReader().vTableGet(buffer, root, 16, false), isTrue);
|
||||
});
|
||||
|
||||
test('verwendet nach lokaler ID-Vergabe weiterhin die Nachrichten-ID', () {
|
||||
final message = _outgoingMessage();
|
||||
final definition = getObjectBoxModel().bindings[ChatMessage]!
|
||||
as EntityDefinition<ChatMessage>;
|
||||
|
||||
definition.setId(message, 99);
|
||||
|
||||
expect(message.id, 99);
|
||||
expect(message.externalId, 42);
|
||||
expect(message.toJson()['id'], 42);
|
||||
});
|
||||
|
||||
test('übernimmt empfangene Server-IDs getrennt von der lokalen ID', () {
|
||||
final message = ChatMessage.fromJson({
|
||||
'id': 21891302,
|
||||
'time': 1791366832,
|
||||
'from': 'Zentrale',
|
||||
'subj': 'Rückfrage',
|
||||
'text': 'Bitte melden',
|
||||
});
|
||||
|
||||
expect(message.id, 0);
|
||||
expect(message.externalId, 21891302);
|
||||
expect(message.read, isFalse);
|
||||
expect(message.toJson(), {
|
||||
'id': 21891302,
|
||||
'time': 1791366832,
|
||||
'from': 'Zentrale',
|
||||
'subj': 'Rückfrage',
|
||||
'text': 'Bitte melden',
|
||||
'read': false,
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
ChatMessage _outgoingMessage() => ChatMessage.outgoing(
|
||||
externalId: 42,
|
||||
time: 1791366832,
|
||||
from: 'courier-123',
|
||||
text: 'Auftrag erledigt',
|
||||
);
|
||||
@@ -0,0 +1,51 @@
|
||||
import 'dart:convert';
|
||||
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:stadtbote/model/job.dart';
|
||||
|
||||
void main() {
|
||||
group('Job map_url', () {
|
||||
test('erhält die Server-URL einschließlich Query und Fragment', () {
|
||||
const mapUrl = 'https://vhs.example.invalid/map?job=21891302#courier';
|
||||
final payload = _jobPayload()..['map_url'] = mapUrl;
|
||||
|
||||
final job = Job.fromJson(payload);
|
||||
final serialized =
|
||||
jsonDecode(jsonEncode(job.toJson())) as Map<String, dynamic>;
|
||||
final restored = Job.fromJson(serialized);
|
||||
|
||||
expect(job.mapUrl, mapUrl);
|
||||
expect(serialized['map_url'], mapUrl);
|
||||
expect(restored.mapUrl, mapUrl);
|
||||
expect(restored.externalId, 21891302);
|
||||
});
|
||||
|
||||
test('liest ältere Aufträge ohne map_url weiterhin ein', () {
|
||||
final job = Job.fromJson(_jobPayload());
|
||||
final restored = Job.fromJson(job.toJson());
|
||||
|
||||
expect(job.mapUrl, isNull);
|
||||
expect(restored.mapUrl, isNull);
|
||||
expect(restored.externalId, 21891302);
|
||||
});
|
||||
|
||||
test('akzeptiert eine explizit fehlende map_url', () {
|
||||
final payload = _jobPayload()..['map_url'] = null;
|
||||
|
||||
expect(Job.fromJson(payload).mapUrl, isNull);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
Map<String, dynamic> _jobPayload() => {
|
||||
'id': 21891302,
|
||||
'ver': '0',
|
||||
'state': 0,
|
||||
'ordertime': 1791285073,
|
||||
'offs_warn': 0,
|
||||
'global': false,
|
||||
'vht_id': 2,
|
||||
'rem': '',
|
||||
'delayOrderTime': 0,
|
||||
'tours': <Map<String, dynamic>>[],
|
||||
};
|
||||
@@ -0,0 +1,82 @@
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:stadtbote/tools/vhs_map_access.dart';
|
||||
|
||||
void main() {
|
||||
group('VHS-Kartenlink', () {
|
||||
test('erlaubt HTTPS inklusive Kartenparameter und Fragment', () {
|
||||
final access = VhsMapAccess(
|
||||
Uri.parse('https://vhs.example/map?job=42#courier'),
|
||||
);
|
||||
expect(access.isValid, isTrue);
|
||||
});
|
||||
|
||||
test('weist unsichere und unvollständige Links zurück', () {
|
||||
for (final url in [
|
||||
'http://vhs.example/map',
|
||||
'/map?job=42',
|
||||
'//vhs.example/map',
|
||||
'javascript:alert(1)',
|
||||
'https:///map',
|
||||
'https://user:password@vhs.example/map',
|
||||
'https://vhs.example:0/map',
|
||||
'https://vhs.example:65536/map',
|
||||
]) {
|
||||
expect(VhsMapAccess(Uri.parse(url)).isValid, isFalse, reason: url);
|
||||
}
|
||||
});
|
||||
|
||||
test('erlaubt Navigation nur innerhalb derselben HTTPS-Origin', () {
|
||||
final access = VhsMapAccess(Uri.parse('https://vhs.example/map?job=42'));
|
||||
expect(
|
||||
access.allowsNavigation(Uri.parse('https://vhs.example:443/other')),
|
||||
isTrue,
|
||||
);
|
||||
for (final url in [
|
||||
'http://vhs.example/map',
|
||||
'https://other.example/map',
|
||||
'https://vhs.example.other.example/map',
|
||||
'https://vhs.example:8443/map',
|
||||
'https://user@vhs.example/map',
|
||||
'file:///map',
|
||||
]) {
|
||||
expect(access.allowsNavigation(Uri.parse(url)), isFalse, reason: url);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
group('VHS-Anmeldung', () {
|
||||
test('sendet Zugangsdaten nur an den exakten Kartenhost', () {
|
||||
final access = VhsMapAccess(Uri.parse('https://vhs.example/map'));
|
||||
expect(access.allowsAuthentication('VHS.EXAMPLE'), isTrue);
|
||||
for (final host in [
|
||||
'other.example',
|
||||
'vhs.example.other.example',
|
||||
'sub.vhs.example',
|
||||
'',
|
||||
]) {
|
||||
expect(access.allowsAuthentication(host), isFalse, reason: host);
|
||||
}
|
||||
expect(
|
||||
VhsMapAccess(
|
||||
Uri.parse('http://vhs.example'),
|
||||
).allowsAuthentication('vhs.example'),
|
||||
isFalse,
|
||||
);
|
||||
});
|
||||
|
||||
test('begrenzt wiederholte Authentifizierungsanfragen', () {
|
||||
final access = VhsMapAccess(Uri.parse('https://vhs.example/map'));
|
||||
expect(access.tryAuthentication('other.example'), isFalse);
|
||||
for (
|
||||
var attempt = 0;
|
||||
attempt < VhsMapAccess.maxAuthenticationAttempts;
|
||||
attempt++
|
||||
) {
|
||||
expect(access.tryAuthentication('vhs.example'), isTrue);
|
||||
}
|
||||
expect(access.tryAuthentication('vhs.example'), isFalse);
|
||||
expect(access.tryAuthentication('vhs.example'), isFalse);
|
||||
expect(VhsMapAccess(access.uri).tryAuthentication('vhs.example'), isTrue);
|
||||
});
|
||||
});
|
||||
}
|
||||
Reference in new issue
Block a user