Add baseline unit tests and fix home server normalization
This commit is contained in:
1 parent
a93d619b4c
commit
ce7c97d13a
6 files changed
+227
-8
No files matched your search
@@ -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
|
||||||
@@ -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
@@ -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:
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ dependencies:
|
|||||||
hotreloader:
|
hotreloader:
|
||||||
|
|
||||||
dev_dependencies:
|
dev_dependencies:
|
||||||
|
test:
|
||||||
objectbox_generator:
|
objectbox_generator:
|
||||||
build_runner:
|
build_runner:
|
||||||
lints:
|
lints:
|
||||||
@@ -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,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
@@ -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);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
Reference in new issue
Block a user