arblant
firefox-vpn-client / app / test / ports_test.dart
ports_test.dart113 lines · 3.9 KB
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113
import 'dart:convert';
import 'dart:io';

import 'package:flutter_test/flutter_test.dart';
import 'package:firefox_vpn/core/core_api.dart';
import 'package:firefox_vpn/core/core_process.dart';
import 'package:firefox_vpn/core/ports.dart';

void main() {
  test('reads and replaces ports', () {
    expect(portOf('127.0.0.1:10810'), 10810);
    expect(portOf('[::1]:10820'), 10820);
    expect(portOf('localhost'), isNull);
    expect(withPort('127.0.0.1:10810', 20000), '127.0.0.1:20000');
    expect(withPort('0.0.0.0:10810', 20000), '0.0.0.0:20000');
    expect(withPort('', 20000), '127.0.0.1:20000');
    expect(validPort(80), isFalse);
    expect(validPort(10810), isTrue);
    expect(validPort(70000), isFalse);
  });

  test('suggests a free port after a taken one', () async {
    final taken = await ServerSocket.bind(InternetAddress.loopbackIPv4, 0);
    addTearDown(taken.close);
    expect(await isFree(taken.port), isFalse);
    final port = await findFreePort(taken.port - 1);
    expect(port, isNot(taken.port));
    expect(await isFree(port), isTrue);
  });

  group('config.json', () {
    late Directory dir;

    setUp(() => dir = Directory.systemTemp.createTempSync('ports_'));
    tearDown(() => dir.deleteSync(recursive: true));

    test('moves the API and keeps every other field', () async {
      final file = File('${dir.path}/config.json')
        ..writeAsStringSync(
          jsonEncode({
            'proxy': {'listen': '127.0.0.1:10810'},
            'dashboard': {'listen': '127.0.0.1:10820', 'enabled': true},
          }),
        );
      await CoreLocator.setDashboardListen(dir.path, '127.0.0.1:10821');
      final config = jsonDecode(file.readAsStringSync());
      expect(config['dashboard'], {
        'listen': '127.0.0.1:10821',
        'enabled': true,
      });
      expect(config['proxy'], {'listen': '127.0.0.1:10810'});
      expect(await CoreLocator.dashboardListen(dir.path), '127.0.0.1:10821');
    });

    test('creates a config when there is none', () async {
      await CoreLocator.setDashboardListen(dir.path, '127.0.0.1:10830');
      expect(await CoreLocator.dashboardListen(dir.path), '127.0.0.1:10830');
    });

    test(
      'another program on the API port moves the API, the proxy stays',
      () async {
        // Something that is not the core answers at the API's address.
        final other = await HttpServer.bind(InternetAddress.loopbackIPv4, 0);
        addTearDown(() => other.close(force: true));
        other.listen((req) {
          req.response.statusCode = 404;
          req.response.close();
        });
        File('${dir.path}/config.json').writeAsStringSync(
          jsonEncode({
            'proxy': {'listen': '127.0.0.1:10810'},
            'dashboard': {'listen': '127.0.0.1:${other.port}'},
          }),
        );
        File('${dir.path}/local-api.token').writeAsStringSync('token');
        final process = CoreProcess(
          executable: '${dir.path}/no-core.exe',
          dataDir: dir.path,
        );

        await expectLater(
          process.connect(),
          throwsA(
            isA<CoreUnavailable>().having(
              (e) => e.problem,
              'problem',
              CoreProblem.starting,
            ),
          ),
        );
        final moved = portOf(await CoreLocator.dashboardListen(dir.path))!;
        expect(moved, greaterThan(other.port));
        expect(await isFree(moved), isTrue);
        final config = jsonDecode(
          File('${dir.path}/config.json').readAsStringSync(),
        );
        expect(config['proxy'], {'listen': '127.0.0.1:10810'});
        // The new port is free: the core is started there (none here).
        await expectLater(
          process.connect(),
          throwsA(
            isA<CoreUnavailable>().having(
              (e) => e.problem,
              'problem',
              CoreProblem.noCore,
            ),
          ),
        );
      },
    );
  });
}