// Copyright (c) 2019, the Dart project authors.  Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.

@TestOn('vm')
@Timeout(Duration(minutes: 2))
library;

import 'dart:io';

import 'package:dwds/dwds.dart';
import 'package:test/test.dart';
import 'package:test_common/test_sdk_configuration.dart';
import 'package:vm_service/vm_service.dart';
import 'package:vm_service/vm_service_io.dart';

import 'fixtures/context.dart';
import 'fixtures/project.dart';
import 'fixtures/utilities.dart';

void main() {
  final provider = TestSdkConfigurationProvider();
  tearDownAll(provider.dispose);

  final context = TestContext(TestProject.test, provider);

  setUpAll(() async {
    // Disable DDS as we're testing DWDS behavior.
    await context.setUp(
      debugSettings: TestDebugSettings.noDevToolsLaunch().copyWith(
        spawnDds: false,
        ddsConfiguration: DartDevelopmentServiceConfiguration(enable: false),
      ),
    );
  });

  tearDownAll(() async {
    await context.tearDown();
  });

  test('Refuses connections without the auth token', () async {
    expect(
      vmServiceConnectUri('ws://localhost:${context.debugConnection.port}/ws'),
      throwsA(isA<WebSocketException>()),
    );
  });

  test('Accepts connections with the auth token', () async {
    expect(
      vmServiceConnectUri(
        '${context.debugConnection.uri}/ws',
      ).then((client) => client.dispose()),
      completes,
    );
  });

  test('Refuses additional connections when in single client mode', () async {
    final fakeDds = await vmServiceConnectUri(
      '${context.debugConnection.uri}/ws',
    );
    final result = await fakeDds.callMethod(
      '_yieldControlToDDS',
      args: {'uri': 'http://localhost:123'},
    );
    expect(result, isA<Success>());

    // While DDS is connected, expect additional connections to fail.
    await expectLater(
      vmServiceConnectUri('${context.debugConnection.uri}/ws'),
      throwsA(isA<WebSocketException>()),
    );

    // However, once DDS is disconnected, additional clients can connect again.
    await fakeDds.dispose();
    expect(
      vmServiceConnectUri(
        '${context.debugConnection.uri}/ws',
      ).then((client) => client.dispose()),
      completes,
    );
  });

  test('Refuses to yield to dwds if existing clients found', () async {
    final fakeDds = await vmServiceConnectUri(
      '${context.debugConnection.uri}/ws',
    );

    // Connect to vm service.
    final client = await vmServiceConnectUri(
      '${context.debugConnection.uri}/ws',
    );

    final result = await fakeDds.callMethod(
      '_yieldControlToDDS',
      args: {'uri': 'http://localhost:123'},
    );
    expect(result, isA<Success>());

    // The other VM service client should be closed automatically.
    await client.onDone;
    await fakeDds.dispose();
  });
}
