mirror of
https://github.com/block/buzz.git
synced 2026-08-18 06:50:31 +02:00
Co-authored-by: npub1ux8n2yfs8qfvgd75s7kyhar2mztac355v6vmrz4juc9l3msw4pgstums9e <e18f3511303812c437d487ac4bf46ad897dc46946699b18ab2e60bf8ee0ea851@buzz.block.builderlab.xyz> Signed-off-by: npub1ux8n2yfs8qfvgd75s7kyhar2mztac355v6vmrz4juc9l3msw4pgstums9e <e18f3511303812c437d487ac4bf46ad897dc46946699b18ab2e60bf8ee0ea851@buzz.block.builderlab.xyz>
194 lines
5.3 KiB
Dart
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,
|
|
);
|
|
}
|
|
});
|
|
});
|
|
}
|