frosty/test/apis/twitch_gql_follow_test.dart
copilot 3fb393166f Fix tag directory sorting and follow Client-Integrity.
Paginate tag (and ASC) lists via GQL with integrity retry; mint follow
integrity from a WebView because Dio /integrity tokens are bad_bot.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-10-07 07:32:16 +02:00

102 lines
2.6 KiB
Dart

import 'dart:convert';
import 'package:dio/dio.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:frosty/apis/base_api_client.dart';
import 'package:frosty/apis/twitch_gql_api.dart';
import 'package:http_mock_adapter/http_mock_adapter.dart';
void main() {
late Dio dio;
late DioAdapter dioAdapter;
late TwitchGqlApi api;
setUp(() {
dio = Dio(BaseOptions());
dioAdapter = DioAdapter(dio: dio);
api = TwitchGqlApi(dio);
});
group('followUser', () {
test('posts FollowButton_FollowUser persisted mutation', () async {
dioAdapter.onPost(
'https://gql.twitch.tv/gql',
(server) => server.reply(200, {
'data': {
'followUser': {
'follow': {
'user': {'id': '999', 'displayName': 'Streamer'},
},
},
},
}),
data: Matchers.any,
);
await api.followUser(targetId: '999', authToken: 'web-token');
});
test('throws IntegrityCheckException on integrity errors', () async {
dioAdapter.onPost(
'https://gql.twitch.tv/gql',
(server) => server.reply(200, {
'errors': [
{'message': 'failed integrity check'},
],
}),
data: Matchers.any,
);
await expectLater(
api.followUser(targetId: '999', authToken: 'web-token'),
throwsA(isA<IntegrityCheckException>()),
);
});
});
group('unfollowUser', () {
test('posts FollowButton_UnfollowUser persisted mutation', () async {
dioAdapter.onPost(
'https://gql.twitch.tv/gql',
(server) => server.reply(200, {
'data': {
'unfollowUser': {
'follow': null,
},
},
}),
data: Matchers.any,
);
await api.unfollowUser(targetId: '999', authToken: 'web-token');
});
});
group('fetchIntegrityToken', () {
test('rejects bad_bot tokens', () async {
final payload = base64Url.encode(
utf8.encode(
jsonEncode({
'is_bad_bot': 'true',
'device_id': 'dev',
'client_id': 'kimne78kx3ncx6brgo4mv6wki5h1ko',
}),
),
);
dioAdapter.onPost(
'https://gql.twitch.tv/integrity',
(server) => server.reply(200, {
'token': 'v4.public.$payload.sig',
'expiration': DateTime.now().millisecondsSinceEpoch + 600000,
}),
data: Matchers.any,
);
await expectLater(
api.fetchIntegrityToken(authToken: 'web', deviceId: 'dev'),
throwsA(isA<IntegrityCheckException>()),
);
});
});
}