blob: 5b017ec15a8a6ca9dc29b4a8f378bedbda0355cd [file] [log] [blame]
// Copyright 2019 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import 'package:fixnum/fixnum.dart';
import 'package:flutter/foundation.dart' show kIsWeb;
import 'package:flutter_dashboard/logic/qualified_task.dart';
import 'package:flutter_dashboard/model/commit.pb.dart';
import 'package:flutter_dashboard/model/key.pb.dart';
import 'package:flutter_dashboard/model/task.pb.dart';
import 'package:flutter_dashboard/service/appengine_cocoon.dart';
import 'package:flutter_dashboard/src/rpc_model.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:http/http.dart' show Request, Response;
import 'package:http/testing.dart';
import '../utils/appengine_cocoon_test_data.dart';
void main() {
group('AppEngine CocoonService fetchCommitStatus', () {
late AppEngineCocoonService service;
setUp(() async {
service = AppEngineCocoonService(
client: MockClient((Request request) async {
return Response(luciJsonGetStatsResponse, 200);
}),
);
});
test('should return expected List<CommitStatus>', () async {
final statuses = await service.fetchCommitStatuses(repo: 'flutter');
final expectedStatus = CommitStatus(
branch: 'master',
commit:
Commit()
..timestamp = Int64(123456789)
..key = (RootKey()..child = (Key()..name = 'iamatestkey'))
..sha = 'ShaShankHash'
..author = 'ShaSha'
..authorAvatarUrl = 'https://flutter.dev'
..repository = 'flutter/cocoon'
..branch = 'master',
tasks: [
Task()
..key = (RootKey()..child = (Key()..name = 'taskKey1'))
..createTimestamp = Int64(1569353940885)
..startTimestamp = Int64(1569354594672)
..endTimestamp = Int64(1569354700642)
..name = 'linux'
..attempts = 1
..isFlaky = false
..timeoutInMinutes = 0
..reason = ''
..requiredCapabilities.add('[linux]')
..reservedForAgentId = ''
..stageName = 'chromebot'
..status = 'Succeeded'
..isTestFlaky = false
..buildNumberList = '123'
..builderName = 'Linux'
..luciBucket = 'luci.flutter.try',
],
);
expect(statuses.data!.length, 1);
expect(statuses.data!.first, expectedStatus);
});
test('should have error if given non-200 response', () async {
service = AppEngineCocoonService(
client: MockClient((Request request) async => Response('', 404)),
);
final response = await service.fetchCommitStatuses(repo: 'flutter');
expect(response.error, isNotNull);
});
test('should have error if given bad response', () async {
service = AppEngineCocoonService(
client: MockClient((Request request) async => Response('bad', 200)),
);
final response = await service.fetchCommitStatuses(repo: 'flutter');
expect(response.error, isNotNull);
});
});
group('AppEngine CocoonService fetchTreeBuildStatus', () {
late AppEngineCocoonService service;
setUp(() async {
service = AppEngineCocoonService(
client: MockClient((Request request) async {
return Response(jsonBuildStatusTrueResponse, 200);
}),
);
});
test('data should be true when given Succeeded', () async {
final treeBuildStatus = await service.fetchTreeBuildStatus(
repo: 'flutter',
);
expect(treeBuildStatus.data!.buildStatus, BuildStatus.success);
});
test('data should be false when given Failed', () async {
service = AppEngineCocoonService(
client: MockClient((Request request) async {
return Response(jsonBuildStatusFalseResponse, 200);
}),
);
final treeBuildStatus = await service.fetchTreeBuildStatus(
repo: 'flutter',
);
expect(treeBuildStatus.data!.buildStatus, BuildStatus.failure);
});
test('should have error if given non-200 response', () async {
service = AppEngineCocoonService(
client: MockClient((Request request) async => Response('', 404)),
);
final response = await service.fetchTreeBuildStatus(repo: 'flutter');
expect(response.error, isNotNull);
});
test('should have error if given bad response', () async {
service = AppEngineCocoonService(
client: MockClient((Request request) async => Response('bad', 200)),
);
final response = await service.fetchTreeBuildStatus(repo: 'flutter');
expect(response.error, isNotNull);
});
});
group('AppEngine CocoonService rerun task', () {
late AppEngineCocoonService service;
late Task task;
setUp(() {
service = AppEngineCocoonService(
client: MockClient((Request request) async {
return Response('', 200);
}),
);
task =
Task()
..key = RootKey()
..stageName = StageName.luci;
});
test('should return true if request succeeds', () async {
final response = await service.rerunTask(
taskName: task.name,
idToken: 'fakeAccessToken',
repo: 'flutter',
commitSha: 'abc123',
branch: 'master',
);
expect(response.error, isNull);
});
test('should set error in response if ID token is null', () async {
final response = await service.rerunTask(
taskName: task.name,
idToken: null,
repo: 'flutter',
commitSha: 'abc123',
branch: 'master',
);
expect(
response.error,
allOf(<Matcher>[isNotNull, contains('Sign in to trigger reruns')]),
);
});
test('should treat rerunCommit as task=all', () async {
service = AppEngineCocoonService(
client: MockClient((request) async {
return Response('${request.url.toString()}|${request.body}', 500);
}),
);
final response = await service.rerunCommit(
idToken: 'fakeAccessToken',
repo: 'flutter',
commitSha: 'abc123',
branch: 'master',
);
expect(
response.error,
endsWith(
'api/rerun-prod-task|{"branch":"master","repo":"flutter","commit":"abc123","task":"all"}',
),
);
});
test('should include statuses with rerunCommit', () async {
service = AppEngineCocoonService(
client: MockClient((request) async {
return Response('${request.url.toString()}|${request.body}', 500);
}),
);
final response = await service.rerunCommit(
idToken: 'fakeAccessToken',
repo: 'flutter',
commitSha: 'abc123',
branch: 'master',
include: ['Foolish', 'Dashing'],
);
expect(
response.error,
endsWith(
'api/rerun-prod-task|{"branch":"master","repo":"flutter","commit":"abc123","task":"all","include":"Foolish,Dashing"}',
),
);
});
test(
'should set error in response if bad status code is returned',
() async {
service = AppEngineCocoonService(
client: MockClient((Request request) async {
return Response('internal server error', 500);
}),
);
final response = await service.rerunTask(
taskName: task.name,
idToken: 'fakeAccessToken',
repo: 'flutter',
commitSha: 'abc123',
branch: 'master',
);
expect(
response.error,
allOf(<Matcher>[
isNotNull,
contains('HTTP Code: 500, internal server error'),
]),
);
},
);
});
group('AppEngine CocoonService refresh github commits', () {
late AppEngineCocoonService service;
setUp(() {
service = AppEngineCocoonService(
client: MockClient((Request request) async {
return Response('', 200);
}),
);
});
test('should return true if request succeeds', () async {
expect(await service.vacuumGitHubCommits('fakeIdToken'), true);
});
test('should return false if request failed', () async {
service = AppEngineCocoonService(
client: MockClient((Request request) async {
return Response('', 500);
}),
);
expect(await service.vacuumGitHubCommits('fakeIdToken'), false);
});
});
group('AppEngine CocoonService fetchFlutterBranches', () {
late AppEngineCocoonService service;
setUp(() async {
service = AppEngineCocoonService(
client: MockClient((Request request) async {
return Response(jsonGetBranchesResponse, 200);
}),
);
});
test('data should be expected list of branches', () async {
final branches = await service.fetchFlutterBranches();
expect(branches.error, isNull);
expect(
branches.data,
unorderedEquals([
Branch(channel: 'stable', reference: 'flutter-3.13-candidate.0'),
Branch(channel: 'beta', reference: 'flutter-3.14-candidate.0'),
Branch(channel: 'dev', reference: 'flutter-3.15-candidate.5'),
Branch(channel: 'master', reference: 'master'),
]),
);
});
test('should have error if given non-200 response', () async {
service = AppEngineCocoonService(
client: MockClient((Request request) async => Response('', 404)),
);
final response = await service.fetchFlutterBranches();
expect(response.error, isNotNull);
});
test('should have error if given bad response', () async {
service = AppEngineCocoonService(
client: MockClient((Request request) async => Response('bad', 200)),
);
final response = await service.fetchFlutterBranches();
expect(response.error, isNotNull);
});
});
group('AppEngine CocoonService fetchRepos', () {
late AppEngineCocoonService service;
setUp(() async {
service = AppEngineCocoonService(
client: MockClient((Request request) async {
return Response(jsonGetReposResponse, 200);
}),
);
});
test('data should be expected list of branches', () async {
final repos = await service.fetchRepos();
expect(repos.data, <String>['flutter', 'cocoon', 'packages']);
});
test('should have error if given non-200 response', () async {
service = AppEngineCocoonService(
client: MockClient((Request request) async => Response('', 404)),
);
final response = await service.fetchRepos();
expect(response.error, isNotNull);
});
test('should have error if given bad response', () async {
service = AppEngineCocoonService(
client: MockClient((Request request) async => Response('bad', 200)),
);
final response = await service.fetchRepos();
expect(response.error, isNotNull);
});
});
group('AppEngine CocoonService apiEndpoint', () {
final service = AppEngineCocoonService(
client: MockClient((Request request) async {
return Response('{"Token": "abc123"}', 200);
}),
);
test('handles url suffix', () {
expect(service.apiEndpoint('/test').toString(), '$baseApiUrl/test');
});
test('single query parameter', () {
expect(
service
.apiEndpoint(
'/test',
queryParameters: <String, String>{'key': 'value'},
)
.toString(),
'$baseApiUrl/test?key=value',
);
});
test('multiple query parameters', () {
expect(
service
.apiEndpoint(
'/test',
queryParameters: <String, String>{
'key': 'value',
'another': 'test',
},
)
.toString(),
'$baseApiUrl/test?key=value&another=test',
);
});
test('query parameter with null value', () {
expect(
service
.apiEndpoint(
'/test',
queryParameters: <String, String?>{'key': null},
)
.toString(),
'$baseApiUrl/test?key',
);
});
/// This test requires runs on different platforms.
test('should query correct endpoint whether web or mobile', () {
final uri =
service
.apiEndpoint(
'/test',
queryParameters: <String, String?>{'key': null},
)
.toString();
if (kIsWeb) {
expect(uri, '/test?key');
} else {
expect(uri, '$baseApiUrl/test?key');
}
});
});
}