Add baseline unit tests and fix home server normalization

This commit is contained in:
Foxyz committed 2026-02-20 17:45:10 +03:00
1 parent a93d619b4c
commit ce7c97d13a
6 files changed
+227 -8

No files matched your search

+3
View File
@@ -24,6 +24,9 @@ Thumbs.db
# Conversation exports (local only) # Conversation exports (local only)
conversation_export/ conversation_export/
uploads/
objectbox/
objectbox.dll
# AI # AI
.claude .claude
+8 -4
View File
@@ -397,10 +397,14 @@ String? normalizeHomeServer(String homeServerOrHost) {
return null; return null;
} }
return uri final normalized = Uri(
.replace(path: '', query: null, fragment: null) scheme: uri.scheme,
.toString() userInfo: uri.userInfo,
.replaceAll(RegExp(r'/$'), ''); host: uri.host,
port: uri.hasPort ? uri.port : null,
).toString();
return normalized.replaceAll(RegExp(r'/$'), '');
} }
String defaultSchemeForHost(String host) { String defaultSchemeForHost(String host) {
+84 -4
View File
@@ -137,6 +137,14 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "2.0.4" version: "2.0.4"
cli_config:
dependency: transitive
description:
name: cli_config
sha256: ac20a183a07002b700f0c25e61b7ee46b23c309d76ab7b7640a028f18e4d99ec
url: "https://pub.dev"
source: hosted
version: "0.2.0"
clock: clock:
dependency: transitive dependency: transitive
description: description:
@@ -169,6 +177,14 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "3.1.2" version: "3.1.2"
coverage:
dependency: transitive
description:
name: coverage
sha256: "5da775aa218eaf2151c721b16c01c7676fbfdd99cebba2bf64e8b807a28ff94d"
url: "https://pub.dev"
source: hosted
version: "1.15.0"
crypto: crypto:
dependency: "direct main" dependency: "direct main"
description: description:
@@ -341,10 +357,10 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: matcher name: matcher
sha256: "12956d0ad8390bbcc63ca2e1469c0619946ccb52809807067a7020d57e647aa6" sha256: dc58c723c3c24bf8d3e2d3ad3f2f9d7bd9cf43ec6feaa64181775e60190153f2
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "0.12.18" version: "0.12.17"
meta: meta:
dependency: transitive dependency: transitive
description: description:
@@ -361,6 +377,14 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "2.0.0" version: "2.0.0"
node_preamble:
dependency: transitive
description:
name: node_preamble
sha256: "6e7eac89047ab8a8d26cf16127b5ed26de65209847630400f9aefd7cd5c730db"
url: "https://pub.dev"
source: hosted
version: "2.0.2"
objectbox: objectbox:
dependency: "direct main" dependency: "direct main"
description: description:
@@ -457,6 +481,22 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "1.4.2" version: "1.4.2"
shelf_packages_handler:
dependency: transitive
description:
name: shelf_packages_handler
sha256: "89f967eca29607c933ba9571d838be31d67f53f6e4ee15147d5dc2934fee1b1e"
url: "https://pub.dev"
source: hosted
version: "3.0.2"
shelf_static:
dependency: transitive
description:
name: shelf_static
sha256: c87c3875f91262785dade62d135760c2c69cb217ac759485334c5857ad89f6e3
url: "https://pub.dev"
source: hosted
version: "1.1.3"
shelf_web_socket: shelf_web_socket:
dependency: transitive dependency: transitive
description: description:
@@ -473,6 +513,22 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "1.5.0" version: "1.5.0"
source_map_stack_trace:
dependency: transitive
description:
name: source_map_stack_trace
sha256: c0713a43e323c3302c2abe2a1cc89aa057a387101ebd280371d6a6c9fa68516b
url: "https://pub.dev"
source: hosted
version: "2.1.2"
source_maps:
dependency: transitive
description:
name: source_maps
sha256: "190222579a448b03896e0ca6eca5998fa810fda630c1d65e2f78b3f638f54812"
url: "https://pub.dev"
source: hosted
version: "0.10.13"
source_span: source_span:
dependency: transitive dependency: transitive
description: description:
@@ -521,14 +577,30 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "1.2.2" version: "1.2.2"
test:
dependency: "direct dev"
description:
name: test
sha256: "75906bf273541b676716d1ca7627a17e4c4070a3a16272b7a3dc7da3b9f3f6b7"
url: "https://pub.dev"
source: hosted
version: "1.26.3"
test_api: test_api:
dependency: transitive dependency: transitive
description: description:
name: test_api name: test_api
sha256: "93167629bfc610f71560ab9312acdda4959de4df6fac7492c89ff0d3886f6636" sha256: ab2726c1a94d3176a45960b6234466ec367179b87dd74f1611adb1f3b5fb9d55
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "0.7.9" version: "0.7.7"
test_core:
dependency: transitive
description:
name: test_core
sha256: "0cc24b5ff94b38d2ae73e1eb43cc302b77964fbf67abad1e296025b78deb53d0"
url: "https://pub.dev"
source: hosted
version: "0.6.12"
timing: timing:
dependency: transitive dependency: transitive
description: description:
@@ -585,6 +657,14 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "3.0.3" version: "3.0.3"
webkit_inspection_protocol:
dependency: transitive
description:
name: webkit_inspection_protocol
sha256: "87d3f2333bb240704cd3f1c6b5b7acd8a10e7f0bc28c28dcf14e782014f4a572"
url: "https://pub.dev"
source: hosted
version: "1.2.1"
yaml: yaml:
dependency: transitive dependency: transitive
description: description:
+1
View File
@@ -16,6 +16,7 @@ dependencies:
hotreloader: hotreloader:
dev_dependencies: dev_dependencies:
test:
objectbox_generator: objectbox_generator:
build_runner: build_runner:
lints: lints:
+89
View File
@@ -0,0 +1,89 @@
import 'package:chat_server/src/handlers/auth_handlers.dart';
import 'package:test/test.dart';
void main() {
group('parseUserId', () {
test('parses valid userId', () {
final parsed = parseUserId('alice@example.com');
expect(parsed, isNotNull);
expect(parsed!.username, 'alice');
expect(parsed.host, 'example.com');
});
test('returns null for invalid userId', () {
expect(parseUserId('alice'), isNull);
expect(parseUserId('alice@'), isNull);
expect(parseUserId('@example.com'), isNull);
expect(parseUserId('alice@example.com@x'), isNull);
});
});
group('defaultSchemeForHost', () {
test('returns http for localhost-style hosts', () {
expect(defaultSchemeForHost('localhost:9870'), 'http');
expect(defaultSchemeForHost('127.0.0.1:9870'), 'http');
expect(defaultSchemeForHost('[::1]:9870'), 'http');
});
test('returns https for non-local hosts', () {
expect(defaultSchemeForHost('chat.example.com'), 'https');
});
});
group('normalizeHomeServer', () {
test('normalizes host without scheme', () {
expect(
normalizeHomeServer('chat.example.com'),
'https://chat.example.com',
);
expect(
normalizeHomeServer('localhost:9870'),
'http://localhost:9870',
);
});
test('strips trailing slash/path/query/fragment', () {
expect(
normalizeHomeServer('https://example.com/a/b?x=1#y'),
'https://example.com',
);
expect(
normalizeHomeServer('https://example.com/'),
'https://example.com',
);
});
test('returns null for invalid input', () {
expect(normalizeHomeServer(''), isNull);
expect(normalizeHomeServer('://bad'), isNull);
});
});
group('homeServerMatchesUserIdHost', () {
test('matches host and explicit port', () {
expect(
homeServerMatchesUserIdHost(
'https://chat.example.com:8443', 'chat.example.com:8443'),
isTrue,
);
expect(
homeServerMatchesUserIdHost(
'https://chat.example.com:8443', 'chat.example.com'),
isFalse,
);
});
test('matches default ports when omitted', () {
expect(
homeServerMatchesUserIdHost(
'https://chat.example.com', 'chat.example.com:443'),
isTrue,
);
expect(
homeServerMatchesUserIdHost(
'http://chat.example.com', 'chat.example.com:80'),
isTrue,
);
});
});
}
+42
View File
@@ -0,0 +1,42 @@
import 'package:chat_server/src/auth/crypto_service.dart';
import 'package:test/test.dart';
void main() {
group('AuthService password hashing', () {
test('hash + verify works', () {
const password = 'pass1234';
final hash = AuthService.hashPassword(password);
expect(hash, isNotEmpty);
expect(AuthService.verifyPassword(password, hash), isTrue);
expect(AuthService.verifyPassword('wrong-password', hash), isFalse);
});
});
group('CryptoService signing', () {
test('signatures verify with matching keypair', () {
final keyPair = CryptoService.generateKeyPair();
const payload = 'alice@localhost:9870:1700000000';
final signature = CryptoService.sign(payload, keyPair.privateKey);
final ok = CryptoService.verify(payload, signature, keyPair.publicKey);
expect(ok, isTrue);
});
test('signature fails for modified payload', () {
final keyPair = CryptoService.generateKeyPair();
const payload = 'payload-a';
const tamperedPayload = 'payload-b';
final signature = CryptoService.sign(payload, keyPair.privateKey);
final ok = CryptoService.verify(
tamperedPayload,
signature,
keyPair.publicKey,
);
expect(ok, isFalse);
});
});
}