Files
buzz/mobile/test/shared/client/client_headers_test.dart
2026-07-23 13:33:51 -07:00

194 lines
5.3 KiB
Dart

import 'package:buzz/shared/client/client_headers.dart';
import 'package:flutter_test/flutter_test.dart';
void main() {
group('buildClientHeaders', () {
test('builds canonical iOS headers and escapes structured strings', () {
final headers = buildClientHeaders(
const ClientHeaderMetadata(
platform: 'ios',
appVersion: '0.4.5',
appBuild: '6',
osVersion: '18.5"preview',
),
);
expect(
headers.buzzClient,
r'v=1, app=buzz-mobile, platform=ios, app-version="0.4.5", '
r'app-build="6", os-version="18.5\"preview"',
);
expect(headers.appVersion, '0.4.5');
expect(headers.userAgent, 'Buzz/0.4.5 (ios; build 6)');
});
test('builds canonical Android headers with the API level', () {
final headers = buildClientHeaders(
const ClientHeaderMetadata(
platform: 'android',
appVersion: '0.4.5',
appBuild: '6',
osVersion: '15',
osApi: 35,
),
);
expect(
headers.buzzClient,
'v=1, app=buzz-mobile, platform=android, '
'app-version="0.4.5", app-build="6", os-version="15", os-api=35',
);
expect(headers.userAgent, 'Buzz/0.4.5 (android; build 6)');
});
test('exposes an inert empty header value', () {
expect(ClientHeaders.empty.appVersion, isEmpty);
expect(ClientHeaders.empty.values, isEmpty);
expect(ClientHeaders.empty.userAgentValue, isEmpty);
});
test('partially initialized values fail loudly for every header view', () {
const partial = ClientHeaders(
appVersion: '1',
buzzClient: 'client',
userAgent: '',
);
expect(() => partial.values, throwsStateError);
expect(() => partial.userAgentValue, throwsStateError);
});
test('rejects app versions outside the relay metric contract', () {
for (final version in ['', '1', '1.2.3.4', '1.2-beta', '123456.2']) {
expect(
() => buildClientHeaders(
ClientHeaderMetadata(
platform: 'ios',
appVersion: version,
appBuild: '1',
osVersion: '18.0',
),
),
throwsFormatException,
reason: version,
);
}
});
test('rejects non-decimal app builds', () {
for (final build in ['', '1.beta', r'6\beta']) {
expect(
() => buildClientHeaders(
ClientHeaderMetadata(
platform: 'ios',
appVersion: '1.0',
appBuild: build,
osVersion: '18.0',
),
),
throwsFormatException,
reason: build,
);
}
});
test('rejects invalid platform-specific metadata', () {
for (final osApi in [null, 0, -1]) {
expect(
() => buildClientHeaders(
ClientHeaderMetadata(
platform: 'android',
appVersion: '1.0',
appBuild: '1',
osVersion: '15',
osApi: osApi,
),
),
throwsArgumentError,
reason: '$osApi',
);
}
expect(
() => buildClientHeaders(
const ClientHeaderMetadata(
platform: 'ios',
appVersion: '1.0',
appBuild: '1',
osVersion: '18.0',
osApi: 35,
),
),
throwsArgumentError,
);
});
});
group('first-party origin gating', () {
const headers = ClientHeaders(
appVersion: '1.0',
buzzClient: 'client',
userAgent: 'agent',
);
test('allows the configured relay across HTTP and WebSocket schemes', () {
expect(
clientHeadersForUrl(
headers: headers,
targetUrl: 'wss://relay.example:443/socket',
relayUrl: 'https://Relay.Example/base',
allowLocalDevelopment: false,
),
{'Buzz-Client': 'client', 'User-Agent': 'agent'},
);
});
test('allows hosted communities and the dedicated pairing relay', () {
for (final url in [
'wss://acme.communities.buzz.xyz',
'https://PAIRING.BUZZ.XYZ/path',
]) {
expect(
isFirstPartyBuzzUrl(url, allowLocalDevelopment: false),
isTrue,
reason: url,
);
}
});
test('sends only User-Agent to other HTTP and WebSocket servers', () {
for (final url in [
'https://cdn.cloudflare.example/attachment.png',
'wss://relay.other.example/socket',
'https://communities.buzz.xyz.evil.example/media/file',
'ws://pairing.buzz.xyz',
'wss://relay.example:444/socket',
]) {
expect(
clientHeadersForUrl(
headers: headers,
targetUrl: url,
relayUrl: 'https://relay.example',
allowLocalDevelopment: false,
),
{'User-Agent': 'agent'},
reason: url,
);
}
});
test('does not send headers to malformed or non-network URLs', () {
for (final url in ['', 'not a URL', 'file:///tmp/media.png']) {
expect(
clientHeadersForUrl(
headers: headers,
targetUrl: url,
relayUrl: 'https://relay.example',
allowLocalDevelopment: false,
),
isEmpty,
reason: url,
);
}
});
});
}